2112 lines · 83.4 KB · TypeScript
RawDownload
// @effect-diagnostics nodeBuiltinImport:off - realpathSync.native resolves Windows 8.3 short names, which the Effect realPath does not.import * as NodeFS from "node:fs";import * as NodeServices from "@effect/platform-node/NodeServices";import { HostProcessPlatform } from "@t3tools/shared/hostProcess";import { assert, it, describe } from "@effect/vitest";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as FileSystem from "effect/FileSystem";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Path from "effect/Path";import * as PlatformError from "effect/PlatformError";import * as Ref from "effect/Ref";import * as Result from "effect/Result";import * as Scope from "effect/Scope";import * as Sink from "effect/Sink";import * as Stream from "effect/Stream";import * as TestClock from "effect/testing/TestClock";import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; import { GitCommandError, type ReviewDiffFileContentsInput } from "@t3tools/contracts";import { ServerConfig } from "../config.ts";import { makeGitVcsDriverCore, splitNullSeparatedGitStdoutPaths } from "./GitVcsDriverCore.ts";import * as GitVcsDriver from "./GitVcsDriver.ts"; const ServerConfigLayer = ServerConfig.layerTest(process.cwd(), {  prefix: "t3-git-vcs-driver-test-",});const TestLayer = GitVcsDriver.layer.pipe(  Layer.provide(ServerConfigLayer),  Layer.provideMerge(NodeServices.layer),); const makeNonRepositoryHandle = () =>  ChildProcessSpawner.makeHandle({    pid: ChildProcessSpawner.ProcessId(1),    exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(128)),    isRunning: Effect.succeed(false),    kill: () => Effect.void,    unref: Effect.succeed(Effect.void),    stdin: Sink.drain,    stdout: Stream.empty,    stderr: Stream.encodeText(Stream.make("fatal: not a git repository")),    all: Stream.empty,    getInputFd: () => Sink.drain,    getOutputFd: () => Stream.empty,  }); const makeSuccessfulHandle = (stdout: string) =>  ChildProcessSpawner.makeHandle({    pid: ChildProcessSpawner.ProcessId(1),    exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(0)),    isRunning: Effect.succeed(false),    kill: () => Effect.void,    unref: Effect.succeed(Effect.void),    stdin: Sink.drain,    stdout: Stream.encodeText(Stream.make(stdout)),    stderr: Stream.empty,    all: Stream.empty,    getInputFd: () => Sink.drain,    getOutputFd: () => Stream.empty,  }); const makeTmpDir = (  prefix = "git-vcs-driver-test-",): Effect.Effect<string, PlatformError.PlatformError, FileSystem.FileSystem | Scope.Scope> =>  Effect.gen(function* () {    const fileSystem = yield* FileSystem.FileSystem;    return yield* fileSystem.makeTempDirectoryScoped({ prefix });  }); const writeTextFile = (  cwd: string,  relativePath: string,  contents: string,): Effect.Effect<void, PlatformError.PlatformError, FileSystem.FileSystem | Path.Path> =>  Effect.gen(function* () {    const fileSystem = yield* FileSystem.FileSystem;    const pathService = yield* Path.Path;    const filePath = pathService.join(cwd, relativePath);    yield* fileSystem.makeDirectory(pathService.dirname(filePath), { recursive: true });    yield* fileSystem.writeFileString(filePath, contents);  }); const makeReviewDiffFileContentsInput = (  cwd: string,  overrides: Partial<Omit<ReviewDiffFileContentsInput, "cwd">> = {},): ReviewDiffFileContentsInput => ({  cwd,  sourceKind: "working-tree",  changeType: "change",  baseRef: "HEAD",  headRef: null,  oldPath: "README.md",  newPath: "README.md",  ...overrides,}); const git = (  cwd: string,  args: ReadonlyArray<string>,  env?: NodeJS.ProcessEnv,): Effect.Effect<string, GitCommandError, GitVcsDriver.GitVcsDriver> =>  Effect.gen(function* () {    const driver = yield* GitVcsDriver.GitVcsDriver;    const result = yield* driver.execute({      operation: "GitVcsDriver.test.git",      cwd,      args,      ...(env ? { env } : {}),      timeoutMs: 10_000,    });    return result.stdout.trim();  }); const initRepoWithCommit = (  cwd: string,): Effect.Effect<  { readonly initialBranch: string },  GitCommandError | PlatformError.PlatformError,  GitVcsDriver.GitVcsDriver | FileSystem.FileSystem | Path.Path> =>  Effect.gen(function* () {    const driver = yield* GitVcsDriver.GitVcsDriver;    yield* driver.initRepo({ cwd });    yield* git(cwd, ["config", "user.email", "test@test.com"]);    yield* git(cwd, ["config", "user.name", "Test"]);    yield* writeTextFile(cwd, "README.md", "# test\n");    yield* git(cwd, ["add", "."]);    yield* git(cwd, ["commit", "-m", "initial commit"]);    const initialBranch = yield* git(cwd, ["branch", "--show-current"]);    return { initialBranch };  }); for (const location of ["root", "nested", "worktree"] as const) {  it.effect(    `skips clean filters while the ${location} index is locked and resumes after unlock`,    () =>      Effect.gen(function* () {        const driver = yield* GitVcsDriver.GitVcsDriver;        const fs = yield* FileSystem.FileSystem;        const path = yield* Path.Path;        const repository = yield* makeTmpDir();        yield* initRepoWithCommit(repository);        const cwd = location === "worktree" ? yield* makeTmpDir() : repository;        if (location === "worktree") {          yield* git(repository, ["worktree", "add", "--detach", cwd]);        }        yield* git(cwd, ["config", "filter.probe.clean", "echo clean >> .filter-runs; cat"]);        yield* writeTextFile(cwd, ".gitattributes", "asset.bin filter=probe\n");        yield* writeTextFile(cwd, ".gitignore", ".filter-runs\n");        yield* writeTextFile(cwd, "asset.bin", "original\n");        yield* git(cwd, ["add", "."]);        yield* git(cwd, ["commit", "-m", "filtered asset"]);        NodeFS.utimesSync(path.join(cwd, "asset.bin"), 1, 1);        const runsPath = path.join(cwd, ".filter-runs");        yield* fs.remove(runsPath, { force: true });        const indexPath = yield* git(cwd, ["rev-parse", "--git-path", "index"]);        const lockPath = `${path.resolve(cwd, indexPath)}.lock`;        yield* fs.writeFileString(lockPath, "");        const statusCwd = location === "nested" ? path.join(cwd, "nested") : cwd;        yield* fs.makeDirectory(statusCwd, { recursive: true });         for (let poll = 0; poll < 3; poll++) {          const result = yield* driver.statusDetailsLocal(statusCwd).pipe(Effect.result);          assert.isTrue(Result.isFailure(result));          if (Result.isFailure(result)) assert.include(result.failure.detail, "index is locked");        }        assert.isFalse(yield* fs.exists(runsPath));        assert.isTrue(yield* fs.exists(lockPath));         yield* fs.remove(lockPath);        const status = yield* driver.statusDetailsLocal(statusCwd);        assert.isFalse(status.hasWorkingTreeChanges);        assert.include(yield* fs.readFileString(runsPath), "clean");      }).pipe(Effect.provide(TestLayer)),  );} it.effect("uses stable diagnostics for every parsed non-repository command", () => {  const commands: Array<{ readonly args: ReadonlyArray<string>; readonly lcAll?: string }> = [];  const spawner = ChildProcessSpawner.make((command) =>    Effect.sync(() => {      if (!ChildProcess.isStandardCommand(command)) {        return assert.fail("expected a standard Git command");      }      commands.push({        args: command.args,        ...(command.options.env?.LC_ALL ? { lcAll: command.options.env.LC_ALL } : {}),      });      return makeNonRepositoryHandle();    }),  );  const nodeServicesLayer = Layer.merge(    NodeServices.layer,    Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner),  );  const layer = GitVcsDriver.layer.pipe(    Layer.provide(ServerConfigLayer),    Layer.provideMerge(nodeServicesLayer),  );   return Effect.gen(function* () {    const driver = yield* GitVcsDriver.GitVcsDriver;    const cwd = "/repo";     yield* driver.statusDetailsLocal(cwd);    yield* driver.statusDetailsRemote(cwd, { refreshUpstream: false });    yield* driver.listRefs({ cwd });     assert.deepStrictEqual(commands, [      { args: ["rev-parse", "--git-path", "index"], lcAll: "C" },      { args: ["status", "--porcelain=2", "--branch"], lcAll: "C" },      { args: ["rev-parse", "--abbrev-ref", "HEAD"], lcAll: "C" },      { args: ["rev-parse", "--git-common-dir"], lcAll: "C" },    ]);  }).pipe(Effect.provide(layer));}); it.effect("invalidates origin remote cache when a driver mutation adds origin", () =>  Effect.gen(function* () {    const driver = yield* GitVcsDriver.GitVcsDriver;    const cwd = yield* makeTmpDir();    const remote = yield* makeTmpDir("git-vcs-driver-remote-");    yield* initRepoWithCommit(cwd);    yield* git(remote, ["init", "--bare"]);     const before = yield* driver.statusDetailsLocal(cwd);    assert.equal(before.hasOriginRemote, false);     yield* driver.ensureRemote({ cwd, preferredName: "origin", url: remote });     const after = yield* driver.statusDetailsLocal(cwd);    assert.equal(after.hasOriginRemote, true);  }).pipe(Effect.provide(TestLayer)),); it.effect("re-reads origin remote status after cache TTL expiry and bypassed invalidation", () =>  Effect.gen(function* () {    const driver = yield* GitVcsDriver.GitVcsDriver;    const cwd = yield* makeTmpDir();    const remote = yield* makeTmpDir("git-vcs-driver-remote-");    yield* initRepoWithCommit(cwd);    yield* git(remote, ["init", "--bare"]);     // First call caches hasOriginRemote = false (5-min TTL)    assert.equal((yield* driver.statusDetailsLocal(cwd)).hasOriginRemote, false);     // Add origin via raw git (bypasses invalidation hook)    yield* git(cwd, ["remote", "add", "origin", remote]);     // Cache still has the stale false (TTL not yet expired)    const stillCached = yield* driver.statusDetailsLocal(cwd);    assert.equal(stillCached.hasOriginRemote, false);     // Advance past the 5-minute TTL so the cache entry expires    yield* TestClock.adjust("6 minutes");     // After expiry, the next call re-executes and picks up the remote    const afterExpiry = yield* driver.statusDetailsLocal(cwd);    assert.equal(afterExpiry.hasOriginRemote, true);  }).pipe(Effect.provide(TestLayer)),); it.effect("coalesces concurrent ref pages into one repository snapshot", () =>  Effect.scoped(    Effect.gen(function* () {      const delegate = yield* ChildProcessSpawner.ChildProcessSpawner;      const spawnedArgs = yield* Ref.make<ReadonlyArray<ReadonlyArray<string>>>([]);      const firstWorktreeScanStarted = yield* Deferred.make<void>();      const remoteNamesScanCompleted = yield* Deferred.make<void>();      const delayFirstWorktreeScan = yield* Ref.make(true);      const countingSpawner = ChildProcessSpawner.make((command) =>        Effect.gen(function* () {          if (!ChildProcess.isStandardCommand(command)) {            return yield* Effect.die("expected a standard Git command");          }          yield* Ref.update(spawnedArgs, (current) => [...current, command.args]);          const isWorktreeScan =            command.args.includes("worktree") && command.args.includes("--porcelain");          const shouldDelay =            isWorktreeScan && (yield* Ref.getAndSet(delayFirstWorktreeScan, false));          if (shouldDelay) {            yield* Deferred.succeed(firstWorktreeScanStarted, undefined);            yield* Effect.sleep("8 seconds");          }          const handle = yield* delegate.spawn(command);          const isRemoteNamesScan =            command.args.length === 3 &&            command.args[0] === "--git-dir" &&            command.args[2] === "remote";          return isRemoteNamesScan            ? ChildProcessSpawner.makeHandle({                ...handle,                exitCode: handle.exitCode.pipe(                  Effect.tap(() => Deferred.succeed(remoteNamesScanCompleted, undefined)),                ),              })            : handle;        }),      );      const driver = yield* makeGitVcsDriverCore().pipe(        Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, countingSpawner),      );      const cwd = yield* makeTmpDir();      const runGit = (args: ReadonlyArray<string>) =>        driver.execute({          operation: "GitVcsDriver.test.coalescedListRefs",          cwd,          args,          timeoutMs: 10_000,        });       yield* driver.initRepo({ cwd });      yield* runGit(["config", "user.email", "test@test.com"]);      yield* runGit(["config", "user.name", "Test"]);      yield* writeTextFile(cwd, "README.md", "# test\n");      yield* runGit(["add", "."]);      yield* runGit(["commit", "-m", "initial commit"]);      yield* Ref.set(spawnedArgs, []);       const initialRequest = yield* driver        .listRefs({ cwd, refresh: true, limit: 100 })        .pipe(Effect.forkChild({ startImmediately: true }));      yield* Deferred.await(firstWorktreeScanStarted);      yield* Deferred.await(remoteNamesScanCompleted);      yield* TestClock.adjust("6 seconds");      const laterRequests = yield* Effect.all(        Array.from({ length: 30 }, (_, index) =>          driver.listRefs({            cwd,            refresh: true,            query: `missing-${index}`,            limit: 100,          }),        ),        { concurrency: "unbounded" },      ).pipe(Effect.forkChild({ startImmediately: true }));      yield* TestClock.adjust("2 seconds");      yield* Fiber.join(initialRequest);      yield* Fiber.join(laterRequests);      yield* driver.listRefs({ cwd, cursor: 1, limit: 100 });       const firstSnapshotCommands = yield* Ref.get(spawnedArgs);      const snapshotRefScans = firstSnapshotCommands.filter(        (args) =>          args.includes("for-each-ref") &&          args.includes("refs/heads") &&          args.includes("refs/remotes"),      );      const worktreeScans = firstSnapshotCommands.filter(        (args) => args.includes("worktree") && args.includes("--porcelain"),      );      assert.equal(snapshotRefScans.length, 1);      assert.equal(worktreeScans.length, 1);       yield* driver.createRef({ cwd, refName: "feature/cache-invalidation" });      const refreshed = yield* driver.listRefs({ cwd, limit: 100 });      assert.equal(        refreshed.refs.some((ref) => ref.name === "feature/cache-invalidation"),        true,      );      const allCommands = yield* Ref.get(spawnedArgs);      assert.equal(        allCommands.filter(          (args) =>            args.includes("for-each-ref") &&            args.includes("refs/heads") &&            args.includes("refs/remotes"),        ).length,        2,      );    }),  ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))),); it.effect("retries an in-flight ref snapshot invalidated by a mutation", () =>  Effect.scoped(    Effect.gen(function* () {      const delegate = yield* ChildProcessSpawner.ChildProcessSpawner;      const firstWorktreeScanStarted = yield* Deferred.make<void>();      const firstRefScanCompleted = yield* Deferred.make<void>();      const releaseFirstWorktreeScan = yield* Deferred.make<void>();      const delayFirstWorktreeScan = yield* Ref.make(true);      const refScans = yield* Ref.make(0);      const coordinatingSpawner = ChildProcessSpawner.make((command) =>        Effect.gen(function* () {          if (!ChildProcess.isStandardCommand(command)) {            return yield* Effect.die("expected a standard Git command");          }          const isWorktreeScan =            command.args.includes("worktree") && command.args.includes("--porcelain");          if (isWorktreeScan && (yield* Ref.getAndSet(delayFirstWorktreeScan, false))) {            yield* Deferred.succeed(firstWorktreeScanStarted, undefined);            yield* Deferred.await(releaseFirstWorktreeScan);          }          const handle = yield* delegate.spawn(command);          const isRefScan =            command.args.includes("for-each-ref") &&            command.args.includes("refs/heads") &&            command.args.includes("refs/remotes");          if (!isRefScan) return handle;          const scan = yield* Ref.updateAndGet(refScans, (count) => count + 1);          return scan === 1            ? ChildProcessSpawner.makeHandle({                ...handle,                exitCode: handle.exitCode.pipe(                  Effect.tap(() => Deferred.succeed(firstRefScanCompleted, undefined)),                ),              })            : handle;        }),      );      const driver = yield* makeGitVcsDriverCore().pipe(        Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, coordinatingSpawner),      );      const cwd = yield* makeTmpDir();      yield* initRepoWithCommit(cwd).pipe(Effect.provideService(GitVcsDriver.GitVcsDriver, driver));       const inFlight = yield* driver        .listRefs({ cwd, refresh: true, limit: 100 })        .pipe(Effect.forkChild({ startImmediately: true }));      yield* Deferred.await(firstWorktreeScanStarted);      yield* Deferred.await(firstRefScanCompleted);       yield* driver.createRef({ cwd, refName: "feature/during-refresh" });      yield* Deferred.succeed(releaseFirstWorktreeScan, undefined);       const refs = yield* Fiber.join(inFlight);      assert.isTrue(refs.refs.some((ref) => ref.name === "feature/during-refresh"));      assert.equal(yield* Ref.get(refScans), 2);    }),  ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))),); it.effect("invalidates a ref snapshot when a mutation fails after changing Git", () =>  Effect.scoped(    Effect.gen(function* () {      const delegate = yield* ChildProcessSpawner.ChildProcessSpawner;      const partiallyFailingSpawner = ChildProcessSpawner.make((command) =>        Effect.gen(function* () {          if (!ChildProcess.isStandardCommand(command)) {            return yield* Effect.die("expected a standard Git command");          }          if (command.args[0] === "branch" && command.args[1] === "feature/partial-failure") {            const handle = yield* delegate.spawn(command);            yield* handle.exitCode;            return makeNonRepositoryHandle();          }          return yield* delegate.spawn(command);        }),      );      const driver = yield* makeGitVcsDriverCore().pipe(        Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, partiallyFailingSpawner),      );      const cwd = yield* makeTmpDir();      yield* initRepoWithCommit(cwd).pipe(Effect.provideService(GitVcsDriver.GitVcsDriver, driver));      yield* driver.listRefs({ cwd, refresh: true });       yield* driver.createRef({ cwd, refName: "feature/partial-failure" }).pipe(Effect.flip);       const refs = yield* driver.listRefs({ cwd });      assert.isTrue(refs.refs.some((ref) => ref.name === "feature/partial-failure"));    }),  ).pipe(Effect.provide(ServerConfigLayer.pipe(Layer.provideMerge(NodeServices.layer)))),); it.effect("fails a ref snapshot when for-each-ref exits unsuccessfully", () =>  Effect.scoped(    Effect.gen(function* () {      const delegate = yield* ChildProcessSpawner.ChildProcessSpawner;      const snapshotAttempts = yield* Ref.make(0);      const failingSnapshotSpawner = ChildProcessSpawner.make((command) =>        Effect.gen(function* () {          if (!ChildProcess.isStandardCommand(command)) {            return yield* Effect.die("expected a standard Git command");          }          if (command.args.includes("for-each-ref")) {            yield* Ref.update(snapshotAttempts, (count) => count + 1);            return makeNonRepositoryHandle();          }          return yield* delegate.spawn(command);        }),      );      const driver = yield* makeGitVcsDriverCore().pipe(        Effect.provideService(ChildProcessSpawner.ChildProcessSpawner, failingSnapshotSpawner),      );      const cwd = yield* makeTmpDir();      yield* initRepoWithCommit(cwd).pipe(Effect.provideService(GitVcsDriver.GitVcsDriver, driver));       const error = yield* driver.listRefs({ cwd, refresh: true }).pipe(Effect.flip);       assert.deepInclude(error, {        _tag: "GitCommandError",        operation: "GitVcsDriver.listRefs.snapshotRefs",        detail: "Git ref snapshot enumeration failed.",        exitCode: 128,      });      assert.equal(yield* Ref.get(snapshotAttempts), 1);    }),

Lines 1–500 of 2112.