1import { SourceControlProviderKind } from "@t3tools/contracts";
2import { assert, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as Layer from "effect/Layer";
5import * as Option from "effect/Option";
6
7import * as BitbucketApi from "./BitbucketApi.ts";
8import * as BitbucketSourceControlProvider from "./BitbucketSourceControlProvider.ts";
9
10function makeProvider(bitbucket: Partial<BitbucketApi.BitbucketApi["Service"]>) {
11 return BitbucketSourceControlProvider.make.pipe(
12 Effect.provide(Layer.mock(BitbucketApi.BitbucketApi)(bitbucket)),
13 );
14}
15
16it.effect("maps Bitbucket PR summaries into provider-neutral change requests", () =>
17 Effect.gen(function* () {
18 const provider = yield* makeProvider({
19 getPullRequest: () =>
20 Effect.succeed({
21 number: 42,
22 title: "Add Bitbucket provider",
23 url: "https://bitbucket.org/pingdotgg/t3code/pull-requests/42",
24 baseRefName: "main",
25 headRefName: "feature/source-control",
26 state: "open",
27 updatedAt: Option.none(),
28 isCrossRepository: true,
29 headRepositoryNameWithOwner: "fork/t3code",
30 headRepositoryOwnerLogin: "fork",
31 }),
32 });
33
34 const changeRequest = yield* provider.getChangeRequest({
35 cwd: "/repo",
36 reference: "42",
37 });
38
39 assert.deepStrictEqual(changeRequest, {
40 provider: SourceControlProviderKind.make("bitbucket"),
41 number: 42,
42 title: "Add Bitbucket provider",
43 url: "https://bitbucket.org/pingdotgg/t3code/pull-requests/42",
44 baseRefName: "main",
45 headRefName: "feature/source-control",
46 state: "open",
47 updatedAt: Option.none(),
48 isCrossRepository: true,
49 headRepositoryNameWithOwner: "fork/t3code",
50 headRepositoryOwnerLogin: "fork",
51 });
52 }),
53);
54
55it.effect("adds repository context while retaining Bitbucket API causes", () =>
56 Effect.gen(function* () {
57 const upstreamCause = new Error("raw upstream failure");
58 const cause = new BitbucketApi.BitbucketRequestError({
59 operation: "getRepository",
60 cause: upstreamCause,
61 });
62 const provider = yield* makeProvider({
63 getRepositoryCloneUrls: () => Effect.fail(cause),
64 });
65
66 const error = yield* provider
67 .getRepositoryCloneUrls({ cwd: "/repo", repository: "owner/repo" })
68 .pipe(Effect.flip);
69
70 assert.deepStrictEqual(
71 {
72 provider: error.provider,
73 operation: error.operation,
74 command: error.command,
75 cwd: error.cwd,
76 repository: error.repository,
77 detail: error.detail,
78 },
79 {
80 provider: "bitbucket",
81 operation: "getRepositoryCloneUrls",
82 command: undefined,
83 cwd: "/repo",
84 repository: "owner/repo",
85 detail: "Failed to get repository clone URLs.",
86 },
87 );
88 assert.strictEqual(error.cause, cause);
89 assert.equal(error.message.includes(upstreamCause.message), false);
90 }),
91);
92
93it.effect("lists Bitbucket PRs through provider-neutral input names", () =>
94 Effect.gen(function* () {
95 let listInput: Parameters<BitbucketApi.BitbucketApi["Service"]["listPullRequests"]>[0] | null =
96 null;
97 const provider = yield* makeProvider({
98 listPullRequests: (input) => {
99 listInput = input;
100 return Effect.succeed([]);
101 },
102 });
103
104 yield* provider.listChangeRequests({
105 cwd: "/repo",
106 headSelector: "feature/provider",
107 state: "all",
108 limit: 10,
109 });
110
111 assert.deepStrictEqual(listInput, {
112 cwd: "/repo",
113 headSelector: "feature/provider",
114 state: "all",
115 limit: 10,
116 });
117 }),
118);
119
120it.effect("creates Bitbucket PRs through provider-neutral input names", () =>
121 Effect.gen(function* () {
122 let createInput:
123 | Parameters<BitbucketApi.BitbucketApi["Service"]["createPullRequest"]>[0]
124 | null = null;
125 const provider = yield* makeProvider({
126 createPullRequest: (input) => {
127 createInput = input;
128 return Effect.void;
129 },
130 });
131
132 yield* provider.createChangeRequest({
133 cwd: "/repo",
134 baseRefName: "main",
135 headSelector: "owner:feature/provider",
136 title: "Provider PR",
137 bodyFile: "/tmp/body.md",
138 });
139
140 assert.deepStrictEqual(createInput, {
141 cwd: "/repo",
142 baseBranch: "main",
143 headSelector: "owner:feature/provider",
144 source: {
145 owner: "owner",
146 refName: "feature/provider",
147 },
148 title: "Provider PR",
149 bodyFile: "/tmp/body.md",
150 });
151 }),
152);