feat: complete TASK-WP5-03 asset allowlist
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 1m5s
Dada P0-A isolated Windows CI / validate-and-package (push) Failing after 1m5s
This commit is contained in:
@@ -74,6 +74,8 @@ function createFixture(): Fixture {
|
||||
json(join(handoffRoot, "validation.json"), { all_valid: true, schema_version: 1 });
|
||||
|
||||
const textRoot = join(sourceRoot, "text");
|
||||
const fixtureFontBytes = Buffer.from("dada-test-font-package");
|
||||
const fixtureFontSha256 = createHash("sha256").update(fixtureFontBytes).digest("hex").toUpperCase();
|
||||
csv(join(textRoot, "flower", "catalog.csv"), [{
|
||||
canonical_dir: "templates/FLOWER001", canonical_id: "FLOWER001", category: "flower", default_text: "春日,计划",
|
||||
display_name: "春日,计划", family: "text_template", font_paths: "fonts/original.ztf", preview_path: "preview.png",
|
||||
@@ -86,17 +88,19 @@ function createFixture(): Fixture {
|
||||
resource_class: "zip_template", runtime: { editable_text: true }, schema_version: 1,
|
||||
source: { archive: join(root, "evidence", "historical") },
|
||||
});
|
||||
mkdirSync(join(textRoot, "flower", "templates", "FLOWER001", "fonts"), { recursive: true });
|
||||
writeFileSync(join(textRoot, "flower", "templates", "FLOWER001", "fonts", "original.ztf"), fixtureFontBytes);
|
||||
writeFileSync(join(textRoot, "unreferenced-source.bin"), Buffer.alloc(128 * 1024, 7));
|
||||
|
||||
const fontRoot = join(sourceRoot, "fonts");
|
||||
const fontDirectory = join(fontRoot, "resources", "font_packages", "FONT001_Test");
|
||||
csv(join(fontRoot, "reports", "font_panel_catalog.csv"), [{
|
||||
candidate_id: "FONT001", display_name: "Test Font", font_family: "Dada Test", local_sha256: "A".repeat(64),
|
||||
candidate_id: "FONT001", display_name: "Test Font", font_family: "Dada Test", local_sha256: fixtureFontSha256,
|
||||
panel_order: "1", resource_dir: fontDirectory, resource_status: "verified_extracted",
|
||||
}]);
|
||||
json(join(fontDirectory, "metadata.json"), {
|
||||
candidate_id: "FONT001", display_name: "Test Font", font_family: "Dada Test", font_file_count: 1,
|
||||
local_sha256: "A".repeat(64), panel_order: 1, resource_status: "verified_extracted",
|
||||
local_sha256: fixtureFontSha256, panel_order: 1, resource_status: "verified_extracted",
|
||||
});
|
||||
|
||||
const colorRoot = join(sourceRoot, "colors");
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
import { mkdirSync, writeFileSync } from "node:fs";
|
||||
import { resolve } from "node:path";
|
||||
|
||||
import { describe, expect, it } from "vitest";
|
||||
|
||||
import type { StaticStickerCatalog } from "../../packages/static-sticker-catalog/src/index.js";
|
||||
import {
|
||||
P0A_COLOR_CARD_IDS,
|
||||
P0A_DYNAMIC_STICKER_IDS,
|
||||
P0A_TEXT_TEMPLATE_IDS,
|
||||
createP0aPublicManifest,
|
||||
} from "../../packages/template-registry/src/index.js";
|
||||
import {
|
||||
P0A_COLOR_CARD_DEFINITIONS,
|
||||
createP0aColorCardRenderPlans,
|
||||
} from "../../packages/asset-renderer/src/index.js";
|
||||
|
||||
type RegistryItem = {
|
||||
canonical_id: string;
|
||||
family: "color_card" | "font_panel" | "interactive_sticker" | "text_template";
|
||||
font_panel_references?: string[];
|
||||
release_status: "enabled" | "imported" | "passed";
|
||||
release_tier: "full_p0";
|
||||
renderer_id?: string;
|
||||
validation_status: "metadata_validated";
|
||||
};
|
||||
|
||||
const referencedFontIds = [
|
||||
"FONT005", "FONT008", "FONT011", "FONT021", "FONT022",
|
||||
"FONT027", "FONT039", "FONT043", "FONT046", "FONT052",
|
||||
] as const;
|
||||
|
||||
function numberedIds(prefix: string, count: number) {
|
||||
return Array.from({ length: count }, (_, index) => `${prefix}${String(index + 1).padStart(3, "0")}`);
|
||||
}
|
||||
|
||||
function registryItem(canonicalId: string, family: RegistryItem["family"], patch: Partial<RegistryItem> = {}): RegistryItem {
|
||||
return {
|
||||
canonical_id: canonicalId,
|
||||
family,
|
||||
release_status: "imported",
|
||||
release_tier: "full_p0",
|
||||
validation_status: "metadata_validated",
|
||||
...patch,
|
||||
};
|
||||
}
|
||||
|
||||
function fullComplexManifest() {
|
||||
const textIds = [
|
||||
...numberedIds("FLOWER", 145),
|
||||
...numberedIds("H", 119),
|
||||
...numberedIds("TAG", 51),
|
||||
...numberedIds("SIMPLE", 17),
|
||||
];
|
||||
const selectedIndex = new Map(P0A_TEXT_TEMPLATE_IDS.map((id, index) => [id, index]));
|
||||
const items: RegistryItem[] = [
|
||||
...textIds.map((id) => registryItem(id, "text_template", {
|
||||
font_panel_references: selectedIndex.has(id)
|
||||
? [referencedFontIds[selectedIndex.get(id)! % referencedFontIds.length]!]
|
||||
: [],
|
||||
})),
|
||||
...numberedIds("FONT", 86).map((id) => registryItem(id, "font_panel")),
|
||||
...numberedIds("COLOR", 16).map((id, index) => registryItem(id, "color_card", { renderer_id: `style_${String(index + 1).padStart(2, "0")}` })),
|
||||
...numberedIds("DYN", 35).map((id) => registryItem(id, "interactive_sticker")),
|
||||
];
|
||||
return {
|
||||
compiler: "dada-asset-compiler",
|
||||
items,
|
||||
release_version: "fixture-complex-v1",
|
||||
schema_version: "asset-release-compiler/v1",
|
||||
} as const;
|
||||
}
|
||||
|
||||
function staticCatalog(): StaticStickerCatalog {
|
||||
const items = Array.from({ length: 1_407 }, (_, index) => {
|
||||
const stableId = `STK${String(index + 1).padStart(index + 1 < 1_000 ? 3 : 4, "0")}`;
|
||||
const part = (index % 25) + 1;
|
||||
return {
|
||||
enabled: true,
|
||||
height: 100,
|
||||
mime: "image/png" as const,
|
||||
mime_type: "image/png" as const,
|
||||
order: Math.floor(index / 25) + 1,
|
||||
original_filename: `${stableId}.png`,
|
||||
original_reference: `/api/v1/assets/public/fixture-static-v1/${stableId}`,
|
||||
origin: "bundled_read_only" as const,
|
||||
part,
|
||||
relative_path: `sticker_part${part}/${stableId}.png`,
|
||||
resource_version: "fixture-static-v1",
|
||||
sha256: String(index + 1).padStart(64, "0"),
|
||||
stable_id: stableId,
|
||||
thumbnail_reference: {
|
||||
media: "thumbnail" as const,
|
||||
resource_id: stableId,
|
||||
resource_version: "fixture-static-v1",
|
||||
url: `/api/v1/assets/public/fixture-static-v1/${stableId}?variant=thumbnail`,
|
||||
},
|
||||
width: 100,
|
||||
};
|
||||
});
|
||||
return {
|
||||
count: items.length,
|
||||
duplicate_sha256_groups: [],
|
||||
items,
|
||||
manifest_sha256: "A".repeat(64),
|
||||
part_counts: Object.fromEntries(Array.from({ length: 25 }, (_, index) => [String(index + 1), items.filter((item) => item.part === index + 1).length])),
|
||||
release_version: "fixture-static-v1",
|
||||
schema_version: "StaticStickerCatalog/v1",
|
||||
};
|
||||
}
|
||||
|
||||
function evidence(name: string, value: unknown) {
|
||||
const root = process.env.DADA_EVIDENCE_DIR_TEMPLATE_REGISTRY;
|
||||
if (!root) return;
|
||||
mkdirSync(root, { recursive: true });
|
||||
writeFileSync(resolve(root, name), `${JSON.stringify(value, null, 2)}\n`);
|
||||
}
|
||||
|
||||
describe("TDD-WP5-WHITE-001 P0-A public allowlist", () => {
|
||||
it("registers the complete archive while publishing only the exact P0-A allowlist", () => {
|
||||
const source = fullComplexManifest();
|
||||
const before = structuredClone(source);
|
||||
const manifest = createP0aPublicManifest({ complexManifest: source, staticCatalog: staticCatalog() });
|
||||
|
||||
expect(source).toEqual(before);
|
||||
expect(P0A_TEXT_TEMPLATE_IDS).toHaveLength(32);
|
||||
expect(P0A_COLOR_CARD_IDS).toEqual(["COLOR001", "COLOR002", "COLOR008", "COLOR016"]);
|
||||
expect(P0A_DYNAMIC_STICKER_IDS).toEqual([
|
||||
"DYN001", "DYN002", "DYN003", "DYN004", "DYN007",
|
||||
"DYN008", "DYN011", "DYN012", "DYN015", "DYN016",
|
||||
]);
|
||||
expect(manifest.counts).toEqual({
|
||||
color_cards: 4,
|
||||
dynamic_stickers: 10,
|
||||
font_panel_items: 11,
|
||||
static_parts: 25,
|
||||
static_stickers: 1_407,
|
||||
text_templates: 32,
|
||||
});
|
||||
expect(manifest.assets.text_templates.map((item) => item.canonical_id)).toEqual(P0A_TEXT_TEMPLATE_IDS);
|
||||
expect(manifest.assets.font_panel_items.map((item) => item.canonical_id)).toEqual([...referencedFontIds, "FONT081"]);
|
||||
expect(manifest.assets.color_cards.map((item) => item.canonical_id)).toEqual(P0A_COLOR_CARD_IDS);
|
||||
expect(manifest.assets.dynamic_stickers.map((item) => item.canonical_id)).toEqual(P0A_DYNAMIC_STICKER_IDS);
|
||||
expect(manifest.assets.static_stickers).toHaveLength(1_407);
|
||||
expect(manifest.assets.static_stickers.every((item) => item.part >= 1 && item.part <= 25)).toBe(true);
|
||||
|
||||
const publicComplex = [
|
||||
...manifest.assets.text_templates,
|
||||
...manifest.assets.font_panel_items,
|
||||
...manifest.assets.color_cards,
|
||||
...manifest.assets.dynamic_stickers,
|
||||
];
|
||||
expect(publicComplex.every((item) => item.release_status === "enabled"
|
||||
&& item.release_tier === "alpha_whitelist"
|
||||
&& item.validation_status === "passed")).toBe(true);
|
||||
const serialized = JSON.stringify(manifest);
|
||||
expect(serialized).not.toContain("FLOWER009");
|
||||
expect(serialized).not.toContain("COLOR003");
|
||||
expect(serialized).not.toContain("DYN005");
|
||||
evidence("unit-allowlist.json", { counts: manifest.counts, hidden: ["FLOWER009", "COLOR003", "DYN005"], status: "passed" });
|
||||
});
|
||||
|
||||
it("rejects incomplete registration and any early full_p0 enablement", () => {
|
||||
const incomplete = fullComplexManifest();
|
||||
incomplete.items.splice(incomplete.items.findIndex((item) => item.canonical_id === "DYN035"), 1);
|
||||
expect(() => createP0aPublicManifest({ complexManifest: incomplete, staticCatalog: staticCatalog() })).toThrow(/interactive_sticker count/i);
|
||||
|
||||
const early = fullComplexManifest();
|
||||
early.items.find((item) => item.canonical_id === "FLOWER009")!.release_status = "enabled";
|
||||
expect(() => createP0aPublicManifest({ complexManifest: early, staticCatalog: staticCatalog() })).toThrow(/full_p0.*enabled/i);
|
||||
});
|
||||
});
|
||||
|
||||
describe("TDD-WP5-COL-001 shared five-color renderer input", () => {
|
||||
it("binds the four enabled layouts to one immutable palette snapshot", () => {
|
||||
const palette = ["#102030", "#405060", "#708090", "#A0B0C0", "#D0E0F0"] as const;
|
||||
const plans = createP0aColorCardRenderPlans(palette);
|
||||
expect(P0A_COLOR_CARD_DEFINITIONS.map((item) => [item.cardId, item.styleId])).toEqual([
|
||||
["COLOR001", "style_01"], ["COLOR002", "style_02"], ["COLOR008", "style_08"], ["COLOR016", "style_16"],
|
||||
]);
|
||||
expect(plans.map((plan) => plan.cardId)).toEqual(P0A_COLOR_CARD_IDS);
|
||||
expect(plans.every((plan) => plan.palette === plans[0]!.palette)).toBe(true);
|
||||
expect(Object.isFrozen(plans[0]!.palette)).toBe(true);
|
||||
expect(plans.every((plan) => JSON.stringify(plan.palette) === JSON.stringify(palette))).toBe(true);
|
||||
expect(() => createP0aColorCardRenderPlans(["#000000"])).toThrow(/five colors/i);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user