chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,264 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
composeBrowserRpcOperationRegistry,
|
||||
composeBrowserRpcProviderProfileRegistry,
|
||||
composeBrowserRpcRequestEncoderRegistry,
|
||||
defineBrowserRpcOperation,
|
||||
defineBrowserRpcProviderProfile,
|
||||
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("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);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user