Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 112 additions & 0 deletions tools/hygiene/check-tick-history-shard-schema.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import { afterAll, beforeAll, describe, expect, test } from "bun:test";
import { mkdirSync, mkdtempSync, rmSync, writeFileSync } from "node:fs";
import { tmpdir } from "node:os";
import { dirname, join } from "node:path";

import type { ScanResult } from "./check-tick-history-shard-schema";

type ScanOne = (shardPath: string) => ScanResult;

let TMPDIR: string;
let scanOne: ScanOne;
let priorRepoRoot: string | undefined;

beforeAll(async () => {
TMPDIR = mkdtempSync(join(tmpdir(), "shard-schema-test-"));
priorRepoRoot = process.env["REPO_ROOT"];
process.env["REPO_ROOT"] = TMPDIR;
const mod = await import("./check-tick-history-shard-schema");
scanOne = mod.scanOne;
});

afterAll(() => {
if (priorRepoRoot === undefined) delete process.env["REPO_ROOT"];
else process.env["REPO_ROOT"] = priorRepoRoot;
Comment on lines +12 to +24
if (TMPDIR) rmSync(TMPDIR, { recursive: true, force: true });
});

function writeShard(relPath: string, content: string): string {
const full = join(TMPDIR, "docs/hygiene-history/ticks", relPath);
mkdirSync(dirname(full), { recursive: true });
writeFileSync(full, content);
return full;
}

const SIX_COLS = "| 2026-05-17T12:34Z | a | b | c | d | e |\n";

describe("scanOne", () => {
test("accepts a valid HHMMZ.md shard", () => {
const path = writeShard("2026/05/17/1234Z.md", SIX_COLS);
const result = scanOne(path);

expect(result.ok).toBe(true);
expect(result.violation).toBeUndefined();
});

test("accepts HHMMZ-<hex>.md filename variant", () => {
const path = writeShard("2026/05/17/1234Z-abc123.md", SIX_COLS);
Comment on lines +46 to +47
const result = scanOne(path);

expect(result.ok).toBe(true);
});

test("accepts col1 with seconds (HH:MM:SSZ) when minutes match filename", () => {
const content = "| 2026-05-17T12:34:56Z | a | b | c | d | e |\n";
const path = writeShard("2026/05/17/1234Z.md", content);
const result = scanOne(path);

expect(result.ok).toBe(true);
});

test("flags filename that does not match any schema regex", () => {
const path = writeShard("2026/05/17/foo.md", SIX_COLS);
const result = scanOne(path);

expect(result.ok).toBe(false);
expect(result.violation).toContain("filename does not match");
});

test("flags col1 date mismatch with path date", () => {
const content = "| 2026-05-18T12:34Z | a | b | c | d | e |\n";
const path = writeShard("2026/05/17/1234Z.md", content);
const result = scanOne(path);

expect(result.ok).toBe(false);
expect(result.violation).toContain("does not match path date");
});

test("flags col1 time mismatch with filename time", () => {
const content = "| 2026-05-17T12:35Z | a | b | c | d | e |\n";
const path = writeShard("2026/05/17/1234Z.md", content);
const result = scanOne(path);

expect(result.ok).toBe(false);
expect(result.violation).toContain("does not match filename");
});

test("flags first row with fewer than 6 columns (7+ pipes)", () => {
const content = "| 2026-05-17T12:34Z | only one col |\n";
const path = writeShard("2026/05/17/1234Z.md", content);
const result = scanOne(path);

expect(result.ok).toBe(false);
expect(result.violation).toContain("pipes");
});

test("flags empty file", () => {
const path = writeShard("2026/05/17/1234Z.md", "");
const result = scanOne(path);

expect(result.ok).toBe(false);
expect(result.violation).toBe("file is empty");
});

test("flags col1 missing leading pipe-space-timestamp format", () => {
const content = "|2026-05-17T12:34Z | a | b | c | d | e |\n";
const path = writeShard("2026/05/17/1234Z.md", content);
const result = scanOne(path);

expect(result.ok).toBe(false);
expect(result.violation).toContain("col1 must be exactly");
});
});
4 changes: 2 additions & 2 deletions tools/hygiene/check-tick-history-shard-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ const HASH_RE = /^(\d{4})(\d{2})Z-[0-9a-f]+$/;
const COL1_RE = /^\|\s(\d{4}-\d{2}-\d{2}T\d{2}:\d{2}(?::\d{2})?Z)\s\|\s/;
const SHARD_PREFIX = "docs/hygiene-history/ticks/";

interface ScanResult {
export interface ScanResult {
path: string;
ok: boolean;
violation?: string;
Expand All @@ -49,7 +49,7 @@ function repoRelative(p: string): string {
return normalizeToPosix(relative(ROOT, p));
}

function scanOne(shardPath: string): ScanResult {
export function scanOne(shardPath: string): ScanResult {
const pathRel = repoRelative(resolve(ROOT, shardPath));
const base = basename(shardPath, ".md");

Expand Down
Loading