| ... | ... | @@ -6,6 +6,9 @@ import * as Layer from "effect/Layer"; |
| 6 | 6 | import * as Path from "effect/Path"; |
| 7 | 7 | import * as PlatformError from "effect/PlatformError"; |
| 8 | 8 | import * as Scope from "effect/Scope"; |
| 9 | import * as Sink from "effect/Sink"; |
| 10 | import * as Stream from "effect/Stream"; |
| 11 | import { ChildProcess, ChildProcessSpawner } from "effect/unstable/process"; |
| 9 | 12 | |
| 10 | 13 | import { GitCommandError } from "@t3tools/contracts"; |
| 11 | 14 | import { ServerConfig } from "../config.ts"; |
| ... | ... | @@ -20,6 +23,21 @@ const TestLayer = GitVcsDriver.layer.pipe( |
| 20 | 23 | Layer.provideMerge(NodeServices.layer), |
| 21 | 24 | ); |
| 22 | 25 | |
| 26 | const makeNonRepositoryHandle = () => |
| 27 | ChildProcessSpawner.makeHandle({ |
| 28 | pid: ChildProcessSpawner.ProcessId(1), |
| 29 | exitCode: Effect.succeed(ChildProcessSpawner.ExitCode(128)), |
| 30 | isRunning: Effect.succeed(false), |
| 31 | kill: () => Effect.void, |
| 32 | unref: Effect.succeed(Effect.void), |
| 33 | stdin: Sink.drain, |
| 34 | stdout: Stream.empty, |
| 35 | stderr: Stream.encodeText(Stream.make("fatal: not a git repository")), |
| 36 | all: Stream.empty, |
| 37 | getInputFd: () => Sink.drain, |
| 38 | getOutputFd: () => Stream.empty, |
| 39 | }); |
| 40 | |
| 23 | 41 | const makeTmpDir = ( |
| 24 | 42 | prefix = "git-vcs-driver-test-", |
| 25 | 43 | ): Effect.Effect<string, PlatformError.PlatformError, FileSystem.FileSystem | Scope.Scope> => |
| ... | ... | @@ -77,7 +95,62 @@ const initRepoWithCommit = ( |
| 77 | 95 | return { initialBranch }; |
| 78 | 96 | }); |
| 79 | 97 | |
| 98 | it.effect("uses stable diagnostics for every parsed non-repository command", () => { |
| 99 | const commands: Array<{ readonly args: ReadonlyArray<string>; readonly lcAll?: string }> = []; |
| 100 | const spawner = ChildProcessSpawner.make((command) => |
| 101 | Effect.sync(() => { |
| 102 | if (!ChildProcess.isStandardCommand(command)) { |
| 103 | return assert.fail("expected a standard Git command"); |
| 104 | } |
| 105 | commands.push({ |
| 106 | args: command.args, |
| 107 | ...(command.options.env?.LC_ALL ? { lcAll: command.options.env.LC_ALL } : {}), |
| 108 | }); |
| 109 | return makeNonRepositoryHandle(); |
| 110 | }), |
| 111 | ); |
| 112 | const nodeServicesLayer = Layer.merge( |
| 113 | NodeServices.layer, |
| 114 | Layer.succeed(ChildProcessSpawner.ChildProcessSpawner, spawner), |
| 115 | ); |
| 116 | const layer = GitVcsDriver.layer.pipe( |
| 117 | Layer.provide(ServerConfigLayer), |
| 118 | Layer.provideMerge(nodeServicesLayer), |
| 119 | ); |
| 120 | |
| 121 | return Effect.gen(function* () { |
| 122 | const driver = yield* GitVcsDriver.GitVcsDriver; |
| 123 | const cwd = "/repo"; |
| 124 | |
| 125 | yield* driver.statusDetailsLocal(cwd); |
| 126 | yield* driver.statusDetailsRemote(cwd, { refreshUpstream: false }); |
| 127 | yield* driver.listRefs({ cwd }); |
| 128 | |
| 129 | assert.deepStrictEqual(commands, [ |
| 130 | { args: ["status", "--porcelain=2", "--branch"], lcAll: "C" }, |
| 131 | { args: ["rev-parse", "--abbrev-ref", "HEAD"], lcAll: "C" }, |
| 132 | { args: ["branch", "--no-color", "--no-column"], lcAll: "C" }, |
| 133 | ]); |
| 134 | }).pipe(Effect.provide(layer)); |
| 135 | }); |
| 136 | |
| 80 | 137 | it.layer(TestLayer)("GitVcsDriver core integration", (it) => { |
| 138 | describe("process environment", () => { |
| 139 | it.effect("preserves the caller locale for general Git subprocesses", () => |
| 140 | Effect.gen(function* () { |
| 141 | const cwd = yield* makeTmpDir(); |
| 142 | |
| 143 | const locale = yield* git( |
| 144 | cwd, |
| 145 | ["-c", 'alias.print-locale=!printf "%s" "$LC_ALL"', "print-locale"], |
| 146 | { LC_ALL: "zh_CN.UTF-8" }, |
| 147 | ); |
| 148 | |
| 149 | assert.equal(locale, "zh_CN.UTF-8"); |
| 150 | }), |
| 151 | ); |
| 152 | }); |
| 153 | |
| 81 | 154 | describe("structured errors", () => { |
| 82 | 155 | it.effect("preserves structured spawn context and the platform cause", () => |
| 83 | 156 | Effect.gen(function* () { |