import { describe, expect, it } from "vitest"; import { composeBrowserRpcOperationRegistry, composeBrowserRpcProviderProfileRegistry, composeBrowserRpcRequestEncoderRegistry, defineBrowserRpcOperation, defineBrowserRpcProviderProfile, installBrowserRpcContractBindings, validateBrowserRpcContractBindings, type BrowserRpcProviderProfile, } from "../../../src/contracts/browser-rpc.ts"; import { DESCRIPTOR_DIGEST, MAPPERS, RUNTIME_DIGEST, SCHEMA_CODECS, STREAM_ENCODER, UNARY_ENCODER, streamOperation, streamProfile, unaryOperation, unaryProfile, } from "./fixture.ts"; describe("Browser RPC contract registry", () => { it("snapshots installed bindings before later source mutation", () => { const operations: Record> = { GET_RPC_RESOURCE: unaryOperation(), }; const installed = installBrowserRpcContractBindings({ operations, profiles: { [unaryProfile().runtimeProfileId]: unaryProfile() }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER }, }); const before = installed.operations.get("GET_RPC_RESOURCE"); expect(before?.totalDeadlineMs).toBeDefined(); // R-04. A post-validation mutation of the source registry must not reach // the installed snapshot. operations.GET_RPC_RESOURCE = { ...operations.GET_RPC_RESOURCE!, totalDeadlineMs: 999_999, }; expect(installed.operations.get("GET_RPC_RESOURCE")).toBe(before); expect( installed.operations.get("GET_RPC_RESOURCE")?.totalDeadlineMs, ).not.toBe(999_999); }); it("rejects extra accessor and symbol keys without invoking getters", () => { let getterCalls = 0; const accessorOperation = Object.defineProperty( { ...unaryOperation() }, "totalDeadlineMs", { enumerable: true, configurable: true, get() { getterCalls += 1; return 1_000; }, }, ); expect(() => installBrowserRpcContractBindings({ operations: { GET_RPC_RESOURCE: accessorOperation }, profiles: { [unaryProfile().runtimeProfileId]: unaryProfile() }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER }, }), ).toThrow(TypeError); expect(getterCalls).toBe(0); const extraKeyOperation = { ...unaryOperation(), unexpectedKey: "smuggled", }; expect(() => installBrowserRpcContractBindings({ operations: { GET_RPC_RESOURCE: extraKeyOperation as never, }, profiles: { [unaryProfile().runtimeProfileId]: unaryProfile() }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER }, }), ).toThrow(/unexpected key/u); const symbolRegistry: Record = { GET_RPC_RESOURCE: unaryOperation(), }; Object.defineProperty(symbolRegistry, Symbol("hidden"), { enumerable: true, value: unaryOperation(), }); expect(() => installBrowserRpcContractBindings({ operations: symbolRegistry as never, profiles: { [unaryProfile().runtimeProfileId]: unaryProfile() }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER }, }), ).toThrow(/symbol keys/u); }); it("closes exact operation, provider, schema, mapper and encoder bindings", () => { const operation = unaryOperation(); const profile = unaryProfile(); const operations = composeBrowserRpcOperationRegistry([ { GET_RPC_RESOURCE: operation }, ]); const profiles = composeBrowserRpcProviderProfileRegistry([ { CONNECT_REFERENCE_UNARY: profile }, ]); const encoders = composeBrowserRpcRequestEncoderRegistry([ { RpcResourceRequestEncoder: UNARY_ENCODER }, ]); expect( validateBrowserRpcContractBindings({ operations, profiles, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: encoders, }), ).toBe(true); expect(Object.isFrozen(operations)).toBe(true); expect(Object.isFrozen(profiles.CONNECT_REFERENCE_UNARY)).toBe(true); expect( Object.isFrozen( profiles.CONNECT_REFERENCE_UNARY?.allowedProcedures, ), ).toBe(true); }); it("rejects duplicate rows and descriptor/provider drift", () => { const operation = unaryOperation(); expect(() => composeBrowserRpcOperationRegistry([ { GET_RPC_RESOURCE: operation }, { GET_RPC_RESOURCE: operation }, ]), ).toThrow("duplicate Browser RPC operation"); expect(() => validateBrowserRpcContractBindings({ operations: { GET_RPC_RESOURCE: operation }, profiles: { CONNECT_REFERENCE_UNARY: unaryProfile({ descriptorDigest: "c".repeat(64), }), }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER, }, }), ).toThrow("provider binding is invalid"); }); it("permits only a public headerless NO_SIDE_EFFECTS Connect GET", () => { const getProfile = defineBrowserRpcProviderProfile({ ...unaryProfile(), runtimeProfileId: "CONNECT_PUBLIC_GET", requestMethod: "GET", authProfileId: "ANONYMOUS", csrfProfileId: "NONE", allowedProcedures: [ "example.resource.v1.ResourceService/GetResource", ], }); const validGet = defineBrowserRpcOperation({ ...unaryOperation(), runtimeProfileId: "CONNECT_PUBLIC_GET", authProfileId: "ANONYMOUS", csrfProfileId: "NONE", idempotencyLevel: "NO_SIDE_EFFECTS", dataClassification: "PUBLIC", }); expect( validateBrowserRpcContractBindings({ operations: { GET_RPC_RESOURCE: validGet }, profiles: { CONNECT_PUBLIC_GET: getProfile }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER, }, }), ).toBe(true); expect(() => validateBrowserRpcContractBindings({ operations: { GET_RPC_RESOURCE: defineBrowserRpcOperation({ ...validGet, dataClassification: "CONFIDENTIAL", }), }, profiles: { CONNECT_PUBLIC_GET: getProfile }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER, }, }), ).toThrow("GET binding is invalid"); }); it("separates official grpc-web, Connect-Web and Connect tuples", () => { expect(() => defineBrowserRpcProviderProfile({ ...streamProfile(), runtimeProfileId: "OFFICIAL_BINARY_STREAM", runtimeId: "official-grpc-web", runtimeVersion: "1.5.0", runtimeDigest: RUNTIME_DIGEST, protocol: "GRPC_WEB", runtimeKind: "OFFICIAL_GRPC_WEB_XHR", clientApiKind: "CALLBACK_STREAM", messageEncoding: "PROTO", framing: "GRPC_WEB_BINARY_ENVELOPE", deadlineDialect: "OFFICIAL_DEADLINE_METADATA", cancelDialect: "CLIENT_READABLE_STREAM_CANCEL", descriptorDigest: DESCRIPTOR_DIGEST, }), ).toThrow("provider profile is invalid"); expect(() => defineBrowserRpcProviderProfile({ ...unaryProfile(), framing: "GRPC_WEB_BINARY_ENVELOPE", }), ).toThrow("provider profile is invalid"); expect( defineBrowserRpcProviderProfile({ ...unaryProfile(), runtimeProfileId: "CONNECT_GRPC_WEB_UNARY", protocol: "GRPC_WEB", framing: "GRPC_WEB_BINARY_ENVELOPE", deadlineDialect: "GRPC_TIMEOUT", }), ).toMatchObject({ protocol: "GRPC_WEB", runtimeKind: "CONNECT_WEB_FETCH", deadlineDialect: "GRPC_TIMEOUT", }); }); it("revalidates raw registry rows instead of trusting TypeScript assertions", () => { const invalidProfile = { ...unaryProfile(), messageEncoding: "XML", } as unknown as BrowserRpcProviderProfile; expect(() => validateBrowserRpcContractBindings({ operations: { GET_RPC_RESOURCE: unaryOperation() }, profiles: { CONNECT_REFERENCE_UNARY: invalidProfile }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER, }, }), ).toThrow("provider profile is invalid"); expect(() => validateBrowserRpcContractBindings({ operations: { WRONG_REGISTRY_KEY: unaryOperation() }, profiles: { CONNECT_REFERENCE_UNARY: unaryProfile() }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: UNARY_ENCODER, }, }), ).toThrow("operation registry is invalid"); }); it("disallows frontend stream retry and unsafe unary replay", () => { expect(() => defineBrowserRpcProviderProfile({ ...streamProfile(), retryOwner: "FRONTEND_ADAPTER", maxAttempts: 2, backoffMs: [10], retryableFailures: ["UNAVAILABLE"], }), ).toThrow("provider profile is invalid"); const retryProfile = unaryProfile({ retryProfileId: "RPC_RETRY_TWO", retryOwner: "FRONTEND_ADAPTER", maxAttempts: 2, backoffMs: [10], retryableFailures: ["UNAVAILABLE"], }); expect(() => validateBrowserRpcContractBindings({ operations: { CREATE_RPC_RESOURCE: defineBrowserRpcOperation({ ...unaryOperation(), operationId: "CREATE_RPC_RESOURCE", semantics: "COMMAND", replayPolicy: "NON_REPLAYABLE", runtimeProfileId: retryProfile.runtimeProfileId, retryProfileId: retryProfile.retryProfileId, }), }, profiles: { CONNECT_REFERENCE_UNARY: retryProfile }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceRequestEncoder: { ...UNARY_ENCODER, operationId: "CREATE_RPC_RESOURCE", }, }, }), ).toThrow("retry binding is invalid"); }); it("supports a bounded Connect server-stream contract without composing it", () => { expect( validateBrowserRpcContractBindings({ operations: { WATCH_RPC_RESOURCES: streamOperation(), }, profiles: { CONNECT_REFERENCE_STREAM: streamProfile(), }, schemaCodecs: SCHEMA_CODECS, mappers: MAPPERS, requestEncoders: { RpcResourceStreamRequestEncoder: STREAM_ENCODER, }, }), ).toBe(true); }); });