Initial Commit
This commit is contained in:
154
docs/old-network-planning/test-isolation-fix-plan.md
Normal file
154
docs/old-network-planning/test-isolation-fix-plan.md
Normal file
@@ -0,0 +1,154 @@
|
||||
# Test Isolation Fix Plan
|
||||
|
||||
## Problem Analysis
|
||||
|
||||
After analyzing the failing tests, we've identified several issues that cause tests to fail when run together but pass individually:
|
||||
|
||||
1. **Singleton TimerController**: The `TimerController` class uses a singleton pattern, which means all tests share the same timer instance. This causes interference between tests.
|
||||
|
||||
2. **Static Registry in MemoryTransport**: The `MemoryTransport` class uses a static registry (`_instances`) to track all transport instances, which isn't properly cleaned up between tests.
|
||||
|
||||
3. **Time-sensitive Tests**: Several tests rely on actual timing with `setTimeout`, making them flaky.
|
||||
|
||||
4. **Date.now() Usage**: Tests using `Date.now()` can be affected by other tests that mock time-related functions.
|
||||
|
||||
## Proposed Solutions
|
||||
|
||||
### 1. Update TimerController to Support Multiple Instances
|
||||
|
||||
The current `TimerController` is implemented as a singleton, which prevents proper test isolation. We need to modify it to:
|
||||
|
||||
- Allow creation of independent instances for each test
|
||||
- Add a method to create a factory that provides isolated instances
|
||||
- Ensure `reset()` is called in `beforeEach` and `afterEach` blocks
|
||||
|
||||
### 2. Fix MemoryTransport Static Registry
|
||||
|
||||
- Add a `resetRegistry()` static method to clear all instances
|
||||
- Ensure this is called in `beforeEach` and `afterEach` hooks in tests
|
||||
- Make sure disconnection properly removes instances from the registry
|
||||
|
||||
### 3. Refactor Time-Sensitive Tests
|
||||
|
||||
For each of the failing tests:
|
||||
|
||||
#### MemoryTransport Tests
|
||||
|
||||
- Update `should simulate connection delay` to use TimerController instead of real delays
|
||||
- Update `should simulate latency` to use TimerController
|
||||
|
||||
#### MessageBatch Tests
|
||||
|
||||
- Update `should track batch age` to use TimerController instead of real setTimeout
|
||||
|
||||
#### TransportTester Tests
|
||||
|
||||
- Update message delivery tests to use TimerController
|
||||
- Update reliability tests to address race conditions
|
||||
|
||||
#### NetworkNodePhase4 Tests
|
||||
|
||||
- Ensure proper cleanup and isolation of network components
|
||||
|
||||
### 4. Create Test Utility Classes
|
||||
|
||||
Create test utility classes/functions that:
|
||||
|
||||
- Provide isolated TimerController instances
|
||||
- Help mock Date.now() consistently
|
||||
- Ensure all static registries are reset between tests
|
||||
|
||||
## Implementation Details
|
||||
|
||||
### TimerController Changes
|
||||
|
||||
```typescript
|
||||
// Modifications needed to TimerController
|
||||
export class TimerController {
|
||||
private _currentTime = 0;
|
||||
private _timers: Map<number, ScheduledTimer> = new Map();
|
||||
private _nextTimerId = 1;
|
||||
|
||||
// Remove static singleton instance
|
||||
// private static _instance: TimerController;
|
||||
|
||||
// Replace getInstance with createInstance
|
||||
public static createInstance(): TimerController {
|
||||
return new TimerController();
|
||||
}
|
||||
|
||||
// Other methods remain the same...
|
||||
}
|
||||
|
||||
// Helper for tests
|
||||
export function createIsolatedTimerController(): TimerController {
|
||||
return TimerController.createInstance();
|
||||
}
|
||||
```
|
||||
|
||||
### MemoryTransport Test Setup
|
||||
|
||||
```typescript
|
||||
// In memory-transport.spec.ts
|
||||
beforeEach(() => {
|
||||
// Reset static registry
|
||||
MemoryTransport.resetRegistry();
|
||||
|
||||
// Create fresh instances with isolated timer controllers
|
||||
const timer1 = createIsolatedTimerController();
|
||||
const timer2 = createIsolatedTimerController();
|
||||
|
||||
// Create transports with these timers
|
||||
transport1 = new MemoryTransport('transport-1', {
|
||||
eventEmitter: eventEmitter1,
|
||||
timerController: timer1
|
||||
});
|
||||
|
||||
transport2 = new MemoryTransport('transport-2', {
|
||||
eventEmitter: eventEmitter2,
|
||||
timerController: timer2
|
||||
});
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Clean up
|
||||
MemoryTransport.resetRegistry();
|
||||
});
|
||||
```
|
||||
|
||||
### TransportTester Tests
|
||||
|
||||
The TransportTester tests require special attention to ensure proper timing control. The issue is that these tests verify asynchronous behaviors like message delivery and reliability.
|
||||
|
||||
```typescript
|
||||
// In transport-testing.spec.ts
|
||||
beforeEach(() => {
|
||||
// Reset static registries and create isolated timer controllers
|
||||
MemoryTransport.resetRegistry();
|
||||
const senderTimer = createIsolatedTimerController();
|
||||
const receiverTimer = createIsolatedTimerController();
|
||||
|
||||
// Configure transports with isolated timers
|
||||
sender = new MockTransport('sender', undefined, senderTimer);
|
||||
receiver = new MockTransport('receiver', undefined, receiverTimer);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
// Clean up
|
||||
MemoryTransport.resetRegistry();
|
||||
// Additional cleanup as needed
|
||||
});
|
||||
```
|
||||
|
||||
## Next Steps
|
||||
|
||||
After implementing these changes:
|
||||
|
||||
1. Run individual test suites to verify they still pass
|
||||
2. Run the full test suite to verify the fixes resolved the isolation issues
|
||||
3. Add documentation about the importance of test isolation
|
||||
4. Consider adding a linting rule to prevent direct usage of setTimeout/setInterval in tests
|
||||
|
||||
## Implementation Recommendation
|
||||
|
||||
To implement these changes, we should switch to Code mode and systematically update each test file, ensuring that each component of the system is properly isolated from others during testing.
|
||||
Reference in New Issue
Block a user