1import {
2 SourceControlProviderKind,
3 type PullRequestReviewCapabilities,
4 type PullRequestReviewerCapabilities,
5} from "@t3tools/contracts";
6import {
7 providerAuth,
8 type SourceControlProviderDiscoverySpec,
9} from "@t3tools/source-control-core/server/discovery";
10import type { SourceControlDriverInstance } from "@t3tools/source-control-core/server/driver";
11import type { PullRequestProviderApi } from "@t3tools/source-control-core/server/PullRequestProvider";
12import type * as SourceControlProvider from "@t3tools/source-control-core/server/SourceControlProvider";
13import * as Effect from "effect/Effect";
14
15const FULL_REVIEW: PullRequestReviewCapabilities = {
16 inlineComment: true,
17 reply: true,
18 resolve: true,
19 verdicts: ["comment", "approve", "request-changes"],
20};
21
22const FULL_REVIEWERS: PullRequestReviewerCapabilities = { request: true, listCandidates: true };
23
24const kindOf = (kind: string) => SourceControlProviderKind.make(kind);
25
26/**
27 * A pull request provider that can do everything and holds nothing: lists are empty, writes
28 * succeed, and reads a test did not supply die.
29 */
30export function fakePullRequestProvider(
31 kind: string,
32 overrides: Partial<PullRequestProviderApi> = {},
33): PullRequestProviderApi {
34 return {
35 kind: kindOf(kind),
36 capabilities: {
37 diff: true,
38 comment: true,
39 actions: ["merge", "ready", "draft", "close", "reopen"],
40 mergeMethods: ["merge"],
41 search: true,
42 reactions: true,
43 review: FULL_REVIEW,
44 reviewers: FULL_REVIEWERS,
45 edit: { changeRequest: true, comment: true },
46 },
47 getViewer: () => Effect.succeed("bilal"),
48 getViewerPermissions: () =>
49 Effect.succeed({
50 actions: ["merge", "ready", "draft", "close", "reopen"],
51 comment: true,
52 resolve: true,
53 verdicts: ["comment", "approve", "request-changes"],
54 requestReviewers: true,
55 }),
56 listChangeRequests: () => Effect.succeed({ items: [], truncated: false, continues: true }),
57 getChangeRequest: () => Effect.die("unused"),
58 getChangeRequestActivity: () => Effect.die("unused"),
59 getDiff: () => Effect.die("unused"),
60 runAction: () => Effect.void,
61 updateChangeRequest: () => Effect.void,
62 comment: () => Effect.void,
63 updateComment: () => Effect.void,
64 submitReview: () => Effect.void,
65 replyToThread: () => Effect.void,
66 setThreadResolution: () => Effect.void,
67 setReaction: () => Effect.void,
68 listReviewerCandidates: () => Effect.succeed({ candidates: [], truncated: false }),
69 setReviewerRequest: () => Effect.void,
70 ...overrides,
71 };
72}
73
74type SourceControlProviderService = SourceControlProvider.SourceControlProvider["Service"];
75
76const unexpected = (kind: string, operation: string) => () =>
77 Effect.die(`Unexpected ${kind} ${operation} in a test that supplied none.`);
78
79/** A source control provider whose every operation dies unless the test supplies it. */
80export function fakeSourceControlProvider(
81 kind: string,
82 overrides: Partial<SourceControlProviderService> = {},
83): SourceControlProviderService {
84 return {
85 kind: kindOf(kind),
86 listChangeRequests: unexpected(kind, "listChangeRequests"),
87 getChangeRequest: unexpected(kind, "getChangeRequest"),
88 createChangeRequest: unexpected(kind, "createChangeRequest"),
89 getRepositoryCloneUrls: unexpected(kind, "getRepositoryCloneUrls"),
90 createRepository: unexpected(kind, "createRepository"),
91 getDefaultBranch: unexpected(kind, "getDefaultBranch"),
92 checkoutChangeRequest: unexpected(kind, "checkoutChangeRequest"),
93 ...overrides,
94 };
95}
96
97/** Discovery over an API integration that reports an unknown sign-in. */
98export function fakeDiscovery(
99 kind: string,
100 overrides: Partial<Extract<SourceControlProviderDiscoverySpec, { readonly type: "api" }>> = {},
101): SourceControlProviderDiscoverySpec {
102 return {
103 type: "api",
104 kind: kindOf(kind),
105 label: kind,
106 installHint: `Install ${kind}.`,
107 probeAuth: Effect.succeed(providerAuth({ status: "unknown" })),
108 ...overrides,
109 };
110}
111
112export interface FakeSourceControlDriverOptions {
113 readonly sourceControl?: Partial<SourceControlProviderService>;
114 /** Null for a host whose pull requests this build cannot read. */
115 readonly pullRequests?: Partial<PullRequestProviderApi> | null;
116 readonly discovery?: SourceControlProviderDiscoverySpec;
117 readonly fetchMedia?: SourceControlDriverInstance["fetchMedia"];
118}
119
120/** A driver instance for a host named `kind` that needs none of the server's services. */
121export function fakeSourceControlDriverInstance(
122 kind: string,
123 options: FakeSourceControlDriverOptions = {},
124): SourceControlDriverInstance {
125 return {
126 sourceControl: fakeSourceControlProvider(kind, options.sourceControl),
127 discovery: options.discovery ?? fakeDiscovery(kind),
128 pullRequests:
129 options.pullRequests === null ? null : fakePullRequestProvider(kind, options.pullRequests),
130 ...(options.fetchMedia === undefined ? {} : { fetchMedia: options.fetchMedia }),
131 };
132}