Files
suaveplan/docs/old-network-planning/test-isolation-fix-plan.md
2026-05-27 23:29:53 +01:00

4.8 KiB

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

// 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

// 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.

// 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.