268 lines · 8.4 KB · TypeScript
RawDownload
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),        }),      ),    );  });});