Files
clean-architecture-frontend…/tests/unit/application-boundary.test.ts

110 lines
3.5 KiB
TypeScript

import { describe, expect, it, vi } from "vitest";
import {
createApplication,
type ApplicationOutputPorts,
} from "../../src/application/create-application.js";
import { createTestApplication } from "../helpers/create-test-application.js";
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(() => application.features.get("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("uses fake output ports for preference, session, and safe diagnostics flows", () => {
const write = vi.fn(() => ({ ok: true as const }));
const emit = vi.fn();
const ports = {
session: {
getState: () => "authenticated" as const,
subscribe: () => () => {},
beginSignIn: async () => {},
signOut: async () => {},
recover: async () => "restored" as const,
},
preferences: {
read: () => ({ ok: true as const, value: "dark" }),
write,
remove: () => ({ ok: true as const }),
},
diagnostics: { 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" },
}),
},
navigation: { reload: () => {} },
} satisfies ApplicationOutputPorts;
const application = createApplication(ports);
expect(application.session.getSnapshot()).toBe("authenticated");
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(emit).toHaveBeenCalledWith("ui.render.failed", {
route_id: "APP_HOME",
build_id: "build-a",
component_boundary: "route",
});
});
it("does not let a failing diagnostics output escape into presentation", () => {
const application = createTestApplication({
diagnostics: {
emit() {
throw new Error("sink details");
},
},
});
expect(() =>
application.diagnostics.reportRenderFailure({
routeId: "APP_HOME",
buildId: "build-a",
boundaryName: "feature",
}),
).not.toThrow();
});
});