7.0 KiB
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
- Structured
NetworkTestingErrorwith stableNET_TEST.*codes - Zod schemas for
NetworkConditions,MemoryTransportOptions MemoryTransportPair— bidirectional in-process transport for unit testsFakeWebSocketAdapter— injectable into@suaveplan/network-websocketsclientsConditionSimulator— wraps any transport and injects latency/jitter/packet-loss via injectableClockandRngTestPeerfactory — fluent builder compatible with@suaveplan/network-registryand@suaveplan/network-bootstrapNetworkEventCapture— wraps anEventBusto 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.