| ... | ... | @@ -6,8 +6,14 @@ import * as Effect from "effect/Effect"; |
| 6 | 6 | import * as FileSystem from "effect/FileSystem"; |
| 7 | 7 | import * as Layer from "effect/Layer"; |
| 8 | 8 | import * as Option from "effect/Option"; |
| 9 | | import { HttpClient, HttpClientRequest, HttpClientResponse } from "effect/unstable/http"; |
| 10 | | |
| 9 | import { |
| 10 | HttpClient, |
| 11 | HttpClientError, |
| 12 | HttpClientRequest, |
| 13 | HttpClientResponse, |
| 14 | } from "effect/unstable/http"; |
| 15 | |
| 16 | import { GitCommandError } from "@t3tools/contracts"; |
| 11 | 17 | import * as BitbucketApi from "./BitbucketApi.ts"; |
| 12 | 18 | import * as GitVcsDriver from "../vcs/GitVcsDriver.ts"; |
| 13 | 19 | import * as VcsDriverRegistry from "../vcs/VcsDriverRegistry.ts"; |
| ... | ... | @@ -53,10 +59,15 @@ const repositoryJson = { |
| 53 | 59 | |
| 54 | 60 | function makeLayer(input: { |
| 55 | 61 | readonly response: (request: HttpClientRequest.HttpClientRequest) => Response; |
| 62 | readonly requestFailure?: ( |
| 63 | request: HttpClientRequest.HttpClientRequest, |
| 64 | ) => HttpClientError.HttpClientError; |
| 56 | 65 | readonly git?: Partial<GitVcsDriver.GitVcsDriver["Service"]>; |
| 57 | 66 | }) { |
| 58 | 67 | const execute = vi.fn((request: HttpClientRequest.HttpClientRequest) => |
| 59 | | Effect.succeed(HttpClientResponse.fromWeb(request, input.response(request))), |
| 68 | input.requestFailure |
| 69 | ? Effect.fail(input.requestFailure(request)) |
| 70 | : Effect.succeed(HttpClientResponse.fromWeb(request, input.response(request))), |
| 60 | 71 | ); |
| 61 | 72 | const gitMock = { |
| 62 | 73 | readConfigValue: vi.fn<GitVcsDriver.GitVcsDriver["Service"]["readConfigValue"]>(() => |
| ... | ... | @@ -497,6 +508,42 @@ it.effect("reports auth status through the Bitbucket REST /user endpoint", () => |
| 497 | 508 | }).pipe(Effect.provide(layer)); |
| 498 | 509 | }); |
| 499 | 510 | |
| 511 | it.effect("preserves the HTTP client failure without deriving the domain message from it", () => { |
| 512 | const transportCause = new Error("socket reset by peer"); |
| 513 | let requestFailure: HttpClientError.HttpClientError | undefined; |
| 514 | const { layer } = makeLayer({ |
| 515 | response: () => Response.json({}), |
| 516 | requestFailure: (request) => { |
| 517 | requestFailure = new HttpClientError.HttpClientError({ |
| 518 | reason: new HttpClientError.TransportError({ |
| 519 | request, |
| 520 | cause: transportCause, |
| 521 | }), |
| 522 | }); |
| 523 | return requestFailure; |
| 524 | }, |
| 525 | }); |
| 526 | |
| 527 | return Effect.gen(function* () { |
| 528 | const bitbucket = yield* BitbucketApi.BitbucketApi; |
| 529 | const error = yield* Effect.flip( |
| 530 | bitbucket.getPullRequest({ |
| 531 | cwd: "/repo", |
| 532 | reference: "42", |
| 533 | }), |
| 534 | ); |
| 535 | |
| 536 | assert.strictEqual(error.operation, "getPullRequest"); |
| 537 | assert.strictEqual(error.detail, "Failed to send the Bitbucket request."); |
| 538 | assert.strictEqual( |
| 539 | error.message, |
| 540 | "Bitbucket API failed in getPullRequest: Failed to send the Bitbucket request.", |
| 541 | ); |
| 542 | assert.strictEqual(error.cause, requestFailure); |
| 543 | assert.strictEqual(requestFailure?.cause, transportCause); |
| 544 | }).pipe(Effect.provide(layer)); |
| 545 | }); |
| 546 | |
| 500 | 547 | it.effect("checks out same-repository pull requests with the existing Bitbucket remote", () => { |
| 501 | 548 | const { git, layer } = makeLayer({ |
| 502 | 549 | response: () => |
| ... | ... | @@ -549,6 +596,50 @@ it.effect("checks out same-repository pull requests with the existing Bitbucket |
| 549 | 596 | }).pipe(Effect.provide(layer)); |
| 550 | 597 | }); |
| 551 | 598 | |
| 599 | it.effect("preserves Git checkout failures without deriving the domain message from them", () => { |
| 600 | const gitCause = new GitCommandError({ |
| 601 | operation: "fetchRemoteBranch", |
| 602 | command: "git fetch origin feature/source-control", |
| 603 | cwd: "/repo", |
| 604 | detail: "remote rejected the request", |
| 605 | }); |
| 606 | const { layer } = makeLayer({ |
| 607 | response: () => |
| 608 | Response.json({ |
| 609 | ...bitbucketPullRequest, |
| 610 | source: { |
| 611 | branch: { name: "feature/source-control" }, |
| 612 | repository: { |
| 613 | full_name: "pingdotgg/t3code", |
| 614 | workspace: { slug: "pingdotgg" }, |
| 615 | }, |
| 616 | }, |
| 617 | }), |
| 618 | git: { |
| 619 | fetchRemoteBranch: () => Effect.fail(gitCause), |
| 620 | }, |
| 621 | }); |
| 622 | |
| 623 | return Effect.gen(function* () { |
| 624 | const bitbucket = yield* BitbucketApi.BitbucketApi; |
| 625 | const error = yield* Effect.flip( |
| 626 | bitbucket.checkoutPullRequest({ |
| 627 | cwd: "/repo", |
| 628 | reference: "42", |
| 629 | force: true, |
| 630 | }), |
| 631 | ); |
| 632 | |
| 633 | assert.strictEqual(error.operation, "checkoutPullRequest"); |
| 634 | assert.strictEqual(error.detail, "Failed to check out the Bitbucket pull request."); |
| 635 | assert.strictEqual( |
| 636 | error.message, |
| 637 | "Bitbucket API failed in checkoutPullRequest: Failed to check out the Bitbucket pull request.", |
| 638 | ); |
| 639 | assert.strictEqual(error.cause, gitCause); |
| 640 | }).pipe(Effect.provide(layer)); |
| 641 | }); |
| 642 | |
| 552 | 643 | it.effect("checks out fork pull requests through an ensured fork remote", () => { |
| 553 | 644 | const { git, layer } = makeLayer({ |
| 554 | 645 | response: (request) => { |