fix(manager): persist environment presentation and media
This commit is contained in:
@@ -2,7 +2,7 @@ import { randomUUID } from "node:crypto";
|
||||
import { mkdir, readFile, rename, writeFile } from "node:fs/promises";
|
||||
import { basename, dirname, extname, join, resolve, sep } from "node:path";
|
||||
|
||||
const DEFAULT_ACCENT = "#b9ff4a";
|
||||
const DEFAULT_ACCENT = "#f5f5f5";
|
||||
|
||||
export function createDeviceManagerPresentationStore({
|
||||
layoutPath,
|
||||
|
||||
@@ -12,6 +12,8 @@ import {
|
||||
|
||||
test("Device Core environment defaults to the product-level canonical identity", () => {
|
||||
const presentation = defaultPresentation();
|
||||
assert.equal(presentation.environment.theme, "dark");
|
||||
assert.equal(presentation.environment.accentHex, "#f5f5f5");
|
||||
assert.deepEqual(presentation.environment.overview, {
|
||||
headerLabel: "Device Core",
|
||||
eyebrow: "NODEDC / DEVICE CORE",
|
||||
@@ -27,6 +29,38 @@ test("Device Core environment defaults to the product-level canonical identity",
|
||||
});
|
||||
});
|
||||
|
||||
test("environment accent keeps a valid user choice and falls back to neutral white", () => {
|
||||
assert.equal(
|
||||
normalizeEnvironmentPresentation({ accentHex: "#8A2BE2" }).accentHex,
|
||||
"#8a2be2",
|
||||
);
|
||||
assert.equal(
|
||||
normalizeEnvironmentPresentation({ accentHex: "not-a-color" }).accentHex,
|
||||
"#f5f5f5",
|
||||
);
|
||||
});
|
||||
|
||||
test("production Compose provides exact persistent writable presentation storage", async () => {
|
||||
const compose = await readFile(
|
||||
new URL("../../../docker-compose.device-manager.yml", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
for (const expected of [
|
||||
"NODEDC_DEVICE_MANAGER_PRESENTATION_PATH: /var/lib/nodedc-device-manager/device-manager-presentation.json",
|
||||
"NODEDC_DEVICE_MANAGER_MEDIA_ROOT: /var/lib/nodedc-device-manager/media",
|
||||
"source: /volume1/docker/nodedc-device-plane/data/device-manager",
|
||||
"target: /var/lib/nodedc-device-manager",
|
||||
"read_only: false",
|
||||
"create_host_path: false",
|
||||
]) assert.ok(compose.includes(expected), expected);
|
||||
const client = await readFile(
|
||||
new URL("../src/DeviceManagerApp.tsx", import.meta.url),
|
||||
"utf8",
|
||||
);
|
||||
assert.ok(client.includes('accentHex: "#f5f5f5"'));
|
||||
assert.ok(!client.includes("#b9ff4a"));
|
||||
});
|
||||
|
||||
test("legacy single teaser migrates into the environment media playlist", () => {
|
||||
const environment = normalizeEnvironmentPresentation({
|
||||
defaultTeaser: {
|
||||
@@ -60,6 +94,23 @@ test("environment media accepts MOV even when the browser omits or varies its MI
|
||||
}
|
||||
});
|
||||
|
||||
test("environment media persists an MP4 upload in the configured data root", async (t) => {
|
||||
const root = await mkdtemp(join(tmpdir(), "nodedc-device-presentation-mp4-"));
|
||||
t.after(() => rm(root, { recursive: true, force: true }));
|
||||
const store = createDeviceManagerPresentationStore({
|
||||
layoutPath: join(root, "device-manager-presentation.json"),
|
||||
uploadRoot: join(root, "media"),
|
||||
});
|
||||
const uploaded = await store.saveMedia({
|
||||
bytes: Buffer.from("mp4-payload"),
|
||||
contentType: "video/mp4",
|
||||
originalName: "environment.mp4",
|
||||
kind: "background",
|
||||
});
|
||||
assert.match(uploaded.fileSrc, /^\/device-manager-media\/background-[0-9a-f-]+\.mp4$/);
|
||||
assert.equal(await readFile(store.resolveMedia(uploaded.fileSrc), "utf8"), "mp4-payload");
|
||||
});
|
||||
|
||||
test("environment presentation persists ordered mixed media and image duration", async (t) => {
|
||||
const root = await mkdtemp(join(tmpdir(), "nodedc-device-presentation-"));
|
||||
t.after(() => rm(root, { recursive: true, force: true }));
|
||||
@@ -68,6 +119,8 @@ test("environment presentation persists ordered mixed media and image duration",
|
||||
uploadRoot: join(root, "media"),
|
||||
});
|
||||
const next = defaultPresentation();
|
||||
next.environment.theme = "light";
|
||||
next.environment.accentHex = "#8a2be2";
|
||||
next.environment.overview.background = {
|
||||
enabled: true,
|
||||
imageDurationSeconds: 17,
|
||||
@@ -93,6 +146,8 @@ test("environment presentation persists ordered mixed media and image duration",
|
||||
|
||||
await store.write(next);
|
||||
const restored = await store.read();
|
||||
assert.equal(restored.environment.theme, "light");
|
||||
assert.equal(restored.environment.accentHex, "#8a2be2");
|
||||
assert.equal(restored.environment.overview.background.imageDurationSeconds, 17);
|
||||
assert.deepEqual(
|
||||
restored.environment.overview.background.items.map((item) => item.id),
|
||||
|
||||
@@ -130,7 +130,7 @@ const emptyMedia = (): DeviceManagerMediaValue => ({
|
||||
const defaultPresentation = (): DeviceManagerPresentation => ({
|
||||
environment: {
|
||||
theme: "dark",
|
||||
accentHex: "#b9ff4a",
|
||||
accentHex: "#f5f5f5",
|
||||
overview: {
|
||||
headerLabel: "Device Core",
|
||||
eyebrow: "NODEDC / DEVICE CORE",
|
||||
|
||||
Reference in New Issue
Block a user