- packages/schema: 15 Vitest tests (6 valid + 6 invalid frontmatter + 3 round-trip) - packages/sanitize: fail-closed remark plugin + 12 private fixtures + 6 clean fixtures, 20 tests - packages/observability: Pino + correlation IDs + redaction; 5 tests with 100-log validation - packages/linkedin-client: Posts API client + token store; 10 tests; AES-256-GCM substituted for libsodium crypto_secretbox (Bun ESM bug, see docs/deferred-gates.md D-001) 50/50 tests pass across 4 packages. All Stage 1 DoDs verified. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
184 lines
4.9 KiB
TypeScript
184 lines
4.9 KiB
TypeScript
import { describe, expect, it } from "vitest";
|
|
import {
|
|
NoteFrontmatterSchema,
|
|
PublishFrontmatterSchema,
|
|
parseFrontmatter,
|
|
} from "./frontmatter";
|
|
|
|
const baseTimestamps = {
|
|
created: "2026-04-01T10:00:00+00:00",
|
|
updated: "2026-04-26T14:00:00+00:00",
|
|
};
|
|
|
|
describe("PublishFrontmatterSchema — 6 valid cases", () => {
|
|
it("accepts a minimal published blog post", () => {
|
|
const parsed = PublishFrontmatterSchema.parse({
|
|
status: "published",
|
|
outlets: [
|
|
{ outlet: "stargue.com", status: "published", published_url: "https://stargue.com/blog/post" },
|
|
],
|
|
category: "blog",
|
|
});
|
|
expect(parsed.language).toBe("en");
|
|
expect(parsed.canonical).toBe("stargue.com");
|
|
expect(parsed.sanitize).toBe(true);
|
|
expect(parsed.version).toBe(1);
|
|
});
|
|
|
|
it("accepts a draft with no outlets yet", () => {
|
|
const parsed = PublishFrontmatterSchema.parse({
|
|
status: "draft",
|
|
outlets: [],
|
|
category: "research",
|
|
});
|
|
expect(parsed.scheduled).toBeNull();
|
|
});
|
|
|
|
it("accepts a queued post with future scheduled time", () => {
|
|
const parsed = PublishFrontmatterSchema.parse({
|
|
status: "queued",
|
|
outlets: [
|
|
{ outlet: "linkedin.member", status: "queued", published_url: null },
|
|
],
|
|
scheduled: "2026-05-01T08:30:00-04:00",
|
|
category: "blog",
|
|
});
|
|
expect(parsed.scheduled).toBe("2026-05-01T08:30:00-04:00");
|
|
});
|
|
|
|
it("accepts multi-outlet partial publish", () => {
|
|
const parsed = PublishFrontmatterSchema.parse({
|
|
status: "partial",
|
|
outlets: [
|
|
{ outlet: "stargue.com", status: "published", published_url: "https://stargue.com/x" },
|
|
{ outlet: "linkedin.org", status: "failed", published_url: null },
|
|
],
|
|
category: "case-study",
|
|
slug: "x",
|
|
});
|
|
expect(parsed.outlets).toHaveLength(2);
|
|
});
|
|
|
|
it("accepts non-English language with explicit canonical", () => {
|
|
const parsed = PublishFrontmatterSchema.parse({
|
|
status: "published",
|
|
language: "pap",
|
|
canonical: "stargue.com",
|
|
outlets: [
|
|
{ outlet: "stargue.com", status: "published", published_url: "https://stargue.com/pap/x" },
|
|
],
|
|
category: "blog",
|
|
});
|
|
expect(parsed.language).toBe("pap");
|
|
});
|
|
|
|
it("accepts sanitize=false explicit override", () => {
|
|
const parsed = PublishFrontmatterSchema.parse({
|
|
status: "ready",
|
|
outlets: [],
|
|
category: "white-paper",
|
|
sanitize: false,
|
|
version: 3,
|
|
});
|
|
expect(parsed.sanitize).toBe(false);
|
|
expect(parsed.version).toBe(3);
|
|
});
|
|
});
|
|
|
|
describe("PublishFrontmatterSchema — 6 invalid cases", () => {
|
|
it("rejects unknown status", () => {
|
|
expect(() =>
|
|
PublishFrontmatterSchema.parse({
|
|
status: "drafted",
|
|
outlets: [],
|
|
category: "blog",
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("rejects unknown outlet", () => {
|
|
expect(() =>
|
|
PublishFrontmatterSchema.parse({
|
|
status: "published",
|
|
outlets: [
|
|
{ outlet: "facebook", status: "published", published_url: "https://fb.com/x" },
|
|
],
|
|
category: "blog",
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("rejects unknown language", () => {
|
|
expect(() =>
|
|
PublishFrontmatterSchema.parse({
|
|
status: "draft",
|
|
language: "fr",
|
|
outlets: [],
|
|
category: "blog",
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("rejects malformed scheduled timestamp (no offset)", () => {
|
|
expect(() =>
|
|
PublishFrontmatterSchema.parse({
|
|
status: "queued",
|
|
outlets: [],
|
|
scheduled: "2026-05-01T08:30:00",
|
|
category: "blog",
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("rejects negative version", () => {
|
|
expect(() =>
|
|
PublishFrontmatterSchema.parse({
|
|
status: "draft",
|
|
outlets: [],
|
|
category: "blog",
|
|
version: -1,
|
|
}),
|
|
).toThrow();
|
|
});
|
|
|
|
it("rejects unknown category", () => {
|
|
expect(() =>
|
|
PublishFrontmatterSchema.parse({
|
|
status: "draft",
|
|
outlets: [],
|
|
category: "newsletter",
|
|
}),
|
|
).toThrow();
|
|
});
|
|
});
|
|
|
|
describe("NoteFrontmatterSchema — round-trip", () => {
|
|
it("round-trips a full vault note frontmatter", () => {
|
|
const input = {
|
|
...baseTimestamps,
|
|
tags: ["MOC"],
|
|
publish: {
|
|
status: "published",
|
|
outlets: [
|
|
{ outlet: "stargue.com", status: "published", published_url: "https://stargue.com/x" },
|
|
],
|
|
category: "blog",
|
|
},
|
|
};
|
|
const parsed = NoteFrontmatterSchema.parse(input);
|
|
expect(parsed.publish?.status).toBe("published");
|
|
expect(parsed.publish?.canonical).toBe("stargue.com");
|
|
});
|
|
|
|
it("accepts a note without publish block (private vault notes)", () => {
|
|
const parsed = parseFrontmatter({ ...baseTimestamps, tags: null });
|
|
expect(parsed.publish).toBeUndefined();
|
|
});
|
|
|
|
it("rejects a note missing required timestamps", () => {
|
|
expect(() =>
|
|
NoteFrontmatterSchema.parse({ tags: [] }),
|
|
).toThrow();
|
|
});
|
|
});
|