weftens 0.1.1 → 0.1.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,110 +0,0 @@
1
- // content-org — no-dependency test script (plain node, no vitest).
2
- // Run: npm test
3
- // Covers: (1) seat schema validation for all 10 seats in dry-run,
4
- // (2) single-seat CLI mode, (3) review/publish queue round-trip.
5
- // The queue round-trip runs in a scratch dir so it never touches the real queue/.
6
-
7
- import { execFileSync } from "node:child_process";
8
- import { mkdirSync, writeFileSync, readFileSync, rmSync, mkdtempSync } from "node:fs";
9
- import { join, dirname } from "node:path";
10
- import { fileURLToPath } from "node:url";
11
- import { tmpdir } from "node:os";
12
- import { SEATS, stub } from "./orchestrator.mjs";
13
- import { SEAT_MODEL, MODELS } from "./config.mjs";
14
-
15
- const ROOT = dirname(fileURLToPath(import.meta.url));
16
- let pass = 0, fail = 0;
17
- function check(name, ok, detail = "") {
18
- if (ok) { pass++; console.log(` ok ${name}`); }
19
- else { fail++; console.error(`FAIL ${name}${detail ? " — " + detail : ""}`); }
20
- }
21
-
22
- // Minimal validator for the subset of JSON Schema the seats use.
23
- function validate(value, schema, path = "$") {
24
- const errs = [];
25
- if (schema.type === "string") { if (typeof value !== "string") errs.push(`${path}: not a string`); return errs; }
26
- if (schema.type === "boolean") { if (typeof value !== "boolean") errs.push(`${path}: not a boolean`); return errs; }
27
- if (schema.type === "number") { if (typeof value !== "number") errs.push(`${path}: not a number`); return errs; }
28
- if (schema.type === "array") {
29
- if (!Array.isArray(value)) { errs.push(`${path}: not an array`); return errs; }
30
- value.forEach((v, i) => errs.push(...validate(v, schema.items, `${path}[${i}]`)));
31
- return errs;
32
- }
33
- if (schema.type === "object") {
34
- if (typeof value !== "object" || value === null || Array.isArray(value)) { errs.push(`${path}: not an object`); return errs; }
35
- for (const req of schema.required || []) if (!(req in value)) errs.push(`${path}.${req}: missing (required)`);
36
- for (const [k, v] of Object.entries(value)) {
37
- if (!schema.properties[k]) { if (schema.additionalProperties === false) errs.push(`${path}.${k}: unexpected property`); continue; }
38
- errs.push(...validate(v, schema.properties[k], `${path}.${k}`));
39
- }
40
- return errs;
41
- }
42
- errs.push(`${path}: unknown schema type ${schema.type}`);
43
- return errs;
44
- }
45
-
46
- // --- 1) All 9 seats: dry-run stub output conforms to each seat's own schema ------------
47
- console.log("\n[1] seat schemas (dry-run stubs) — 10 seats");
48
- const EXPECTED_SEATS = ["showrunner", "strategy", "ideator", "research", "script", "editor", "cut", "feed", "qc", "growth"];
49
- check("all 10 seats registered", EXPECTED_SEATS.every((k) => SEATS[k]) && Object.keys(SEATS).length === 10,
50
- `have: ${Object.keys(SEATS).join(",")}`);
51
- for (const key of EXPECTED_SEATS) {
52
- const seat = SEATS[key];
53
- if (!seat) { check(`seat ${key}`, false, "missing"); continue; }
54
- const out = stub(seat.schema, key);
55
- const errs = validate(out, seat.schema);
56
- check(`stub(${key}) matches schema`, errs.length === 0, errs.join("; "));
57
- check(`${key} routes to a real model`, !!MODELS[SEAT_MODEL[key]], `SEAT_MODEL.${key}=${SEAT_MODEL[key]}`);
58
- }
59
-
60
- // --- 2) Single-seat CLI mode (dry-run, no API key needed) ------------------------------
61
- console.log("\n[2] single-seat CLI mode");
62
- for (const key of ["showrunner", "qc"]) {
63
- let out, parsed = null, errs = ["did not run"];
64
- try {
65
- out = execFileSync(process.execPath, ["orchestrator.mjs", "--seat", key, "--input", '{"format":"scam-reveal"}', "--dry-run"],
66
- { cwd: ROOT, encoding: "utf8" });
67
- parsed = JSON.parse(out);
68
- errs = validate(parsed, SEATS[key].schema);
69
- } catch (e) { errs = [e.message]; }
70
- check(`--seat ${key} --dry-run emits schema-valid JSON`, parsed !== null && errs.length === 0, errs.join("; "));
71
- }
72
- // unknown seat must exit non-zero
73
- let badExit = false;
74
- try { execFileSync(process.execPath, ["orchestrator.mjs", "--seat", "nope", "--input", "x", "--dry-run"], { cwd: ROOT, stdio: "pipe" }); }
75
- catch { badExit = true; }
76
- check("--seat with unknown key exits non-zero", badExit);
77
-
78
- // --- 3) review/publish queue round-trip (scratch dir; real queue/ untouched) -----------
79
- console.log("\n[3] review/publish queue round-trip");
80
- const scratch = mkdtempSync(join(tmpdir(), "content-org-test-"));
81
- try {
82
- const slug = "test-piece";
83
- mkdirSync(join(scratch, "queue", slug), { recursive: true });
84
- const manifest = {
85
- slug, format: "nostalgia", client: "testco", tier: "starter",
86
- approval: "auto", reel: `${slug}.mp4`, status: "ready-for-approval",
87
- qc: { blockers: [] }, feed: { caption: "test caption" },
88
- };
89
- writeFileSync(join(scratch, "queue", slug, "manifest.json"), JSON.stringify(manifest, null, 2));
90
-
91
- const reviewOut = execFileSync(process.execPath, [join(ROOT, "review.mjs")], { cwd: scratch, encoding: "utf8" });
92
- check("review.mjs lists the queued piece", reviewOut.includes(slug) && reviewOut.includes("1 in queue"));
93
- check("review.mjs shows auto approval", /auto/.test(reviewOut));
94
-
95
- execFileSync(process.execPath, [join(ROOT, "publish.mjs"), slug], { cwd: scratch, encoding: "utf8" });
96
- const log = readFileSync(join(scratch, "published", "log.jsonl"), "utf8").trim().split("\n").map((l) => JSON.parse(l));
97
- check("publish.mjs appends to published/log.jsonl", log.length === 1 && log[0].slug === slug);
98
- check("published entry carries caption + reel", log[0].caption === "test caption" && log[0].reel === `${slug}.mp4`);
99
- check("published entry marks Meta post as not wired", /not wired/.test(log[0].note || ""));
100
-
101
- // 'auto' mode publishes only clean auto-approved pieces
102
- execFileSync(process.execPath, [join(ROOT, "publish.mjs"), "auto"], { cwd: scratch, encoding: "utf8" });
103
- const log2 = readFileSync(join(scratch, "published", "log.jsonl"), "utf8").trim().split("\n");
104
- check("publish.mjs auto mode picks up the auto-approved piece", log2.length === 2);
105
- } finally {
106
- rmSync(scratch, { recursive: true, force: true });
107
- }
108
-
109
- console.log(`\n${pass} passed, ${fail} failed`);
110
- process.exit(fail ? 1 : 0);