--- description: Critical rules for avoiding out-of-memory issues in tests, specifically preventing nested require() calls that cause pipeline failures version: 1.0.0 status: active owners: - team: mobile-identity - team: platform-infrastructure lastUpdated: 2025-01-12 specId: test-memory-optimization importanceScore: 100 importanceJustification: Prevents catastrophic pipeline failures due to out-of-memory errors caused by nested require() calls, especially with react-native modules in test environments. contextUsageNote: If this file is used to add in-context notes, include a single italicized line stating what specific information was used from this file in sentence case. --- # Test Memory Optimization Rules ## Critical: Never Nest require() Calls ### The Problem Nested `require('react-native')` calls within tests cause **out-of-memory (OOM) errors** in CI/CD pipelines. This happens because: 1. **Module Resolution Loops**: Each nested require can trigger additional module resolution and initialization 2. **Memory Accumulation**: React Native modules are large and complex; nested requires multiply memory usage 3. **Test Environment Overhead**: Jest/Vitest test runners already load modules; nested requires create duplicate module instances 4. **Hermes Parser Issues**: Nested requires can trigger WASM memory issues with hermes-parser ### The Rule **NEVER create nested `require('react-native')` calls within test files or test setup files.** ### Examples of FORBIDDEN Patterns #### ❌ FORBIDDEN: Nested require in test files ```typescript // BAD - This will cause OOM issues describe('MyComponent', () => { beforeEach(() => { const RN = require('react-native'); const Component = require('./MyComponent'); // Component internally does: require('react-native') again // This creates nested requires = OOM }); }); ``` #### ❌ FORBIDDEN: require() inside module that's required in tests ```typescript // BAD - If this module is required in tests, it creates nested requires // app/src/utils/myUtil.ts export function myFunction() { const RN = require('react-native'); // Nested if called from test return RN.Platform.OS; } ``` #### ❌ FORBIDDEN: Dynamic requires in test hooks ```typescript // BAD - Dynamic requires in beforeEach/afterEach create nested requires beforeEach(() => { jest.resetModules(); const RN = require('react-native'); // First require const service = require('@/utils/service'); // May internally require RN again }); ``` ### Examples of CORRECT Patterns #### ✅ CORRECT: Use ES6 imports at top level ```typescript // GOOD - Single import at top level import { Platform } from 'react-native'; describe('MyComponent', () => { it('should work', () => { expect(Platform.OS).toBe('ios'); }); }); ``` **Key Rule**: Use `import` statements, not `require()`. React Native is already mocked in setup files (`jest.setup.js` for Jest, `tests/setup.ts` for Vitest), so imports work correctly. ## React Native Module Handling in Tests ### Jest Setup Pattern (app/jest.setup.js) The project uses a custom require override in `jest.setup.js` to handle React Native mocks: ```javascript // This is OK - it's in setup file, runs once const Module = require('module'); const originalRequire = Module.prototype.require; Module.prototype.require = function (id) { if (id === 'react-native') { const RN = originalRequire.apply(this, arguments); // Add mocks if needed return RN; } return originalRequire.apply(this, arguments); }; ``` **Key Point**: This override runs ONCE during test setup. Tests should NOT create additional require() calls that would trigger this override multiple times. ### Vitest Setup Pattern (packages/mobile-sdk-alpha/tests/setup.ts) Vitest uses `vi.mock()` to mock React Native: ```typescript // This is OK - runs once during setup vi.mock('react-native', () => ({ Platform: { OS: 'web' }, // ... other mocks })); ``` **Key Point**: Tests should use `import` statements, not `require()`, after mocks are set up. ## Best Practices 1. **Always use ES6 `import` statements** - Never use `require('react')` or `require('react-native')` in test files 2. **Put all imports at the top of the file** - No dynamic imports in hooks 3. **Avoid `jest.resetModules()`** - Only use when absolutely necessary for module initialization tests 4. **Use setup file mocks** - React Native is already mocked in `jest.setup.js` (Jest) or `tests/setup.ts` (Vitest) ## Automated Enforcement The project has multiple layers of protection against nested require() patterns: ### 1. ESLint Rule (app/.eslintrc.cjs) ESLint will fail on `require('react')` and `require('react-native')` in test files: ```javascript 'no-restricted-syntax': [ 'error', { selector: "CallExpression[callee.name='require'][arguments.0.value='react']", message: "Do not use require('react') in tests..." }, { selector: "CallExpression[callee.name='require'][arguments.0.value='react-native']", message: "Do not use require('react-native') in tests..." } ] ``` Run `yarn lint` to check for violations. ### 2. Validation Script (app/scripts/check-test-requires.cjs) Automated script to detect nested require patterns: ```bash node scripts/check-test-requires.cjs ``` This script: - Scans all test files for `require('react')` and `require('react-native')` - Reports exact file locations and line numbers - Exits with error code 1 if issues found ### 3. CI Fast-Fail Check GitHub Actions runs the validation script before tests: ```yaml - name: Check for nested require() in tests run: node scripts/check-test-requires.cjs working-directory: ./app ``` This prevents wasting CI time on tests that will OOM. ## Quick Checklist Before committing test changes: - [ ] No `require('react')` calls in test files (use `import React from 'react'` instead) - [ ] No `require('react-native')` calls in test files (use `import { ... } from 'react-native'` instead) - [ ] All imports at top of file (not in hooks or jest.mock() factories) - [ ] Run validation: `node scripts/check-test-requires.cjs` - [ ] Run lint: `yarn lint` ## Detection **Signs of nested require issues**: CI OOM errors, test timeouts, memory spikes, "Call stack size exceeded" errors, tests hiding actual failures **Fix**: 1. Search for `require('react')` and `require('react-native')` in tests 2. Replace with `import` statements at the top of the file 3. Run `node scripts/check-test-requires.cjs` to verify ## Related Files - `app/.eslintrc.cjs` - ESLint rules blocking nested requires - `app/scripts/check-test-requires.cjs` - Validation script - `.github/workflows/mobile-ci.yml` - CI enforcement - `app/jest.setup.js` - Jest setup with React Native mocks - `packages/mobile-sdk-alpha/tests/setup.ts` - Vitest setup with React Native mocks - `app/jest.config.cjs` - Jest configuration - `packages/mobile-sdk-alpha/vitest.config.ts` - Vitest configuration $END$