mirror of
https://github.com/selfxyz/self.git
synced 2026-02-19 02:24:25 -05:00
* update lock * fix types * bump version * fix nested react requires * fix heavy tests * address fake mocks * fix test * remove last borked react test
204 lines
6.8 KiB
Plaintext
204 lines
6.8 KiB
Plaintext
---
|
|
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$
|