// E2E PROOF: tdd.md's CMS commits to a local bare git repo, not Forgejo. // // What this test demonstrates, end-to-end against the live deployed // container: // 1. The admin (syntaxai, via storage-state) saves an edit through // the web editor at /edit/sama/skill. // 2. The "applied live" page shows a fresh full SHA. // 3. That SHA matches refs/heads/main of the bare repo on p620 // (/home/scri/repos/tdd.md.git) — proving the commit landed in // our own git storage, not via a Forgejo HTTP round-trip. // 4. /GIT/syntaxai/tdd.md/commit/ renders the same commit // (read-side also flows through c14_git, not c14_forgejo). // 5. The commit's parent matches what HEAD was *before* the edit — // proving we observed the commit graph genuinely advance, not // a coincidental SHA match. // // The dramatic version of this test is in git-native-forgejo-down.spec.ts // (separate file because it stops the forgejo service on p620 — only // run that one when you can tolerate brief downtime). import { test, expect } from "@playwright/test"; import * as fs from "fs"; import * as path from "path"; import { execSync } from "child_process"; const SCREENSHOT_DIR = "test-results/git-native-proof"; // When run inside a Flatpak sandbox (e.g. the VS Code .flatpak), `ssh` lives // on the host so we have to hop through flatpak-spawn. When run on the host // directly, flatpak-spawn isn't on PATH — call ssh straight. Detect by env. const SSH_HOP = process.env.FLATPAK_ID ? "flatpak-spawn --host ssh" : "ssh"; const sshGit = (args: string): string => execSync(`${SSH_HOP} p620 'git --git-dir=/home/scri/repos/tdd.md.git ${args}'`, { encoding: "utf8", }).trim(); test.beforeAll(() => { fs.mkdirSync(SCREENSHOT_DIR, { recursive: true }); }); const ADMIN_AUTH_FILE = ".auth/admin.json"; test.describe("CMS commits to local bare repo, not Forgejo", () => { test.skip(!fs.existsSync(ADMIN_AUTH_FILE), `no ${ADMIN_AUTH_FILE} found`); test.use({ storageState: ADMIN_AUTH_FILE }); test("admin web-edit lands as a commit in /home/scri/repos/tdd.md.git on p620", async ({ page, request, }) => { // Snapshot HEAD before the edit. Read directly from the bare repo // via SSH — no HTTP, no Forgejo, just `git` on disk. const headBefore = sshGit("rev-parse refs/heads/main"); expect(headBefore).toMatch(/^[a-f0-9]{40}$/); // Load current source so we can busy-the-no-op-skip with a marker. const before = await (await request.get("/content/sama/skill.md")).text(); const marker = ``; const newBody = before + "\n" + marker + "\n"; // Save through the web editor. await page.goto("/edit/sama/skill"); const textarea = page.locator("textarea[name='body']"); await expect(textarea).toBeVisible(); await textarea.fill(newBody); await page.locator("button[type='submit']").click(); // Land on "applied live" with a 40-char commit SHA. await expect(page.getByRole("heading", { name: /applied live/i })).toBeVisible(); const commitLink = page.locator('a[href^="/GIT/syntaxai/tdd.md/commit/"]'); await expect(commitLink).toBeVisible(); const href = await commitLink.getAttribute("href"); const newSha = href!.replace("/GIT/syntaxai/tdd.md/commit/", ""); expect(newSha).toMatch(/^[a-f0-9]{40}$/); await page.screenshot({ path: path.join(SCREENSHOT_DIR, "1-applied-live-with-bare-repo-sha.png"), fullPage: true, }); // The bare repo on p620 — read-only check via SSH — must now point // at this exact SHA. If it doesn't, the commit either didn't land // or landed somewhere else (e.g. Forgejo). const headAfter = sshGit("rev-parse refs/heads/main"); expect(headAfter).toBe(newSha); expect(headAfter).not.toBe(headBefore); // The new commit's parent must be the OLD HEAD — proving the graph // genuinely advanced and our optimistic-concurrency check held. const parentOfNew = sshGit(`rev-parse ${newSha}^`); expect(parentOfNew).toBe(headBefore); // The commit message subject must match what c31_commit_meta emits. const msgSubject = sshGit(`log -1 --format=%s ${newSha}`); expect(msgSubject).toBe("edit content/sama/skill.md via web"); // The author must be syntaxai (set by c21_handlers_edit using viewer). const authorName = sshGit(`log -1 --format=%an ${newSha}`); expect(authorName).toBe("syntaxai"); // /GIT/.../commit/ reads from the same bare repo via c14_git // and renders Bun-native. Walk it and check it matches. const commitPage = await page.goto(`/GIT/syntaxai/tdd.md/commit/${newSha}`); expect(commitPage?.status()).toBe(200); await expect(page.locator(".commit-meta")).toContainText(newSha); await expect(page.locator(".commit-meta")).toContainText(headBefore.slice(0, 7)); await page.screenshot({ path: path.join(SCREENSHOT_DIR, "2-commit-view-served-from-bare-repo.png"), fullPage: true, }); // The raw .diff endpoint also goes through c14_git (git diff-tree // against the local bare repo, no Forgejo). const diffRes = await request.get(`/GIT/syntaxai/tdd.md/commit/${newSha}.diff`); expect(diffRes.status()).toBe(200); const diffBody = await diffRes.text(); expect(diffBody).toContain("diff --git"); expect(diffBody).toContain(marker); }); });