105 lines
3.3 KiB
TypeScript
105 lines
3.3 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",
|
|
]);
|
|
expect(application).not.toHaveProperty("storage");
|
|
expect(application).not.toHaveProperty("telemetry");
|
|
expect(application).not.toHaveProperty("releaseInfo");
|
|
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();
|
|
});
|
|
});
|