import { assert, describe, expect, it, vi } from "@effect/vitest";import * as DateTime from "effect/DateTime";import * as Effect from "effect/Effect";import * as Layer from "effect/Layer";import * as Option from "effect/Option"; import { GitCommandError, VcsRepositoryDetectionError } from "@t3tools/contracts"; import * as GitManager from "./GitManager.ts";import * as GitWorkflowService from "./GitWorkflowService.ts";import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";import * as VcsDriver from "../vcs/VcsDriver.ts";import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; const unusedVcsDriver = VcsDriver.VcsDriver.of({ capabilities: { kind: "git", supportsWorktrees: true, supportsBookmarks: false, supportsAtomicSnapshot: false, supportsPushDefaultRemote: true, ignoreClassifier: "native", }, execute: () => Effect.die("unused VCS driver execute"), detectRepository: () => Effect.die("unused VCS repository detection"), isInsideWorkTree: () => Effect.die("unused VCS worktree lookup"), listWorkspaceFiles: () => Effect.die("unused VCS workspace listing"), listRemotes: () => Effect.die("unused VCS remote listing"), filterIgnoredPaths: () => Effect.die("unused VCS ignore classification"), initRepository: () => Effect.die("unused VCS repository initialization"),}); function gitHandle(cwd: string): VcsDriverRegistry.VcsDriverHandle { return { kind: "git", repository: { kind: "git", rootPath: cwd, metadataPath: `${cwd}/.git`, freshness: { source: "live-local", observedAt: DateTime.makeUnsafe("2026-08-30T00:00:00.000Z"), expiresAt: Option.none(), }, }, driver: unusedVcsDriver, };} function makeLayer(input: { readonly detect: VcsDriverRegistry.VcsDriverRegistry["Service"]["detect"]; readonly resolve?: VcsDriverRegistry.VcsDriverRegistry["Service"]["resolve"]; readonly execute?: GitVcsDriver.GitVcsDriver["Service"]["execute"];}) { return GitWorkflowService.layer.pipe( Layer.provide( Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ detect: input.detect, ...(input.resolve === undefined ? {} : { resolve: input.resolve }), }), ), Layer.provide( Layer.mock(GitVcsDriver.GitVcsDriver)( input.execute === undefined ? {} : { execute: input.execute }, ), ), Layer.provide(Layer.mock(GitManager.GitManager)({})), );} describe("GitWorkflowService", () => { it.effect("returns an empty local status when no VCS repository is detected", () => Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; const status = yield* workflow.localStatus({ cwd: "/not-a-repo" }); assert.deepStrictEqual(status, { isRepo: false, hasPrimaryRemote: false, isDefaultRef: false, refName: null, hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0, }, }); }).pipe( Effect.provide( makeLayer({ detect: () => Effect.succeed(null), }), ), ), ); it.effect("returns an empty full status when no VCS repository is detected", () => Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; const status = yield* workflow.status({ cwd: "/not-a-repo" }); assert.deepStrictEqual(status, { isRepo: false, hasPrimaryRemote: false, isDefaultRef: false, refName: null, hasWorkingTreeChanges: false, workingTree: { files: [], insertions: 0, deletions: 0, }, hasUpstream: false, aheadCount: 0, behindCount: 0, aheadOfDefaultCount: 0, pr: null, }); }).pipe( Effect.provide( makeLayer({ detect: () => Effect.succeed(null), }), ), ), ); it.effect("does not call GitManager status methods when no VCS repository is detected", () => { const localStatus = vi.fn(); const remoteStatus = vi.fn(); const status = vi.fn(); const testLayer = GitWorkflowService.layer.pipe( Layer.provide( Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ detect: () => Effect.succeed(null), }), ), Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})), Layer.provide( Layer.mock(GitManager.GitManager)({ localStatus, remoteStatus, status, }), ), ); return Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; yield* workflow.localStatus({ cwd: "/not-a-repo" }); yield* workflow.remoteStatus({ cwd: "/not-a-repo" }); yield* workflow.status({ cwd: "/not-a-repo" }); assert.equal(localStatus.mock.calls.length, 0); assert.equal(remoteStatus.mock.calls.length, 0); assert.equal(status.mock.calls.length, 0); }).pipe(Effect.provide(testLayer)); }); it.effect("returns an empty ref list when no VCS repository is detected", () => Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; const refs = yield* workflow.listRefs({ cwd: "/not-a-repo" }); assert.deepStrictEqual(refs, { refs: [], isRepo: false, hasPrimaryRemote: false, nextCursor: null, totalCount: 0, }); }).pipe( Effect.provide( makeLayer({ detect: () => Effect.succeed(null), }), ), ), ); it.effect("structures workflow detection failures without exposing upstream details", () => { const cause = new VcsRepositoryDetectionError({ operation: "VcsDriverRegistry.detect", cwd: "/repo", detail: "upstream detail must stay in the cause chain", }); return Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; const error = yield* workflow.status({ cwd: "/repo" }).pipe(Effect.flip); expect(error).toMatchObject({ _tag: "GitManagerError", operation: "GitWorkflowService.status", cwd: "/repo", detail: "Failed to detect a VCS repository for this Git workflow.", }); expect(error.message).not.toContain(cause.detail); }).pipe( Effect.provide( makeLayer({ detect: () => Effect.fail(cause), }), ), ); }); it.effect("structures command detection failures without exposing upstream details", () => { const cause = new VcsRepositoryDetectionError({ operation: "VcsDriverRegistry.detect", cwd: "/repo", detail: "upstream command detail must stay in the cause chain", }); return Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; const error = yield* workflow.listRefs({ cwd: "/repo" }).pipe(Effect.flip); expect(error).toMatchObject({ _tag: "GitCommandError", operation: "GitWorkflowService.listRefs", command: "vcs-route", cwd: "/repo", detail: "Failed to detect a VCS repository for this Git command.", }); expect(error.message).not.toContain(cause.detail); }).pipe( Effect.provide( makeLayer({ detect: () => Effect.fail(cause), }), ), ); }); it.effect("distinguishes a detached HEAD from a failed symbolic-ref command", () => { const detached = new GitCommandError({ operation: "GitWorkflowService.currentBranch", command: "git symbolic-ref", cwd: "/detached", exitCode: 1, detail: "HEAD is detached.", }); const corrupt = new GitCommandError({ operation: "GitWorkflowService.currentBranch", command: "git symbolic-ref", cwd: "/corrupt", exitCode: 128, detail: "HEAD cannot be read.", }); return Effect.gen(function* () { const workflow = yield* GitWorkflowService.GitWorkflowService; assert.isNull(yield* workflow.currentBranch("/detached")); assert.strictEqual(yield* workflow.currentBranch("/corrupt").pipe(Effect.flip), corrupt); }).pipe( Effect.provide( makeLayer({ detect: () => Effect.succeed(null), resolve: ({ cwd }) => Effect.succeed(gitHandle(cwd)), execute: (input) => Effect.fail(input.cwd === "/detached" ? detached : corrupt), }), ), ); });});
import { assert, describe, expect, it, vi } from "@effect/vitest";
import * as DateTime from "effect/DateTime";
import * as Effect from "effect/Effect";
import * as Layer from "effect/Layer";
import * as Option from "effect/Option";
import { GitCommandError, VcsRepositoryDetectionError } from "@t3tools/contracts";
import * as GitManager from "./GitManager.ts";
import * as GitWorkflowService from "./GitWorkflowService.ts";
import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";
import * as VcsDriver from "../vcs/VcsDriver.ts";
import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts";
const unusedVcsDriver = VcsDriver.VcsDriver.of({
capabilities: {
kind: "git",
supportsWorktrees: true,
supportsBookmarks: false,
supportsAtomicSnapshot: false,
supportsPushDefaultRemote: true,
ignoreClassifier: "native",
},
execute: () => Effect.die("unused VCS driver execute"),
detectRepository: () => Effect.die("unused VCS repository detection"),
isInsideWorkTree: () => Effect.die("unused VCS worktree lookup"),
listWorkspaceFiles: () => Effect.die("unused VCS workspace listing"),
listRemotes: () => Effect.die("unused VCS remote listing"),
filterIgnoredPaths: () => Effect.die("unused VCS ignore classification"),
initRepository: () => Effect.die("unused VCS repository initialization"),
});
function gitHandle(cwd: string): VcsDriverRegistry.VcsDriverHandle {
return {
kind: "git",
repository: {
kind: "git",
rootPath: cwd,
metadataPath: `${cwd}/.git`,
freshness: {
source: "live-local",
observedAt: DateTime.makeUnsafe("2026-08-30T00:00:00.000Z"),
expiresAt: Option.none(),
},
},
driver: unusedVcsDriver,
};
}
function makeLayer(input: {
readonly detect: VcsDriverRegistry.VcsDriverRegistry["Service"]["detect"];
readonly resolve?: VcsDriverRegistry.VcsDriverRegistry["Service"]["resolve"];
readonly execute?: GitVcsDriver.GitVcsDriver["Service"]["execute"];
}) {
return GitWorkflowService.layer.pipe(
Layer.provide(
Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
detect: input.detect,
...(input.resolve === undefined ? {} : { resolve: input.resolve }),
}),
),
Layer.provide(
Layer.mock(GitVcsDriver.GitVcsDriver)(
input.execute === undefined ? {} : { execute: input.execute },
),
),
Layer.provide(Layer.mock(GitManager.GitManager)({})),
);
}
describe("GitWorkflowService", () => {
it.effect("returns an empty local status when no VCS repository is detected", () =>
Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
const status = yield* workflow.localStatus({ cwd: "/not-a-repo" });
assert.deepStrictEqual(status, {
isRepo: false,
hasPrimaryRemote: false,
isDefaultRef: false,
refName: null,
hasWorkingTreeChanges: false,
workingTree: {
files: [],
insertions: 0,
deletions: 0,
},
});
}).pipe(
Effect.provide(
makeLayer({
detect: () => Effect.succeed(null),
}),
),
),
);
it.effect("returns an empty full status when no VCS repository is detected", () =>
Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
const status = yield* workflow.status({ cwd: "/not-a-repo" });
assert.deepStrictEqual(status, {
isRepo: false,
hasPrimaryRemote: false,
isDefaultRef: false,
refName: null,
hasWorkingTreeChanges: false,
workingTree: {
files: [],
insertions: 0,
deletions: 0,
},
hasUpstream: false,
aheadCount: 0,
behindCount: 0,
aheadOfDefaultCount: 0,
pr: null,
});
}).pipe(
Effect.provide(
makeLayer({
detect: () => Effect.succeed(null),
}),
),
),
);
it.effect("does not call GitManager status methods when no VCS repository is detected", () => {
const localStatus = vi.fn();
const remoteStatus = vi.fn();
const status = vi.fn();
const testLayer = GitWorkflowService.layer.pipe(
Layer.provide(
Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({
detect: () => Effect.succeed(null),
}),
),
Layer.provide(Layer.mock(GitVcsDriver.GitVcsDriver)({})),
Layer.provide(
Layer.mock(GitManager.GitManager)({
localStatus,
remoteStatus,
status,
}),
),
);
return Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
yield* workflow.localStatus({ cwd: "/not-a-repo" });
yield* workflow.remoteStatus({ cwd: "/not-a-repo" });
yield* workflow.status({ cwd: "/not-a-repo" });
assert.equal(localStatus.mock.calls.length, 0);
assert.equal(remoteStatus.mock.calls.length, 0);
assert.equal(status.mock.calls.length, 0);
}).pipe(Effect.provide(testLayer));
});
it.effect("returns an empty ref list when no VCS repository is detected", () =>
Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
const refs = yield* workflow.listRefs({ cwd: "/not-a-repo" });
assert.deepStrictEqual(refs, {
refs: [],
isRepo: false,
hasPrimaryRemote: false,
nextCursor: null,
totalCount: 0,
});
}).pipe(
Effect.provide(
makeLayer({
detect: () => Effect.succeed(null),
}),
),
),
);
it.effect("structures workflow detection failures without exposing upstream details", () => {
const cause = new VcsRepositoryDetectionError({
operation: "VcsDriverRegistry.detect",
cwd: "/repo",
detail: "upstream detail must stay in the cause chain",
});
return Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
const error = yield* workflow.status({ cwd: "/repo" }).pipe(Effect.flip);
expect(error).toMatchObject({
_tag: "GitManagerError",
operation: "GitWorkflowService.status",
cwd: "/repo",
detail: "Failed to detect a VCS repository for this Git workflow.",
});
expect(error.message).not.toContain(cause.detail);
}).pipe(
Effect.provide(
makeLayer({
detect: () => Effect.fail(cause),
}),
),
);
});
it.effect("structures command detection failures without exposing upstream details", () => {
const cause = new VcsRepositoryDetectionError({
operation: "VcsDriverRegistry.detect",
cwd: "/repo",
detail: "upstream command detail must stay in the cause chain",
});
return Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
const error = yield* workflow.listRefs({ cwd: "/repo" }).pipe(Effect.flip);
expect(error).toMatchObject({
_tag: "GitCommandError",
operation: "GitWorkflowService.listRefs",
command: "vcs-route",
cwd: "/repo",
detail: "Failed to detect a VCS repository for this Git command.",
});
expect(error.message).not.toContain(cause.detail);
}).pipe(
Effect.provide(
makeLayer({
detect: () => Effect.fail(cause),
}),
),
);
});
it.effect("distinguishes a detached HEAD from a failed symbolic-ref command", () => {
const detached = new GitCommandError({
operation: "GitWorkflowService.currentBranch",
command: "git symbolic-ref",
cwd: "/detached",
exitCode: 1,
detail: "HEAD is detached.",
});
const corrupt = new GitCommandError({
operation: "GitWorkflowService.currentBranch",
command: "git symbolic-ref",
cwd: "/corrupt",
exitCode: 128,
detail: "HEAD cannot be read.",
});
return Effect.gen(function* () {
const workflow = yield* GitWorkflowService.GitWorkflowService;
assert.isNull(yield* workflow.currentBranch("/detached"));
assert.strictEqual(yield* workflow.currentBranch("/corrupt").pipe(Effect.flip), corrupt);
}).pipe(
Effect.provide(
makeLayer({
detect: () => Effect.succeed(null),
resolve: ({ cwd }) => Effect.succeed(gitHandle(cwd)),
execute: (input) => Effect.fail(input.cwd === "/detached" ? detached : corrupt),
}),
),
);
});
});