@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,394 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RPC semantics — properties that compare the real server's observable
|
|
3
|
+
* outcome against the reference-model reducer, and exercise authority
|
|
4
|
+
* + request-id + idempotence invariants end-to-end.
|
|
5
|
+
*
|
|
6
|
+
* Historical grouping note: spec #181 §5 calls this "Tier B". Code uses
|
|
7
|
+
* semantic names only.
|
|
8
|
+
*
|
|
9
|
+
* Principle 3: every property body is `Effect<void, PropertyFailure>`.
|
|
10
|
+
*/
|
|
11
|
+
import * as fc from "fast-check";
|
|
12
|
+
import { Effect } from "effect";
|
|
13
|
+
import { arbitraryAnyCall, arbitraryConfidentCall, confidentOracleMethods, } from "../arbitraries/rpc.js";
|
|
14
|
+
import { applyCall, authorizationOutcome, isIdempotent, } from "../models/dispatch.js";
|
|
15
|
+
import { initialReferenceState } from "../models/state.js";
|
|
16
|
+
import { ErrorCodes } from "../../schema/errors.js";
|
|
17
|
+
import { canonicalJson, sortJsonArray } from "../canonicalize.js";
|
|
18
|
+
import { makeTestClient } from "../test-client.js";
|
|
19
|
+
import { registerTestAgent } from "../agent-registration.js";
|
|
20
|
+
import { allRpcMethods } from "../arbitraries/rpc.js";
|
|
21
|
+
import { assertProperty, PropertyInvariantViolation, PropertyUnavailable, registerProperty, } from "./registry.js";
|
|
22
|
+
const CATEGORY = "rpc-semantics";
|
|
23
|
+
const DEFAULT_TIMEOUT_MS = 3000;
|
|
24
|
+
const DEFAULT_CAPTURE_CAPACITY = 64;
|
|
25
|
+
/**
|
|
26
|
+
* Model-equivalence — conditional oracle over the model-derived
|
|
27
|
+
* confident set (architect #195 §4.1 + #197 §2).
|
|
28
|
+
*
|
|
29
|
+
* Spec §5 B1: the server must produce what the model predicts when
|
|
30
|
+
* the model is confident. `arbitraryConfidentCall()` draws calls via
|
|
31
|
+
* the architect-literal shape `fc.constantFrom(...kept).chain(
|
|
32
|
+
* arbitraryCallFor)` — probe and execution share the same generator
|
|
33
|
+
* so confidence is checked on the same distribution the property
|
|
34
|
+
* exercises (round-8 finding: a `.map(m => ({method: m, params: {}}))`
|
|
35
|
+
* shortcut narrowed execution below the probe and hid real
|
|
36
|
+
* param-dependent divergences).
|
|
37
|
+
*
|
|
38
|
+
* Param-invariance safety net (#197 §2.2 + §6.1): if a drawn call
|
|
39
|
+
* comes back `_tag: "error"` from the model, the single-probe
|
|
40
|
+
* derivation has diverged from runtime truth (applyCall became
|
|
41
|
+
* param-sensitive for that method under a later draw). The property
|
|
42
|
+
* raises `PropertyInvariantViolation` instead of silently short-
|
|
43
|
+
* circuiting; the fix is to widen the derivation (probe with K > 1
|
|
44
|
+
* samples), not extend this property.
|
|
45
|
+
*
|
|
46
|
+
* Current K = 1 (agents/list only). Architect #197 §2.3 notes that
|
|
47
|
+
* "when K ≤ 2, the property is operating as a small number of hand-
|
|
48
|
+
* picked examples; document it in JSDoc, don't pretend it's a fuzz
|
|
49
|
+
* property." Widening K requires either teaching `applyCall` per-
|
|
50
|
+
* method param filters (e.g. `conversations/list` confident only
|
|
51
|
+
* when cursor is undefined/valid) or fixing server-side parsers that
|
|
52
|
+
* error on pathological schema-valid params (e.g. `cursor: " "` →
|
|
53
|
+
* SqlError on pglite cursor parsing). Tracked under #186.
|
|
54
|
+
*
|
|
55
|
+
* numRuns floor: `max(10, 2K)` = 10 today.
|
|
56
|
+
*/
|
|
57
|
+
export function registerModelEquivalence(ctx) {
|
|
58
|
+
const K = confidentOracleMethods.length;
|
|
59
|
+
const numRunsFloor = Math.max(10, 2 * K);
|
|
60
|
+
registerProperty(ctx, CATEGORY, "model-equivalence", `when model predicts ok, server MUST return ok (K=${K} confident methods)`, assertProperty(CATEGORY, "model-equivalence", () => fc.assert(
|
|
61
|
+
// #ignore-sloppy-code-next-line[async-keyword]: fast-check asyncProperty contract requires Promise-returning callback
|
|
62
|
+
fc.asyncProperty(arbitraryConfidentCall(), async (call) => {
|
|
63
|
+
const modelTag = applyCall(initialReferenceState, call).outcome._tag;
|
|
64
|
+
if (modelTag === "error") {
|
|
65
|
+
// Safety-net guard: `arbitraryConfidentCall` derived this
|
|
66
|
+
// method as confident at module load. If the model now
|
|
67
|
+
// disagrees, applyCall became param-sensitive for the
|
|
68
|
+
// kept method and the derivation must widen. Surface
|
|
69
|
+
// loudly instead of silent short-circuit.
|
|
70
|
+
throw new Error(`arbitraryConfidentCall drew ${call.method} with params ${JSON.stringify(call.params)} → model _tag: "error" — param-invariance contract broken; widen derivation to fc.sample-based check per architect #197 §2.2`);
|
|
71
|
+
}
|
|
72
|
+
const serverTag = await Effect.runPromise(Effect.scoped(Effect.gen(function* () {
|
|
73
|
+
const agent = yield* registerTestAgent({
|
|
74
|
+
baseUrl: ctx.realServer.baseUrl,
|
|
75
|
+
name: "me",
|
|
76
|
+
});
|
|
77
|
+
const client = yield* makeTestClient({
|
|
78
|
+
serverUrl: ctx.realServer.wsUrl,
|
|
79
|
+
agentKey: agent.apiKey,
|
|
80
|
+
agentId: agent.agentId,
|
|
81
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
82
|
+
captureCapacity: DEFAULT_CAPTURE_CAPACITY,
|
|
83
|
+
});
|
|
84
|
+
const outcome = yield* client
|
|
85
|
+
.sendRpc(call.method, call.params)
|
|
86
|
+
.pipe(Effect.either);
|
|
87
|
+
return outcome._tag === "Right" ? "ok" : "error";
|
|
88
|
+
})));
|
|
89
|
+
// Model is confident it's `ok`. Server MUST agree.
|
|
90
|
+
return serverTag === "ok";
|
|
91
|
+
}), { seed: ctx.seed, numRuns: ctx.opts.numRuns ?? numRunsFloor })));
|
|
92
|
+
// Keep `arbitraryAnyCall` import alive — used by sibling properties.
|
|
93
|
+
void arbitraryAnyCall;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* Authorized caller → typed success on at least one known-safe RPC.
|
|
97
|
+
* Registers a fresh agent, completes the handshake, calls
|
|
98
|
+
* `conversations/list` (empty-collection result is defined for every
|
|
99
|
+
* newly-registered agent), asserts a Right outcome.
|
|
100
|
+
*/
|
|
101
|
+
export function registerAuthorityPositive(ctx) {
|
|
102
|
+
registerProperty(ctx, CATEGORY, "authority-positive", "authorized agent → typed success on conversations/list", Effect.scoped(Effect.gen(function* () {
|
|
103
|
+
const agent = yield* registerTestAgent({
|
|
104
|
+
baseUrl: ctx.realServer.baseUrl,
|
|
105
|
+
name: "ap",
|
|
106
|
+
}).pipe(Effect.mapError((e) => new PropertyInvariantViolation({
|
|
107
|
+
category: CATEGORY,
|
|
108
|
+
name: "authority-positive",
|
|
109
|
+
reason: `agent registration failed: ${e.body}`,
|
|
110
|
+
})));
|
|
111
|
+
const client = yield* makeTestClient({
|
|
112
|
+
serverUrl: ctx.realServer.wsUrl,
|
|
113
|
+
agentKey: agent.apiKey,
|
|
114
|
+
agentId: agent.agentId,
|
|
115
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
116
|
+
captureCapacity: DEFAULT_CAPTURE_CAPACITY,
|
|
117
|
+
}).pipe(Effect.mapError((e) => new PropertyInvariantViolation({
|
|
118
|
+
category: CATEGORY,
|
|
119
|
+
name: "authority-positive",
|
|
120
|
+
reason: `client acquire failed: ${String(e)}`,
|
|
121
|
+
})));
|
|
122
|
+
const outcome = yield* client
|
|
123
|
+
.sendRpc("conversations/list", {})
|
|
124
|
+
.pipe(Effect.either);
|
|
125
|
+
if (outcome._tag === "Left") {
|
|
126
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
127
|
+
category: CATEGORY,
|
|
128
|
+
name: "authority-positive",
|
|
129
|
+
reason: `authorized conversations/list failed: ${outcome.left._tag}`,
|
|
130
|
+
}));
|
|
131
|
+
}
|
|
132
|
+
})));
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Unauthenticated caller → typed denial on an auth-gated RPC. Opens a
|
|
136
|
+
* TestClient with `autoConnect: false` and calls `conversations/list`
|
|
137
|
+
* without first completing `auth/connect`; asserts the server replies
|
|
138
|
+
* with a typed error (not a success, not a crash).
|
|
139
|
+
*/
|
|
140
|
+
export function registerAuthorityNegative(ctx) {
|
|
141
|
+
registerProperty(ctx, CATEGORY, "authority-negative", "unauthenticated agent → typed denial on conversations/list", Effect.scoped(Effect.gen(function* () {
|
|
142
|
+
// We still need an agentKey to open the socket, but we skip
|
|
143
|
+
// `auth/connect` so the server sees an un-authed session.
|
|
144
|
+
const agent = yield* registerTestAgent({
|
|
145
|
+
baseUrl: ctx.realServer.baseUrl,
|
|
146
|
+
name: "an",
|
|
147
|
+
}).pipe(Effect.mapError((e) => new PropertyInvariantViolation({
|
|
148
|
+
category: CATEGORY,
|
|
149
|
+
name: "authority-negative",
|
|
150
|
+
reason: `agent registration failed: ${e.body}`,
|
|
151
|
+
})));
|
|
152
|
+
const client = yield* makeTestClient({
|
|
153
|
+
serverUrl: ctx.realServer.wsUrl,
|
|
154
|
+
agentKey: agent.apiKey,
|
|
155
|
+
agentId: agent.agentId,
|
|
156
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
157
|
+
captureCapacity: DEFAULT_CAPTURE_CAPACITY,
|
|
158
|
+
autoConnect: false,
|
|
159
|
+
}).pipe(Effect.mapError((e) => new PropertyInvariantViolation({
|
|
160
|
+
category: CATEGORY,
|
|
161
|
+
name: "authority-negative",
|
|
162
|
+
reason: `client acquire failed: ${String(e)}`,
|
|
163
|
+
})));
|
|
164
|
+
const outcome = yield* client
|
|
165
|
+
.sendRpc("conversations/list", {})
|
|
166
|
+
.pipe(Effect.either);
|
|
167
|
+
if (outcome._tag === "Right") {
|
|
168
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
169
|
+
category: CATEGORY,
|
|
170
|
+
name: "authority-negative",
|
|
171
|
+
reason: "pre-handshake conversations/list returned success — expected typed denial",
|
|
172
|
+
}));
|
|
173
|
+
}
|
|
174
|
+
// Narrow the Left: must be a typed auth-shaped RpcResponseError
|
|
175
|
+
// (Unauthorized / Forbidden). A timeout or transport-close
|
|
176
|
+
// would also surface as `Left` but does NOT satisfy the
|
|
177
|
+
// property — it proves nothing about authorization.
|
|
178
|
+
if (outcome.left._tag !== "TestingRpcResponseError") {
|
|
179
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
180
|
+
category: CATEGORY,
|
|
181
|
+
name: "authority-negative",
|
|
182
|
+
reason: `expected RpcResponseError, got ${outcome.left._tag}`,
|
|
183
|
+
}));
|
|
184
|
+
}
|
|
185
|
+
const code = outcome.left.code;
|
|
186
|
+
const isAuthShaped = code === ErrorCodes.Unauthorized || code === ErrorCodes.Forbidden;
|
|
187
|
+
if (!isAuthShaped) {
|
|
188
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
189
|
+
category: CATEGORY,
|
|
190
|
+
name: "authority-negative",
|
|
191
|
+
reason: `expected Unauthorized/Forbidden code (${ErrorCodes.Unauthorized} / ${ErrorCodes.Forbidden}), got ${code}`,
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
// Oracle cross-check: the model also predicts deny for this
|
|
195
|
+
// unauthenticated caller. Keeps the model honest alongside the
|
|
196
|
+
// server.
|
|
197
|
+
const modelVerdict = authorizationOutcome(initialReferenceState, { method: "conversations/list", params: {} }, "unknown-agent");
|
|
198
|
+
if (modelVerdict !== "deny-unauthenticated") {
|
|
199
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
200
|
+
category: CATEGORY,
|
|
201
|
+
name: "authority-negative",
|
|
202
|
+
reason: `model oracle disagrees: expected deny-unauthenticated, got ${modelVerdict}`,
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
})));
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* Request-IDs are unique per inbound response. Sends N RPCs and asserts
|
|
209
|
+
* every id in the captured response stream appears exactly once.
|
|
210
|
+
*/
|
|
211
|
+
export function registerRequestIdUniqueness(ctx) {
|
|
212
|
+
registerProperty(ctx, CATEGORY, "request-id-uniqueness", "every request-id appears in exactly one response", assertProperty(CATEGORY, "request-id-uniqueness", () => fc.assert(
|
|
213
|
+
// #ignore-sloppy-code-next-line[async-keyword]: fast-check asyncProperty contract requires Promise-returning callback
|
|
214
|
+
fc.asyncProperty(fc.integer({ min: 2, max: 6 }), async (n) => {
|
|
215
|
+
const counts = await Effect.runPromise(Effect.scoped(Effect.gen(function* () {
|
|
216
|
+
const agent = yield* registerTestAgent({
|
|
217
|
+
baseUrl: ctx.realServer.baseUrl,
|
|
218
|
+
name: "ru",
|
|
219
|
+
});
|
|
220
|
+
const client = yield* makeTestClient({
|
|
221
|
+
serverUrl: ctx.realServer.wsUrl,
|
|
222
|
+
agentKey: agent.apiKey,
|
|
223
|
+
agentId: agent.agentId,
|
|
224
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
225
|
+
captureCapacity: n * 4,
|
|
226
|
+
});
|
|
227
|
+
// Snapshot the capture boundary after handshake so we
|
|
228
|
+
// only tally response ids for the N RPCs below — not
|
|
229
|
+
// the auto-connect reply.
|
|
230
|
+
const handshakeEnd = (yield* client.snapshot).length;
|
|
231
|
+
yield* Effect.forEach(Array.from({ length: n }, (_, i) => i), () => client
|
|
232
|
+
.sendRpc("conversations/list", {})
|
|
233
|
+
.pipe(Effect.either), { concurrency: n });
|
|
234
|
+
const snap = (yield* client.snapshot).slice(handshakeEnd);
|
|
235
|
+
const outboundIds = new Set();
|
|
236
|
+
const inboundIds = new Set();
|
|
237
|
+
let inboundCount = 0;
|
|
238
|
+
for (const entry of snap) {
|
|
239
|
+
if (entry.frame?.type !== "request" &&
|
|
240
|
+
entry.frame?.type !== "response")
|
|
241
|
+
continue;
|
|
242
|
+
if (entry.kind === "outbound" &&
|
|
243
|
+
entry.frame.type === "request") {
|
|
244
|
+
outboundIds.add(entry.frame.id);
|
|
245
|
+
}
|
|
246
|
+
if (entry.kind === "inbound" &&
|
|
247
|
+
entry.frame.type === "response") {
|
|
248
|
+
inboundIds.add(entry.frame.id);
|
|
249
|
+
inboundCount += 1;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
return { outboundIds, inboundIds, inboundCount };
|
|
253
|
+
})));
|
|
254
|
+
// Architect §4.2 set-equality predicate. Conjunction:
|
|
255
|
+
// - outboundIds.size === n (driver produced n frames)
|
|
256
|
+
// - inboundIds.size === outboundIds.size (cardinality match)
|
|
257
|
+
// - every outbound id is matched inbound (no drops, no strays)
|
|
258
|
+
// - inboundCount === inboundIds.size (no inbound duplicates)
|
|
259
|
+
// Stray IDs, dropped replies, and id-reuse all fail the property.
|
|
260
|
+
const { outboundIds, inboundIds, inboundCount } = counts;
|
|
261
|
+
if (outboundIds.size !== n)
|
|
262
|
+
return false;
|
|
263
|
+
if (inboundIds.size !== outboundIds.size)
|
|
264
|
+
return false;
|
|
265
|
+
if (inboundCount !== inboundIds.size)
|
|
266
|
+
return false;
|
|
267
|
+
for (const id of outboundIds) {
|
|
268
|
+
if (!inboundIds.has(id))
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
return true;
|
|
272
|
+
}), { seed: ctx.seed, numRuns: ctx.opts.numRuns ?? 5 })));
|
|
273
|
+
}
|
|
274
|
+
/**
|
|
275
|
+
* Idempotent RPCs yield equivalent responses on replay. For every
|
|
276
|
+
* list-shaped method where empty params are valid and `isIdempotent`
|
|
277
|
+
* says replay is safe, sends the same params twice and asserts both
|
|
278
|
+
* succeed with **identical results** (not just identical tags).
|
|
279
|
+
*
|
|
280
|
+
* Architect §4.4: removed `.pipe(Effect.orElseSucceed(["skip","skip"]))`
|
|
281
|
+
* masking. Transport failures now surface as `PropertyUnavailable` so
|
|
282
|
+
* the runner reports them explicitly instead of folding them into a
|
|
283
|
+
* silent pass. Predicate compares response bodies via canonical JSON
|
|
284
|
+
* — spec B5 says "identical results", not "identical outcome kinds".
|
|
285
|
+
*/
|
|
286
|
+
export function registerIdempotence(ctx) {
|
|
287
|
+
registerProperty(ctx, CATEGORY, "idempotence", "isIdempotent methods: two sends yield identical response bodies", Effect.gen(function* () {
|
|
288
|
+
const emptyParamIdempotents = [
|
|
289
|
+
"agents/list",
|
|
290
|
+
"conversations/list",
|
|
291
|
+
];
|
|
292
|
+
for (const method of emptyParamIdempotents) {
|
|
293
|
+
if (!isIdempotent(method)) {
|
|
294
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
295
|
+
category: CATEGORY,
|
|
296
|
+
name: "idempotence",
|
|
297
|
+
reason: `isIdempotent(${method}) is false — oracle disagreement`,
|
|
298
|
+
}));
|
|
299
|
+
}
|
|
300
|
+
const pair = yield* Effect.scoped(Effect.gen(function* () {
|
|
301
|
+
const agent = yield* registerTestAgent({
|
|
302
|
+
baseUrl: ctx.realServer.baseUrl,
|
|
303
|
+
name: "id",
|
|
304
|
+
});
|
|
305
|
+
const client = yield* makeTestClient({
|
|
306
|
+
serverUrl: ctx.realServer.wsUrl,
|
|
307
|
+
agentKey: agent.apiKey,
|
|
308
|
+
agentId: agent.agentId,
|
|
309
|
+
defaultTimeoutMs: DEFAULT_TIMEOUT_MS,
|
|
310
|
+
captureCapacity: DEFAULT_CAPTURE_CAPACITY,
|
|
311
|
+
});
|
|
312
|
+
const a = yield* client.sendRpc(method, {}).pipe(Effect.either);
|
|
313
|
+
const b = yield* client.sendRpc(method, {}).pipe(Effect.either);
|
|
314
|
+
return { a, b };
|
|
315
|
+
})).pipe(Effect.catchTags({
|
|
316
|
+
TestingAgentRegistrationError: (e) => Effect.fail(new PropertyUnavailable({
|
|
317
|
+
category: CATEGORY,
|
|
318
|
+
name: "idempotence",
|
|
319
|
+
reason: `register: ${e.body}`,
|
|
320
|
+
})),
|
|
321
|
+
TestingTransportIoError: (e) => Effect.fail(new PropertyUnavailable({
|
|
322
|
+
category: CATEGORY,
|
|
323
|
+
name: "idempotence",
|
|
324
|
+
reason: `transport io: ${String(e.cause)}`,
|
|
325
|
+
})),
|
|
326
|
+
TestingTransportClosedError: (e) => Effect.fail(new PropertyUnavailable({
|
|
327
|
+
category: CATEGORY,
|
|
328
|
+
name: "idempotence",
|
|
329
|
+
reason: `transport closed: ${e.reason}`,
|
|
330
|
+
})),
|
|
331
|
+
TestingRpcResponseError: (e) => Effect.fail(new PropertyUnavailable({
|
|
332
|
+
category: CATEGORY,
|
|
333
|
+
name: "idempotence",
|
|
334
|
+
reason: `rpc response error: ${e.message}`,
|
|
335
|
+
})),
|
|
336
|
+
}));
|
|
337
|
+
if (pair.a._tag !== pair.b._tag) {
|
|
338
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
339
|
+
category: CATEGORY,
|
|
340
|
+
name: "idempotence",
|
|
341
|
+
reason: `${method}: replay outcome-tag mismatch ${pair.a._tag} → ${pair.b._tag}`,
|
|
342
|
+
}));
|
|
343
|
+
}
|
|
344
|
+
if (pair.a._tag === "Right" && pair.b._tag === "Right") {
|
|
345
|
+
// Canonical-projection comparison per architect #197 §3.3.
|
|
346
|
+
// Direct JSON.stringify on wire-derived values is byte-
|
|
347
|
+
// equality, not semantic equality; a conforming server may
|
|
348
|
+
// return the list in a different row order across replays.
|
|
349
|
+
const aCanon = canonIdempotenceResult(method, pair.a.right);
|
|
350
|
+
const bCanon = canonIdempotenceResult(method, pair.b.right);
|
|
351
|
+
if (aCanon !== bCanon) {
|
|
352
|
+
return yield* Effect.fail(new PropertyInvariantViolation({
|
|
353
|
+
category: CATEGORY,
|
|
354
|
+
name: "idempotence",
|
|
355
|
+
reason: `${method}: replay bodies diverge under canonical projection`,
|
|
356
|
+
}));
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}));
|
|
361
|
+
void allRpcMethods;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Idempotence canonical projection — architect #197 §3.3.
|
|
365
|
+
*
|
|
366
|
+
* Spec B5: agents/list.agents and conversations/list.conversations are
|
|
367
|
+
* unordered row sets across replays. Every OTHER array (including any
|
|
368
|
+
* nested `participants`, future nested message lists, and every
|
|
369
|
+
* payload field that is not one of the two named arrays) remains
|
|
370
|
+
* order-sensitive.
|
|
371
|
+
*
|
|
372
|
+
* The projection sorts ONLY the specific top-level array the spec
|
|
373
|
+
* marks unordered, then finalizes via `canonicalJson` (which
|
|
374
|
+
* normalizes object-key order but preserves every remaining array's
|
|
375
|
+
* order). A real re-ordering regression inside nested arrays still
|
|
376
|
+
* fails the property.
|
|
377
|
+
*/
|
|
378
|
+
function canonIdempotenceResult(method, result) {
|
|
379
|
+
if (method === "agents/list") {
|
|
380
|
+
const r = result;
|
|
381
|
+
return canonicalJson({
|
|
382
|
+
...r,
|
|
383
|
+
agents: Array.isArray(r.agents) ? sortJsonArray(r.agents) : r.agents,
|
|
384
|
+
});
|
|
385
|
+
}
|
|
386
|
+
const r = result;
|
|
387
|
+
return canonicalJson({
|
|
388
|
+
...r,
|
|
389
|
+
conversations: Array.isArray(r.conversations)
|
|
390
|
+
? sortJsonArray(r.conversations)
|
|
391
|
+
: r.conversations,
|
|
392
|
+
});
|
|
393
|
+
}
|
|
394
|
+
//# sourceMappingURL=rpc-semantics.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rpc-semantics.js","sourceRoot":"","sources":["../../../src/testing/conformance/rpc-semantics.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conformance-suite runner.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates tiers A → E under one entrypoint so
|
|
5
|
+
* `pnpm -F @moltzap/protocol test:conformance` is the only command a CI
|
|
6
|
+
* job needs (AC11).
|
|
7
|
+
*
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* - receive a real MoltZap server handle (built externally to preserve
|
|
10
|
+
* AC13 one-way imports);
|
|
11
|
+
* - build a Toxiproxy client when Tier D is in scope;
|
|
12
|
+
* - pin fast-check seeds and export them on failure (AC10);
|
|
13
|
+
* - tear everything down in reverse order.
|
|
14
|
+
*/
|
|
15
|
+
import { Effect, Ref, Scope } from "effect";
|
|
16
|
+
import { type ToxiproxyClient } from "../toxics/client.js";
|
|
17
|
+
import { RealServerAcquireError, ToxicControlError } from "../errors.js";
|
|
18
|
+
/**
|
|
19
|
+
* Opaque handle to a running real MoltZap server. The conformance runner
|
|
20
|
+
* accepts this as an injected dependency so the protocol package has no
|
|
21
|
+
* compile-time import of `packages/server` (AC13). The consuming suite
|
|
22
|
+
* file supplies a concrete value built from `startCoreTestServer`.
|
|
23
|
+
*/
|
|
24
|
+
export interface RealServerHandle {
|
|
25
|
+
readonly wsUrl: string;
|
|
26
|
+
readonly baseUrl: string;
|
|
27
|
+
/** Teardown hook; the runner's Scope calls this on release. */
|
|
28
|
+
readonly close: () => Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export interface ConformanceRunOptions {
|
|
31
|
+
readonly tiers: ReadonlyArray<"A" | "B" | "C" | "D" | "E">;
|
|
32
|
+
/** Supplier for the real server; invoked once per run. */
|
|
33
|
+
readonly realServer: () => Promise<RealServerHandle>;
|
|
34
|
+
/** If provided, replay this exact fast-check seed (AC10 reproducibility). */
|
|
35
|
+
readonly replaySeed?: number;
|
|
36
|
+
/** Number of runs per property; fast-check default is 100. */
|
|
37
|
+
readonly numRuns?: number;
|
|
38
|
+
/** When `true`, bring up docker-compose Toxiproxy; else assume running. */
|
|
39
|
+
readonly manageToxiproxy?: boolean;
|
|
40
|
+
/** Toxiproxy control URL — defaults to `http://127.0.0.1:8474`. */
|
|
41
|
+
readonly toxiproxyUrl?: string;
|
|
42
|
+
/** Output directory for seed + toxic-config dump on failure. */
|
|
43
|
+
readonly artifactDir?: string;
|
|
44
|
+
}
|
|
45
|
+
export interface ConformanceRunContext {
|
|
46
|
+
readonly realServer: RealServerHandle;
|
|
47
|
+
readonly toxiproxy: ToxiproxyClient | null;
|
|
48
|
+
readonly opts: ConformanceRunOptions;
|
|
49
|
+
/** Seed to pin every property to. Exported on failure for replay. */
|
|
50
|
+
readonly seed: number;
|
|
51
|
+
/**
|
|
52
|
+
* Per-property artifact sink. The tier modules call `record` to stash a
|
|
53
|
+
* seed + toxic profile when a property fails; the suite post-process
|
|
54
|
+
* writes to `opts.artifactDir`.
|
|
55
|
+
*/
|
|
56
|
+
readonly artifacts: Ref.Ref<ReadonlyArray<ConformanceArtifact>>;
|
|
57
|
+
}
|
|
58
|
+
export interface ConformanceArtifact {
|
|
59
|
+
readonly tierId: string;
|
|
60
|
+
readonly propId: string;
|
|
61
|
+
readonly seed: number;
|
|
62
|
+
readonly toxicProfile?: string;
|
|
63
|
+
readonly commandSequence?: ReadonlyArray<unknown>;
|
|
64
|
+
readonly captures?: ReadonlyArray<unknown>;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Acquire the full context (real server + optional Toxiproxy) under one
|
|
68
|
+
* Scope; Vitest's `beforeAll`/`afterAll` tick the scope.
|
|
69
|
+
*/
|
|
70
|
+
export declare function acquireRunContext(opts: ConformanceRunOptions): Effect.Effect<ConformanceRunContext, ToxicControlError | RealServerAcquireError, Scope.Scope>;
|
|
71
|
+
/**
|
|
72
|
+
* Entry point the runner script calls. Iterates `opts.tiers` and writes a
|
|
73
|
+
* summary line per tier; the actual properties register themselves with
|
|
74
|
+
* Vitest via each tier module's `register*` functions — so this only
|
|
75
|
+
* orchestrates output + seed plumbing.
|
|
76
|
+
*/
|
|
77
|
+
export declare function runConformance(ctx: ConformanceRunContext): Effect.Effect<void>;
|
|
78
|
+
//# sourceMappingURL=runner.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../../../src/testing/conformance/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAuB,KAAK,eAAe,EAAE,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AAGzE;;;;;GAKG;AACH,MAAM,WAAW,gBAAgB;IAC/B,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,+DAA+D;IAC/D,QAAQ,CAAC,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;CACrC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,KAAK,EAAE,aAAa,CAAC,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC;IAC3D,0DAA0D;IAC1D,QAAQ,CAAC,UAAU,EAAE,MAAM,OAAO,CAAC,gBAAgB,CAAC,CAAC;IACrD,6EAA6E;IAC7E,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,8DAA8D;IAC9D,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC;IAC1B,2EAA2E;IAC3E,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,mEAAmE;IACnE,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,gEAAgE;IAChE,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,UAAU,EAAE,gBAAgB,CAAC;IACtC,QAAQ,CAAC,SAAS,EAAE,eAAe,GAAG,IAAI,CAAC;IAC3C,QAAQ,CAAC,IAAI,EAAE,qBAAqB,CAAC;IACrC,qEAAqE;IACrE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB;;;;OAIG;IACH,QAAQ,CAAC,SAAS,EAAE,GAAG,CAAC,GAAG,CAAC,aAAa,CAAC,mBAAmB,CAAC,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAC/B,QAAQ,CAAC,eAAe,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;IAClD,QAAQ,CAAC,QAAQ,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAC;CAC5C;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,qBAAqB,GAC1B,MAAM,CAAC,MAAM,CACd,qBAAqB,EACrB,iBAAiB,GAAG,sBAAsB,EAC1C,KAAK,CAAC,KAAK,CACZ,CA2CA;AAED;;;;;GAKG;AACH,wBAAgB,cAAc,CAC5B,GAAG,EAAE,qBAAqB,GACzB,MAAM,CAAC,MAAM,CAAC,IAAI,CAAC,CAMrB"}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Conformance-suite runner.
|
|
3
|
+
*
|
|
4
|
+
* Orchestrates tiers A → E under one entrypoint so
|
|
5
|
+
* `pnpm -F @moltzap/protocol test:conformance` is the only command a CI
|
|
6
|
+
* job needs (AC11).
|
|
7
|
+
*
|
|
8
|
+
* Responsibilities:
|
|
9
|
+
* - receive a real MoltZap server handle (built externally to preserve
|
|
10
|
+
* AC13 one-way imports);
|
|
11
|
+
* - build a Toxiproxy client when Tier D is in scope;
|
|
12
|
+
* - pin fast-check seeds and export them on failure (AC10);
|
|
13
|
+
* - tear everything down in reverse order.
|
|
14
|
+
*/
|
|
15
|
+
import { Effect, Ref, Scope } from "effect";
|
|
16
|
+
import { makeToxiproxyClient } from "../toxics/client.js";
|
|
17
|
+
import { RealServerAcquireError, ToxicControlError } from "../errors.js";
|
|
18
|
+
import { conformanceNumRunsFromEnv } from "./env.js";
|
|
19
|
+
/**
|
|
20
|
+
* Acquire the full context (real server + optional Toxiproxy) under one
|
|
21
|
+
* Scope; Vitest's `beforeAll`/`afterAll` tick the scope.
|
|
22
|
+
*/
|
|
23
|
+
export function acquireRunContext(opts) {
|
|
24
|
+
return Effect.gen(function* () {
|
|
25
|
+
const effectiveOpts = {
|
|
26
|
+
...opts,
|
|
27
|
+
numRuns: opts.numRuns ?? conformanceNumRunsFromEnv(),
|
|
28
|
+
};
|
|
29
|
+
const seed = effectiveOpts.replaySeed ??
|
|
30
|
+
Number(process.env.FC_SEED ?? Date.now() & 0x7fffffff);
|
|
31
|
+
const artifacts = yield* Ref.make([]);
|
|
32
|
+
const realServer = yield* Effect.acquireRelease(Effect.tryPromise({
|
|
33
|
+
try: () => opts.realServer(),
|
|
34
|
+
catch: (cause) => new RealServerAcquireError({ cause }),
|
|
35
|
+
}), (handle) => Effect.tryPromise({
|
|
36
|
+
try: () => handle.close(),
|
|
37
|
+
catch: () => new Error("realServer.close() threw"),
|
|
38
|
+
}).pipe(Effect.orElseSucceed(() => undefined), Effect.asVoid));
|
|
39
|
+
let toxiproxy = null;
|
|
40
|
+
if (effectiveOpts.tiers.includes("D")) {
|
|
41
|
+
const url = effectiveOpts.toxiproxyUrl ?? "http://127.0.0.1:8474";
|
|
42
|
+
toxiproxy = yield* makeToxiproxyClient({ apiUrl: url });
|
|
43
|
+
yield* toxiproxy.ping.pipe(Effect.retry({ times: 10, schedule: undefined }));
|
|
44
|
+
}
|
|
45
|
+
return {
|
|
46
|
+
realServer,
|
|
47
|
+
toxiproxy,
|
|
48
|
+
opts: effectiveOpts,
|
|
49
|
+
seed,
|
|
50
|
+
artifacts,
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Entry point the runner script calls. Iterates `opts.tiers` and writes a
|
|
56
|
+
* summary line per tier; the actual properties register themselves with
|
|
57
|
+
* Vitest via each tier module's `register*` functions — so this only
|
|
58
|
+
* orchestrates output + seed plumbing.
|
|
59
|
+
*/
|
|
60
|
+
export function runConformance(ctx) {
|
|
61
|
+
return Effect.sync(() => {
|
|
62
|
+
console.log(`[conformance] seed=${ctx.seed} tiers=${ctx.opts.tiers.join(",")} toxiproxy=${ctx.toxiproxy !== null}`);
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
//# sourceMappingURL=runner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"runner.js","sourceRoot":"","sources":["../../../src/testing/conformance/runner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC;AAC5C,OAAO,EAAE,mBAAmB,EAAwB,MAAM,qBAAqB,CAAC;AAChF,OAAO,EAAE,sBAAsB,EAAE,iBAAiB,EAAE,MAAM,cAAc,CAAC;AACzE,OAAO,EAAE,yBAAyB,EAAE,MAAM,UAAU,CAAC;AAsDrD;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAC/B,IAA2B;IAM3B,OAAO,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QACzB,MAAM,aAAa,GAAG;YACpB,GAAG,IAAI;YACP,OAAO,EAAE,IAAI,CAAC,OAAO,IAAI,yBAAyB,EAAE;SACrD,CAAC;QACF,MAAM,IAAI,GACR,aAAa,CAAC,UAAU;YACxB,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,OAAO,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,UAAU,CAAC,CAAC;QACzD,MAAM,SAAS,GAAG,KAAK,CAAC,CAAC,GAAG,CAAC,IAAI,CAAqC,EAAE,CAAC,CAAC;QAE1E,MAAM,UAAU,GAAG,KAAK,CAAC,CAAC,MAAM,CAAC,cAAc,CAC7C,MAAM,CAAC,UAAU,CAAC;YAChB,GAAG,EAAE,GAAG,EAAE,CAAC,IAAI,CAAC,UAAU,EAAE;YAC5B,KAAK,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,sBAAsB,CAAC,EAAE,KAAK,EAAE,CAAC;SACxD,CAAC,EACF,CAAC,MAAM,EAAE,EAAE,CACT,MAAM,CAAC,UAAU,CAAC;YAChB,GAAG,EAAE,GAAG,EAAE,CAAC,MAAM,CAAC,KAAK,EAAE;YACzB,KAAK,EAAE,GAAG,EAAE,CAAC,IAAI,KAAK,CAAC,0BAA0B,CAAC;SACnD,CAAC,CAAC,IAAI,CACL,MAAM,CAAC,aAAa,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,EACrC,MAAM,CAAC,MAAM,CACd,CACJ,CAAC;QAEF,IAAI,SAAS,GAA2B,IAAI,CAAC;QAC7C,IAAI,aAAa,CAAC,KAAK,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACtC,MAAM,GAAG,GAAG,aAAa,CAAC,YAAY,IAAI,uBAAuB,CAAC;YAClE,SAAS,GAAG,KAAK,CAAC,CAAC,mBAAmB,CAAC,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAC;YACxD,KAAK,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CACxB,MAAM,CAAC,KAAK,CAAC,EAAE,KAAK,EAAE,EAAE,EAAE,QAAQ,EAAE,SAAS,EAAE,CAAC,CACjD,CAAC;QACJ,CAAC;QAED,OAAO;YACL,UAAU;YACV,SAAS;YACT,IAAI,EAAE,aAAa;YACnB,IAAI;YACJ,SAAS;SACsB,CAAC;IACpC,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,cAAc,CAC5B,GAA0B;IAE1B,OAAO,MAAM,CAAC,IAAI,CAAC,GAAG,EAAE;QACtB,OAAO,CAAC,GAAG,CACT,sBAAsB,GAAG,CAAC,IAAI,UAAU,GAAG,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,GAAG,CAAC,SAAS,KAAK,IAAI,EAAE,CACvG,CAAC;IACJ,CAAC,CAAC,CAAC;AACL,CAAC"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import type { ConformanceRunContext } from "./runner.js";
|
|
2
|
+
/**
|
|
3
|
+
* Valid request ⇒ valid-shape response. Drives fast-check RPC calls
|
|
4
|
+
* through a real TestClient against the real server and asserts every
|
|
5
|
+
* returned frame parses against `ResponseFrameSchema`.
|
|
6
|
+
*/
|
|
7
|
+
export declare function registerRequestWellFormedness(ctx: ConformanceRunContext): void;
|
|
8
|
+
/**
|
|
9
|
+
* Valid event frame round-trips the codec cleanly.
|
|
10
|
+
*
|
|
11
|
+
* `@pure-codec` — does NOT drive a real server or real client. Spec A2's
|
|
12
|
+
* "accepted by a real client" assertion requires a TestServer + real
|
|
13
|
+
* client driver; that property lives in the consumer package (e.g.,
|
|
14
|
+
* `packages/client` or `moltzap-arena`) and is tracked under #186.
|
|
15
|
+
*
|
|
16
|
+
* Tightened per architect §4.4: predicate demands `Right` AND schema
|
|
17
|
+
* check of the specific event variant — the previous `Right || Left`
|
|
18
|
+
* shape was a tautology over the union.
|
|
19
|
+
*/
|
|
20
|
+
export declare function registerEventWellFormedness(ctx: ConformanceRunContext): void;
|
|
21
|
+
/** parse(serialize(frame)) ≡ frame — pure codec round-trip. */
|
|
22
|
+
export declare function registerRoundTripIdentity(ctx: ConformanceRunContext): void;
|
|
23
|
+
/**
|
|
24
|
+
* Malformed bytes on the wire → the server drops or returns a typed
|
|
25
|
+
* error, never crashes. Drives `sendMalformed` through a real WS and
|
|
26
|
+
* asserts the observable outcome.
|
|
27
|
+
*/
|
|
28
|
+
export declare function registerMalformedFrameHandling(ctx: ConformanceRunContext): void;
|
|
29
|
+
export declare function registerRpcMapCoverage(ctx: ConformanceRunContext): void;
|
|
30
|
+
//# sourceMappingURL=schema-conformance.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"schema-conformance.d.ts","sourceRoot":"","sources":["../../../src/testing/conformance/schema-conformance.ts"],"names":[],"mappings":"AAiCA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAWzD;;;;GAIG;AACH,wBAAgB,6BAA6B,CAC3C,GAAG,EAAE,qBAAqB,GACzB,IAAI,CAqEN;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,2BAA2B,CAAC,GAAG,EAAE,qBAAqB,GAAG,IAAI,CAyB5E;AAED,+DAA+D;AAC/D,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,qBAAqB,GAAG,IAAI,CA6B1E;AAED;;;;GAIG;AACH,wBAAgB,8BAA8B,CAC5C,GAAG,EAAE,qBAAqB,GACzB,IAAI,CAyDN;AAgBD,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,qBAAqB,GAAG,IAAI,CAqEvE"}
|