chore: initialize from frontend template 4dc033c
This commit is contained in:
@@ -0,0 +1,70 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
validateEnvelope,
|
||||
validateOperationPayload,
|
||||
validateOperationRequest,
|
||||
} from "../../src/adapters/http/schema-registry.ts";
|
||||
import {
|
||||
composeRuntimeSchemaCodecs,
|
||||
validateWithRuntimeSchemaRegistry,
|
||||
} from "../../src/contracts/schema-registry.ts";
|
||||
|
||||
describe("HTTP platform schema boundary", () => {
|
||||
it("rejects an invalid top-level envelope", () => {
|
||||
expect(validateEnvelope({ success: true }).success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts and clones a generic valid response envelope", () => {
|
||||
const source = {
|
||||
success: true,
|
||||
data: [],
|
||||
meta: { requestId: "request-1", traceId: "trace-1" },
|
||||
};
|
||||
const result = validateEnvelope(source);
|
||||
expect(result).toMatchObject({ success: true, data: source });
|
||||
if (!result.success) throw new Error("expected valid envelope");
|
||||
expect(result.data).not.toBe(source);
|
||||
});
|
||||
|
||||
it("fails closed without leaking input when a feature schema is absent", () => {
|
||||
const result = validateOperationPayload("UnknownPayload", {
|
||||
secret: "not-projected",
|
||||
});
|
||||
expect(result).toMatchObject({
|
||||
success: false,
|
||||
issues: [{ path: "", code: "SCHEMA_NOT_REGISTERED" }],
|
||||
});
|
||||
expect(JSON.stringify(result)).not.toContain("not-projected");
|
||||
expect(
|
||||
validateOperationRequest("UnknownCommand", { name: "Example" }).success,
|
||||
).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe("runtime schema codec contribution", () => {
|
||||
const codec = {
|
||||
schemaId: "Example",
|
||||
parse: (value: unknown) => ({ success: true as const, data: value }),
|
||||
};
|
||||
|
||||
it("resolves installed codecs and fails missing IDs closed", () => {
|
||||
const registry = composeRuntimeSchemaCodecs([{ Example: codec }]);
|
||||
expect(validateWithRuntimeSchemaRegistry("Example", 42, registry)).toEqual({
|
||||
success: true,
|
||||
data: 42,
|
||||
});
|
||||
expect(
|
||||
validateWithRuntimeSchemaRegistry("Missing", 42, registry),
|
||||
).toMatchObject({
|
||||
success: false,
|
||||
issues: [{ code: "SCHEMA_NOT_REGISTERED" }],
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects duplicate codec contributions", () => {
|
||||
expect(() =>
|
||||
composeRuntimeSchemaCodecs([{ Example: codec }, { Example: codec }]),
|
||||
).toThrow("duplicate runtime schema codec");
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,242 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import {
|
||||
loadReleaseManifest,
|
||||
ReleaseManifestError,
|
||||
} from "../../src/bootstrap/load-release-manifest.ts";
|
||||
import { computeContractSetDigest } from "../../src/contracts/contract-set-canonical.ts";
|
||||
|
||||
const EMPTY_SET_DIGEST = await computeContractSetDigest([]);
|
||||
|
||||
const runtime: Parameters<typeof loadReleaseManifest>[0] = {
|
||||
build: {
|
||||
buildId: "build-a",
|
||||
commitSha: "abc123",
|
||||
routerBasePath: "/",
|
||||
runtimeConfigUrl: "/config.json",
|
||||
},
|
||||
config: {
|
||||
APP_ENV: "local",
|
||||
API_BASE_URL: "https://api.test",
|
||||
REQUEST_TIMEOUT_MS: 10_000,
|
||||
MAX_RETRY_ATTEMPTS: 2,
|
||||
TELEMETRY_ENABLED: false,
|
||||
AUTH_MODE: "external",
|
||||
RELEASE_MANIFEST_URL: "/release-manifest.json",
|
||||
BUILD_ID: "build-a",
|
||||
RELEASE_ID: "release-a",
|
||||
CONFIG_SCHEMA_VERSION: "2.0",
|
||||
CAPABILITY_OVERRIDES: {
|
||||
REALTIME: "DEFAULT",
|
||||
WEB_WORKER: "DEFAULT",
|
||||
SERVICE_WORKER: "DEFAULT",
|
||||
OFFLINE_COMMANDS: "DEFAULT",
|
||||
},
|
||||
},
|
||||
configSchema: "V2",
|
||||
validationDurationMs: 0,
|
||||
};
|
||||
const manifest = {
|
||||
schemaVersion: 2,
|
||||
appVersion: "0.1.0",
|
||||
buildId: "build-a",
|
||||
commitSha: "abc123",
|
||||
configSchemaVersion: "2.0",
|
||||
assetManifestHash: "hash-a",
|
||||
releaseId: "release-a",
|
||||
builtAt: "2026-07-25T00:00:00Z",
|
||||
routeChunks: { "route-home": "assets/home.js" },
|
||||
contractSet: {
|
||||
setAlgorithm: "CA_CONTRACT_SET_V1",
|
||||
setDigest: EMPTY_SET_DIGEST,
|
||||
packages: [],
|
||||
},
|
||||
};
|
||||
|
||||
const runtimeV1: Parameters<typeof loadReleaseManifest>[0] = {
|
||||
...runtime,
|
||||
config: {
|
||||
...runtime.config,
|
||||
CONFIG_SCHEMA_VERSION: "1",
|
||||
LEGACY_API_CONTRACT_VERSION: "1",
|
||||
},
|
||||
configSchema: "V1",
|
||||
};
|
||||
|
||||
const manifestV1 = {
|
||||
schemaVersion: 1,
|
||||
appVersion: "0.1.0",
|
||||
buildId: "build-a",
|
||||
commitSha: "abc123",
|
||||
configSchemaVersion: "1",
|
||||
apiContractVersion: "1",
|
||||
assetManifestHash: "hash-a",
|
||||
releaseId: "release-a",
|
||||
builtAt: "2026-07-25T00:00:00Z",
|
||||
routeChunks: { "route-home": "assets/home.js" },
|
||||
};
|
||||
|
||||
function jsonResponse(body: unknown): Response {
|
||||
return new Response(JSON.stringify(body), {
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
}
|
||||
|
||||
describe("release manifest boot boundary", () => {
|
||||
it("loads a coherent release tuple", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtime,
|
||||
{ fetcher: async () => jsonResponse(manifest) },
|
||||
),
|
||||
).resolves.toMatchObject({ releaseId: "release-a" });
|
||||
});
|
||||
|
||||
it("fails before mount when release and runtime differ", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtime,
|
||||
{
|
||||
fetcher: async () =>
|
||||
jsonResponse({ ...manifest, buildId: "build-b" }),
|
||||
},
|
||||
),
|
||||
).rejects.toMatchObject({
|
||||
kind: "BUILD_MISMATCH",
|
||||
code: "MANIFEST_BUILD_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it.each([
|
||||
[
|
||||
{ releaseId: "release-b" },
|
||||
{},
|
||||
"RELEASE_MISMATCH",
|
||||
"MANIFEST_RELEASE_MISMATCH",
|
||||
],
|
||||
[
|
||||
{},
|
||||
{ expectedAssetManifestHash: "different" },
|
||||
"ASSET_MISMATCH",
|
||||
"MANIFEST_ASSET_MISMATCH",
|
||||
],
|
||||
])(
|
||||
"classifies tuple mismatch %# without a generic deploy error",
|
||||
async (manifestOverride, options, kind, code) => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtime,
|
||||
{
|
||||
fetcher: async () =>
|
||||
jsonResponse({ ...manifest, ...manifestOverride }),
|
||||
...options,
|
||||
},
|
||||
),
|
||||
).rejects.toMatchObject({ kind, code });
|
||||
},
|
||||
);
|
||||
|
||||
it.each(["3.0", "9.0"])(
|
||||
"rejects unsupported V2 manifest config version %s at the schema boundary",
|
||||
async (configSchemaVersion) => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtime, {
|
||||
fetcher: async () =>
|
||||
jsonResponse({ ...manifest, configSchemaVersion }),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "RELEASE_MANIFEST_FAILURE",
|
||||
code: "MANIFEST_SCHEMA_INVALID",
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
it("rejects a contract set the build did not compile", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtime, {
|
||||
fetcher: async () =>
|
||||
jsonResponse({
|
||||
...manifest,
|
||||
contractSet: {
|
||||
setAlgorithm: "CA_CONTRACT_SET_V1",
|
||||
setDigest: EMPTY_SET_DIGEST,
|
||||
packages: [
|
||||
{
|
||||
packageId: "@org-contracts/worklog",
|
||||
version: "1.2.3",
|
||||
digest: `sha256:${"a".repeat(64)}`,
|
||||
runtimeProtocolVersion: 1,
|
||||
sourceRevision: "abc1234",
|
||||
},
|
||||
],
|
||||
},
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "CONTRACT_SET_MISMATCH",
|
||||
code: "CONTRACT_SET_PACKAGE_UNEXPECTED",
|
||||
});
|
||||
});
|
||||
|
||||
it("still reads a V1 manifest during the compatibility window", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtimeV1,
|
||||
{
|
||||
fetcher: async () => jsonResponse(manifestV1),
|
||||
},
|
||||
),
|
||||
).resolves.toMatchObject({ schemaVersion: 1, contractSet: null });
|
||||
});
|
||||
|
||||
it("rejects a V2 runtime paired with a V1 manifest", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtime, {
|
||||
fetcher: async () => jsonResponse({
|
||||
...manifestV1,
|
||||
configSchemaVersion: "2.0",
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "PROTOCOL_PAIR_MISMATCH",
|
||||
code: "MANIFEST_PROTOCOL_PAIR_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a V1 runtime paired with a V2 manifest", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtimeV1, {
|
||||
fetcher: async () => jsonResponse(manifest),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "PROTOCOL_PAIR_MISMATCH",
|
||||
code: "MANIFEST_PROTOCOL_PAIR_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it("requires matching legacy scalar versions for a V1 pair", async () => {
|
||||
await expect(
|
||||
loadReleaseManifest(runtimeV1, {
|
||||
fetcher: async () => jsonResponse({
|
||||
...manifestV1,
|
||||
apiContractVersion: "2",
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
kind: "API_CONTRACT_MISMATCH",
|
||||
code: "MANIFEST_API_CONTRACT_MISMATCH",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a manifest without a complete route chunk map", async () => {
|
||||
const malformed = Object.fromEntries(
|
||||
Object.entries(manifest).filter(([key]) => key !== "routeChunks"),
|
||||
);
|
||||
await expect(
|
||||
loadReleaseManifest(
|
||||
runtime,
|
||||
{ fetcher: async () => jsonResponse(malformed) },
|
||||
),
|
||||
).rejects.toBeInstanceOf(ReleaseManifestError);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,189 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import { loadRuntimeConfig } from "../../src/bootstrap/load-runtime-config.ts";
|
||||
import { validateRuntimeConfig } from "../../src/bootstrap/runtime-config-schema.ts";
|
||||
import { assertSafeConfigNames } from "../../src/contracts/env.ts";
|
||||
|
||||
const validConfig = {
|
||||
APP_ENV: "local",
|
||||
API_BASE_URL: "http://localhost:8080/",
|
||||
REQUEST_TIMEOUT_MS: 10_000,
|
||||
MAX_RETRY_ATTEMPTS: 2,
|
||||
TELEMETRY_ENABLED: false,
|
||||
AUTH_MODE: "external",
|
||||
CONFIG_SCHEMA_VERSION: "2.0",
|
||||
RELEASE_MANIFEST_URL: "/release-manifest.json",
|
||||
BUILD_ID: "build-a",
|
||||
};
|
||||
|
||||
const validV1Config = {
|
||||
...validConfig,
|
||||
CONFIG_SCHEMA_VERSION: "1",
|
||||
API_CONTRACT_VERSION: "1.4.0",
|
||||
};
|
||||
|
||||
describe("runtime configuration boundary", () => {
|
||||
it.each([
|
||||
[{ ...validConfig, API_BASE_URL: undefined }, "required key"],
|
||||
[{ ...validConfig, REQUEST_TIMEOUT_MS: 0 }, "integer range"],
|
||||
[{ ...validConfig, MAX_RETRY_ATTEMPTS: 3 }, "retry cap"],
|
||||
[{ ...validConfig, TELEMETRY_ENABLED: "false" }, "ambiguous boolean"],
|
||||
[{ ...validConfig, CONFIG_SCHEMA_VERSION: "next" }, "version"],
|
||||
[
|
||||
{ ...validConfig, API_CONTRACT_VERSION: "1" },
|
||||
"§5.1 scalar contract version is removed from V2",
|
||||
],
|
||||
[
|
||||
{ ...validConfig, API_BASE_URL: "http://localhost:8080/api" },
|
||||
"§6.2 base URL path must end with /",
|
||||
],
|
||||
[
|
||||
{ ...validConfig, API_BASE_URL: "http://localhost:8080/#frag" },
|
||||
"§6.2 hash in base URL",
|
||||
],
|
||||
[
|
||||
{ ...validConfig, API_BASE_URL: "http://user:pw@localhost:8080/" },
|
||||
"§6.2 credentials in URL",
|
||||
],
|
||||
[
|
||||
{ ...validConfig, RELEASE_MANIFEST_URL: "/a/../b.json" },
|
||||
"§6.2 dot segment traversal",
|
||||
],
|
||||
[
|
||||
{ ...validConfig, RELEASE_MANIFEST_URL: "/manifest.json?v=1" },
|
||||
"§6.2 query in manifest URL",
|
||||
],
|
||||
[{ ...validConfig, UNKNOWN_KEY: true }, "unknown key"],
|
||||
])("rejects invalid config: %s (%s)", (candidate, _reason) => {
|
||||
void _reason;
|
||||
expect(validateRuntimeConfig(candidate).success).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects secret-like names before schema validation", () => {
|
||||
expect(() => assertSafeConfigNames({ CLIENT_SECRET: "not-safe" })).toThrow(
|
||||
"Forbidden client configuration key",
|
||||
);
|
||||
});
|
||||
|
||||
it("allows demo authentication only for local runtime configuration", () => {
|
||||
expect(
|
||||
validateRuntimeConfig({ ...validConfig, AUTH_MODE: "demo" }).success,
|
||||
).toBe(true);
|
||||
expect(
|
||||
validateRuntimeConfig({
|
||||
...validConfig,
|
||||
APP_ENV: "production",
|
||||
API_BASE_URL: "https://api.example.test/",
|
||||
AUTH_MODE: "demo",
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts only the explicitly supported V1 and V2 boot versions", () => {
|
||||
expect(validateRuntimeConfig(validV1Config)).toMatchObject({
|
||||
success: true,
|
||||
schema: "V1",
|
||||
});
|
||||
expect(validateRuntimeConfig(validConfig)).toMatchObject({
|
||||
success: true,
|
||||
schema: "V2",
|
||||
});
|
||||
|
||||
for (const version of ["0", "1.0", "2.0.1", "3.0"]) {
|
||||
expect(
|
||||
validateRuntimeConfig({
|
||||
...validV1Config,
|
||||
CONFIG_SCHEMA_VERSION: version,
|
||||
}).success,
|
||||
).toBe(false);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
"file:///tmp/api/",
|
||||
"data:text/plain,/",
|
||||
"blob:https://example.test/00000000-0000-0000-0000-000000000000",
|
||||
])("rejects a non-HTTP API endpoint in local mode: %s", (API_BASE_URL) => {
|
||||
expect(validateRuntimeConfig({ ...validConfig, API_BASE_URL }).success).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
|
||||
it.each(["file:///tmp/telemetry", "data:text/plain,telemetry"])(
|
||||
"rejects a non-HTTP telemetry endpoint in local mode: %s",
|
||||
(TELEMETRY_ENDPOINT) => {
|
||||
expect(
|
||||
validateRuntimeConfig({
|
||||
...validConfig,
|
||||
TELEMETRY_ENABLED: true,
|
||||
TELEMETRY_ENDPOINT,
|
||||
}).success,
|
||||
).toBe(false);
|
||||
},
|
||||
);
|
||||
|
||||
it("validates a fetched config under the 500ms budget excluding network", async () => {
|
||||
let current = 100;
|
||||
const result = await loadRuntimeConfig({
|
||||
buildConfig: {
|
||||
buildId: "build-a",
|
||||
commitSha: "local",
|
||||
routerBasePath: "/",
|
||||
runtimeConfigUrl: "/config.json",
|
||||
},
|
||||
fetcher: async () =>
|
||||
new Response(JSON.stringify(validConfig), {
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
now: () => (current += 2),
|
||||
});
|
||||
|
||||
expect(result.validationDurationMs).toBeLessThanOrEqual(500);
|
||||
expect(result.config.API_BASE_URL).toBe("http://localhost:8080/");
|
||||
expect(result.configSchema).toBe("V2");
|
||||
expect(result.config.CAPABILITY_OVERRIDES.SERVICE_WORKER).toBe("DEFAULT");
|
||||
});
|
||||
|
||||
it("does not include network acquisition in validationDurationMs", async () => {
|
||||
let current = 0;
|
||||
const result = await loadRuntimeConfig({
|
||||
buildConfig: {
|
||||
buildId: "build-a",
|
||||
commitSha: "local",
|
||||
routerBasePath: "/",
|
||||
runtimeConfigUrl: "/config.json",
|
||||
},
|
||||
fetcher: async () => {
|
||||
current = 10_000;
|
||||
return new Response(JSON.stringify(validConfig), {
|
||||
headers: { "content-type": "application/json" },
|
||||
});
|
||||
},
|
||||
now: () => current,
|
||||
});
|
||||
|
||||
expect(result.validationDurationMs).toBe(0);
|
||||
});
|
||||
|
||||
it("returns only safe boot fields on failure", async () => {
|
||||
await expect(
|
||||
loadRuntimeConfig({
|
||||
buildConfig: {
|
||||
buildId: "build-a",
|
||||
commitSha: "local",
|
||||
routerBasePath: "/",
|
||||
runtimeConfigUrl: "/config.json",
|
||||
},
|
||||
fetcher: async () =>
|
||||
new Response("{", {
|
||||
headers: { "content-type": "application/json" },
|
||||
}),
|
||||
}),
|
||||
).rejects.toMatchObject({
|
||||
safe: {
|
||||
kind: "BOOT_CONFIG_FAILURE",
|
||||
buildId: "build-a",
|
||||
},
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user