Files
2026-05-27 23:29:53 +01:00

7.0 KiB
Raw Permalink Blame History

OpenSpec Change: enterprise-network-testing

  • Change ID: enterprise-network-testing
  • Package: @suaveplan/network-testing
  • Category: universal/network
  • Target path: universal/network/network-testing
  • Status: PROPOSED
  • Date: 2026-04-24
  • Reference spec: /mnt/s/web/suaveplan/enterprise/network-testing.md

Summary

Deliver @suaveplan/network-testing on the genesis branch from scratch at enterprise quality. The package provides network testing utilities: in-memory transport implementations, fake WebSocket adapters, network condition simulators (latency, jitter, packet loss, bandwidth), test peer factories, clock-controlled timing helpers for network scenarios, and assertion utilities for network events. Genesis adds structured errors via @suaveplan/error, Zod schemas for condition simulators, OTel integration for verifying instrumentation in tests, and a comprehensive test harness API.


Current-state audit (genesis)

The package does not exist on genesis yet — this is a fresh build.


Available workspace deps on genesis

Package Role
@suaveplan/error (workspace:^) Base class for NetworkTestingError; stable error codes
@suaveplan/types (workspace:^) LoggerLike, Clock, EventBusLike — all injectable
@suaveplan/events (workspace:^) Real EventBus for test event capture
@suaveplan/testing (workspace:^) FakeClock, MemoryStore, testing helpers
zod (catalog:forms) Zod schemas for condition simulator config
@opentelemetry/api (catalog:observability) OTel no-op for instrumentation testing
tslib (catalog:build) Runtime helpers

Dev deps: vitest, @vitest/coverage-istanbul (all catalog:testing).


Public API surface

In-memory transport

function createMemoryTransport(options?: MemoryTransportOptions): MemoryTransportPair

interface MemoryTransportPair {
  readonly client: MemoryTransport
  readonly server: MemoryTransport
  connect(): void
  disconnect(): void
}

interface MemoryTransport {
  readonly id: string
  readonly state: TransportState
  send(message: unknown, signal?: AbortSignal): Promise<void>
  connect(signal?: AbortSignal): Promise<void>
  disconnect(): void
  on(event: string, handler: (data: unknown) => void): () => void
  messages: unknown[]  // captured sent messages
}

Fake WebSocket adapter

function createFakeWebSocketAdapter(options?: FakeWebSocketOptions): FakeWebSocketAdapter

interface FakeWebSocketAdapter {
  connect(url: string, protocols?: string[]): FakeWebSocketHandle
  simulateMessage(data: string | Uint8Array): void
  simulateError(error: Error): void
  simulateClose(code?: number, reason?: string): void
  readonly connections: ReadonlyArray<{ url: string; handle: FakeWebSocketHandle }>
}

interface FakeWebSocketHandle {
  readonly readyState: number
  readonly sentMessages: ReadonlyArray<string | Uint8Array>
  send(data: string | Uint8Array): void
  close(code?: number, reason?: string): void
  onopen: (() => void) | null
  onclose: ((code: number, reason: string) => void) | null
  onmessage: ((data: string | Uint8Array) => void) | null
  onerror: ((error: Error) => void) | null
}

Network condition simulator

function createConditionSimulator(conditions: NetworkConditions): ConditionSimulator

interface NetworkConditions {
  latencyMs?: number | LatencyDistribution
  jitterMs?: number
  packetLossRate?: number    // 0–1
  bandwidthBps?: number
  disconnectAfterMs?: number
}

interface LatencyDistribution {
  min: number
  max: number
  distribution: 'uniform' | 'normal' | 'bimodal'
}

interface ConditionSimulator {
  wrap<T extends { send: (...args: unknown[]) => Promise<unknown> }>(transport: T): T
  setConditions(conditions: Partial<NetworkConditions>): void
  reset(): void
  readonly stats: SimulationStats
}

interface SimulationStats {
  readonly sentMessages: number
  readonly droppedMessages: number
  readonly totalLatencyMs: number
  readonly avgLatencyMs: number
}

Test peer factory

function createTestPeer(options?: TestPeerOptions): TestPeer

interface TestPeer {
  readonly id: string
  readonly address: string
  readonly capabilities: string[]
  asRegistryEntry(): RegistryEntry
  asBootstrapSeed(): SeedConfig
  withCapabilities(capabilities: string[]): TestPeer
  withRegion(region: string): TestPeer
  withHealth(healthy: boolean, latencyMs?: number): TestPeer
}

interface TestPeerOptions {
  id?: string
  address?: string
  capabilities?: string[]
  region?: string
  clock?: Clock
}

Network event assertions

function createNetworkEventCapture(eventBus: EventBusLike): NetworkEventCapture

interface NetworkEventCapture {
  events: unknown[]
  waitFor(event: string, timeout?: number): Promise<unknown>
  assertEventEmitted(event: string, predicate?: (data: unknown) => boolean): void
  assertEventCount(event: string, count: number): void
  clear(): void
}

Options and error codes

interface MemoryTransportOptions {
  latencyMs?: number
  clock?: Clock
}

interface FakeWebSocketOptions {
  autoConnect?: boolean
  clock?: Clock
}

type NetworkTestingErrorCode =
  | 'NET_TEST_TIMEOUT'
  | 'NET_TEST_EVENT_NOT_EMITTED'
  | 'NET_TEST_CONNECT_FAILED'

function registerNetworkTestingErrorCodes(): void

Genesis additions

  1. Structured NetworkTestingError with stable NET_TEST.* codes
  2. Zod schemas for NetworkConditions, MemoryTransportOptions
  3. MemoryTransportPair — bidirectional in-process transport for unit tests
  4. FakeWebSocketAdapter — injectable into @suaveplan/network-websockets clients
  5. ConditionSimulator — wraps any transport and injects latency/jitter/packet-loss via injectable Clock and Rng
  6. TestPeer factory — fluent builder compatible with @suaveplan/network-registry and @suaveplan/network-bootstrap
  7. NetworkEventCapture — wraps an EventBus to capture and assert emitted events in tests

File layout

universal/network/network-testing/
  package.json
  tsconfig.json
  vitest.config.ts
  src/
    index.ts
    error-codes.ts
    constants/
      index.ts
    types/
      common.ts
      common.md
    transports/
      memory-transport/
        memory-transport.ts
        memory-transport.md
        memory-transport.test.ts
        index.ts
      fake-websocket/
        fake-websocket.ts
        fake-websocket.md
        fake-websocket.test.ts
        index.ts
    simulators/
      condition-simulator/
        condition-simulator.ts
        condition-simulator.md
        condition-simulator.test.ts
        index.ts
    factories/
      test-peer/
        test-peer.ts
        test-peer.md
        test-peer.test.ts
        index.ts
    assertions/
      event-capture/
        event-capture.ts
        event-capture.md
        event-capture.test.ts
        index.ts
  tests/
    integration.test.ts

Impact

Workspace dep prerequisites: @suaveplan/error, @suaveplan/types, @suaveplan/events, @suaveplan/testing must be published.