Skip to content
Closed
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
21 changes: 13 additions & 8 deletions docs-internal/registry-parity-worklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
155 changes: 97 additions & 58 deletions packages/core/tests/git-quickstart.test.ts
Original file line number Diff line number Diff line change
@@ -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;
Expand All @@ -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<T extends { packagePath: string }>(
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
Expand All @@ -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<ExecResult> {
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<ExecResult> {
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);
});