records: the record is read where it is written
A module keeping a checkout of a repository of decisions, designs and issues from the git seat, current on every announced merge and on a timer, answering records_search / records_read / records_list / records_status / records_sync at the commit it read (novox/hq ADR 0025, ADR 0153). The repository is a setting; it names no mesh.
This commit is contained in:
@@ -0,0 +1,78 @@
|
||||
/**
|
||||
* The reader against a real repository: a checkout, a phrase found where it is written, a document
|
||||
* read whole, a merge pulled — and the check novox/hq ADR 0025 names: search for a phrase that appears
|
||||
* only in one design document, and get it back.
|
||||
*/
|
||||
import assert from "node:assert/strict";
|
||||
import { test } from "node:test";
|
||||
import { execFileSync } from "node:child_process";
|
||||
import { mkdtempSync, mkdirSync, writeFileSync } from "node:fs";
|
||||
import { join } from "node:path";
|
||||
|
||||
import { Records } from "../records.ts";
|
||||
|
||||
function aRepository(): string {
|
||||
const dir = mkdtempSync("/tmp/records-origin-");
|
||||
const git = (...args: string[]) => execFileSync("git", ["-C", dir, ...args], { stdio: "pipe" });
|
||||
git("init", "--quiet", "--initial-branch=main");
|
||||
git("config", "user.email", "t@example.invalid");
|
||||
git("config", "user.name", "t");
|
||||
mkdirSync(join(dir, "02-DECISIONS"));
|
||||
mkdirSync(join(dir, "03-DESIGN"));
|
||||
writeFileSync(join(dir, "README.md"), "# A repository\n\nWhat this is.\n");
|
||||
writeFileSync(join(dir, "02-DECISIONS/0001-a-decision.md"), "# 1. A decision\n\n## Context\n\nThe context.\n\n## Decision\n\nWe decided the thing.\n");
|
||||
writeFileSync(join(dir, "03-DESIGN/07-knowledge.md"), "# Knowledge\n\n## The stores\n\nSilence and success must never look alike.\n");
|
||||
git("add", "-A");
|
||||
git("commit", "--quiet", "-m", "first");
|
||||
return dir;
|
||||
}
|
||||
|
||||
test("a phrase that appears in one design document comes back from where it is written", async () => {
|
||||
const origin = aRepository();
|
||||
const records = new Records(mkdtempSync("/tmp/records-checkout-"), "file://" + origin.replace(/\/[^/]+$/, ""), origin.split("/").pop()!);
|
||||
// A file:// origin has no `.git` suffix; point the clone at the directory itself.
|
||||
Object.defineProperty(records, "url", { get: () => origin });
|
||||
await records.sync();
|
||||
const found = await records.search("never look alike");
|
||||
assert.equal(found.hits.length, 1);
|
||||
assert.equal(found.hits[0]!.path, "03-DESIGN/07-knowledge.md");
|
||||
assert.equal(found.hits[0]!.heading, "The stores");
|
||||
assert.match(found.commit, /^[0-9a-f]{40}$/, "the answer names the commit it was read at");
|
||||
|
||||
const doc = await records.read("02-DECISIONS/0001-a-decision.md");
|
||||
assert.match(doc.content, /We decided the thing/);
|
||||
assert.equal(doc.truncated, false);
|
||||
|
||||
const root = await records.list();
|
||||
assert.deepEqual(root.folders, ["02-DECISIONS", "03-DESIGN"]);
|
||||
assert.deepEqual(root.documents, ["README.md"]);
|
||||
|
||||
const standing = await records.standing();
|
||||
assert.equal(standing.documents, 3);
|
||||
assert.equal(standing.lastError, undefined);
|
||||
|
||||
// A merge on the origin, pulled: the checkout follows the source and names the new commit.
|
||||
writeFileSync(join(origin, "02-DECISIONS/0002-another.md"), "# 2. Another\n\nA phrase nobody wrote before.\n");
|
||||
execFileSync("git", ["-C", origin, "add", "-A"], { stdio: "pipe" });
|
||||
execFileSync("git", ["-C", origin, "commit", "--quiet", "-m", "second"], { stdio: "pipe" });
|
||||
await records.sync();
|
||||
const after = await records.search("nobody wrote before");
|
||||
assert.equal(after.hits.length, 1);
|
||||
assert.notEqual(after.commit, found.commit);
|
||||
});
|
||||
|
||||
test("a path outside the repository is refused, and an empty search is too", async () => {
|
||||
const records = new Records(mkdtempSync("/tmp/records-checkout-"), "http://forge.invalid:3000", "novox/hq");
|
||||
await assert.rejects(() => records.read("../etc/passwd"), /not a path inside/);
|
||||
await assert.rejects(() => records.read("/etc/passwd"), /not a path inside/);
|
||||
await assert.rejects(() => records.search(" "), /empty one/);
|
||||
assert.equal(records.url, "http://forge.invalid:3000/novox/hq.git");
|
||||
});
|
||||
|
||||
test("a sync that fails leaves the checkout standing and says why", async () => {
|
||||
const records = new Records(mkdtempSync("/tmp/records-checkout-"), "http://127.0.0.1:1", "novox/hq");
|
||||
await records.sync();
|
||||
const standing = await records.standing();
|
||||
assert.equal(standing.commit, "");
|
||||
assert.ok(standing.lastError, "a failed sync is said, not swallowed");
|
||||
});
|
||||
Reference in New Issue
Block a user