Require a caller-owned artifact resource scope for source traversal and Git metadata reads. Preserve cancellation, filesystem reasons, partial entries and bytes, and cleanup observations through import and the CLI. Transfer exact failed file owners at regular-file admission and close boundaries, including multiple failures encountered by Git reference resolution. Validation: 169 focused tests passed across 26 files; two Windows-only cases skipped. Build, typecheck, lint, formatting, knip, architecture guards, discovery and docs checks passed. BREAKING CHANGE: Reference source readers and imports require an ArtifactResourceScope. VCS metadata reads return Result, and CLI import failures include observed details and partial evidence.
111 lines
3.2 KiB
TypeScript
111 lines
3.2 KiB
TypeScript
import { mkdir, writeFile } from "node:fs/promises";
|
|
import { join } from "node:path";
|
|
import { expect, it } from "vitest";
|
|
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
|
|
import { withArtifactResourceScope } from "../../fixtures/artifactInventory.js";
|
|
import { execFileOutput } from "../../../src/process/ExecFileOutput.js";
|
|
import { readReferenceSourceVcs } from "../../../src/application/ReferenceSourceVcsAdapter.js";
|
|
|
|
const readVcs = (root: string, signal?: AbortSignal) =>
|
|
withArtifactResourceScope(async (resources) => {
|
|
const result = await readReferenceSourceVcs(root, resources, signal);
|
|
if (!result.ok) throw result.error;
|
|
return result.value;
|
|
});
|
|
|
|
it("reads a linked worktree's own branch and detached HEAD through shared Git refs", async () => {
|
|
const root = await createTestTempDirectory("rea-reference-worktree-");
|
|
const repository = join(root, "repository");
|
|
await mkdir(repository);
|
|
const git = (...args: string[]) =>
|
|
execFileOutput("git", args, { cwd: repository });
|
|
await git("init", "--initial-branch=main");
|
|
const commit = () =>
|
|
git(
|
|
"-c",
|
|
"user.name=REA fixture",
|
|
"-c",
|
|
"user.email=fixture@example.invalid",
|
|
"-c",
|
|
"commit.gpgsign=false",
|
|
"commit",
|
|
"--allow-empty",
|
|
"-m",
|
|
"fixture",
|
|
);
|
|
await commit();
|
|
const mainHead = (await git("rev-parse", "HEAD")).stdout.trim();
|
|
const linked = join(root, "linked");
|
|
await git("worktree", "add", "-b", "linked", linked);
|
|
await execFileOutput(
|
|
"git",
|
|
[
|
|
"-c",
|
|
"user.name=REA fixture",
|
|
"-c",
|
|
"user.email=fixture@example.invalid",
|
|
"-c",
|
|
"commit.gpgsign=false",
|
|
"commit",
|
|
"--allow-empty",
|
|
"-m",
|
|
"linked fixture",
|
|
],
|
|
{ cwd: linked },
|
|
);
|
|
const linkedHead = (
|
|
await execFileOutput("git", ["rev-parse", "HEAD"], { cwd: linked })
|
|
).stdout.trim();
|
|
expect(linkedHead).not.toBe(mainHead);
|
|
expect(await readVcs(repository)).toEqual({
|
|
kind: "git",
|
|
head: mainHead,
|
|
dirty: null,
|
|
});
|
|
expect(await readVcs(linked)).toEqual({
|
|
kind: "git",
|
|
head: linkedHead,
|
|
dirty: null,
|
|
});
|
|
await git("pack-refs", "--all", "--prune");
|
|
expect(await readVcs(linked)).toEqual({
|
|
kind: "git",
|
|
head: linkedHead,
|
|
dirty: null,
|
|
});
|
|
await execFileOutput("git", ["checkout", "--detach", mainHead], {
|
|
cwd: linked,
|
|
});
|
|
expect(await readVcs(linked)).toEqual({
|
|
kind: "git",
|
|
head: mainHead,
|
|
dirty: null,
|
|
});
|
|
const relativePointer = join(root, "relative");
|
|
await mkdir(relativePointer);
|
|
await writeFile(
|
|
join(relativePointer, ".git"),
|
|
"gitdir: ../repository/.git\r\n",
|
|
);
|
|
expect(await readVcs(relativePointer)).toEqual({
|
|
kind: "git",
|
|
head: mainHead,
|
|
dirty: null,
|
|
});
|
|
// A non-regular loose ref must remain unknown even when packed-refs has
|
|
// a readable value. The library's promise-API probe is not a source failure.
|
|
await mkdir(join(repository, ".git", "refs", "heads", "main"));
|
|
expect(await readVcs(repository)).toEqual({
|
|
kind: "unknown",
|
|
head: null,
|
|
dirty: null,
|
|
});
|
|
const controller = new AbortController();
|
|
controller.abort();
|
|
expect(await readVcs(linked, controller.signal)).toEqual({
|
|
kind: "unknown",
|
|
head: null,
|
|
dirty: null,
|
|
});
|
|
});
|