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

252 lines
7.0 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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
```ts
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
```ts
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
```ts
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
```ts
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
```ts
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
```ts
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
```text
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.