feat: add display kiosk host
This commit is contained in:
@@ -1,13 +1,13 @@
|
||||
import { describe, expect, it } from "vitest";
|
||||
import {
|
||||
isVersionCompatible,
|
||||
isModuleConfigurationBlockedReason,
|
||||
isVersionCompatible,
|
||||
MODULE_BLOCKED_REASONS,
|
||||
MODULE_CONFIGURATION_BLOCKED_REASONS,
|
||||
MODULE_DEPENDENCY_KINDS,
|
||||
MODULE_STORAGE_BLOCKED_REASON,
|
||||
MODULE_MANIFEST_VERSION,
|
||||
MODULE_RUNTIME_STATES,
|
||||
MODULE_STORAGE_BLOCKED_REASON,
|
||||
packageRelativePathSchema,
|
||||
parseModuleManifest,
|
||||
parsePermissionId,
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
|
||||
function validManifest(): Record<string, unknown> {
|
||||
return {
|
||||
manifestVersion: 5,
|
||||
manifestVersion: 6,
|
||||
id: "calendar",
|
||||
name: "Calendar",
|
||||
version: "0.1.0",
|
||||
@@ -226,9 +226,15 @@ describe("module manifest contract", () => {
|
||||
expect(safeParseModuleManifest(version4Manifest).success).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects a version 5 manifest rather than silently interpreting it as version 6", () => {
|
||||
const version5Manifest = { ...validManifest(), manifestVersion: 5 };
|
||||
|
||||
expect(safeParseModuleManifest(version5Manifest).success).toBe(false);
|
||||
});
|
||||
|
||||
it("accepts exactly one manifest version, so no document can be read as two versions", () => {
|
||||
expect(MODULE_MANIFEST_VERSION).toBe(5);
|
||||
for (const manifestVersion of [1, 2, 3, 4, 6, "5", 5.5, null]) {
|
||||
expect(MODULE_MANIFEST_VERSION).toBe(6);
|
||||
for (const manifestVersion of [1, 2, 3, 4, 5, 7, "6", 6.5, null]) {
|
||||
expect(safeParseModuleManifest({ ...validManifest(), manifestVersion }).success).toBe(false);
|
||||
}
|
||||
expect(
|
||||
@@ -391,6 +397,7 @@ describe("module manifest contract", () => {
|
||||
const manifest = parseModuleManifest(validManifest());
|
||||
|
||||
expect(manifest.entrypoints.ui).toBeUndefined();
|
||||
expect(manifest.entrypoints.display).toBeUndefined();
|
||||
expect(manifest.entrypoints.server).toBe("./dist/server/index.js");
|
||||
});
|
||||
|
||||
@@ -405,6 +412,153 @@ describe("module manifest contract", () => {
|
||||
expect(manifest.entrypoints.server).toBe("./dist/server/index.js");
|
||||
});
|
||||
|
||||
it("accepts an independently declared display UI protected by a module-owned permission", () => {
|
||||
const manifest = parseModuleManifest({
|
||||
...validManifest(),
|
||||
entrypoints: {
|
||||
server: "./dist/server/index.js",
|
||||
display: {
|
||||
artifact: "./dist/display/index.js",
|
||||
permission: "calendar.events.read",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(manifest.entrypoints.ui).toBeUndefined();
|
||||
expect(manifest.entrypoints.display).toEqual({
|
||||
artifact: "./dist/display/index.js",
|
||||
permission: "calendar.events.read",
|
||||
});
|
||||
});
|
||||
|
||||
it("rejects a display permission that this module did not declare", () => {
|
||||
const result = safeParseModuleManifest({
|
||||
...validManifest(),
|
||||
entrypoints: {
|
||||
server: "./dist/server/index.js",
|
||||
display: {
|
||||
artifact: "./dist/display/index.js",
|
||||
permission: "calendar.display.view",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.map((issue) => issue.path.join("."))).toContain(
|
||||
"entrypoints.display.permission",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it("rejects a display permission whose baseline would admit an anonymous principal", () => {
|
||||
const result = safeParseModuleManifest({
|
||||
...validManifest(),
|
||||
permissions: [
|
||||
{
|
||||
id: "calendar.display.view",
|
||||
baseline: "public",
|
||||
description: "View the calendar display.",
|
||||
},
|
||||
],
|
||||
entrypoints: {
|
||||
server: "./dist/server/index.js",
|
||||
display: {
|
||||
artifact: "./dist/display/index.js",
|
||||
permission: "calendar.display.view",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.map((issue) => issue.path.join("."))).toContain(
|
||||
"entrypoints.display.permission",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
for (const entrypointName of ["server", "schema", "migrations", "jobs", "ui"] as const) {
|
||||
it(`rejects a display artifact that is also configured as the ${entrypointName} entrypoint`, () => {
|
||||
const sharedPath = "./dist/shared.js";
|
||||
const result = safeParseModuleManifest({
|
||||
...validManifest(),
|
||||
entrypoints: {
|
||||
server: entrypointName === "server" ? sharedPath : "./dist/server/index.js",
|
||||
...(entrypointName === "server" ? {} : { [entrypointName]: sharedPath }),
|
||||
display: {
|
||||
artifact: sharedPath,
|
||||
permission: "calendar.events.read",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.map((issue) => issue.path.join("."))).toContain(
|
||||
"entrypoints.display.artifact",
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
it("rejects primary and display UI declarations that name the same artifact", () => {
|
||||
const result = safeParseModuleManifest({
|
||||
...validManifest(),
|
||||
entrypoints: {
|
||||
server: "./dist/server/index.js",
|
||||
ui: "./dist/ui/index.js",
|
||||
display: {
|
||||
artifact: "./dist/ui/index.js",
|
||||
permission: "calendar.events.read",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(result.success).toBe(false);
|
||||
if (!result.success) {
|
||||
expect(result.error.issues.map((issue) => issue.path.join("."))).toContain(
|
||||
"entrypoints.display.artifact",
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
it.each([
|
||||
"../outside/display.js",
|
||||
"/absolute/display.js",
|
||||
"./dist/../../display.js",
|
||||
"file:///dist/display.js",
|
||||
".\\dist\\display.js",
|
||||
"./dist/display.js?raw",
|
||||
"",
|
||||
])("rejects the unsafe display artifact path %s", (artifact) => {
|
||||
expect(
|
||||
safeParseModuleManifest({
|
||||
...validManifest(),
|
||||
entrypoints: {
|
||||
server: "./dist/server/index.js",
|
||||
display: { artifact, permission: "calendar.events.read" },
|
||||
},
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it("rejects unknown properties in the static display declaration", () => {
|
||||
expect(
|
||||
safeParseModuleManifest({
|
||||
...validManifest(),
|
||||
entrypoints: {
|
||||
server: "./dist/server/index.js",
|
||||
display: {
|
||||
artifact: "./dist/display/index.js",
|
||||
permission: "calendar.events.read",
|
||||
public: true,
|
||||
},
|
||||
},
|
||||
}).success,
|
||||
).toBe(false);
|
||||
});
|
||||
|
||||
it.each([
|
||||
["../outside/ui.js", "escapes the package boundary"],
|
||||
["/absolute/ui.js", "is an absolute path"],
|
||||
|
||||
Reference in New Issue
Block a user