Showing the changes to apps/server/src/sourceControl/SourceControlRepositoryService.test.ts (1 of 2 files). Show all
Unified · Split
| @@ -1,11 +1,13 @@ | ||
| 1 | +import * as NodePath from "@effect/platform-node/NodePath"; | |
| 1 | 2 | import * as NodeServices from "@effect/platform-node/NodeServices"; |
| 2 | 3 | import { assert, it } from "@effect/vitest"; |
| 3 | 4 | import * as Effect from "effect/Effect"; |
| 4 | 5 | import * as FileSystem from "effect/FileSystem"; |
| 5 | 6 | import * as Layer from "effect/Layer"; |
| 7 | +import * as PlatformError from "effect/PlatformError"; | |
| 6 | 8 | import { ChildProcessSpawner } from "effect/unstable/process"; |
| 7 | 9 | |
| 8 | −import { GitCommandError, type SourceControlProviderError } from "@t3tools/contracts"; | |
| 10 | +import { GitCommandError, SourceControlProviderError } from "@t3tools/contracts"; | |
| 9 | 11 | |
| 10 | 12 | import * as ServerConfig from "../config.ts"; |
| 11 | 13 | import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; |
| @@ -54,8 +56,9 @@ function processOutput(): GitVcsDriver.ExecuteGitResult { · 42 unmodified lines | ||
| 54 | 56 | function makeLayer(input: { |
| 55 | 57 | readonly provider?: SourceControlProvider.SourceControlProvider["Service"]; |
| 56 | 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 | 62 | Layer.provide( |
| 60 | 63 | Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ |
| 61 | 64 | get: () => Effect.succeed(input.provider ?? makeProvider()), |
| @@ -75,9 +78,20 @@ function makeLayer(input: { · 13 unmodified lines | ||
| 75 | 78 | ...input.git, |
| 76 | 79 | }), |
| 77 | 80 | ), |
| 78 | − Layer.provide(ServerConfig.layerTest(process.cwd(), { prefix: "t3-source-control-repos-" })), | |
| 79 | − Layer.provideMerge(NodeServices.layer), | |
| 81 | + Layer.provide( | |
| 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 | |
| 83 | 97 | it.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 | 117 | }).pipe(Effect.provide(makeLayer({ provider }))); |
| 104 | 118 | }); |
| 105 | 119 | |
| 120 | +it.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 | + | |
| 106 | 153 | it.effect("clones a looked-up repository into the requested destination", () => |
| 107 | 154 | Effect.gen(function* () { |
| 108 | 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 | 195 | }).pipe(Effect.provide(NodeServices.layer)), |
| 149 | 196 | ); |
| 150 | 197 | |
| 198 | +it.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 | + | |
| 151 | 230 | it.effect("publishes by creating the repository, adding a remote, and pushing upstream", () => { |
| 152 | 231 | const createCalls: Array<{ cwd: string; repository: string; visibility: string }> = []; |
| 153 | 232 | const remoteCalls: Array<{ cwd: string; preferredName: string; url: string }> = []; |