diff --git a/docs-internal/registry-parity-worklist.md b/docs-internal/registry-parity-worklist.md index c0e4f3b5eb..82a651a0e6 100644 --- a/docs-internal/registry-parity-worklist.md +++ b/docs-internal/registry-parity-worklist.md @@ -527,14 +527,19 @@ For each: replace `describe.skip` with `describeIf(binaryPresent)` **and** write real e2e tests that prove Linux-parity behavior — not smoke tests. ### 11. Disabled suites — git, duckdb, codex -- **Broken:** tests exist but are `describe.skip`, so nothing runs even when the - binary is present. -- **Objective (per command, Linux parity):** - - **git** — clone/commit/log/diff/branch against a real repo & remote. - - **duckdb** — real analytical SQL, CSV read/write, file-backed DBs (the bar - example: real duckdb, not a WASI-stripped `SELECT 1`). - - **codex** — real run (after #9). -- **Proof:** each un-skipped suite passes with real behavior. +- **Broken:** tests exist but are skipped or excluded from the default run, so + coverage can disappear even when the binary is present. +- **Status:** + - **git — DONE.** The core quickstart e2e now exercises real upstream Git in a + VM for local origin creation, commit, branch, clone, checkout, `log`, and a + working-tree `diff`. It validates only the git package it uses instead of + eagerly requiring every registry package to be built. Proof: + `2026-07-08T13-13-00-0700-item11-git-quickstart-final.log`. + Rev: `svltqsmx`. + - **duckdb — TODO.** Needs real analytical SQL, CSV read/write, and file-backed + DB e2e kept runnable when the DuckDB package artifacts are present. + - **codex — TODO.** Needs the remaining real full-turn coverage un-skipped after + #9. - **rev:** one per command, e.g. `test(duckdb): real analytical-SQL e2e; un-skip` ### 12. No tests at all — 9 software + 5 agents diff --git a/packages/core/tests/git-quickstart.test.ts b/packages/core/tests/git-quickstart.test.ts index 0413bf7e59..cb1a72b9ff 100644 --- a/packages/core/tests/git-quickstart.test.ts +++ b/packages/core/tests/git-quickstart.test.ts @@ -1,10 +1,10 @@ +import { existsSync, statSync } from "node:fs"; +import { dirname, join, resolve } from "node:path"; import common from "@agentos-software/common"; import git from "@agentos-software/git"; -import { moduleAccessMounts } from "./helpers/node-modules-mount.js"; -import { resolve } from "node:path"; import { afterEach, beforeEach, describe, expect, test } from "vitest"; import { AgentOs } from "../src/index.js"; -import { requireBuilt } from "./helpers/registry-commands.js"; +import { moduleAccessMounts } from "./helpers/node-modules-mount.js"; type ExecResult = { stdout: string; @@ -19,8 +19,34 @@ const GIT_QUICKSTART_PERMISSIONS = { } as const; const COMMON_SOFTWARE = common; -const GIT_PACKAGE = requireBuilt(git, "git"); +const GIT_PACKAGE = requireBuiltPackage(git, "git"); const MODULE_ACCESS_CWD = resolve(import.meta.dirname, ".."); +const GIT_CONFIG = [ + "-c safe.directory=*", + "-c init.defaultBranch=main", + "-c user.name=agentos", + "-c user.email=agentos@example.invalid", +].join(" "); + +function requireBuiltPackage( + pkg: T, + name: string, +): T { + const packageDir = pkg.packagePath.endsWith(".aospkg") + ? join(dirname(pkg.packagePath), "package") + : pkg.packagePath; + const built = + existsSync(pkg.packagePath) && + statSync(pkg.packagePath).size > 16 && + existsSync(join(packageDir, "agentos-package.json")) && + existsSync(join(packageDir, "bin", name)); + if (!built) { + throw new Error( + `software package ${name} is NOT BUILT (no valid ${pkg.packagePath}).`, + ); + } + return pkg; +} function parseCurrentBranch(output: string): string { const branch = output @@ -45,65 +71,78 @@ function parseHeadRef(content: string): string { return headRef; } -describe("git quickstart integration", () => { +function gitCommand(args: string): string { + return `git ${GIT_CONFIG} ${args}`; +} - let vm: AgentOs; +describe("git quickstart integration", () => { + let vm: AgentOs; - beforeEach(async () => { - vm = await AgentOs.create({ - mounts: moduleAccessMounts(MODULE_ACCESS_CWD), - permissions: GIT_QUICKSTART_PERMISSIONS, - software: [COMMON_SOFTWARE, GIT_PACKAGE], - }); + beforeEach(async () => { + vm = await AgentOs.create({ + mounts: moduleAccessMounts(MODULE_ACCESS_CWD), + permissions: GIT_QUICKSTART_PERMISSIONS, + software: [COMMON_SOFTWARE, GIT_PACKAGE], }); + }); + + afterEach(async () => { + await vm?.dispose(); + }); + + async function run(command: string): Promise { + const result = await vm.exec(command); + if (result.exitCode !== 0) { + throw new Error( + `command failed: ${command}\n${result.stderr || result.stdout}`, + ); + } + return result; + } - afterEach(async () => { - await vm.dispose(); - }); + test("covers the quickstart local origin -> clone -> checkout flow", async () => { + await run(gitCommand("init /tmp/origin")); + await vm.writeFile("/tmp/origin/README.md", "# demo repo\n"); + await run(gitCommand("-C /tmp/origin add README.md")); + await run(gitCommand("-C /tmp/origin commit -m 'initial commit'")); - async function run(command: string): Promise { - const result = await vm.exec(command); - if (result.exitCode !== 0) { - throw new Error( - `command failed: ${command}\n${result.stderr || result.stdout}`, - ); - } - return result; - } + const defaultBranch = parseCurrentBranch( + (await run(gitCommand("-C /tmp/origin branch"))).stdout, + ); + + await run(gitCommand("-C /tmp/origin checkout -b feature")); + await vm.writeFile("/tmp/origin/feature.txt", "checked out from feature\n"); + await run(gitCommand("-C /tmp/origin add feature.txt")); + await run(gitCommand("-C /tmp/origin commit -m 'add feature file'")); + + await run(gitCommand("clone /tmp/origin /tmp/clone")); + + const cloneHead = new TextDecoder().decode( + await vm.readFile("/tmp/clone/.git/HEAD"), + ); + expect(parseHeadRef(cloneHead)).toBe("feature"); + expect(defaultBranch).not.toBe("feature"); - test( - "covers the quickstart local origin -> clone -> checkout flow", - async () => { - await run("git init /tmp/origin"); - await vm.writeFile("/tmp/origin/README.md", "# demo repo\n"); - await run("git -C /tmp/origin add README.md"); - await run("git -C /tmp/origin commit -m 'initial commit'"); - - const defaultBranch = parseCurrentBranch( - (await run("git -C /tmp/origin branch")).stdout, - ); - - await run("git -C /tmp/origin checkout -b feature"); - await vm.writeFile("/tmp/origin/feature.txt", "checked out from feature\n"); - await run("git -C /tmp/origin add feature.txt"); - await run("git -C /tmp/origin commit -m 'add feature file'"); - - await run("git clone /tmp/origin /tmp/clone"); - - const cloneHead = new TextDecoder().decode( - await vm.readFile("/tmp/clone/.git/HEAD"), - ); - expect(parseHeadRef(cloneHead)).toBe("feature"); - expect(defaultBranch).not.toBe("feature"); - - const featureFile = await vm.readFile("/tmp/clone/feature.txt"); - expect(new TextDecoder().decode(featureFile)).toBe( - "checked out from feature\n", - ); - - const readme = await vm.readFile("/tmp/clone/README.md"); - expect(new TextDecoder().decode(readme)).toBe("# demo repo\n"); - }, - 120_000, + const featureFile = await vm.readFile("/tmp/clone/feature.txt"); + expect(new TextDecoder().decode(featureFile)).toBe( + "checked out from feature\n", ); + + const readme = await vm.readFile("/tmp/clone/README.md"); + expect(new TextDecoder().decode(readme)).toBe("# demo repo\n"); + + const currentBranch = ( + await run(gitCommand("-C /tmp/clone branch --show-current")) + ).stdout.trim(); + expect(currentBranch).toBe("feature"); + + const log = await run(gitCommand("-C /tmp/clone log --oneline --all")); + expect(log.stdout).toContain("add feature file"); + expect(log.stdout).toContain("initial commit"); + + await vm.writeFile("/tmp/clone/README.md", "# changed demo repo\n"); + const diff = await run(gitCommand("-C /tmp/clone diff -- README.md")); + expect(diff.stdout).toContain("-# demo repo"); + expect(diff.stdout).toContain("+# changed demo repo"); + }, 120_000); });