feat: 기능 추가 과정중
This commit is contained in:
@@ -0,0 +1,184 @@
|
||||
import { describe, expect, it, vi } from "vitest";
|
||||
|
||||
import {
|
||||
createRuntimeAdapters,
|
||||
createRuntimeHttpClient,
|
||||
} from "../../src/bootstrap/runtime-adapters.ts";
|
||||
import { TEST_HTTP_CONTRACT } from "../helpers/http-contract-fixture.ts";
|
||||
|
||||
type Runtime = Parameters<typeof createRuntimeAdapters>[0]["runtime"];
|
||||
type Release = Parameters<typeof createRuntimeAdapters>[0]["release"];
|
||||
|
||||
const runtime: Runtime = {
|
||||
config: {
|
||||
APP_ENV: "local",
|
||||
API_BASE_URL: "http://localhost:8080",
|
||||
TELEMETRY_ENABLED: false,
|
||||
AUTH_MODE: "demo",
|
||||
REQUEST_TIMEOUT_MS: 4321,
|
||||
MAX_RETRY_ATTEMPTS: 0,
|
||||
RELEASE_MANIFEST_URL: "/release-manifest.json",
|
||||
CONFIG_SCHEMA_VERSION: "1",
|
||||
API_CONTRACT_VERSION: "1",
|
||||
},
|
||||
build: {
|
||||
buildId: "build-a",
|
||||
commitSha: "abc123",
|
||||
routerBasePath: "/",
|
||||
runtimeConfigUrl: "/config.json",
|
||||
},
|
||||
validationDurationMs: 0,
|
||||
};
|
||||
const release: Release = {
|
||||
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" },
|
||||
};
|
||||
|
||||
describe("runtime adapter composition", () => {
|
||||
it("constructs the local demo seam and infrastructure adapters", async () => {
|
||||
const adapters = await createRuntimeAdapters({
|
||||
runtime,
|
||||
release,
|
||||
host: {},
|
||||
});
|
||||
|
||||
expect(adapters.outputPorts.session.getState()).toBe("unauthenticated");
|
||||
await expect(adapters.outputPorts.releaseInfo.getCurrent()).resolves.toMatchObject({
|
||||
releaseId: "release-a",
|
||||
});
|
||||
expect(adapters.infrastructure.queryClient).toBeDefined();
|
||||
expect(adapters.infrastructure.queryInvalidation).toBeDefined();
|
||||
expect(
|
||||
adapters.infrastructure.crossContextInvalidationStatus(),
|
||||
).toBe("DEGRADED_LOCAL_ONLY");
|
||||
expect(adapters.outputPorts.diagnostics.record).toEqual(expect.any(Function));
|
||||
expect(adapters.outputPorts.telemetry.emit).toEqual(expect.any(Function));
|
||||
expect(adapters.outputPorts.telemetry.pendingCount()).toBe(0);
|
||||
expect(adapters).not.toHaveProperty("http");
|
||||
expect(adapters).not.toHaveProperty("storage");
|
||||
adapters.infrastructure.dispose();
|
||||
});
|
||||
|
||||
it("does not fail boot when Web Storage capability getters throw", async () => {
|
||||
const host: Record<string, unknown> = {};
|
||||
Object.defineProperties(host, {
|
||||
localStorage: {
|
||||
get() {
|
||||
throw new DOMException("denied", "SecurityError");
|
||||
},
|
||||
},
|
||||
sessionStorage: {
|
||||
get() {
|
||||
throw new DOMException("denied", "SecurityError");
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const adapters = await createRuntimeAdapters({
|
||||
runtime,
|
||||
release,
|
||||
host,
|
||||
});
|
||||
|
||||
expect(adapters.infrastructure.queryClient).toBeDefined();
|
||||
expect(adapters.outputPorts.preferences.read("COLOR_SCHEME")).toEqual({
|
||||
ok: true,
|
||||
value: undefined,
|
||||
});
|
||||
adapters.infrastructure.dispose();
|
||||
});
|
||||
|
||||
it("fails closed when an external auth owner was not installed", async () => {
|
||||
const adapters = await createRuntimeAdapters({
|
||||
runtime: {
|
||||
...runtime,
|
||||
config: { ...runtime.config, AUTH_MODE: "external" },
|
||||
},
|
||||
release,
|
||||
host: {},
|
||||
});
|
||||
expect(adapters.outputPorts.session.getState()).toBe("integration-failed");
|
||||
});
|
||||
|
||||
it("refetches the active release manifest with no-store semantics", async () => {
|
||||
const activeRelease = {
|
||||
...release,
|
||||
buildId: "build-b",
|
||||
releaseId: "release-b",
|
||||
routeChunks: { "route-home": "assets/home-b.js" },
|
||||
};
|
||||
const fetcher = vi.fn(async () => Response.json(activeRelease));
|
||||
const adapters = await createRuntimeAdapters({
|
||||
runtime,
|
||||
release,
|
||||
fetcher,
|
||||
host: {},
|
||||
});
|
||||
|
||||
await expect(adapters.outputPorts.releaseInfo.refresh()).resolves.toMatchObject({
|
||||
buildId: "build-b",
|
||||
releaseId: "release-b",
|
||||
});
|
||||
expect(fetcher).toHaveBeenCalledWith("/release-manifest.json", {
|
||||
cache: "no-store",
|
||||
headers: { Accept: "application/json" },
|
||||
});
|
||||
});
|
||||
|
||||
it("injects runtime timeout and max-attempt policy into HTTP execution", async () => {
|
||||
const scheduled: Array<{ callback: () => void; milliseconds: number }> = [];
|
||||
const scheduler = {
|
||||
setTimeout: vi.fn((callback: () => void, milliseconds: number) => {
|
||||
scheduled.push({ callback, milliseconds });
|
||||
return scheduled.length;
|
||||
}),
|
||||
clearTimeout: vi.fn(),
|
||||
};
|
||||
const fetcher = vi.fn(async () =>
|
||||
Response.json(
|
||||
{
|
||||
success: false,
|
||||
error: { code: "TEMPORARY" },
|
||||
meta: { requestId: "request-1", traceId: "trace-1" },
|
||||
},
|
||||
{ status: 503 },
|
||||
),
|
||||
);
|
||||
const authSession =
|
||||
(await createRuntimeAdapters({
|
||||
runtime,
|
||||
release,
|
||||
host: {},
|
||||
})).outputPorts.session;
|
||||
const client = createRuntimeHttpClient(
|
||||
{
|
||||
runtime,
|
||||
authSession,
|
||||
fetcher,
|
||||
clock: { now: () => 0, sleep: async () => {} },
|
||||
scheduler,
|
||||
},
|
||||
TEST_HTTP_CONTRACT,
|
||||
);
|
||||
|
||||
await client.execute({
|
||||
operationId: "LIST_ENTITIES",
|
||||
routeId: "TEST_ROUTE",
|
||||
});
|
||||
|
||||
expect(fetcher).toHaveBeenCalledOnce();
|
||||
expect(scheduler.setTimeout).toHaveBeenCalledWith(
|
||||
expect.any(Function),
|
||||
4321,
|
||||
);
|
||||
expect(scheduler.clearTimeout).toHaveBeenCalledOnce();
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user