Skip to main content

protocol/testing/toxics

packages/protocol/src/testing/toxics

Purpose

Public barrel for Toxiproxy toxic profiles and control helpers.

Public surface

allToxicTags

Variable
export const allToxicTags = [
  "latency",
  "bandwidth",
  "slicer",
  "reset_peer",
  "timeout",
  "slow_close",
] as const
All six toxic tags, enumerated for coverage assertions in Tier D.

defaultToxicProfile

Variable
export const defaultToxicProfile:

deliveryInvariantFor

Function
export function deliveryInvariantFor(toxic: ToxicTag): DeliveryInvariantName
Selects the delivery invariant for a toxic profile.

DeliveryInvariantName

TypeAlias
export type DeliveryInvariantName =
  | "fan-out-cardinality"
  | "store-and-replay"
  | "payload-opacity"
  | "task-boundary-isolation";

/** Selects the delivery invariant for a toxic profile. */
export function deliveryInvariantFor(toxic: ToxicTag): DeliveryInvariantName {
  switch (toxic) {
    case "latency":
    case "bandwidth":
    case "timeout":
      return "fan-out-cardinality";
    case "reset_peer":
      return "store-and-replay";
    case "slicer":
      return "payload-opacity";
    case "slow_close":
      return "task-boundary-isolation";
    default: {
      const _exhaustive: never = toxic;
      return absurdToxicTag(_exhaustive);
    }
  }
}
Every delivery-layer property one toxic is expected to re-exercise. Keyed by the property’s semantic name (matches the register-function names in conformance/task/{fan-out-cardinality,store-and-replay, payload-opacity,task-boundary-isolation}.ts).

makeToxiproxyClient

Function
export function makeToxiproxyClient(
  config: ToxiproxyConfig,
): Effect.Effect<ToxiproxyClient, ToxicControlError>

ToxicControlError

Class
export class ToxicControlError extends Data.TaggedError(
  "TestingToxicControlError",
)<{
  readonly op: "create-proxy" | "delete-proxy" | "add-toxic" | "remove-toxic";
  readonly status: number;
  readonly body: string;
}> {}
Toxiproxy HTTP API returned a non-2xx, or the control endpoint is down.

ToxicHandle

Interface
export interface ToxicHandle {
  readonly name: string;
  readonly profile: ToxicProfile;
}
A live toxic attachment. Scoped: acquiring adds the toxic to the proxy, releasing the scope removes it. Tier D properties acquire a ToxicHandle inside Effect.scoped so a crashed property still cleans up.

ToxicProfile

TypeAlias
export type ToxicProfile =
  | {
      readonly _tag: "latency";
      /** Added latency in milliseconds, per-packet. */
      readonly latencyMs: number;
      /** Random jitter in ms, uniform [0, jitterMs). */
      readonly jitterMs: number;
    }
Toxic profile DSL. Per D2 and Invariant I4, adversity is a parameter selected at suite invocation, not hardcoded case-by-case. A ToxicProfile is a named preset (one of the six toxics) plus its parameters; the Tier D runner picks the matching Tier C invariant and re-runs it with the toxic attached. Exhaustiveness: the _tag union covers every toxic named in §5 Tier D (D1–D6) so the implementer cannot forget a branch in the client dispatch.

ToxicTag

TypeAlias
export type ToxicTag = (typeof allToxicTags)[number];

ToxiproxyClient

Interface
export interface ToxiproxyClient {
  /** Create a scoped proxy; teardown on release. */
  readonly proxy: (opts: {
    readonly name: string;
    readonly upstream: string;
  }) => Effect.Effect<ToxiproxyProxy, ToxicControlError, Scope.Scope>;
  /** Probe: control plane reachable. */
  readonly ping: Effect.Effect<void, ToxicControlError>;
}

ToxiproxyConfig

Interface
export interface ToxiproxyConfig {
  /** Control-plane URL, e.g. `http://localhost:8474`. */
  readonly apiUrl: string;
}

ToxiproxyProxy

Interface
export interface ToxiproxyProxy {
  /** Upstream (real server) address the proxy forwards to. */
  readonly upstream: string;
  /** Client-facing URL (`ws://127.0.0.1:&lt;ephemeralPort>`). */
  readonly listenUrl: string;
  /** Attach a toxic inside a Scope; removed on release. */
  readonly withToxic: (
    profile: ToxicProfile,
  ) => Effect.Effect<ToxicHandle, ToxicControlError, Scope.Scope>;
}
A live proxy that sits between TestClient and the real server (or between real client and TestServer). Acquiring the scope allocates an ephemeral port and registers the proxy; releasing deletes it.

Files

  • client.ts
  • defaults.ts
  • errors.ts
  • profile.ts