History

[codex] structure source-control repository failures (#3336)

Julius Marminge committed 4 months ago

Commit a26f0dc1 parent 6ff6c132 files · +98 −45Browse files

Showing the changes to apps/server/src/sourceControl/SourceControlRepositoryService.test.ts (1 of 2 files). Show all

Unified · Split

apps/server/src/sourceControl/SourceControlRepositoryService.test.ts+83−4
@@ -1,11 +1,13 @@
1import * as NodePath from "@effect/platform-node/NodePath";
1import * as NodeServices from "@effect/platform-node/NodeServices";2import * as NodeServices from "@effect/platform-node/NodeServices";
2import { assert, it } from "@effect/vitest";3import { assert, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";4import * as Effect from "effect/Effect";
4import * as FileSystem from "effect/FileSystem";5import * as FileSystem from "effect/FileSystem";
5import * as Layer from "effect/Layer";6import * as Layer from "effect/Layer";
7import * as PlatformError from "effect/PlatformError";
6import { ChildProcessSpawner } from "effect/unstable/process";8import { ChildProcessSpawner } from "effect/unstable/process";
7 9
8import { GitCommandError, type SourceControlProviderError } from "@t3tools/contracts";10import { GitCommandError, SourceControlProviderError } from "@t3tools/contracts";
9 11
10import * as ServerConfig from "../config.ts";12import * as ServerConfig from "../config.ts";
11import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";13import * as GitVcsDriver from "../vcs/GitVcsDriver.ts";
@@ -54,8 +56,9 @@ function processOutput(): GitVcsDriver.ExecuteGitResult { · 42 unmodified lines
54function makeLayer(input: {56function makeLayer(input: {
55 readonly provider?: SourceControlProvider.SourceControlProvider["Service"];57 readonly provider?: SourceControlProvider.SourceControlProvider["Service"];
56 readonly git?: Partial<GitVcsDriver.GitVcsDriver["Service"]>;58 readonly git?: Partial<GitVcsDriver.GitVcsDriver["Service"]>;
59 readonly fileSystem?: FileSystem.FileSystem;
57}) {60}) {
58 return SourceControlRepositoryService.layer.pipe(61 const serviceLayer = SourceControlRepositoryService.layer.pipe(
59 Layer.provide(62 Layer.provide(
60 Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({63 Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({
61 get: () => Effect.succeed(input.provider ?? makeProvider()),64 get: () => Effect.succeed(input.provider ?? makeProvider()),
@@ -75,9 +78,20 @@ function makeLayer(input: { · 13 unmodified lines
75 ...input.git,78 ...input.git,
76 }),79 }),
77 ),80 ),
78 Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-source-control-repos-" })),81 Layer.provide(
79 Layer.provideMerge(NodeServices.layer),82 ServerConfig.layerTest(
83 process.cwd(),
84 input.fileSystem ? "/tmp/t3-source-control-repos" : { prefix: "t3-source-control-repos-" },
85 ),
86 ),
80 );87 );
88
89 return input.fileSystem
90 ? serviceLayer.pipe(
91 Layer.provide(Layer.succeed(FileSystem.FileSystem, input.fileSystem)),
92 Layer.provideMerge(NodePath.layer),
93 )
94 : serviceLayer.pipe(Layer.provideMerge(NodeServices.layer));
81}95}
82 96
83it.effect("looks up repositories through the requested provider without search", () => {97it.effect("looks up repositories through the requested provider without search", () => {
@@ -103,6 +117,39 @@ it.effect("looks up repositories through the requested provider without search", · 19 unmodified lines
103 }).pipe(Effect.provide(makeLayer({ provider })));117 }).pipe(Effect.provide(makeLayer({ provider })));
104});118});
105 119
120it.effect("preserves provider failures without deriving the repository message from them", () => {
121 const providerCause = new SourceControlProviderError({
122 provider: "github",
123 operation: "getRepositoryCloneUrls",
124 cwd: "/workspace",
125 repository: "octocat/t3code",
126 detail: "credential token abc123 was rejected",
127 });
128 const provider = makeProvider({
129 getRepositoryCloneUrls: () => Effect.fail(providerCause),
130 });
131
132 return Effect.gen(function* () {
133 const service = yield* SourceControlRepositoryService.SourceControlRepositoryService;
134 const error = yield* Effect.flip(
135 service.lookupRepository({
136 provider: "github",
137 repository: "octocat/t3code",
138 cwd: "/workspace",
139 }),
140 );
141
142 assert.strictEqual(error.provider, "github");
143 assert.strictEqual(error.operation, "lookupRepository");
144 assert.strictEqual(error.detail, "The source control operation could not be completed.");
145 assert.strictEqual(
146 error.message,
147 "Source control repository operation lookupRepository failed for github: The source control operation could not be completed.",
148 );
149 assert.strictEqual(error.cause, providerCause);
150 }).pipe(Effect.provide(makeLayer({ provider })));
151});
152
106it.effect("clones a looked-up repository into the requested destination", () =>153it.effect("clones a looked-up repository into the requested destination", () =>
107 Effect.gen(function* () {154 Effect.gen(function* () {
108 const fs = yield* FileSystem.FileSystem;155 const fs = yield* FileSystem.FileSystem;
@@ -148,6 +195,38 @@ it.effect("clones a looked-up repository into the requested destination", () => · 39 unmodified lines
148 }).pipe(Effect.provide(NodeServices.layer)),195 }).pipe(Effect.provide(NodeServices.layer)),
149);196);
150 197
198it.effect("preserves destination probe failures instead of treating them as missing paths", () => {
199 const fileSystemCause = PlatformError.systemError({
200 _tag: "PermissionDenied",
201 module: "FileSystem",
202 method: "exists",
203 pathOrDescriptor: "/restricted/t3code",
204 });
205
206 return Effect.gen(function* () {
207 const service = yield* SourceControlRepositoryService.SourceControlRepositoryService;
208 const error = yield* Effect.flip(
209 service.cloneRepository({
210 remoteUrl: CLONE_URLS.sshUrl,
211 destinationPath: "/restricted/t3code",
212 }),
213 );
214
215 assert.strictEqual(error.provider, "unknown");
216 assert.strictEqual(error.operation, "cloneRepository");
217 assert.strictEqual(error.cause, fileSystemCause);
218 }).pipe(
219 Effect.provide(
220 makeLayer({
221 fileSystem: FileSystem.makeNoop({
222 exists: () => Effect.fail(fileSystemCause),
223 makeDirectory: () => Effect.void,
224 }),
225 }),
226 ),
227 );
228});
229
151it.effect("publishes by creating the repository, adding a remote, and pushing upstream", () => {230it.effect("publishes by creating the repository, adding a remote, and pushing upstream", () => {
152 const createCalls: Array<{ cwd: string; repository: string; visibility: string }> = [];231 const createCalls: Array<{ cwd: string; repository: string; visibility: string }> = [];
153 const remoteCalls: Array<{ cwd: string; preferredName: string; url: string }> = [];232 const remoteCalls: Array<{ cwd: string; preferredName: string; url: string }> = [];