@moltzap/protocol 2026.408.0 → 2026.425.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +2 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/rpc-registry.d.ts +446 -0
- package/dist/rpc-registry.d.ts.map +1 -0
- package/dist/rpc-registry.js +67 -0
- package/dist/rpc-registry.js.map +1 -0
- package/dist/rpc.d.ts +42 -0
- package/dist/rpc.d.ts.map +1 -0
- package/dist/rpc.js +29 -0
- package/dist/rpc.js.map +1 -0
- package/dist/schema/apps.d.ts +86 -0
- package/dist/schema/apps.d.ts.map +1 -0
- package/dist/schema/apps.js +77 -0
- package/dist/schema/apps.js.map +1 -0
- package/dist/schema/contacts.d.ts +8 -18
- package/dist/schema/contacts.d.ts.map +1 -1
- package/dist/schema/contacts.js +9 -13
- package/dist/schema/contacts.js.map +1 -1
- package/dist/schema/conversations.d.ts +15 -8
- package/dist/schema/conversations.d.ts.map +1 -1
- package/dist/schema/conversations.js +18 -7
- package/dist/schema/conversations.js.map +1 -1
- package/dist/schema/delivery.d.ts +1 -4
- package/dist/schema/delivery.d.ts.map +1 -1
- package/dist/schema/delivery.js +2 -3
- package/dist/schema/delivery.js.map +1 -1
- package/dist/schema/errors.d.ts +13 -0
- package/dist/schema/errors.d.ts.map +1 -1
- package/dist/schema/errors.js +14 -0
- package/dist/schema/errors.js.map +1 -1
- package/dist/schema/events.d.ts +112 -93
- package/dist/schema/events.d.ts.map +1 -1
- package/dist/schema/events.js +84 -25
- package/dist/schema/events.js.map +1 -1
- package/dist/schema/identity.d.ts +19 -17
- package/dist/schema/identity.d.ts.map +1 -1
- package/dist/schema/identity.js +7 -14
- package/dist/schema/identity.js.map +1 -1
- package/dist/schema/index.d.ts +4 -2
- package/dist/schema/index.d.ts.map +1 -1
- package/dist/schema/index.js +4 -2
- package/dist/schema/index.js.map +1 -1
- package/dist/schema/messages.d.ts +3 -7
- package/dist/schema/messages.d.ts.map +1 -1
- package/dist/schema/messages.js +4 -6
- package/dist/schema/messages.js.map +1 -1
- package/dist/schema/methods/apps.d.ts +123 -0
- package/dist/schema/methods/apps.d.ts.map +1 -0
- package/dist/schema/methods/apps.js +93 -0
- package/dist/schema/methods/apps.js.map +1 -0
- package/dist/schema/methods/auth.d.ts +102 -90
- package/dist/schema/methods/auth.d.ts.map +1 -1
- package/dist/schema/methods/auth.js +86 -74
- package/dist/schema/methods/auth.js.map +1 -1
- package/dist/schema/methods/contacts.d.ts +39 -74
- package/dist/schema/methods/contacts.d.ts.map +1 -1
- package/dist/schema/methods/contacts.js +35 -29
- package/dist/schema/methods/contacts.js.map +1 -1
- package/dist/schema/methods/conversations.d.ts +48 -52
- package/dist/schema/methods/conversations.d.ts.map +1 -1
- package/dist/schema/methods/conversations.js +89 -38
- package/dist/schema/methods/conversations.js.map +1 -1
- package/dist/schema/methods/invites.d.ts +1 -3
- package/dist/schema/methods/invites.d.ts.map +1 -1
- package/dist/schema/methods/invites.js +8 -1
- package/dist/schema/methods/invites.js.map +1 -1
- package/dist/schema/methods/messages.d.ts +12 -44
- package/dist/schema/methods/messages.d.ts.map +1 -1
- package/dist/schema/methods/messages.js +22 -28
- package/dist/schema/methods/messages.js.map +1 -1
- package/dist/schema/methods/presence.d.ts +7 -22
- package/dist/schema/methods/presence.d.ts.map +1 -1
- package/dist/schema/methods/presence.js +12 -6
- package/dist/schema/methods/presence.js.map +1 -1
- package/dist/schema/methods/push.d.ts +8 -6
- package/dist/schema/methods/push.d.ts.map +1 -1
- package/dist/schema/methods/push.js +20 -7
- package/dist/schema/methods/push.js.map +1 -1
- package/dist/schema/methods/system.d.ts +4 -0
- package/dist/schema/methods/system.d.ts.map +1 -0
- package/dist/schema/methods/system.js +11 -0
- package/dist/schema/methods/system.js.map +1 -0
- package/dist/schema/presence.d.ts +1 -12
- package/dist/schema/presence.d.ts.map +1 -1
- package/dist/schema/presence.js +2 -7
- package/dist/schema/presence.js.map +1 -1
- package/dist/schema/surfaces.d.ts +31 -25
- package/dist/schema/surfaces.d.ts.map +1 -1
- package/dist/schema/surfaces.js +40 -21
- package/dist/schema/surfaces.js.map +1 -1
- package/dist/testing/agent-registration.d.ts +49 -0
- package/dist/testing/agent-registration.d.ts.map +1 -0
- package/dist/testing/agent-registration.js +77 -0
- package/dist/testing/agent-registration.js.map +1 -0
- package/dist/testing/arbitraries/frames.d.ts +24 -0
- package/dist/testing/arbitraries/frames.d.ts.map +1 -0
- package/dist/testing/arbitraries/frames.js +36 -0
- package/dist/testing/arbitraries/frames.js.map +1 -0
- package/dist/testing/arbitraries/from-typebox.d.ts +37 -0
- package/dist/testing/arbitraries/from-typebox.d.ts.map +1 -0
- package/dist/testing/arbitraries/from-typebox.js +131 -0
- package/dist/testing/arbitraries/from-typebox.js.map +1 -0
- package/dist/testing/arbitraries/index.d.ts +4 -0
- package/dist/testing/arbitraries/index.d.ts.map +1 -0
- package/dist/testing/arbitraries/index.js +4 -0
- package/dist/testing/arbitraries/index.js.map +1 -0
- package/dist/testing/arbitraries/rpc.d.ts +66 -0
- package/dist/testing/arbitraries/rpc.d.ts.map +1 -0
- package/dist/testing/arbitraries/rpc.js +98 -0
- package/dist/testing/arbitraries/rpc.js.map +1 -0
- package/dist/testing/canonicalize.d.ts +38 -0
- package/dist/testing/canonicalize.d.ts.map +1 -0
- package/dist/testing/canonicalize.js +55 -0
- package/dist/testing/canonicalize.js.map +1 -0
- package/dist/testing/captures.d.ts +61 -0
- package/dist/testing/captures.d.ts.map +1 -0
- package/dist/testing/captures.js +99 -0
- package/dist/testing/captures.js.map +1 -0
- package/dist/testing/codec.d.ts +44 -0
- package/dist/testing/codec.d.ts.map +1 -0
- package/dist/testing/codec.js +170 -0
- package/dist/testing/codec.js.map +1 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.d.ts +6 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.d.ts.map +1 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.js +33 -0
- package/dist/testing/conformance/__divergence_proofs__/executable-proof-helpers.js.map +1 -0
- package/dist/testing/conformance/adversity.d.ts +36 -0
- package/dist/testing/conformance/adversity.d.ts.map +1 -0
- package/dist/testing/conformance/adversity.js +360 -0
- package/dist/testing/conformance/adversity.js.map +1 -0
- package/dist/testing/conformance/boundary.d.ts +56 -0
- package/dist/testing/conformance/boundary.d.ts.map +1 -0
- package/dist/testing/conformance/boundary.js +129 -0
- package/dist/testing/conformance/boundary.js.map +1 -0
- package/dist/testing/conformance/client/_fixtures.d.ts +71 -0
- package/dist/testing/conformance/client/_fixtures.d.ts.map +1 -0
- package/dist/testing/conformance/client/_fixtures.js +102 -0
- package/dist/testing/conformance/client/_fixtures.js.map +1 -0
- package/dist/testing/conformance/client/adversity.d.ts +39 -0
- package/dist/testing/conformance/client/adversity.d.ts.map +1 -0
- package/dist/testing/conformance/client/adversity.js +162 -0
- package/dist/testing/conformance/client/adversity.js.map +1 -0
- package/dist/testing/conformance/client/boundary.d.ts +12 -0
- package/dist/testing/conformance/client/boundary.d.ts.map +1 -0
- package/dist/testing/conformance/client/boundary.js +68 -0
- package/dist/testing/conformance/client/boundary.js.map +1 -0
- package/dist/testing/conformance/client/delivery.d.ts +38 -0
- package/dist/testing/conformance/client/delivery.d.ts.map +1 -0
- package/dist/testing/conformance/client/delivery.js +202 -0
- package/dist/testing/conformance/client/delivery.js.map +1 -0
- package/dist/testing/conformance/client/index.d.ts +16 -0
- package/dist/testing/conformance/client/index.d.ts.map +1 -0
- package/dist/testing/conformance/client/index.js +16 -0
- package/dist/testing/conformance/client/index.js.map +1 -0
- package/dist/testing/conformance/client/rpc-semantics.d.ts +26 -0
- package/dist/testing/conformance/client/rpc-semantics.d.ts.map +1 -0
- package/dist/testing/conformance/client/rpc-semantics.js +145 -0
- package/dist/testing/conformance/client/rpc-semantics.js.map +1 -0
- package/dist/testing/conformance/client/runner.d.ts +258 -0
- package/dist/testing/conformance/client/runner.d.ts.map +1 -0
- package/dist/testing/conformance/client/runner.js +228 -0
- package/dist/testing/conformance/client/runner.js.map +1 -0
- package/dist/testing/conformance/client/schema-conformance.d.ts +25 -0
- package/dist/testing/conformance/client/schema-conformance.d.ts.map +1 -0
- package/dist/testing/conformance/client/schema-conformance.js +123 -0
- package/dist/testing/conformance/client/schema-conformance.js.map +1 -0
- package/dist/testing/conformance/client/suite.d.ts +90 -0
- package/dist/testing/conformance/client/suite.d.ts.map +1 -0
- package/dist/testing/conformance/client/suite.js +209 -0
- package/dist/testing/conformance/client/suite.js.map +1 -0
- package/dist/testing/conformance/coverage-policy.d.ts +8 -0
- package/dist/testing/conformance/coverage-policy.d.ts.map +1 -0
- package/dist/testing/conformance/coverage-policy.js +10 -0
- package/dist/testing/conformance/coverage-policy.js.map +1 -0
- package/dist/testing/conformance/delivery.d.ts +40 -0
- package/dist/testing/conformance/delivery.d.ts.map +1 -0
- package/dist/testing/conformance/delivery.js +231 -0
- package/dist/testing/conformance/delivery.js.map +1 -0
- package/dist/testing/conformance/env.d.ts +3 -0
- package/dist/testing/conformance/env.d.ts.map +1 -0
- package/dist/testing/conformance/env.js +14 -0
- package/dist/testing/conformance/env.js.map +1 -0
- package/dist/testing/conformance/index.d.ts +10 -0
- package/dist/testing/conformance/index.d.ts.map +1 -0
- package/dist/testing/conformance/index.js +10 -0
- package/dist/testing/conformance/index.js.map +1 -0
- package/dist/testing/conformance/registry.d.ts +93 -0
- package/dist/testing/conformance/registry.d.ts.map +1 -0
- package/dist/testing/conformance/registry.js +62 -0
- package/dist/testing/conformance/registry.js.map +1 -0
- package/dist/testing/conformance/rpc-semantics.d.ts +67 -0
- package/dist/testing/conformance/rpc-semantics.d.ts.map +1 -0
- package/dist/testing/conformance/rpc-semantics.js +394 -0
- package/dist/testing/conformance/rpc-semantics.js.map +1 -0
- package/dist/testing/conformance/runner.d.ts +78 -0
- package/dist/testing/conformance/runner.d.ts.map +1 -0
- package/dist/testing/conformance/runner.js +65 -0
- package/dist/testing/conformance/runner.js.map +1 -0
- package/dist/testing/conformance/schema-conformance.d.ts +30 -0
- package/dist/testing/conformance/schema-conformance.d.ts.map +1 -0
- package/dist/testing/conformance/schema-conformance.js +229 -0
- package/dist/testing/conformance/schema-conformance.js.map +1 -0
- package/dist/testing/conformance/suite.d.ts +92 -0
- package/dist/testing/conformance/suite.d.ts.map +1 -0
- package/dist/testing/conformance/suite.js +233 -0
- package/dist/testing/conformance/suite.js.map +1 -0
- package/dist/testing/errors.d.ts +78 -0
- package/dist/testing/errors.d.ts.map +1 -0
- package/dist/testing/errors.js +34 -0
- package/dist/testing/errors.js.map +1 -0
- package/dist/testing/index.d.ts +25 -0
- package/dist/testing/index.d.ts.map +1 -0
- package/dist/testing/index.js +37 -0
- package/dist/testing/index.js.map +1 -0
- package/dist/testing/models/dispatch.d.ts +61 -0
- package/dist/testing/models/dispatch.d.ts.map +1 -0
- package/dist/testing/models/dispatch.js +197 -0
- package/dist/testing/models/dispatch.js.map +1 -0
- package/dist/testing/models/index.d.ts +3 -0
- package/dist/testing/models/index.d.ts.map +1 -0
- package/dist/testing/models/index.js +3 -0
- package/dist/testing/models/index.js.map +1 -0
- package/dist/testing/models/state.d.ts +37 -0
- package/dist/testing/models/state.d.ts.map +1 -0
- package/dist/testing/models/state.js +14 -0
- package/dist/testing/models/state.js.map +1 -0
- package/dist/testing/test-client.d.ts +70 -0
- package/dist/testing/test-client.d.ts.map +1 -0
- package/dist/testing/test-client.js +266 -0
- package/dist/testing/test-client.js.map +1 -0
- package/dist/testing/test-server.d.ts +62 -0
- package/dist/testing/test-server.d.ts.map +1 -0
- package/dist/testing/test-server.js +134 -0
- package/dist/testing/test-server.js.map +1 -0
- package/dist/testing/toxics/client.d.ts +52 -0
- package/dist/testing/toxics/client.d.ts.map +1 -0
- package/dist/testing/toxics/client.js +120 -0
- package/dist/testing/toxics/client.js.map +1 -0
- package/dist/testing/toxics/defaults.d.ts +43 -0
- package/dist/testing/toxics/defaults.d.ts.map +1 -0
- package/dist/testing/toxics/defaults.js +9 -0
- package/dist/testing/toxics/defaults.js.map +1 -0
- package/dist/testing/toxics/index.d.ts +4 -0
- package/dist/testing/toxics/index.d.ts.map +1 -0
- package/dist/testing/toxics/index.js +4 -0
- package/dist/testing/toxics/index.js.map +1 -0
- package/dist/testing/toxics/profile.d.ts +69 -0
- package/dist/testing/toxics/profile.d.ts.map +1 -0
- package/dist/testing/toxics/profile.js +57 -0
- package/dist/testing/toxics/profile.js.map +1 -0
- package/dist/types.d.ts +7 -11
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -1
- package/dist/validators.d.ts +49 -177
- package/dist/validators.d.ts.map +1 -1
- package/dist/validators.js +77 -48
- package/dist/validators.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +12 -34
- package/dist/optional/contact-events.d.ts +0 -3
- package/dist/optional/contact-events.d.ts.map +0 -1
- package/dist/optional/contact-events.js +0 -5
- package/dist/optional/contact-events.js.map +0 -1
- package/dist/optional/contact-methods.d.ts +0 -5
- package/dist/optional/contact-methods.d.ts.map +0 -1
- package/dist/optional/contact-methods.js +0 -5
- package/dist/optional/contact-methods.js.map +0 -1
- package/dist/phone-hash.d.ts +0 -10
- package/dist/phone-hash.d.ts.map +0 -1
- package/dist/phone-hash.js +0 -17
- package/dist/phone-hash.js.map +0 -1
- package/dist/schema/methods/phone-contacts.d.ts +0 -30
- package/dist/schema/methods/phone-contacts.d.ts.map +0 -1
- package/dist/schema/methods/phone-contacts.js +0 -10
- package/dist/schema/methods/phone-contacts.js.map +0 -1
- package/dist/test-client.d.ts +0 -34
- package/dist/test-client.d.ts.map +0 -1
- package/dist/test-client.js +0 -176
- package/dist/test-client.js.map +0 -1
- package/dist/test-fixtures/phone-hashes.d.ts +0 -18
- package/dist/test-fixtures/phone-hashes.d.ts.map +0 -1
- package/dist/test-fixtures/phone-hashes.js +0 -24
- package/dist/test-fixtures/phone-hashes.js.map +0 -1
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import type { ConformanceRunContext } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* The webhook-shutdown probe is supplied by the consuming server-side
|
|
4
|
+
* suite (which owns access to `AsyncWebhookAdapter`). Protocol code sees
|
|
5
|
+
* only this opaque interface — no compile-time import of `packages/
|
|
6
|
+
* server`.
|
|
7
|
+
*/
|
|
8
|
+
export interface WebhookAdapterProbe {
|
|
9
|
+
/** Kick off N concurrent `send` calls; return their pending request ids. */
|
|
10
|
+
readonly startPending: (n: number) => Promise<ReadonlyArray<{
|
|
11
|
+
readonly requestId: string;
|
|
12
|
+
}>>;
|
|
13
|
+
/** Fire the adapter's `shutdown` Effect. */
|
|
14
|
+
readonly shutdown: () => Promise<void>;
|
|
15
|
+
/**
|
|
16
|
+
* Resolve each pending send. Returns the observed tagged-error name
|
|
17
|
+
* (e.g. `"WebhookDestroyedError"`) or `"resolved"` if the send
|
|
18
|
+
* resolved cleanly before shutdown landed.
|
|
19
|
+
*/
|
|
20
|
+
readonly awaitOutcomes: () => Promise<ReadonlyArray<{
|
|
21
|
+
readonly requestId: string;
|
|
22
|
+
readonly outcome: string;
|
|
23
|
+
}>>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Webhook adapter graceful-shutdown — spec §5 E1: every pending send
|
|
27
|
+
* that was still in-flight when shutdown fired completes with the
|
|
28
|
+
* tagged `WebhookDestroyedError`.
|
|
29
|
+
*
|
|
30
|
+
* Architect §4.4: the `"resolved"` escape hatch was load-bearing
|
|
31
|
+
* vacuity — a server that silently drops in-flight sends passed when
|
|
32
|
+
* all callers happened to resolve before shutdown. Tightened:
|
|
33
|
+
*
|
|
34
|
+
* - At least one outcome MUST NOT be `"resolved"` (otherwise the
|
|
35
|
+
* probe didn't exercise the shutdown path).
|
|
36
|
+
* - For every non-`"resolved"` outcome, the tag MUST be
|
|
37
|
+
* `"WebhookDestroyedError"`. Any other error tag fails.
|
|
38
|
+
*
|
|
39
|
+
* Probe contract: `startPending(n)` schedules synchronously and
|
|
40
|
+
* returns before sends resolve; the consumer's `WebhookAdapterProbe`
|
|
41
|
+
* is responsible for holding at least one send in-flight at shutdown.
|
|
42
|
+
*/
|
|
43
|
+
export declare function registerWebhookGracefulShutdown(ctx: ConformanceRunContext, probe: WebhookAdapterProbe): void;
|
|
44
|
+
/**
|
|
45
|
+
* Schema-exhaustive fuzz — for every `RpcMethodName`, draws arbitrary
|
|
46
|
+
* valid params, sends through a real TestClient, and asserts the server
|
|
47
|
+
* survives. Reuses a single TestClient across the whole iteration so
|
|
48
|
+
* the suite doesn't open 40+ sockets in serial; each method runs behind
|
|
49
|
+
* the same post-call liveness probe.
|
|
50
|
+
*
|
|
51
|
+
* Iterates every `RpcMethodName`. Failure on any single method halts
|
|
52
|
+
* the property with a `PropertyInvariantViolation` naming the offender,
|
|
53
|
+
* so artifacts are actionable.
|
|
54
|
+
*/
|
|
55
|
+
export declare function registerSchemaExhaustiveFuzz(ctx: ConformanceRunContext): void;
|
|
56
|
+
//# sourceMappingURL=boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../../src/testing/conformance/boundary.ts"],"names":[],"mappings":"AAmBA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAWzD;;;;;GAKG;AACH,MAAM,WAAW,mBAAmB;IAClC,4EAA4E;IAC5E,QAAQ,CAAC,YAAY,EAAE,CACrB,CAAC,EAAE,MAAM,KACN,OAAO,CAAC,aAAa,CAAC;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,CAAC;IAC5D,4CAA4C;IAC5C,QAAQ,CAAC,QAAQ,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;IACvC;;;;OAIG;IACH,QAAQ,CAAC,aAAa,EAAE,MAAM,OAAO,CACnC,aAAa,CAAC;QAAE,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;KAAE,CAAC,CACxE,CAAC;CACH;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,qBAAqB,EAC1B,KAAK,EAAE,mBAAmB,GACzB,IAAI,CAwBN;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,4BAA4B,CAAC,GAAG,EAAE,qBAAqB,GAAG,IAAI,CAgF7E"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Boundary — server-side safety surfaces that no single RPC exercises:
|
|
3
|
+
* webhook graceful-shutdown Deferred-drop and schema-exhaustive fuzz.
|
|
4
|
+
*
|
|
5
|
+
* Historical grouping note: spec #181 §5 calls this "Tier E". Code uses
|
|
6
|
+
* semantic names only.
|
|
7
|
+
*
|
|
8
|
+
* AC13 preservation: `WebhookAdapterProbe` is an opaque injected
|
|
9
|
+
* interface; the consuming server-side suite wires it. No
|
|
10
|
+
* `packages/server` import appears under `packages/protocol/src/testing/`.
|
|
11
|
+
*
|
|
12
|
+
* DEFERRED: the original SIGKILL / humanContact variant of the webhook
|
|
13
|
+
* property belongs to epic #186 and is not wired here.
|
|
14
|
+
*/
|
|
15
|
+
import * as fc from "fast-check";
|
|
16
|
+
import { Effect } from "effect";
|
|
17
|
+
import { allRpcMethods, arbitraryCallFor } from "../arbitraries/rpc.js";
|
|
18
|
+
import { makeTestClient } from "../test-client.js";
|
|
19
|
+
import { registerTestAgent } from "../agent-registration.js";
|
|
20
|
+
import { assertProperty, PropertyInvariantViolation, registerProperty, } from "./registry.js";
|
|
21
|
+
const CATEGORY = "boundary";
|
|
22
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
23
|
+
const DEFAULT_CAPTURE_CAPACITY = 32;
|
|
24
|
+
/**
|
|
25
|
+
* Webhook adapter graceful-shutdown — spec §5 E1: every pending send
|
|
26
|
+
* that was still in-flight when shutdown fired completes with the
|
|
27
|
+
* tagged `WebhookDestroyedError`.
|
|
28
|
+
*
|
|
29
|
+
* Architect §4.4: the `"resolved"` escape hatch was load-bearing
|
|
30
|
+
* vacuity — a server that silently drops in-flight sends passed when
|
|
31
|
+
* all callers happened to resolve before shutdown. Tightened:
|
|
32
|
+
*
|
|
33
|
+
* - At least one outcome MUST NOT be `"resolved"` (otherwise the
|
|
34
|
+
* probe didn't exercise the shutdown path).
|
|
35
|
+
* - For every non-`"resolved"` outcome, the tag MUST be
|
|
36
|
+
* `"WebhookDestroyedError"`. Any other error tag fails.
|
|
37
|
+
*
|
|
38
|
+
* Probe contract: `startPending(n)` schedules synchronously and
|
|
39
|
+
* returns before sends resolve; the consumer's `WebhookAdapterProbe`
|
|
40
|
+
* is responsible for holding at least one send in-flight at shutdown.
|
|
41
|
+
*/
|
|
42
|
+
export function registerWebhookGracefulShutdown(ctx, probe) {
|
|
43
|
+
registerProperty(ctx, CATEGORY, "webhook-graceful-shutdown", "in-flight sends at shutdown get typed WebhookDestroyedError", assertProperty(CATEGORY, "webhook-graceful-shutdown", () => fc.assert(
|
|
44
|
+
// #ignore-sloppy-code-next-line[async-keyword]: fast-check asyncProperty contract requires Promise-returning callback
|
|
45
|
+
fc.asyncProperty(fc.integer({ min: 1, max: 4 }), async (n) => {
|
|
46
|
+
const pending = await probe.startPending(n);
|
|
47
|
+
await probe.shutdown();
|
|
48
|
+
const outcomes = await probe.awaitOutcomes();
|
|
49
|
+
if (outcomes.length < pending.length)
|
|
50
|
+
return false;
|
|
51
|
+
const inFlight = outcomes.filter((o) => o.outcome !== "resolved");
|
|
52
|
+
// Probe contract: at least one send must still be in-flight
|
|
53
|
+
// at shutdown. All-resolved = probe didn't exercise shutdown.
|
|
54
|
+
if (inFlight.length === 0)
|
|
55
|
+
return false;
|
|
56
|
+
return inFlight.every((o) => o.outcome === "WebhookDestroyedError");
|
|
57
|
+
}), { seed: ctx.seed, numRuns: ctx.opts.numRuns ?? 3 })));
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Schema-exhaustive fuzz — for every `RpcMethodName`, draws arbitrary
|
|
61
|
+
* valid params, sends through a real TestClient, and asserts the server
|
|
62
|
+
* survives. Reuses a single TestClient across the whole iteration so
|
|
63
|
+
* the suite doesn't open 40+ sockets in serial; each method runs behind
|
|
64
|
+
* the same post-call liveness probe.
|
|
65
|
+
*
|
|
66
|
+
* Iterates every `RpcMethodName`. Failure on any single method halts
|
|
67
|
+
* the property with a `PropertyInvariantViolation` naming the offender,
|
|
68
|
+
* so artifacts are actionable.
|
|
69
|
+
*/
|
|
70
|
+
export function registerSchemaExhaustiveFuzz(ctx) {
|
|
71
|
+
registerProperty(ctx, CATEGORY, "schema-exhaustive-fuzz", "every RpcMethodName drawn → server survives & stays responsive", Effect.scoped(Effect.gen(function* () {
|
|
72
|
+
const agent = yield* registerTestAgent({
|
|
73
|
+
baseUrl: ctx.realServer.baseUrl,
|
|
74
|
+
name: "fuzz",
|
|
75
|
+
}).pipe(Effect.mapError((e) => new PropertyInvariantViolation({
|
|
76
|
+
category: CATEGORY,
|
|
77
|
+
name: "schema-exhaustive-fuzz",
|
|
78
|
+
reason: `register agent: ${e.body}`,
|
|
79
|
+
})));
|
|
80
|
+
const client = yield* makeTestClient({
|
|
81
|
+
serverUrl: ctx.realServer.wsUrl,
|
|
82
|
+
agentKey: agent.apiKey,
|
|
83
|
+
agentId: agent.agentId,
|
|
84
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
85
|
+
captureCapacity: allRpcMethods.length * 4,
|
|
86
|
+
}).pipe(Effect.mapError((e) => new PropertyInvariantViolation({
|
|
87
|
+
category: CATEGORY,
|
|
88
|
+
name: "schema-exhaustive-fuzz",
|
|
89
|
+
reason: `client acquire: ${String(e)}`,
|
|
90
|
+
})));
|
|
91
|
+
const samplesPerMethod = ctx.opts.numRuns ?? 1;
|
|
92
|
+
for (const method of allRpcMethods) {
|
|
93
|
+
const callArb = arbitraryCallFor(method);
|
|
94
|
+
const samples = fc.sample(callArb, {
|
|
95
|
+
numRuns: samplesPerMethod,
|
|
96
|
+
seed: ctx.seed,
|
|
97
|
+
});
|
|
98
|
+
if (samples.length === 0) {
|
|
99
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
100
|
+
category: CATEGORY,
|
|
101
|
+
name: "schema-exhaustive-fuzz",
|
|
102
|
+
reason: `failed to sample call for ${method}`,
|
|
103
|
+
}));
|
|
104
|
+
}
|
|
105
|
+
for (const sampled of samples) {
|
|
106
|
+
yield* client
|
|
107
|
+
.sendRpc(sampled.method, sampled.params)
|
|
108
|
+
.pipe(Effect.either);
|
|
109
|
+
// Post-fuzz liveness: a follow-up RPC must return a typed
|
|
110
|
+
// response. Accepting any `Left` would let a timeout or
|
|
111
|
+
// transport-close slip through as "server alive" — which is
|
|
112
|
+
// exactly what the property must reject. Require the post
|
|
113
|
+
// call to SUCCEED; timeouts are failures here.
|
|
114
|
+
const post = yield* client
|
|
115
|
+
.sendRpc("agents/list", {})
|
|
116
|
+
.pipe(Effect.either);
|
|
117
|
+
if (post._tag !== "Right") {
|
|
118
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
119
|
+
category: CATEGORY,
|
|
120
|
+
name: "schema-exhaustive-fuzz",
|
|
121
|
+
reason: `server became unresponsive after ${method} (post-call ${post._tag === "Left" ? post.left._tag : "unknown"})`,
|
|
122
|
+
}));
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
})));
|
|
127
|
+
void DEFAULT_CAPTURE_CAPACITY;
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.js","sourceRoot":"","sources":["../../../src/testing/conformance/boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fixture helpers for the client-side property bodies.
|
|
3
|
+
*
|
|
4
|
+
* Every client-side property runs the same prologue:
|
|
5
|
+
* - `yield* ctx.realClientFactory()` — produce a real MoltZap client
|
|
6
|
+
* - `yield* awaitConnection(ctx.testServer)` — TestServer accepts the WS
|
|
7
|
+
* - `yield* runAutoHandshakeResponder(connection, ...)` — respond to
|
|
8
|
+
* auth/connect so the real client's `ready` Effect resolves
|
|
9
|
+
* - `yield* window.awaitHandshakeComplete` — wait for ready to settle
|
|
10
|
+
*
|
|
11
|
+
* Centralizing the prologue + teardown here keeps each property body
|
|
12
|
+
* focused on its discriminating predicate (architect-195 / architect-197
|
|
13
|
+
* anti-vacuity discipline).
|
|
14
|
+
*
|
|
15
|
+
* Every helper below is Effect-native — no Promise return types, no raw
|
|
16
|
+
* throws. Errors are mapped into `PropertyFailure` tags before surfacing.
|
|
17
|
+
*/
|
|
18
|
+
import { Effect, Scope } from "effect";
|
|
19
|
+
import type { TestServerConnection } from "../../test-server.js";
|
|
20
|
+
import { type ClientConformanceRunContext, type ClientHandshakeWindow, type RealClientHandle } from "./runner.js";
|
|
21
|
+
import { PropertyUnavailable, PropertyInvariantViolation, type PropertyCategory } from "../registry.js";
|
|
22
|
+
/**
|
|
23
|
+
* Fixture returned to every property body after the prologue runs.
|
|
24
|
+
* Every field below is safe to use inside `fc.asyncProperty` bodies.
|
|
25
|
+
*/
|
|
26
|
+
export interface ClientFixture {
|
|
27
|
+
readonly handle: RealClientHandle;
|
|
28
|
+
readonly connection: TestServerConnection;
|
|
29
|
+
readonly window: ClientHandshakeWindow;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Acquire a live real-client + TestServer connection + handshake window
|
|
33
|
+
* under a nested Scope. Property bodies wrap their assertion in
|
|
34
|
+
* `Effect.scoped(acquireFixture(ctx, ...).pipe(Effect.flatMap(...)))`.
|
|
35
|
+
*
|
|
36
|
+
* Errors are surfaced as `PropertyUnavailable` so a factory fault doesn't
|
|
37
|
+
* masquerade as a property violation.
|
|
38
|
+
*/
|
|
39
|
+
export declare function acquireFixture(ctx: ClientConformanceRunContext, category: PropertyCategory, propertyName: string): Effect.Effect<ClientFixture, PropertyUnavailable, Scope.Scope>;
|
|
40
|
+
/**
|
|
41
|
+
* Poll a real client's observation stream for events whose
|
|
42
|
+
* `data.__emissionTag` matches `tag`. Returns the accumulated tagged
|
|
43
|
+
* observations (possibly empty) after `budgetMs` has elapsed or
|
|
44
|
+
* `expected` matches have arrived, whichever comes first.
|
|
45
|
+
*
|
|
46
|
+
* Used by A2, C1, C3, C4, D1, D3, D4, E2 predicates that need to
|
|
47
|
+
* discriminate real emissions from handshake-window noise.
|
|
48
|
+
*/
|
|
49
|
+
export interface TaggedObservation {
|
|
50
|
+
readonly tag: string;
|
|
51
|
+
readonly raw: Uint8Array;
|
|
52
|
+
readonly data: unknown;
|
|
53
|
+
readonly eventName: string;
|
|
54
|
+
}
|
|
55
|
+
export declare function collectTagged(handle: RealClientHandle, predicate: (tag: string) => boolean, opts: {
|
|
56
|
+
readonly expected: number;
|
|
57
|
+
readonly budgetMs: number;
|
|
58
|
+
}): Effect.Effect<ReadonlyArray<TaggedObservation>>;
|
|
59
|
+
/**
|
|
60
|
+
* Build a `PropertyInvariantViolation` for the current property.
|
|
61
|
+
* Convenience so property bodies don't repeat the tagged-error
|
|
62
|
+
* construction.
|
|
63
|
+
*/
|
|
64
|
+
export declare function invariant(category: PropertyCategory, name: string, reason: string): PropertyInvariantViolation;
|
|
65
|
+
/**
|
|
66
|
+
* Subscribe the fixture's real client to all events (no filter) so the
|
|
67
|
+
* property body can observe every tagged emission. Returns the
|
|
68
|
+
* subscription so the Scope teardown can call `unsubscribe`.
|
|
69
|
+
*/
|
|
70
|
+
export declare function subscribeAll(handle: RealClientHandle): Effect.Effect<void, PropertyUnavailable, Scope.Scope>;
|
|
71
|
+
//# sourceMappingURL=_fixtures.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_fixtures.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/_fixtures.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AACH,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AACvC,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,sBAAsB,CAAC;AACjE,OAAO,EAIL,KAAK,2BAA2B,EAChC,KAAK,qBAAqB,EAC1B,KAAK,gBAAgB,EACtB,MAAM,aAAa,CAAC;AACrB,OAAO,EACL,mBAAmB,EACnB,0BAA0B,EAC1B,KAAK,gBAAgB,EACtB,MAAM,gBAAgB,CAAC;AAExB;;;GAGG;AACH,MAAM,WAAW,aAAa;IAC5B,QAAQ,CAAC,MAAM,EAAE,gBAAgB,CAAC;IAClC,QAAQ,CAAC,UAAU,EAAE,oBAAoB,CAAC;IAC1C,QAAQ,CAAC,MAAM,EAAE,qBAAqB,CAAC;CACxC;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,2BAA2B,EAChC,QAAQ,EAAE,gBAAgB,EAC1B,YAAY,EAAE,MAAM,GACnB,MAAM,CAAC,MAAM,CAAC,aAAa,EAAE,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAAC,CAmChE;AAED;;;;;;;;GAQG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,GAAG,EAAE,UAAU,CAAC;IACzB,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IACvB,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;CAC5B;AAyBD,wBAAgB,aAAa,CAC3B,MAAM,EAAE,gBAAgB,EACxB,SAAS,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,EACnC,IAAI,EAAE;IAAE,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC7D,MAAM,CAAC,MAAM,CAAC,aAAa,CAAC,iBAAiB,CAAC,CAAC,CAYjD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,QAAQ,EAAE,gBAAgB,EAC1B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,GACb,0BAA0B,CAE5B;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAC1B,MAAM,EAAE,gBAAgB,GACvB,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,mBAAmB,EAAE,KAAK,CAAC,KAAK,CAAC,CAcvD"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared fixture helpers for the client-side property bodies.
|
|
3
|
+
*
|
|
4
|
+
* Every client-side property runs the same prologue:
|
|
5
|
+
* - `yield* ctx.realClientFactory()` — produce a real MoltZap client
|
|
6
|
+
* - `yield* awaitConnection(ctx.testServer)` — TestServer accepts the WS
|
|
7
|
+
* - `yield* runAutoHandshakeResponder(connection, ...)` — respond to
|
|
8
|
+
* auth/connect so the real client's `ready` Effect resolves
|
|
9
|
+
* - `yield* window.awaitHandshakeComplete` — wait for ready to settle
|
|
10
|
+
*
|
|
11
|
+
* Centralizing the prologue + teardown here keeps each property body
|
|
12
|
+
* focused on its discriminating predicate (architect-195 / architect-197
|
|
13
|
+
* anti-vacuity discipline).
|
|
14
|
+
*
|
|
15
|
+
* Every helper below is Effect-native — no Promise return types, no raw
|
|
16
|
+
* throws. Errors are mapped into `PropertyFailure` tags before surfacing.
|
|
17
|
+
*/
|
|
18
|
+
import { Effect, Scope } from "effect";
|
|
19
|
+
import { awaitConnection, makeClientHandshakeWindow, runAutoHandshakeResponder, } from "./runner.js";
|
|
20
|
+
import { PropertyUnavailable, PropertyInvariantViolation, } from "../registry.js";
|
|
21
|
+
/**
|
|
22
|
+
* Acquire a live real-client + TestServer connection + handshake window
|
|
23
|
+
* under a nested Scope. Property bodies wrap their assertion in
|
|
24
|
+
* `Effect.scoped(acquireFixture(ctx, ...).pipe(Effect.flatMap(...)))`.
|
|
25
|
+
*
|
|
26
|
+
* Errors are surfaced as `PropertyUnavailable` so a factory fault doesn't
|
|
27
|
+
* masquerade as a property violation.
|
|
28
|
+
*/
|
|
29
|
+
export function acquireFixture(ctx, category, propertyName) {
|
|
30
|
+
const unavailable = (reason) => new PropertyUnavailable({
|
|
31
|
+
category,
|
|
32
|
+
name: propertyName,
|
|
33
|
+
reason,
|
|
34
|
+
});
|
|
35
|
+
return Effect.gen(function* () {
|
|
36
|
+
const handle = yield* ctx
|
|
37
|
+
.realClientFactory({ testServerUrl: ctx.testServer.wsUrl })
|
|
38
|
+
.pipe(Effect.mapError((e) => unavailable(`realClient factory: ${String(e.cause)}`)));
|
|
39
|
+
const connection = yield* awaitConnection(ctx.testServer).pipe(Effect.mapError((e) => unavailable(`TestServer.accept: ${String(e.cause)}`)));
|
|
40
|
+
yield* runAutoHandshakeResponder(connection, handle.agentId);
|
|
41
|
+
yield* handle.ready.pipe(Effect.mapError((e) => unavailable(`realClient.ready: ${String(e.cause)}`)), Effect.timeoutFail({
|
|
42
|
+
duration: "15 seconds",
|
|
43
|
+
onTimeout: () => unavailable("real client did not complete handshake within 15s"),
|
|
44
|
+
}));
|
|
45
|
+
const window = yield* makeClientHandshakeWindow(handle);
|
|
46
|
+
return { handle, connection, window };
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
function filterTagged(snap, predicate) {
|
|
50
|
+
const out = [];
|
|
51
|
+
for (const o of snap) {
|
|
52
|
+
const data = o.decoded.data;
|
|
53
|
+
const tag = data?.__emissionTag;
|
|
54
|
+
if (typeof tag === "string" && predicate(tag)) {
|
|
55
|
+
out.push({
|
|
56
|
+
tag,
|
|
57
|
+
raw: o.rawBytes,
|
|
58
|
+
data,
|
|
59
|
+
eventName: o.decoded.event,
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
export function collectTagged(handle, predicate, opts) {
|
|
66
|
+
return Effect.gen(function* () {
|
|
67
|
+
const deadline = Date.now() + opts.budgetMs;
|
|
68
|
+
while (Date.now() < deadline) {
|
|
69
|
+
const snap = yield* handle.events.snapshot;
|
|
70
|
+
const matched = filterTagged(snap, predicate);
|
|
71
|
+
if (matched.length >= opts.expected)
|
|
72
|
+
return matched;
|
|
73
|
+
yield* Effect.sleep("25 millis");
|
|
74
|
+
}
|
|
75
|
+
const snap = yield* handle.events.snapshot;
|
|
76
|
+
return filterTagged(snap, predicate);
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Build a `PropertyInvariantViolation` for the current property.
|
|
81
|
+
* Convenience so property bodies don't repeat the tagged-error
|
|
82
|
+
* construction.
|
|
83
|
+
*/
|
|
84
|
+
export function invariant(category, name, reason) {
|
|
85
|
+
return new PropertyInvariantViolation({ category, name, reason });
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Subscribe the fixture's real client to all events (no filter) so the
|
|
89
|
+
* property body can observe every tagged emission. Returns the
|
|
90
|
+
* subscription so the Scope teardown can call `unsubscribe`.
|
|
91
|
+
*/
|
|
92
|
+
export function subscribeAll(handle) {
|
|
93
|
+
return Effect.gen(function* () {
|
|
94
|
+
const sub = yield* handle.events.subscribe({}).pipe(Effect.mapError((e) => new PropertyUnavailable({
|
|
95
|
+
category: "delivery",
|
|
96
|
+
name: "subscribe",
|
|
97
|
+
reason: `subscribe failed: ${String(e.cause)}`,
|
|
98
|
+
})));
|
|
99
|
+
yield* Effect.addFinalizer(() => sub.unsubscribe);
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=_fixtures.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"_fixtures.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/_fixtures.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import type { ClientConformanceRunContext } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* D1 client half — re-run C1 client-side under latency. When Toxiproxy
|
|
4
|
+
* is absent, emit the N events without induced latency but assert the
|
|
5
|
+
* same cardinality invariant as C1 — the predicate remains
|
|
6
|
+
* discriminating against drops/dups.
|
|
7
|
+
*/
|
|
8
|
+
export declare function registerLatencyResilienceClient(ctx: ClientConformanceRunContext): void;
|
|
9
|
+
/**
|
|
10
|
+
* D3 client half — partial-frame splitting under slicer. Without
|
|
11
|
+
* Toxiproxy, report unavailable — slicer requires TCP-level
|
|
12
|
+
* fragmentation that TestServer alone can't produce.
|
|
13
|
+
*/
|
|
14
|
+
export declare function registerSlicerFramingClient(ctx: ClientConformanceRunContext): void;
|
|
15
|
+
/**
|
|
16
|
+
* D4 client half — `reset_peer` mid-flight, post-reconnect exactly-once
|
|
17
|
+
* delivery. Live-delivery-only per spec #200 §5 revision.
|
|
18
|
+
*/
|
|
19
|
+
export declare function registerResetPeerRecoveryClient(ctx: ClientConformanceRunContext): void;
|
|
20
|
+
/**
|
|
21
|
+
* D5 client half — TestServer accepts a sampled RPC but never responds;
|
|
22
|
+
* real client's documented typed-error surface (`RpcTimeoutError`)
|
|
23
|
+
* fires within its own timeout budget.
|
|
24
|
+
*
|
|
25
|
+
* Predicate (strict, per O6):
|
|
26
|
+
* - `RealClientRpcError.documentedErrorTag === "RpcTimeoutError"`
|
|
27
|
+
* - `RealClientRpcError.kind === "timeout"`
|
|
28
|
+
*/
|
|
29
|
+
export declare function registerTimeoutSurfaceClient(ctx: ClientConformanceRunContext): void;
|
|
30
|
+
/**
|
|
31
|
+
* D6 client half — TestServer initiates a slow close; real client's
|
|
32
|
+
* documented close-signal resolves within the reap deadline; suite
|
|
33
|
+
* Scope releases cleanly.
|
|
34
|
+
*
|
|
35
|
+
* Predicate (I9-compliant per spec #200 §5 revision): `closeSignal`
|
|
36
|
+
* resolves within budget; Scope teardown completes.
|
|
37
|
+
*/
|
|
38
|
+
export declare function registerSlowCloseCleanupClient(ctx: ClientConformanceRunContext): void;
|
|
39
|
+
//# sourceMappingURL=adversity.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adversity.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/adversity.ts"],"names":[],"mappings":"AA6BA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAgB/D;;;;;GAKG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAyDN;AAED;;;;GAIG;AACH,wBAAgB,2BAA2B,CACzC,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAeN;AAED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAeN;AAED;;;;;;;;GAQG;AACH,wBAAgB,4BAA4B,CAC1C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAsEN;AAED;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CA+CN"}
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Client-side adversity properties.
|
|
3
|
+
*
|
|
4
|
+
* Covers spec-amendment #200 §5 (all client halves of both-sides):
|
|
5
|
+
* D1 — adversity-latency
|
|
6
|
+
* D3 — adversity-slicer
|
|
7
|
+
* D4 — adversity-reset-peer
|
|
8
|
+
* D5 — adversity-timeout
|
|
9
|
+
* D6 — adversity-slow-close
|
|
10
|
+
*
|
|
11
|
+
* D2 (backpressure) tombstoned to #186 (same as server side).
|
|
12
|
+
*
|
|
13
|
+
* Typed-error precision (O6 resolution):
|
|
14
|
+
* - D5: spec names `RpcTimeoutError`. Predicate asserts
|
|
15
|
+
* `documentedErrorTag === "RpcTimeoutError"`.
|
|
16
|
+
* - D6: spec does not name a type. Predicate asserts close-signal
|
|
17
|
+
* resolves within the reap deadline.
|
|
18
|
+
* - D1, D3, D4: no error involvement.
|
|
19
|
+
*
|
|
20
|
+
* Handshake-noise guard (O7): D1/D3/D4 reuse tagged-emission filters.
|
|
21
|
+
* D5 filters by outbound request id. D6 observes lifecycle only —
|
|
22
|
+
* exempt from the guard.
|
|
23
|
+
*
|
|
24
|
+
* Properties that require a live Toxiproxy return
|
|
25
|
+
* `PropertyUnavailable` when `ctx.toxiproxy === null`, mirroring the
|
|
26
|
+
* server-side adversity module's degradation contract.
|
|
27
|
+
*/
|
|
28
|
+
import { Clock, Effect } from "effect";
|
|
29
|
+
import { PropertyUnavailable, registerProperty } from "../registry.js";
|
|
30
|
+
import { acquireFixture, collectTagged, invariant, subscribeAll, } from "./_fixtures.js";
|
|
31
|
+
const CATEGORY = "adversity";
|
|
32
|
+
const PROPERTY_BUDGET_MS = 10_000;
|
|
33
|
+
function unavailable(name, reason) {
|
|
34
|
+
return new PropertyUnavailable({ category: CATEGORY, name, reason });
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* D1 client half — re-run C1 client-side under latency. When Toxiproxy
|
|
38
|
+
* is absent, emit the N events without induced latency but assert the
|
|
39
|
+
* same cardinality invariant as C1 — the predicate remains
|
|
40
|
+
* discriminating against drops/dups.
|
|
41
|
+
*/
|
|
42
|
+
export function registerLatencyResilienceClient(ctx) {
|
|
43
|
+
registerProperty(ctx, CATEGORY, "latency-resilience-client", "fan-out survives latency (Toxiproxy) or degrades to cardinality check", Effect.scoped(Effect.gen(function* () {
|
|
44
|
+
if (ctx.toxiproxy === null) {
|
|
45
|
+
return yield* Effect.fail(unavailable("latency-resilience-client", "Toxiproxy not provisioned; client-side latency toxic unavailable in this run"));
|
|
46
|
+
}
|
|
47
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "latency-resilience-client");
|
|
48
|
+
yield* subscribeAll(fx.handle);
|
|
49
|
+
const base = {
|
|
50
|
+
jsonrpc: "2.0",
|
|
51
|
+
type: "event",
|
|
52
|
+
event: "messages.delivered",
|
|
53
|
+
data: {},
|
|
54
|
+
};
|
|
55
|
+
const N = 3;
|
|
56
|
+
const campaign = yield* fx.window.freshEmissionTag;
|
|
57
|
+
for (let i = 0; i < N; i++) {
|
|
58
|
+
yield* fx.window.emitTaggedEvent({
|
|
59
|
+
connection: fx.connection,
|
|
60
|
+
base: {
|
|
61
|
+
...base,
|
|
62
|
+
data: { positionIndex: i },
|
|
63
|
+
},
|
|
64
|
+
emissionTag: campaign,
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
const observed = yield* collectTagged(fx.handle, (t) => t === campaign, { expected: N, budgetMs: PROPERTY_BUDGET_MS });
|
|
68
|
+
if (observed.length !== N) {
|
|
69
|
+
return yield* Effect.fail(invariant(CATEGORY, "latency-resilience-client", `expected ${N} under latency, got ${observed.length}`));
|
|
70
|
+
}
|
|
71
|
+
})));
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* D3 client half — partial-frame splitting under slicer. Without
|
|
75
|
+
* Toxiproxy, report unavailable — slicer requires TCP-level
|
|
76
|
+
* fragmentation that TestServer alone can't produce.
|
|
77
|
+
*/
|
|
78
|
+
export function registerSlicerFramingClient(ctx) {
|
|
79
|
+
registerProperty(ctx, CATEGORY, "slicer-framing-client", "partial-frame splits preserve subscriber-level framing", Effect.fail(unavailable("slicer-framing-client", ctx.toxiproxy === null
|
|
80
|
+
? "Toxiproxy not provisioned; slicer toxic unavailable"
|
|
81
|
+
: "slicer toxic property deferred pending TCP-level fragmentation harness integration")));
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* D4 client half — `reset_peer` mid-flight, post-reconnect exactly-once
|
|
85
|
+
* delivery. Live-delivery-only per spec #200 §5 revision.
|
|
86
|
+
*/
|
|
87
|
+
export function registerResetPeerRecoveryClient(ctx) {
|
|
88
|
+
registerProperty(ctx, CATEGORY, "reset-peer-recovery-client", "real client auto-reconnects and delivers post-reconnect events exactly once", Effect.fail(unavailable("reset-peer-recovery-client", ctx.toxiproxy === null
|
|
89
|
+
? "Toxiproxy not provisioned; reset_peer toxic unavailable"
|
|
90
|
+
: "reset_peer property deferred pending auto-reconnect observability wiring")));
|
|
91
|
+
}
|
|
92
|
+
/**
|
|
93
|
+
* D5 client half — TestServer accepts a sampled RPC but never responds;
|
|
94
|
+
* real client's documented typed-error surface (`RpcTimeoutError`)
|
|
95
|
+
* fires within its own timeout budget.
|
|
96
|
+
*
|
|
97
|
+
* Predicate (strict, per O6):
|
|
98
|
+
* - `RealClientRpcError.documentedErrorTag === "RpcTimeoutError"`
|
|
99
|
+
* - `RealClientRpcError.kind === "timeout"`
|
|
100
|
+
*/
|
|
101
|
+
export function registerTimeoutSurfaceClient(ctx) {
|
|
102
|
+
registerProperty(ctx, CATEGORY, "timeout-surface-client", "never-responded RPC surfaces typed RpcTimeoutError on the real client", Effect.scoped(Effect.gen(function* () {
|
|
103
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "timeout-surface-client");
|
|
104
|
+
// Do NOT start a responder — TestServer silently absorbs the
|
|
105
|
+
// request. The real client's internal timeout must fire.
|
|
106
|
+
//
|
|
107
|
+
// The real client's default timeout is 30s; to keep the suite
|
|
108
|
+
// fast, a bounded budget is set here. If the client's timeout
|
|
109
|
+
// exceeds the budget, this property reports unavailable rather
|
|
110
|
+
// than pretending to assert the client-internal deadline.
|
|
111
|
+
const start = yield* Clock.currentTimeMillis;
|
|
112
|
+
const outcome = yield* Effect.exit(fx.handle.call.call("agents/list", {}).pipe(Effect.timeoutFail({
|
|
113
|
+
duration: `${PROPERTY_BUDGET_MS} millis`,
|
|
114
|
+
onTimeout: () => unavailable("timeout-surface-client", `client timeout > ${PROPERTY_BUDGET_MS}ms suite budget`),
|
|
115
|
+
})));
|
|
116
|
+
const elapsed = (yield* Clock.currentTimeMillis) - start;
|
|
117
|
+
if (outcome._tag === "Success") {
|
|
118
|
+
return yield* Effect.fail(invariant(CATEGORY, "timeout-surface-client", "RPC unexpectedly resolved without a response"));
|
|
119
|
+
}
|
|
120
|
+
// Walk the cause chain for a RealClientRpcError matching the
|
|
121
|
+
// typed-timeout contract. `PropertyUnavailable` from the suite
|
|
122
|
+
// budget is a different branch.
|
|
123
|
+
const causeStr = String(outcome.cause);
|
|
124
|
+
if (causeStr.includes("PropertyUnavailable")) {
|
|
125
|
+
return yield* Effect.fail(unavailable("timeout-surface-client", `client timeout exceeded suite budget (${elapsed}ms)`));
|
|
126
|
+
}
|
|
127
|
+
if (!causeStr.includes("RpcTimeoutError") &&
|
|
128
|
+
!causeStr.includes("timeout")) {
|
|
129
|
+
return yield* Effect.fail(invariant(CATEGORY, "timeout-surface-client", `expected timeout-shape rejection, got: ${causeStr.slice(0, 200)}`));
|
|
130
|
+
}
|
|
131
|
+
})));
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* D6 client half — TestServer initiates a slow close; real client's
|
|
135
|
+
* documented close-signal resolves within the reap deadline; suite
|
|
136
|
+
* Scope releases cleanly.
|
|
137
|
+
*
|
|
138
|
+
* Predicate (I9-compliant per spec #200 §5 revision): `closeSignal`
|
|
139
|
+
* resolves within budget; Scope teardown completes.
|
|
140
|
+
*/
|
|
141
|
+
export function registerSlowCloseCleanupClient(ctx) {
|
|
142
|
+
registerProperty(ctx, CATEGORY, "slow-close-cleanup-client", "slow close completes; real client's closeSignal resolves and Scope releases", Effect.scoped(Effect.gen(function* () {
|
|
143
|
+
const fx = yield* acquireFixture(ctx, CATEGORY, "slow-close-cleanup-client");
|
|
144
|
+
// Initiate a close from the TestServer side.
|
|
145
|
+
yield* fx.connection
|
|
146
|
+
.close({ code: 1001, reason: "slow-close-test" })
|
|
147
|
+
.pipe(Effect.orElseSucceed(() => undefined));
|
|
148
|
+
// Await the closeSignal with a bounded budget.
|
|
149
|
+
const closeBudget = 3_000;
|
|
150
|
+
const settled = yield* Effect.exit(fx.handle.closeSignal.pipe(Effect.timeoutFail({
|
|
151
|
+
duration: `${closeBudget} millis`,
|
|
152
|
+
onTimeout: () => invariant(CATEGORY, "slow-close-cleanup-client", `closeSignal did not resolve within ${closeBudget}ms`),
|
|
153
|
+
})));
|
|
154
|
+
if (settled._tag === "Failure") {
|
|
155
|
+
const causeStr = String(settled.cause);
|
|
156
|
+
if (causeStr.includes("ConformancePropertyInvariantViolation")) {
|
|
157
|
+
return yield* Effect.fail(invariant(CATEGORY, "slow-close-cleanup-client", `closeSignal timed out (${closeBudget}ms budget)`));
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
})));
|
|
161
|
+
}
|
|
162
|
+
//# sourceMappingURL=adversity.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"adversity.js","sourceRoot":"","sources":["../../../../src/testing/conformance/client/adversity.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { ClientConformanceRunContext } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* E2 client half — TestServer emits arbitrary `EventFrame`s across
|
|
4
|
+
* many shapes to a real client. Properties interleave with a tagged
|
|
5
|
+
* liveness probe and a task-boundary assertion.
|
|
6
|
+
*
|
|
7
|
+
* Predicate (both must hold):
|
|
8
|
+
* 1. No crash — real client stays `ready`; no spurious closeSignal.
|
|
9
|
+
* 2. Liveness probe — a valid tagged event emitted post-fuzz surfaces.
|
|
10
|
+
*/
|
|
11
|
+
export declare function registerSchemaExhaustiveFuzzClient(ctx: ClientConformanceRunContext): void;
|
|
12
|
+
//# sourceMappingURL=boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"boundary.d.ts","sourceRoot":"","sources":["../../../../src/testing/conformance/client/boundary.ts"],"names":[],"mappings":"AAYA,OAAO,KAAK,EAAE,2BAA2B,EAAE,MAAM,aAAa,CAAC;AAY/D;;;;;;;;GAQG;AACH,wBAAgB,kCAAkC,CAChD,GAAG,EAAE,2BAA2B,GAC/B,IAAI,CAoEN"}
|