import { assert, it } from "@effect/vitest";import * as Deferred from "effect/Deferred";import * as Effect from "effect/Effect";import * as Fiber from "effect/Fiber";import * as Layer from "effect/Layer";import * as Option from "effect/Option";import * as Stream from "effect/Stream";import * as TestClock from "effect/testing/TestClock";import type { OrchestrationProjectShell, ProjectId, PullRequestReviewCapabilities, PullRequestReviewerCapabilities, SourceControlProviderKind,} from "@t3tools/contracts"; import * as ProjectionSnapshotQuery from "../orchestration/Services/ProjectionSnapshotQuery.ts";import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts";import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts";import { PullRequestProviderError, type ProviderChangeRequest, type PullRequestProviderApi,} from "./PullRequestProvider.ts";import { PullRequestProviderRegistry, fromProviders } from "./PullRequestProviderRegistry.ts";import * as PullRequestService from "./PullRequestService.ts"; function project(input: { readonly id: string; readonly title: string; readonly workspaceRoot: string; readonly repository?: string; readonly provider?: string; readonly host?: string;}): OrchestrationProjectShell { // The host defaults from the provider, so a fixture only names one when the point of the // test is two hosts of the same kind. const host = input.host ?? (input.provider === "gitlab" ? "gitlab.com" : "github.com"); return { id: input.id as ProjectId, title: input.title, workspaceRoot: input.workspaceRoot, ...(input.repository ? { repositoryIdentity: { canonicalKey: `${host}/${input.repository}`, locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: `https://${host}/${input.repository}.git`, }, provider: input.provider ?? "github", displayName: input.repository, }, } : {}), defaultModelSelection: null, scripts: [], createdAt: "2026-07-01T00:00:00Z", updatedAt: "2026-07-01T00:00:00Z", };} function changeRequest(number: number, updatedAt: string): ProviderChangeRequest { return { number, title: `Change request ${number}`, url: `https://host/pull/${number}`, author: { login: "octocat", name: null, avatarUrl: null }, headBranch: `feat/${number}`, baseBranch: "main", state: "open", isDraft: false, mergeability: "mergeable", additions: 1, deletions: 0, createdAt: "2026-07-01T00:00:00Z", updatedAt, reviewRequestLogins: [], labels: [], };} function hostedChangeRequest(body: string, additions = 1) { return { ...changeRequest(1, "2026-07-02T00:00:00Z"), body, additions, changedFiles: 2, mergedAt: null, closedAt: null, reviewers: [], checks: [], mergeCapabilities: { merge: true, squash: true, rebase: true }, viewerPermissions: { actions: ["merge"] as const, comment: true, resolve: true, verdicts: ["comment", "approve", "request-changes"] as const, requestReviewers: true, }, };} function unusable(provider: SourceControlProviderKind, reason: "missing-tool" | "unauthenticated") { return new PullRequestProviderError({ provider, operation: "getViewer", reason, detail: `${provider} is not usable.`, });} const requestFailed = new PullRequestProviderError({ provider: "github", operation: "listChangeRequests", reason: "failed", detail: "HTTP 404",}); /** Everything a host could offer, so a fixture only narrows what its own test is about. */const FULL_REVIEW: PullRequestReviewCapabilities = { inlineComment: true, reply: true, resolve: true, verdicts: ["comment", "approve", "request-changes"],}; const FULL_REVIEWERS: PullRequestReviewerCapabilities = { request: true, listCandidates: true }; /** A provider whose every call is supplied by the test; anything unset succeeds emptily. */function fakeProvider( kind: SourceControlProviderKind, overrides: Partial<PullRequestProviderApi> = {},): PullRequestProviderApi { return { kind, capabilities: { diff: true, comment: true, actions: ["merge", "ready", "draft", "close", "reopen"], mergeMethods: ["merge"], search: true, reactions: true, review: FULL_REVIEW, reviewers: FULL_REVIEWERS, edit: { changeRequest: true, comment: true }, }, getViewer: () => Effect.succeed("bilal"), // A viewer who may do everything the host can, so a test only narrows what it is about. getViewerPermissions: () => Effect.succeed({ actions: ["merge", "ready", "draft", "close", "reopen"], comment: true, resolve: true, verdicts: ["comment", "approve", "request-changes"], requestReviewers: true, }), listChangeRequests: () => Effect.succeed({ items: [], truncated: false, continues: true }), getChangeRequest: () => Effect.die("unused"), getChangeRequestActivity: () => Effect.die("unused"), getDiff: () => Effect.die("unused"), runAction: () => Effect.void, updateChangeRequest: () => Effect.void, comment: () => Effect.void, updateComment: () => Effect.void, submitReview: () => Effect.void, replyToThread: () => Effect.void, setThreadResolution: () => Effect.void, setReaction: () => Effect.void, listReviewerCandidates: () => Effect.succeed({ candidates: [], truncated: false }), setReviewerRequest: () => Effect.void, ...overrides, };} function makeService(input: { readonly projects: ReadonlyArray<OrchestrationProjectShell>; readonly providers: ReadonlyArray<PullRequestProviderApi>; readonly resolveHandle?: SourceControlProviderRegistry.SourceControlProviderRegistry["Service"]["resolveHandle"];}) { return PullRequestService.make.pipe( Effect.provide( Layer.mergeAll( Layer.succeed(PullRequestProviderRegistry, fromProviders(input.providers)), Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ resolveHandle: input.resolveHandle ?? (() => Effect.die("Unexpected provider refinement")), }), Layer.mock(ProjectionSnapshotQuery.ProjectionSnapshotQuery)({ getShellSnapshot: () => Effect.succeed({ snapshotSequence: 1, projects: input.projects, threads: [], updatedAt: "2026-07-01T00:00:00Z", }), }), SourceControlRateLimit.layer, ), ), );} it.effect("refines unknown self-hosted GitLab projects before listing merge requests", () => Effect.gen(function* () { let refinementCalls = 0; const selfHosted = project({ id: "p1", title: "self-hosted", workspaceRoot: "/gitlab", repository: "group/project", provider: "unknown", host: "code.example.test", }); const service = yield* makeService({ projects: [ selfHosted, { ...selfHosted, id: "p2" as ProjectId, workspaceRoot: "/gitlab-worktree" }, ], providers: [fakeProvider("gitlab")], resolveHandle: ({ context }) => { refinementCalls += 1; assert.strictEqual(context?.remoteUrl, "https://code.example.test/group/project.git"); return Effect.succeed({ context: { ...context!, provider: { ...context!.provider, kind: "gitlab" } }, provider: undefined as never, }); }, }); const result = yield* service.list({ state: "open" }); assert.strictEqual(refinementCalls, 1); assert.strictEqual(result.providers[0]?.host, "code.example.test"); assert.strictEqual(result.providers[0]?.kind, "gitlab"); }),); it.effect("derives a legacy repository host after refining its provider", () => Effect.gen(function* () { const current = project({ id: "p1", title: "legacy self-hosted", workspaceRoot: "/gitlab", repository: "group/project", provider: "unknown", host: "code.example.test", }); const identity = current.repositoryIdentity!; // Persisted identities from before canonicalKey existed are still accepted at runtime. const legacy = { ...current, repositoryIdentity: { locator: identity.locator, provider: identity.provider, displayName: identity.displayName, }, } as unknown as OrchestrationProjectShell; const service = yield* makeService({ projects: [legacy], providers: [fakeProvider("gitlab")], resolveHandle: ({ context }) => Effect.succeed({ context: { ...context!, provider: { ...context!.provider, kind: "gitlab" } }, provider: undefined as never, }), }); const result = yield* service.list({ state: "open", host: "gitlab" }); assert.strictEqual(result.providers[0]?.host, "gitlab"); assert.strictEqual(result.providers[0]?.kind, "gitlab"); }),); it.effect("tries another checkout when provider refinement remains unknown", () => Effect.gen(function* () { const asked: string[] = []; const selfHosted = project({ id: "p1", title: "self-hosted", workspaceRoot: "/gone", repository: "group/project", provider: "unknown", host: "code.example.test", }); const service = yield* makeService({ projects: [selfHosted, { ...selfHosted, id: "p2" as ProjectId, workspaceRoot: "/healthy" }], providers: [fakeProvider("gitlab")], resolveHandle: ({ cwd, context }) => { asked.push(cwd); return cwd === "/gone" ? Effect.succeed({ context: context!, provider: undefined as never }) : Effect.succeed({ context: { ...context!, provider: { ...context!.provider, kind: "gitlab" } }, provider: undefined as never, }); }, }); const result = yield* service.list({ state: "open" }); assert.deepStrictEqual(asked, ["/gone", "/healthy"]); assert.strictEqual(result.providers[0]?.kind, "gitlab"); }),); /** A row as a host that reads several repositories at once hands it over. */function batchedChangeRequest(number: number, repository: string, updatedAt: string) { return { ...changeRequest(number, updatedAt), repository };} it.effect("reads nothing from a host with no implementation, but reports it", () => Effect.gen(function* () { const listed: string[] = []; const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), project({ id: "p2", title: "notes", workspaceRoot: "/b" }), project({ id: "p3", title: "on gitlab", workspaceRoot: "/c", repository: "group/project", provider: "gitlab", }), ], providers: [ fakeProvider("github", { listChangeRequests: (input) => { listed.push(input.repository); return Effect.succeed({ items: [changeRequest(1, "2026-07-02T00:00:00Z")], truncated: false, continues: true, }); }, }), ], }); const result = yield* service.list({ state: "open" }); assert.deepStrictEqual(listed, ["pingdotgg/t3code"]); assert.strictEqual(result.entries[0]?.provider, "github"); // The GitLab project is explained rather than quietly missing from the page. assert.deepStrictEqual( result.providers.map((summary) => ({ kind: summary.kind, configured: summary.configured, projectCount: summary.projectCount, })), [ { kind: "github", configured: true, projectCount: 1 }, { kind: "gitlab", configured: false, projectCount: 1 }, ], ); }),); it.effect("asks for a whole page of a host, and for the reader's own size when given one", () => Effect.gen(function* () { const limits: number[] = []; const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), ], providers: [ fakeProvider("github", { listChangeRequests: (input) => { limits.push(input.limit); return Effect.succeed({ items: [], truncated: false, continues: true }); }, }), ], }); yield* service.list({ state: "open" }); yield* service.list({ state: "open", limit: 10 }); // Providers probe with one row over this, so 99 asks a host for 100 — the most GitHub and // GitLab serve in one request. 100 here would cost a second round trip for a single row. assert.deepStrictEqual(limits, [99, 10]); }),); it.effect("says where each repository carries on, and from nothing it has run out of", () => Effect.gen(function* () { const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), project({ id: "p2", title: "web", workspaceRoot: "/b", repository: "acme/web" }), ], providers: [ fakeProvider("github", { listChangeRequests: ({ repository }) => Effect.succeed({ items: [changeRequest(1, "2026-07-02T00:00:00Z")], truncated: repository === "pingdotgg/t3code", continues: true, }), }), ], }); const result = yield* service.list({ state: "open" }); // The instant of the oldest row, how many rows have gone, and the row already sent at that // instant. The repository that had nothing more is simply not in it. assert.deepStrictEqual(result.nextCursors, { "github.com pingdotgg/t3code": "2026-07-02T00:00:00Z|1|1", }); }),); it.effect("offers no continuation for a host that cannot be carried on from", () => Effect.gen(function* () { const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), ], providers: [ fakeProvider("github", { listChangeRequests: () => Effect.succeed({ items: [changeRequest(1, "2026-07-02T00:00:00Z")], truncated: true, continues: false, }), }), ], }); const result = yield* service.list({ state: "open" }); // More rows exist and no cursor reaches them, which is what asking for a larger page is for. assert.isTrue(result.truncated); assert.deepStrictEqual(result.nextCursors, {}); }),); it.effect("uses a provider's raw cursor advance when it consumed malformed rows", () => Effect.gen(function* () { const service = yield* makeService({ projects: [ project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web", provider: "azure-devops", host: "dev.azure.com", }), ], providers: [ fakeProvider("azure-devops", { listChangeRequests: () => Effect.succeed({ items: [changeRequest(7, "2026-07-02T00:00:00Z")], truncated: true, cursorAdvance: 4, continues: true, }), }), ], }); const result = yield* service.list({ state: "open" }); // Keyed by the selector Azure is actually asked with, which is the repository's own name. assert.deepStrictEqual(result.nextCursors, { "dev.azure.com web": "2026-07-02T00:00:00Z|4|7", }); }),); it.effect("reads only the repositories it was asked to carry on with", () => Effect.gen(function* () { const listed: string[] = []; const cursors: Array<unknown> = []; const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), project({ id: "p2", title: "web", workspaceRoot: "/b", repository: "acme/web" }), ], providers: [ fakeProvider("github", { listChangeRequests: (input) => { listed.push(input.repository); cursors.push(input.cursor); return Effect.succeed({ items: [], truncated: false, continues: true }); }, }), ], }); const result = yield* service.list({ state: "open", cursors: { "github.com acme/web": "2026-07-02T00:00:00Z|99|7" },Lines 1–500 of 4139. Load more
import { assert, it } from "@effect/vitest"; import * as Deferred from "effect/Deferred"; import * as Effect from "effect/Effect"; import * as Fiber from "effect/Fiber"; import * as Layer from "effect/Layer"; import * as Option from "effect/Option"; import * as Stream from "effect/Stream"; import * as TestClock from "effect/testing/TestClock"; import type { OrchestrationProjectShell, ProjectId, PullRequestReviewCapabilities, PullRequestReviewerCapabilities, SourceControlProviderKind, } from "@t3tools/contracts"; import * as ProjectionSnapshotQuery from "../orchestration/Services/ProjectionSnapshotQuery.ts"; import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; import * as SourceControlRateLimit from "../sourceControl/SourceControlRateLimit.ts"; import { PullRequestProviderError, type ProviderChangeRequest, type PullRequestProviderApi, } from "./PullRequestProvider.ts"; import { PullRequestProviderRegistry, fromProviders } from "./PullRequestProviderRegistry.ts"; import * as PullRequestService from "./PullRequestService.ts"; function project(input: { readonly id: string; readonly title: string; readonly workspaceRoot: string; readonly repository?: string; readonly provider?: string; readonly host?: string; }): OrchestrationProjectShell { // The host defaults from the provider, so a fixture only names one when the point of the // test is two hosts of the same kind. const host = input.host ?? (input.provider === "gitlab" ? "gitlab.com" : "github.com"); return { id: input.id as ProjectId, title: input.title, workspaceRoot: input.workspaceRoot, ...(input.repository ? { repositoryIdentity: { canonicalKey: `${host}/${input.repository}`, locator: { source: "git-remote" as const, remoteName: "origin", remoteUrl: `https://${host}/${input.repository}.git`, }, provider: input.provider ?? "github", displayName: input.repository, }, } : {}), defaultModelSelection: null, scripts: [], createdAt: "2026-07-01T00:00:00Z", updatedAt: "2026-07-01T00:00:00Z", }; } function changeRequest(number: number, updatedAt: string): ProviderChangeRequest { return { number, title: `Change request ${number}`, url: `https://host/pull/${number}`, author: { login: "octocat", name: null, avatarUrl: null }, headBranch: `feat/${number}`, baseBranch: "main", state: "open", isDraft: false, mergeability: "mergeable", additions: 1, deletions: 0, createdAt: "2026-07-01T00:00:00Z", updatedAt, reviewRequestLogins: [], labels: [], }; } function hostedChangeRequest(body: string, additions = 1) { return { ...changeRequest(1, "2026-07-02T00:00:00Z"), body, additions, changedFiles: 2, mergedAt: null, closedAt: null, reviewers: [], checks: [], mergeCapabilities: { merge: true, squash: true, rebase: true }, viewerPermissions: { actions: ["merge"] as const, comment: true, resolve: true, verdicts: ["comment", "approve", "request-changes"] as const, requestReviewers: true, }, }; } function unusable(provider: SourceControlProviderKind, reason: "missing-tool" | "unauthenticated") { return new PullRequestProviderError({ provider, operation: "getViewer", reason, detail: `${provider} is not usable.`, }); } const requestFailed = new PullRequestProviderError({ provider: "github", operation: "listChangeRequests", reason: "failed", detail: "HTTP 404", }); /** Everything a host could offer, so a fixture only narrows what its own test is about. */ const FULL_REVIEW: PullRequestReviewCapabilities = { inlineComment: true, reply: true, resolve: true, verdicts: ["comment", "approve", "request-changes"], }; const FULL_REVIEWERS: PullRequestReviewerCapabilities = { request: true, listCandidates: true }; /** A provider whose every call is supplied by the test; anything unset succeeds emptily. */ function fakeProvider( kind: SourceControlProviderKind, overrides: Partial<PullRequestProviderApi> = {}, ): PullRequestProviderApi { return { kind, capabilities: { diff: true, comment: true, actions: ["merge", "ready", "draft", "close", "reopen"], mergeMethods: ["merge"], search: true, reactions: true, review: FULL_REVIEW, reviewers: FULL_REVIEWERS, edit: { changeRequest: true, comment: true }, }, getViewer: () => Effect.succeed("bilal"), // A viewer who may do everything the host can, so a test only narrows what it is about. getViewerPermissions: () => Effect.succeed({ actions: ["merge", "ready", "draft", "close", "reopen"], comment: true, resolve: true, verdicts: ["comment", "approve", "request-changes"], requestReviewers: true, }), listChangeRequests: () => Effect.succeed({ items: [], truncated: false, continues: true }), getChangeRequest: () => Effect.die("unused"), getChangeRequestActivity: () => Effect.die("unused"), getDiff: () => Effect.die("unused"), runAction: () => Effect.void, updateChangeRequest: () => Effect.void, comment: () => Effect.void, updateComment: () => Effect.void, submitReview: () => Effect.void, replyToThread: () => Effect.void, setThreadResolution: () => Effect.void, setReaction: () => Effect.void, listReviewerCandidates: () => Effect.succeed({ candidates: [], truncated: false }), setReviewerRequest: () => Effect.void, ...overrides, }; } function makeService(input: { readonly projects: ReadonlyArray<OrchestrationProjectShell>; readonly providers: ReadonlyArray<PullRequestProviderApi>; readonly resolveHandle?: SourceControlProviderRegistry.SourceControlProviderRegistry["Service"]["resolveHandle"]; }) { return PullRequestService.make.pipe( Effect.provide( Layer.mergeAll( Layer.succeed(PullRequestProviderRegistry, fromProviders(input.providers)), Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry)({ resolveHandle: input.resolveHandle ?? (() => Effect.die("Unexpected provider refinement")), }), Layer.mock(ProjectionSnapshotQuery.ProjectionSnapshotQuery)({ getShellSnapshot: () => Effect.succeed({ snapshotSequence: 1, projects: input.projects, threads: [], updatedAt: "2026-07-01T00:00:00Z", }), }), SourceControlRateLimit.layer, ), ), ); } it.effect("refines unknown self-hosted GitLab projects before listing merge requests", () => Effect.gen(function* () { let refinementCalls = 0; const selfHosted = project({ id: "p1", title: "self-hosted", workspaceRoot: "/gitlab", repository: "group/project", provider: "unknown", host: "code.example.test", }); const service = yield* makeService({ projects: [ selfHosted, { ...selfHosted, id: "p2" as ProjectId, workspaceRoot: "/gitlab-worktree" }, ], providers: [fakeProvider("gitlab")], resolveHandle: ({ context }) => { refinementCalls += 1; assert.strictEqual(context?.remoteUrl, "https://code.example.test/group/project.git"); return Effect.succeed({ context: { ...context!, provider: { ...context!.provider, kind: "gitlab" } }, provider: undefined as never, }); }, }); const result = yield* service.list({ state: "open" }); assert.strictEqual(refinementCalls, 1); assert.strictEqual(result.providers[0]?.host, "code.example.test"); assert.strictEqual(result.providers[0]?.kind, "gitlab"); }), ); it.effect("derives a legacy repository host after refining its provider", () => Effect.gen(function* () { const current = project({ id: "p1", title: "legacy self-hosted", workspaceRoot: "/gitlab", repository: "group/project", provider: "unknown", host: "code.example.test", }); const identity = current.repositoryIdentity!; // Persisted identities from before canonicalKey existed are still accepted at runtime. const legacy = { ...current, repositoryIdentity: { locator: identity.locator, provider: identity.provider, displayName: identity.displayName, }, } as unknown as OrchestrationProjectShell; const service = yield* makeService({ projects: [legacy], providers: [fakeProvider("gitlab")], resolveHandle: ({ context }) => Effect.succeed({ context: { ...context!, provider: { ...context!.provider, kind: "gitlab" } }, provider: undefined as never, }), }); const result = yield* service.list({ state: "open", host: "gitlab" }); assert.strictEqual(result.providers[0]?.host, "gitlab"); assert.strictEqual(result.providers[0]?.kind, "gitlab"); }), ); it.effect("tries another checkout when provider refinement remains unknown", () => Effect.gen(function* () { const asked: string[] = []; const selfHosted = project({ id: "p1", title: "self-hosted", workspaceRoot: "/gone", repository: "group/project", provider: "unknown", host: "code.example.test", }); const service = yield* makeService({ projects: [selfHosted, { ...selfHosted, id: "p2" as ProjectId, workspaceRoot: "/healthy" }], providers: [fakeProvider("gitlab")], resolveHandle: ({ cwd, context }) => { asked.push(cwd); return cwd === "/gone" ? Effect.succeed({ context: context!, provider: undefined as never }) : Effect.succeed({ context: { ...context!, provider: { ...context!.provider, kind: "gitlab" } }, provider: undefined as never, }); }, }); const result = yield* service.list({ state: "open" }); assert.deepStrictEqual(asked, ["/gone", "/healthy"]); assert.strictEqual(result.providers[0]?.kind, "gitlab"); }), ); /** A row as a host that reads several repositories at once hands it over. */ function batchedChangeRequest(number: number, repository: string, updatedAt: string) { return { ...changeRequest(number, updatedAt), repository }; } it.effect("reads nothing from a host with no implementation, but reports it", () => Effect.gen(function* () { const listed: string[] = []; const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), project({ id: "p2", title: "notes", workspaceRoot: "/b" }), project({ id: "p3", title: "on gitlab", workspaceRoot: "/c", repository: "group/project", provider: "gitlab", }), ], providers: [ fakeProvider("github", { listChangeRequests: (input) => { listed.push(input.repository); return Effect.succeed({ items: [changeRequest(1, "2026-07-02T00:00:00Z")], truncated: false, continues: true, }); }, }), ], }); const result = yield* service.list({ state: "open" }); assert.deepStrictEqual(listed, ["pingdotgg/t3code"]); assert.strictEqual(result.entries[0]?.provider, "github"); // The GitLab project is explained rather than quietly missing from the page. assert.deepStrictEqual( result.providers.map((summary) => ({ kind: summary.kind, configured: summary.configured, projectCount: summary.projectCount, })), [ { kind: "github", configured: true, projectCount: 1 }, { kind: "gitlab", configured: false, projectCount: 1 }, ], ); }), ); it.effect("asks for a whole page of a host, and for the reader's own size when given one", () => Effect.gen(function* () { const limits: number[] = []; const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), ], providers: [ fakeProvider("github", { listChangeRequests: (input) => { limits.push(input.limit); return Effect.succeed({ items: [], truncated: false, continues: true }); }, }), ], }); yield* service.list({ state: "open" }); yield* service.list({ state: "open", limit: 10 }); // Providers probe with one row over this, so 99 asks a host for 100 — the most GitHub and // GitLab serve in one request. 100 here would cost a second round trip for a single row. assert.deepStrictEqual(limits, [99, 10]); }), ); it.effect("says where each repository carries on, and from nothing it has run out of", () => Effect.gen(function* () { const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), project({ id: "p2", title: "web", workspaceRoot: "/b", repository: "acme/web" }), ], providers: [ fakeProvider("github", { listChangeRequests: ({ repository }) => Effect.succeed({ items: [changeRequest(1, "2026-07-02T00:00:00Z")], truncated: repository === "pingdotgg/t3code", continues: true, }), }), ], }); const result = yield* service.list({ state: "open" }); // The instant of the oldest row, how many rows have gone, and the row already sent at that // instant. The repository that had nothing more is simply not in it. assert.deepStrictEqual(result.nextCursors, { "github.com pingdotgg/t3code": "2026-07-02T00:00:00Z|1|1", }); }), ); it.effect("offers no continuation for a host that cannot be carried on from", () => Effect.gen(function* () { const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), ], providers: [ fakeProvider("github", { listChangeRequests: () => Effect.succeed({ items: [changeRequest(1, "2026-07-02T00:00:00Z")], truncated: true, continues: false, }), }), ], }); const result = yield* service.list({ state: "open" }); // More rows exist and no cursor reaches them, which is what asking for a larger page is for. assert.isTrue(result.truncated); assert.deepStrictEqual(result.nextCursors, {}); }), ); it.effect("uses a provider's raw cursor advance when it consumed malformed rows", () => Effect.gen(function* () { const service = yield* makeService({ projects: [ project({ id: "p1", title: "web", workspaceRoot: "/a", repository: "acme/web", provider: "azure-devops", host: "dev.azure.com", }), ], providers: [ fakeProvider("azure-devops", { listChangeRequests: () => Effect.succeed({ items: [changeRequest(7, "2026-07-02T00:00:00Z")], truncated: true, cursorAdvance: 4, continues: true, }), }), ], }); const result = yield* service.list({ state: "open" }); // Keyed by the selector Azure is actually asked with, which is the repository's own name. assert.deepStrictEqual(result.nextCursors, { "dev.azure.com web": "2026-07-02T00:00:00Z|4|7", }); }), ); it.effect("reads only the repositories it was asked to carry on with", () => Effect.gen(function* () { const listed: string[] = []; const cursors: Array<unknown> = []; const service = yield* makeService({ projects: [ project({ id: "p1", title: "t3code", workspaceRoot: "/a", repository: "pingdotgg/t3code" }), project({ id: "p2", title: "web", workspaceRoot: "/b", repository: "acme/web" }), ], providers: [ fakeProvider("github", { listChangeRequests: (input) => { listed.push(input.repository); cursors.push(input.cursor); return Effect.succeed({ items: [], truncated: false, continues: true }); }, }), ], }); const result = yield* service.list({ state: "open", cursors: { "github.com acme/web": "2026-07-02T00:00:00Z|99|7" },