logicsrc/apps/logicsrc-web/contract/openinstall.contract.test.ts
Anthony Ettinger 8d063eb97c OpenInstall 0.1: one idempotent bin/install.sh that puts an app into service
A repository carries bin/install.sh and, when the defaults are not right,
bin/install.conf. Run on a box from a checkout it installs the runtime and a
local Postgres or Redis, builds, writes a systemd unit and an nginx site with
TLS, restarts and health-checks the app. Phases setup, build, activate and
status; exit 0, 1 or 3; never overwrites a file without its marker line;
secrets only in STATE_DIR/app.env. sh1pt's deploy-ssh install.sh is the
reference script and `sh1pt ship --target deploy-ssh` the reference deployer.

Registered in the process family, with a landing page at /openinstall and a
contract test holding the page's tables to the spec.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
2026-10-01 10:00:36 +00:00

63 lines
2.5 KiB
TypeScript

import { describe, expect, it } from "vitest";
import { readDoc } from "../src/lib/docs";
import { CONF, EXIT_CODES, PHASES, SETTINGS } from "../src/app/openinstall/data";
// The landing page restates the spec's tables. These tests keep the two, and
// the spec's own worked example, from drifting apart.
const doc = readDoc("openinstall") ?? "";
/** The first-column `code` cells of the table under a `## heading`. */
function tableKeys(heading: string): string[] {
const start = doc.indexOf(`\n## ${heading}\n`);
expect(start, `docs/openinstall.md has a ## ${heading} section`).toBeGreaterThan(-1);
const next = doc.indexOf("\n## ", start + 1);
const section = doc.slice(start, next === -1 ? undefined : next);
return section
.split("\n")
.map((line) => line.match(/^\| `([^`]+)` \|/))
.filter((m): m is RegExpMatchArray => m !== null)
.map((m) => m[1]);
}
function confKeys(conf: string): string[] {
return conf
.split("\n")
.filter((line) => line && !line.startsWith("#"))
.map((line) => line.slice(0, line.indexOf("=")));
}
describe("OpenInstall: the spec and its landing page agree", () => {
it("lists the same settings, in the same order", () => {
expect(SETTINGS.map(([key]) => key)).toEqual(tableKeys("Settings"));
});
it("lists the same phases and exit codes", () => {
expect(PHASES.map(([name]) => name)).toEqual(tableKeys("Phases"));
expect(EXIT_CODES.map(([code]) => code)).toEqual(tableKeys("Exit codes"));
expect(tableKeys("Exit codes")).toEqual(["0", "1", "3"]);
});
it("uses only documented settings in the worked example, which the page shows verbatim", () => {
const documented = new Set(tableKeys("Settings"));
const fence = doc.match(/```\n(# bin\/install\.conf[^`]*?)```/);
expect(fence, "the worked example's install.conf").not.toBeNull();
const example = fence![1].trim();
expect(example).toBe(CONF);
for (const key of confKeys(example)) expect(documented.has(key), key).toBe(true);
});
it("numbers its conformance rules without gaps", () => {
const numbers = [...doc.matchAll(/^### (\d+)\. /gm)].map((m) => Number(m[1]));
expect(numbers.length).toBeGreaterThanOrEqual(10);
expect(numbers).toEqual(numbers.map((_, i) => i + 1));
});
it("names the ownership marker the reference script writes", () => {
expect(doc).toContain("`# managed by bin/install.sh`");
});
it("has no em dashes", () => {
expect(doc).not.toContain(String.fromCharCode(0x2014));
});
});