import fs from "node:fs";import path from "node:path";import { spawnSync } from "node:child_process"; import * as NodeServices from "@effect/platform-node/NodeServices";import { it } from "@effect/vitest";import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect";import { expect } from "vitest"; import { GitCommandError, GitHubCliError, TextGenerationError } from "../Errors.ts";import { type GitManagerShape } from "../Services/GitManager.ts";import { type GitHubCliShape, type GitHubPullRequestSummary, GitHubCli,} from "../Services/GitHubCli.ts";import { type TextGenerationShape, TextGeneration } from "../Services/TextGeneration.ts";import { GitServiceLive } from "./GitService.ts";import { GitService } from "../Services/GitService.ts";import { GitCoreLive } from "./GitCore.ts";import { makeGitManager } from "./GitManager.ts"; interface FakeGhScenario { prListSequence?: string[]; prListByHeadSelector?: Record<string, string>; createdPrUrl?: string; defaultBranch?: string; pullRequest?: { number: number; title: string; url: string; baseRefName: string; headRefName: string; state?: "open" | "closed" | "merged"; isCrossRepository?: boolean; headRepositoryNameWithOwner?: string | null; headRepositoryOwnerLogin?: string | null; }; repositoryCloneUrls?: Record<string, { url: string; sshUrl: string }>; failWith?: GitHubCliError;} interface FakeGitTextGeneration { generateCommitMessage: (input: { cwd: string; branch: string | null; stagedSummary: string; stagedPatch: string; includeBranch?: boolean; }) => Effect.Effect< { subject: string; body: string; branch?: string | undefined }, TextGenerationError >; generatePrContent: (input: { cwd: string; baseBranch: string; headBranch: string; commitSummary: string; diffSummary: string; diffPatch: string; }) => Effect.Effect<{ title: string; body: string }, TextGenerationError>; generateBranchName: (input: { cwd: string; message: string; }) => Effect.Effect<{ branch: string }, TextGenerationError>;} type FakePullRequest = NonNullable<FakeGhScenario["pullRequest"]>; function runGitSyncForFakeGh(cwd: string, args: readonly string[]): void { const result = spawnSync("git", args, { cwd, encoding: "utf8", }); if (result.status === 0) { return; } throw new GitHubCliError({ operation: "execute", detail: `Failed to simulate gh checkout with git ${args.join(" ")}: ${result.stderr?.trim() || "unknown error"}`, });} function isGitHubCliError(error: unknown): error is GitHubCliError { return ( typeof error === "object" && error !== null && "_tag" in error && (error as { _tag?: unknown })._tag === "GitHubCliError" );} function makeTempDir( prefix: string,): Effect.Effect<string, PlatformError.PlatformError, FileSystem.FileSystem | Scope.Scope> { return Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; return yield* fileSystem.makeTempDirectoryScoped({ prefix }); });} function runGit( cwd: string, args: readonly string[], allowNonZeroExit = false,): Effect.Effect< { readonly code: number; readonly stdout: string; readonly stderr: string }, GitCommandError, GitService> { return Effect.gen(function* () { const gitService = yield* GitService; return yield* gitService.execute({ operation: "GitManager.test.runGit", cwd, args, allowNonZeroExit, }); });} function initRepo( cwd: string,): Effect.Effect< void, PlatformError.PlatformError | GitCommandError, FileSystem.FileSystem | Scope.Scope | GitService> { return Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; yield* runGit(cwd, ["init", "--initial-branch=main"]); yield* runGit(cwd, ["config", "user.email", "test@example.com"]); yield* runGit(cwd, ["config", "user.name", "Test User"]); yield* fs.writeFileString(path.join(cwd, "README.md"), "hello\n"); yield* runGit(cwd, ["add", "README.md"]); yield* runGit(cwd, ["commit", "-m", "Initial commit"]); });} function createBareRemote(): Effect.Effect< string, PlatformError.PlatformError | GitCommandError, FileSystem.FileSystem | Scope.Scope | GitService> { return Effect.gen(function* () { const remoteDir = yield* makeTempDir("t3code-git-remote-"); yield* runGit(remoteDir, ["init", "--bare"]); return remoteDir; });} function createTextGeneration(overrides: Partial<FakeGitTextGeneration> = {}): TextGenerationShape { const implementation: FakeGitTextGeneration = { generateCommitMessage: (input) => Effect.succeed({ subject: "Implement stacked git actions", body: "", ...(input.includeBranch ? { branch: "feature/implement-stacked-git-actions" } : {}), }), generatePrContent: () => Effect.succeed({ title: "Add stacked git actions", body: "## Summary\n- Add stacked git workflow\n\n## Testing\n- Not run", }), generateBranchName: () => Effect.succeed({ branch: "update-workflow", }), ...overrides, }; return { generateCommitMessage: (input) => implementation.generateCommitMessage(input).pipe( Effect.mapError( (cause) => new TextGenerationError({ operation: "generateCommitMessage", detail: "fake text generation failed", ...(cause !== undefined ? { cause } : {}), }), ), ), generatePrContent: (input) => implementation.generatePrContent(input).pipe( Effect.mapError( (cause) => new TextGenerationError({ operation: "generatePrContent", detail: "fake text generation failed", ...(cause !== undefined ? { cause } : {}), }), ), ), generateBranchName: (input) => implementation.generateBranchName(input).pipe( Effect.mapError( (cause) => new TextGenerationError({ operation: "generateBranchName", detail: "fake text generation failed", ...(cause !== undefined ? { cause } : {}), }), ), ), };} function createGitHubCliWithFakeGh(scenario: FakeGhScenario = {}): { service: GitHubCliShape; ghCalls: string[];} { const prListQueue = [...(scenario.prListSequence ?? [])]; const ghCalls: string[] = []; const execute: GitHubCliShape["execute"] = (input) => { const args = [...input.args]; ghCalls.push(args.join(" ")); if (scenario.failWith) { return Effect.fail(scenario.failWith); } if (args[0] === "pr" && args[1] === "list") { const headSelectorIndex = args.findIndex((value) => value === "--head"); const headSelector = headSelectorIndex >= 0 && headSelectorIndex < args.length - 1 ? args[headSelectorIndex + 1] : undefined; const mappedStdout = typeof headSelector === "string" ? scenario.prListByHeadSelector?.[headSelector] : undefined; const stdout = (mappedStdout ?? prListQueue.shift() ?? "[]") + "\n"; return Effect.succeed({ stdout, stderr: "", code: 0, signal: null, timedOut: false, }); } if (args[0] === "pr" && args[1] === "create") { return Effect.succeed({ stdout: (scenario.createdPrUrl ?? "https://github.com/pingdotgg/codething-mvp/pull/101") + "\n", stderr: "", code: 0, signal: null, timedOut: false, }); } if (args[0] === "pr" && args[1] === "view") { const pullRequest: FakePullRequest = scenario.pullRequest ?? { number: 101, title: "Pull request", url: "https://github.com/pingdotgg/codething-mvp/pull/101", baseRefName: "main", headRefName: "feature/pull-request", state: "open", }; return Effect.succeed({ stdout: JSON.stringify({ ...pullRequest, ...(pullRequest.headRepositoryNameWithOwner ? { headRepository: { nameWithOwner: pullRequest.headRepositoryNameWithOwner, }, } : {}), ...(pullRequest.headRepositoryOwnerLogin ? { headRepositoryOwner: { login: pullRequest.headRepositoryOwnerLogin, }, } : {}), }) + "\n", stderr: "", code: 0, signal: null, timedOut: false, }); } if (args[0] === "pr" && args[1] === "checkout") { return Effect.try({ try: () => { const headBranch = scenario.pullRequest?.headRefName; if (headBranch) { const existingBranch = spawnSync( "git", ["show-ref", "--verify", "--quiet", `refs/heads/${headBranch}`], { cwd: input.cwd, encoding: "utf8", }, ); if (existingBranch.status === 0) { runGitSyncForFakeGh(input.cwd, ["checkout", headBranch]); } else { runGitSyncForFakeGh(input.cwd, ["checkout", "-b", headBranch]); } } return { stdout: "", stderr: "", code: 0, signal: null, timedOut: false, }; }, catch: (error) => isGitHubCliError(error) ? error : new GitHubCliError({ operation: "execute", detail: error instanceof Error ? `Failed to simulate gh checkout: ${error.message}` : "Failed to simulate gh checkout.", }), }); } if (args[0] === "repo" && args[1] === "view") { const repository = args[2]; if (typeof repository === "string" && args.includes("nameWithOwner,url,sshUrl")) { const cloneUrls = scenario.repositoryCloneUrls?.[repository]; if (!cloneUrls) { return Effect.fail( new GitHubCliError({ operation: "execute", detail: `Unexpected repository lookup: ${repository}`, }), ); } return Effect.succeed({ stdout: JSON.stringify({ nameWithOwner: repository, url: cloneUrls.url, sshUrl: cloneUrls.sshUrl, }) + "\n", stderr: "", code: 0, signal: null, timedOut: false, }); } return Effect.succeed({ stdout: `${scenario.defaultBranch ?? "main"}\n`, stderr: "", code: 0, signal: null, timedOut: false, }); } return Effect.fail( new GitHubCliError({ operation: "execute", detail: `Unexpected gh command: ${args.join(" ")}`, }), ); }; return { service: { execute, listOpenPullRequests: (input) => execute({ cwd: input.cwd, args: [ "pr", "list", "--head", input.headSelector, "--state", "open", "--limit", String(input.limit ?? 1), "--json", "number,title,url,baseRefName,headRefName", ], }).pipe( Effect.map( (result) => JSON.parse(result.stdout) as ReadonlyArray<GitHubPullRequestSummary>, ), ), createPullRequest: (input) => execute({ cwd: input.cwd, args: [ "pr", "create", "--base", input.baseBranch, "--head", input.headSelector, "--title", input.title, "--body-file", input.bodyFile, ], }).pipe(Effect.asVoid), getDefaultBranch: (input) => execute({ cwd: input.cwd, args: ["repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"], }).pipe( Effect.map((result) => { const value = result.stdout.trim(); return value.length > 0 ? value : null; }), ), getPullRequest: (input) => execute({ cwd: input.cwd, args: [ "pr", "view", input.reference, "--json", "number,title,url,baseRefName,headRefName,state,mergedAt,isCrossRepository,headRepository,headRepositoryOwner", ], }).pipe(Effect.map((result) => JSON.parse(result.stdout) as GitHubPullRequestSummary)), getRepositoryCloneUrls: (input) => execute({ cwd: input.cwd, args: ["repo", "view", input.repository, "--json", "nameWithOwner,url,sshUrl"], }).pipe(Effect.map((result) => JSON.parse(result.stdout))), checkoutPullRequest: (input) => execute({ cwd: input.cwd, args: ["pr", "checkout", input.reference, ...(input.force ? ["--force"] : [])], }).pipe(Effect.asVoid), }, ghCalls, };} function runStackedAction( manager: GitManagerShape, input: { cwd: string; action: "commit" | "commit_push" | "commit_push_pr"; commitMessage?: string; featureBranch?: boolean; filePaths?: readonly string[]; },) { return manager.runStackedAction(input);} function resolvePullRequest(manager: GitManagerShape, input: { cwd: string; reference: string }) { return manager.resolvePullRequest(input);} function preparePullRequestThread( manager: GitManagerShape, input: { cwd: string; reference: string; mode: "local" | "worktree" },) { return manager.preparePullRequestThread(input);} function makeManager(input?: { ghScenario?: FakeGhScenario; textGeneration?: Partial<FakeGitTextGeneration>;}) { const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario); const textGeneration = createTextGeneration(input?.textGeneration); const gitCoreLayer = GitCoreLive.pipe( Layer.provideMerge(GitServiceLive), Layer.provideMerge(NodeServices.layer), ); const managerLayer = Layer.mergeAll( Layer.succeed(GitHubCli, gitHubCli), Layer.succeed(TextGeneration, textGeneration), gitCoreLayer, NodeServices.layer, ); return makeGitManager.pipe( Effect.provide(managerLayer), Effect.map((manager) => ({ manager, ghCalls })), );} const GitManagerTestLayer = Layer.provideMerge(GitServiceLive, NodeServices.layer); it.layer(GitManagerTestLayer)("GitManager", (it) => { it.effect("status includes PR metadata when branch already has an open PR", () => Effect.gen(function* () {Lines 1–500 of 1939. Load more
import fs from "node:fs"; import path from "node:path"; import { spawnSync } from "node:child_process"; import * as NodeServices from "@effect/platform-node/NodeServices"; import { it } from "@effect/vitest"; import { Effect, FileSystem, Layer, PlatformError, Scope } from "effect"; import { expect } from "vitest"; import { GitCommandError, GitHubCliError, TextGenerationError } from "../Errors.ts"; import { type GitManagerShape } from "../Services/GitManager.ts"; import { type GitHubCliShape, type GitHubPullRequestSummary, GitHubCli, } from "../Services/GitHubCli.ts"; import { type TextGenerationShape, TextGeneration } from "../Services/TextGeneration.ts"; import { GitServiceLive } from "./GitService.ts"; import { GitService } from "../Services/GitService.ts"; import { GitCoreLive } from "./GitCore.ts"; import { makeGitManager } from "./GitManager.ts"; interface FakeGhScenario { prListSequence?: string[]; prListByHeadSelector?: Record<string, string>; createdPrUrl?: string; defaultBranch?: string; pullRequest?: { number: number; title: string; url: string; baseRefName: string; headRefName: string; state?: "open" | "closed" | "merged"; isCrossRepository?: boolean; headRepositoryNameWithOwner?: string | null; headRepositoryOwnerLogin?: string | null; }; repositoryCloneUrls?: Record<string, { url: string; sshUrl: string }>; failWith?: GitHubCliError; } interface FakeGitTextGeneration { generateCommitMessage: (input: { cwd: string; branch: string | null; stagedSummary: string; stagedPatch: string; includeBranch?: boolean; }) => Effect.Effect< { subject: string; body: string; branch?: string | undefined }, TextGenerationError >; generatePrContent: (input: { cwd: string; baseBranch: string; headBranch: string; commitSummary: string; diffSummary: string; diffPatch: string; }) => Effect.Effect<{ title: string; body: string }, TextGenerationError>; generateBranchName: (input: { cwd: string; message: string; }) => Effect.Effect<{ branch: string }, TextGenerationError>; } type FakePullRequest = NonNullable<FakeGhScenario["pullRequest"]>; function runGitSyncForFakeGh(cwd: string, args: readonly string[]): void { const result = spawnSync("git", args, { cwd, encoding: "utf8", }); if (result.status === 0) { return; } throw new GitHubCliError({ operation: "execute", detail: `Failed to simulate gh checkout with git ${args.join(" ")}: ${result.stderr?.trim() || "unknown error"}`, }); } function isGitHubCliError(error: unknown): error is GitHubCliError { return ( typeof error === "object" && error !== null && "_tag" in error && (error as { _tag?: unknown })._tag === "GitHubCliError" ); } function makeTempDir( prefix: string, ): Effect.Effect<string, PlatformError.PlatformError, FileSystem.FileSystem | Scope.Scope> { return Effect.gen(function* () { const fileSystem = yield* FileSystem.FileSystem; return yield* fileSystem.makeTempDirectoryScoped({ prefix }); }); } function runGit( cwd: string, args: readonly string[], allowNonZeroExit = false, ): Effect.Effect< { readonly code: number; readonly stdout: string; readonly stderr: string }, GitCommandError, GitService > { return Effect.gen(function* () { const gitService = yield* GitService; return yield* gitService.execute({ operation: "GitManager.test.runGit", cwd, args, allowNonZeroExit, }); }); } function initRepo( cwd: string, ): Effect.Effect< void, PlatformError.PlatformError | GitCommandError, FileSystem.FileSystem | Scope.Scope | GitService > { return Effect.gen(function* () { const fs = yield* FileSystem.FileSystem; yield* runGit(cwd, ["init", "--initial-branch=main"]); yield* runGit(cwd, ["config", "user.email", "test@example.com"]); yield* runGit(cwd, ["config", "user.name", "Test User"]); yield* fs.writeFileString(path.join(cwd, "README.md"), "hello\n"); yield* runGit(cwd, ["add", "README.md"]); yield* runGit(cwd, ["commit", "-m", "Initial commit"]); }); } function createBareRemote(): Effect.Effect< string, PlatformError.PlatformError | GitCommandError, FileSystem.FileSystem | Scope.Scope | GitService > { return Effect.gen(function* () { const remoteDir = yield* makeTempDir("t3code-git-remote-"); yield* runGit(remoteDir, ["init", "--bare"]); return remoteDir; }); } function createTextGeneration(overrides: Partial<FakeGitTextGeneration> = {}): TextGenerationShape { const implementation: FakeGitTextGeneration = { generateCommitMessage: (input) => Effect.succeed({ subject: "Implement stacked git actions", body: "", ...(input.includeBranch ? { branch: "feature/implement-stacked-git-actions" } : {}), }), generatePrContent: () => Effect.succeed({ title: "Add stacked git actions", body: "## Summary\n- Add stacked git workflow\n\n## Testing\n- Not run", }), generateBranchName: () => Effect.succeed({ branch: "update-workflow", }), ...overrides, }; return { generateCommitMessage: (input) => implementation.generateCommitMessage(input).pipe( Effect.mapError( (cause) => new TextGenerationError({ operation: "generateCommitMessage", detail: "fake text generation failed", ...(cause !== undefined ? { cause } : {}), }), ), ), generatePrContent: (input) => implementation.generatePrContent(input).pipe( Effect.mapError( (cause) => new TextGenerationError({ operation: "generatePrContent", detail: "fake text generation failed", ...(cause !== undefined ? { cause } : {}), }), ), ), generateBranchName: (input) => implementation.generateBranchName(input).pipe( Effect.mapError( (cause) => new TextGenerationError({ operation: "generateBranchName", detail: "fake text generation failed", ...(cause !== undefined ? { cause } : {}), }), ), ), }; } function createGitHubCliWithFakeGh(scenario: FakeGhScenario = {}): { service: GitHubCliShape; ghCalls: string[]; } { const prListQueue = [...(scenario.prListSequence ?? [])]; const ghCalls: string[] = []; const execute: GitHubCliShape["execute"] = (input) => { const args = [...input.args]; ghCalls.push(args.join(" ")); if (scenario.failWith) { return Effect.fail(scenario.failWith); } if (args[0] === "pr" && args[1] === "list") { const headSelectorIndex = args.findIndex((value) => value === "--head"); const headSelector = headSelectorIndex >= 0 && headSelectorIndex < args.length - 1 ? args[headSelectorIndex + 1] : undefined; const mappedStdout = typeof headSelector === "string" ? scenario.prListByHeadSelector?.[headSelector] : undefined; const stdout = (mappedStdout ?? prListQueue.shift() ?? "[]") + "\n"; return Effect.succeed({ stdout, stderr: "", code: 0, signal: null, timedOut: false, }); } if (args[0] === "pr" && args[1] === "create") { return Effect.succeed({ stdout: (scenario.createdPrUrl ?? "https://github.com/pingdotgg/codething-mvp/pull/101") + "\n", stderr: "", code: 0, signal: null, timedOut: false, }); } if (args[0] === "pr" && args[1] === "view") { const pullRequest: FakePullRequest = scenario.pullRequest ?? { number: 101, title: "Pull request", url: "https://github.com/pingdotgg/codething-mvp/pull/101", baseRefName: "main", headRefName: "feature/pull-request", state: "open", }; return Effect.succeed({ stdout: JSON.stringify({ ...pullRequest, ...(pullRequest.headRepositoryNameWithOwner ? { headRepository: { nameWithOwner: pullRequest.headRepositoryNameWithOwner, }, } : {}), ...(pullRequest.headRepositoryOwnerLogin ? { headRepositoryOwner: { login: pullRequest.headRepositoryOwnerLogin, }, } : {}), }) + "\n", stderr: "", code: 0, signal: null, timedOut: false, }); } if (args[0] === "pr" && args[1] === "checkout") { return Effect.try({ try: () => { const headBranch = scenario.pullRequest?.headRefName; if (headBranch) { const existingBranch = spawnSync( "git", ["show-ref", "--verify", "--quiet", `refs/heads/${headBranch}`], { cwd: input.cwd, encoding: "utf8", }, ); if (existingBranch.status === 0) { runGitSyncForFakeGh(input.cwd, ["checkout", headBranch]); } else { runGitSyncForFakeGh(input.cwd, ["checkout", "-b", headBranch]); } } return { stdout: "", stderr: "", code: 0, signal: null, timedOut: false, }; }, catch: (error) => isGitHubCliError(error) ? error : new GitHubCliError({ operation: "execute", detail: error instanceof Error ? `Failed to simulate gh checkout: ${error.message}` : "Failed to simulate gh checkout.", }), }); } if (args[0] === "repo" && args[1] === "view") { const repository = args[2]; if (typeof repository === "string" && args.includes("nameWithOwner,url,sshUrl")) { const cloneUrls = scenario.repositoryCloneUrls?.[repository]; if (!cloneUrls) { return Effect.fail( new GitHubCliError({ operation: "execute", detail: `Unexpected repository lookup: ${repository}`, }), ); } return Effect.succeed({ stdout: JSON.stringify({ nameWithOwner: repository, url: cloneUrls.url, sshUrl: cloneUrls.sshUrl, }) + "\n", stderr: "", code: 0, signal: null, timedOut: false, }); } return Effect.succeed({ stdout: `${scenario.defaultBranch ?? "main"}\n`, stderr: "", code: 0, signal: null, timedOut: false, }); } return Effect.fail( new GitHubCliError({ operation: "execute", detail: `Unexpected gh command: ${args.join(" ")}`, }), ); }; return { service: { execute, listOpenPullRequests: (input) => execute({ cwd: input.cwd, args: [ "pr", "list", "--head", input.headSelector, "--state", "open", "--limit", String(input.limit ?? 1), "--json", "number,title,url,baseRefName,headRefName", ], }).pipe( Effect.map( (result) => JSON.parse(result.stdout) as ReadonlyArray<GitHubPullRequestSummary>, ), ), createPullRequest: (input) => execute({ cwd: input.cwd, args: [ "pr", "create", "--base", input.baseBranch, "--head", input.headSelector, "--title", input.title, "--body-file", input.bodyFile, ], }).pipe(Effect.asVoid), getDefaultBranch: (input) => execute({ cwd: input.cwd, args: ["repo", "view", "--json", "defaultBranchRef", "--jq", ".defaultBranchRef.name"], }).pipe( Effect.map((result) => { const value = result.stdout.trim(); return value.length > 0 ? value : null; }), ), getPullRequest: (input) => execute({ cwd: input.cwd, args: [ "pr", "view", input.reference, "--json", "number,title,url,baseRefName,headRefName,state,mergedAt,isCrossRepository,headRepository,headRepositoryOwner", ], }).pipe(Effect.map((result) => JSON.parse(result.stdout) as GitHubPullRequestSummary)), getRepositoryCloneUrls: (input) => execute({ cwd: input.cwd, args: ["repo", "view", input.repository, "--json", "nameWithOwner,url,sshUrl"], }).pipe(Effect.map((result) => JSON.parse(result.stdout))), checkoutPullRequest: (input) => execute({ cwd: input.cwd, args: ["pr", "checkout", input.reference, ...(input.force ? ["--force"] : [])], }).pipe(Effect.asVoid), }, ghCalls, }; } function runStackedAction( manager: GitManagerShape, input: { cwd: string; action: "commit" | "commit_push" | "commit_push_pr"; commitMessage?: string; featureBranch?: boolean; filePaths?: readonly string[]; }, ) { return manager.runStackedAction(input); } function resolvePullRequest(manager: GitManagerShape, input: { cwd: string; reference: string }) { return manager.resolvePullRequest(input); } function preparePullRequestThread( manager: GitManagerShape, input: { cwd: string; reference: string; mode: "local" | "worktree" }, ) { return manager.preparePullRequestThread(input); } function makeManager(input?: { ghScenario?: FakeGhScenario; textGeneration?: Partial<FakeGitTextGeneration>; }) { const { service: gitHubCli, ghCalls } = createGitHubCliWithFakeGh(input?.ghScenario); const textGeneration = createTextGeneration(input?.textGeneration); const gitCoreLayer = GitCoreLive.pipe( Layer.provideMerge(GitServiceLive), Layer.provideMerge(NodeServices.layer), ); const managerLayer = Layer.mergeAll( Layer.succeed(GitHubCli, gitHubCli), Layer.succeed(TextGeneration, textGeneration), gitCoreLayer, NodeServices.layer, ); return makeGitManager.pipe( Effect.provide(managerLayer), Effect.map((manager) => ({ manager, ghCalls })), ); } const GitManagerTestLayer = Layer.provideMerge(GitServiceLive, NodeServices.layer); it.layer(GitManagerTestLayer)("GitManager", (it) => { it.effect("status includes PR metadata when branch already has an open PR", () => Effect.gen(function* () {