fix(housekeeping): preserve radio key compatibility

This commit is contained in:
Simo committed 2026-09-05 16:04:10 +02:00
1 parent 9b83cdc3c3
commit 36ac116dc2
2 files changed
+55 -5

No files matched your search

@@ -1,3 +1,4 @@
import { readFileSync } from "node:fs";
import type { SQL } from "drizzle-orm";
import { beforeEach, describe, expect, it, vi } from "vitest";
import type { HousekeepingCapabilityContext } from "../../../foundation/contracts";
@@ -52,6 +53,24 @@ const context = {
legacy: true,
};
function readCuratedRadioSettingKeys(): string[] {
const pagePath = new URL(
"../../../../../app/admin/radio/settings/page.tsx",
import.meta.url,
);
const source = readFileSync(pagePath, "utf8");
const groupsStart = source.indexOf("const GROUPS: Group[] = [");
const groupsEnd = source.indexOf("const CURATED_KEYS", groupsStart);
if (groupsStart < 0 || groupsEnd < 0) {
throw new Error("Radio settings GROUPS source contract was not found");
}
return Array.from(
source.slice(groupsStart, groupsEnd).matchAll(/\bkey:\s*"([^"]+)"/g),
(match) => match[1],
);
}
beforeEach(() => {
state.rows = [];
state.writes = [];
@@ -69,6 +88,34 @@ describe("Hotel radio settings runtime", () => {
expect(state.writes).toEqual([]);
});
it.each(["radio_custom-setting.v2", "auto_dj_vendor:key/variant"])(
"accepts an existing custom prefixed setting key: %s",
async (key) => {
await executeHotelMutationOperation(
"radio.settings.save-one",
{ key, value: "configured" },
context,
);
expect(state.writes).toEqual([
expect.objectContaining({ key, value: "configured" }),
]);
},
);
it.each(["Radio_name", "AUTO_DJ_mode"])(
"rejects an uppercase setting prefix: %s",
async (key) => {
await expect(
executeHotelMutationOperation(
"radio.settings.save-one",
{ key, value: "configured" },
context,
),
).rejects.toBeInstanceOf(HotelMutationFailure);
expect(state.writes).toEqual([]);
},
);
it("rejects duplicate and invalid bulk entries atomically", async () => {
for (const entries of [
[
@@ -92,10 +139,13 @@ describe("Hotel radio settings runtime", () => {
});
it("accepts the complete 102-entry curated form within the 500-entry bound", async () => {
const entries = Array.from({ length: 102 }, (_, index) => ({
key: `radio_curated_${index}`,
const keys = readCuratedRadioSettingKeys();
const entries = keys.map((key, index) => ({
key,
value: `value-${index}`,
}));
expect(keys).toHaveLength(102);
expect(new Set(keys).size).toBe(102);
await executeHotelMutationOperation(
"radio.settings.save-many",
@@ -103,7 +153,7 @@ describe("Hotel radio settings runtime", () => {
context,
);
expect(state.writes).toHaveLength(102);
expect(state.writes.map(({ key }) => key)).toEqual(keys);
});
it("rejects a bulk request above 500 entries", async () => {
@@ -127,8 +127,8 @@ const roomRuntimeSchema = z
})
.strict();
const settingKey = requiredText(255).refine((key) =>
/^(?:radio_|auto_dj_)[a-z0-9_]+$/i.test(key),
const settingKey = requiredText(255).refine(
(key) => key.startsWith("radio_") || key.startsWith("auto_dj_"),
);
const settingSchema = z
.object({