Files
tech-log-frontend/tests/unit/application-boundary.test.ts

172 lines
5.5 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
createApplication,
type ApplicationOutputPorts,
} from "../../src/application/create-application.ts";
import { createTestApplication } from "../helpers/create-test-application.ts";
import { createRuntimeCapabilitiesStub } from "../helpers/runtime-capabilities-stub.ts";
declare module "../../src/application/ports/in/application-api.ts" {
interface ApplicationFeatureInputs {
"test-feature": Readonly<{
run(): "ok";
}>;
}
}
describe("application input/output boundary", () => {
it("exposes intent-oriented input APIs without leaking output ports", async () => {
const application = createTestApplication();
expect(Object.keys(application)).toEqual([
"session",
"preferences",
"diagnostics",
"runtime",
"recovery",
"features",
]);
expect(application).not.toHaveProperty("storage");
expect(application).not.toHaveProperty("telemetry");
expect(application).not.toHaveProperty("releaseInfo");
expect(application.features.has("not-installed")).toBe(false);
expect(() =>
Reflect.apply(application.features.get, application.features, [
"not-installed",
]),
).toThrow("Application feature is not installed");
await expect(application.runtime.getReleaseSummary()).resolves.toEqual({
buildId: "test-build",
releaseId: "test-release",
configSchemaVersion: "1",
apiContractVersion: "1",
});
});
it("returns a typed input for an installed generic feature", () => {
const featureInput = Object.freeze({
run: () => "ok" as const,
});
const application = createTestApplication({
featureInputs: { "test-feature": featureInput },
});
expect(application.features.has("test-feature")).toBe(true);
expect(application.features.get("test-feature")).toBe(featureInput);
expect(application.features.get("test-feature").run()).toBe("ok");
});
it("uses fake output ports for preference, session, and safe diagnostics flows", async () => {
const write = vi.fn(() => ({ ok: true as const }));
const emit = vi.fn();
const record = vi.fn();
const subscribe = vi.fn(() => () => {});
const beginSignIn = vi.fn(async () => {});
const signOut = vi.fn(async () => {});
const recover = vi.fn(async () => "restored" as const);
const ports = {
session: {
getState: () => "authenticated" as const,
subscribe,
beginSignIn,
signOut,
recover,
},
preferences: {
read: () => ({ ok: true as const, value: "dark" }),
write,
remove: () => ({ ok: true as const }),
},
diagnostics: { record },
telemetry: { emit },
releaseInfo: {
getCurrent: async () => ({
buildId: "build-a",
releaseId: "release-a",
configSchemaVersion: "1",
apiContractVersion: "1",
assetManifestHash: "hash-a",
routeChunks: { "route-home": "assets/home.js" },
}),
refresh: async () => ({
buildId: "build-a",
releaseId: "release-a",
configSchemaVersion: "1",
apiContractVersion: "1",
assetManifestHash: "hash-a",
routeChunks: { "route-home": "assets/home.js" },
}),
},
runtimeCapabilities: createRuntimeCapabilitiesStub(),
navigation: { reload: () => {} },
} satisfies ApplicationOutputPorts;
const application = createApplication(ports);
expect(application.session.getSnapshot()).toBe("authenticated");
const listener = vi.fn();
expect(application.session.subscribe(listener)).toBeTypeOf("function");
expect(subscribe).toHaveBeenCalledWith(listener);
await application.session.beginSignIn("/return");
expect(beginSignIn).toHaveBeenCalledWith("/return");
await application.session.signOut();
expect(signOut).toHaveBeenCalledOnce();
await expect(application.session.recover()).resolves.toBe("restored");
expect(application.preferences.getColorScheme()).toBe("dark");
expect(application.preferences.setColorScheme("light")).toEqual({ ok: true });
expect(write).toHaveBeenCalledWith("COLOR_SCHEME", "light");
application.diagnostics.reportRenderFailure({
routeId: "APP_HOME",
buildId: "build-a",
boundaryName: "route",
});
expect(record).toHaveBeenCalledWith({
level: "error",
eventId: "ui.render.failed",
context: {
route_id: "APP_HOME",
build_id: "build-a",
component_boundary: "route",
},
});
expect(emit).toHaveBeenCalledWith("ui.render.failed", {
route_id: "APP_HOME",
build_id: "build-a",
component_boundary: "route",
});
application.diagnostics.reportRouteChanged({
routeId: "APP_HOME",
buildId: "build-a",
});
expect(record).toHaveBeenCalledWith({
level: "info",
eventId: "route.changed",
context: { route_id: "APP_HOME", build_id: "build-a" },
});
});
it("does not let a failing diagnostics output escape into presentation", () => {
const application = createTestApplication({
diagnostics: {
record() {
throw new Error("sink details");
},
},
telemetry: {
emit() {
throw new Error("telemetry details");
},
},
});
expect(() =>
application.diagnostics.reportRenderFailure({
routeId: "APP_HOME",
buildId: "build-a",
boundaryName: "feature",
}),
).not.toThrow();
});
});