import { assert, it } from "@effect/vitest"; import * as NodeServices from "@effect/platform-node/NodeServices"; 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 { ChildProcessSpawner } from "effect/unstable/process"; import { ServerConfig } from "../config.ts"; import type * as VcsDriver from "../vcs/VcsDriver.ts"; import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; import * as VcsProcess from "../vcs/VcsProcess.ts"; import * as AzureDevOpsCli from "./AzureDevOpsCli.ts"; import * as BitbucketApi from "./BitbucketApi.ts"; import * as GitHubCli from "./GitHubCli.ts"; import * as GitLabCli from "./GitLabCli.ts"; import * as SourceControlProviderRegistry from "./SourceControlProviderRegistry.ts"; const TEST_EPOCH = DateTime.makeUnsafe("1970-01-01T00:00:00.000Z"); const processOutput = ( stdout: string, options?: { readonly stderr?: string; readonly exitCode?: ChildProcessSpawner.ExitCode; }, ): VcsProcess.VcsProcessOutput => ({ exitCode: options?.exitCode ?? ChildProcessSpawner.ExitCode(0), stdout, stderr: options?.stderr ?? "", stdoutTruncated: false, stderrTruncated: false, }); function makeRegistry(input: { readonly remotes: ReadonlyArray<{ readonly name: string; readonly url: string; }>; readonly process?: Partial; }) { const driver = { listRemotes: () => Effect.succeed({ remotes: input.remotes.map((remote) => ({ ...remote, pushUrl: Option.none(), isPrimary: remote.name === "origin", })), freshness: { source: "live-local" as const, observedAt: TEST_EPOCH, expiresAt: Option.none(), }, }), } satisfies Partial; const registryLayer = Layer.mock(VcsDriverRegistry.VcsDriverRegistry)({ get: () => Effect.succeed(driver as unknown as VcsDriver.VcsDriverShape), resolve: () => Effect.succeed({ kind: "git", repository: { kind: "git", rootPath: "/repo", metadataPath: null, freshness: { source: "live-local" as const, observedAt: TEST_EPOCH, expiresAt: Option.none(), }, }, driver: driver as unknown as VcsDriver.VcsDriverShape, }), }); const processLayer = Layer.mock(VcsProcess.VcsProcess)({ run: () => Effect.succeed(processOutput("")), ...input.process, }); return SourceControlProviderRegistry.make().pipe( Effect.provide( Layer.mergeAll( registryLayer, processLayer, Layer.mock(AzureDevOpsCli.AzureDevOpsCli)({}), Layer.mock(BitbucketApi.BitbucketApi)({}), Layer.mock(GitHubCli.GitHubCli)({}), Layer.mock(GitLabCli.GitLabCli)({}), ServerConfig.layerTest(process.cwd(), { prefix: "t3-source-control-registry-test-" }).pipe( Layer.provide(NodeServices.layer), ), ), ), ); } it.effect("routes GitHub remotes to the GitHub provider", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "origin", url: "git@github.com:pingdotgg/t3code.git" }], }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "github"); }), ); it.effect("routes directly by provider kind for remote-first workflows", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [], }); const provider = yield* registry.get("github"); assert.strictEqual(provider.kind, "github"); }), ); it.effect("routes GitLab remotes to the GitLab provider", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "origin", url: "git@gitlab.com:group/project.git" }], }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "gitlab"); }), ); it.effect("routes authenticated self-hosted GitLab remotes without relying on host naming", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "origin", url: "https://self-hosted.example.test/group/project.git" }], process: { run: () => Effect.succeed( processOutput( `gitlab.com x gitlab.com: API call failed: 401 Unauthorized ! No token found self-hosted.example.test ✓ Logged in to self-hosted.example.test as gitlab-user ✓ Token found: ****** `, { exitCode: ChildProcessSpawner.ExitCode(1) }, ), ), }, }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "gitlab"); }), ); it.effect("routes authenticated self-hosted GitLab remotes on non-standard ports", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "origin", url: "https://self-hosted.example.test:8443/group/project.git" }], process: { run: () => Effect.succeed( processOutput( `self-hosted.example.test:8443 ✓ Logged in to self-hosted.example.test:8443 as gitlab-user ✓ Token found: ****** `, ), ), }, }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "gitlab"); }), ); it.effect("routes Bitbucket remotes to the Bitbucket provider", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "origin", url: "git@bitbucket.org:pingdotgg/t3code.git" }], }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "bitbucket"); }), ); it.effect("routes Azure DevOps remotes to the Azure DevOps provider", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "origin", url: "https://dev.azure.com/acme/project/_git/repo" }], }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "azure-devops"); }), ); it.effect("falls back to a non-origin remote when origin is not configured", () => Effect.gen(function* () { const registry = yield* makeRegistry({ remotes: [{ name: "upstream", url: "https://dev.azure.com/acme/project/_git/repo" }], }); const provider = yield* registry.resolve({ cwd: "/repo" }); assert.strictEqual(provider.kind, "azure-devops"); }), );