1import {
2 ProviderDriverKind,
3 ProviderInstanceId,
4 type RepositoryIdentity,
5 type ThreadPullRequestLink,
6} from "@t3tools/contracts";
7import { describe, expect, it } from "vite-plus/test";
8
9import {
10 findPromptLinkProject,
11 findPromptLinkPullRequestThread,
12 resolvePromptLinkModelSelection,
13} from "./promptLinks.ts";
14
15const identity = (canonicalKey: string, origin?: string): RepositoryIdentity => ({
16 canonicalKey,
17 locator: { source: "git-remote", remoteName: "origin", remoteUrl: `https://${canonicalKey}` },
18 ...(origin ? { origin: { canonicalKey: origin } } : {}),
19});
20
21describe("findPromptLinkProject", () => {
22 it("takes the first exact match and never a fork of the repository", () => {
23 const projects = [
24 {
25 id: "fork",
26 repositoryIdentity: identity("github.com/pingdotgg/t3code", "github.com/me/t3code"),
27 },
28 { id: "none", repositoryIdentity: null },
29 { id: "recent", repositoryIdentity: identity("github.com/PingDotGG/T3Code") },
30 { id: "older", repositoryIdentity: identity("github.com/pingdotgg/t3code") },
31 ];
32 expect(findPromptLinkProject(projects, { owner: "pingdotgg", name: "t3code" })?.id).toBe(
33 "recent",
34 );
35 expect(findPromptLinkProject(projects, { owner: "me", name: "t3code" })?.id).toBe("fork");
36 expect(findPromptLinkProject(projects, { owner: "gitlab", name: "x" })).toBeUndefined();
37 });
38});
39
40describe("findPromptLinkPullRequestThread", () => {
41 const link = (number: number, source: ThreadPullRequestLink["source"] = "created") =>
42 ({
43 host: "github.com",
44 repository: "pingdotgg/t3code",
45 number,
46 url: `https://github.com/pingdotgg/t3code/pull/${number}`,
47 source,
48 linkedAt: "2026-01-01T00:00:00.000Z",
49 snapshot: null,
50 stack: null,
51 }) satisfies ThreadPullRequestLink;
52
53 it("finds the most recently updated thread linked to the pull request", () => {
54 const threads = [
55 { id: "old", updatedAt: "2026-01-01T00:00:00.000Z", pullRequests: [link(7)] },
56 { id: "new", updatedAt: "2026-02-01T00:00:00.000Z", pullRequests: [link(7)] },
57 { id: "other", updatedAt: "2026-03-01T00:00:00.000Z", pullRequests: [link(8)] },
58 {
59 id: "dismissed",
60 updatedAt: "2026-04-01T00:00:00.000Z",
61 pullRequests: [link(7, "stack-dismissed")],
62 },
63 ];
64 const pullRequest = { owner: "PingDotGG", name: "t3code", number: 7 };
65 expect(findPromptLinkPullRequestThread(threads, pullRequest)?.id).toBe("new");
66 expect(findPromptLinkPullRequestThread(threads, { ...pullRequest, number: 9 })).toBeUndefined();
67 });
68});
69
70describe("resolvePromptLinkModelSelection", () => {
71 const provider = (
72 instanceId: string,
73 driver: string,
74 status: "ready" | "warning" | "error" | "disabled" = "ready",
75 ) => ({
76 instanceId: ProviderInstanceId.make(instanceId),
77 driver: ProviderDriverKind.make(driver),
78 enabled: true,
79 status,
80 models: [
81 { slug: "small", name: "Small" },
82 { slug: "big", name: "Big", aliases: ["opus"], isDefault: true },
83 ],
84 });
85
86 it("maps the requested provider and model onto a usable instance", () => {
87 const providers = [provider("broken", "claudeAgent", "error"), provider("work", "claudeAgent")];
88 expect(
89 resolvePromptLinkModelSelection(providers, { provider: "claude", model: "small" }),
90 ).toEqual({
91 selection: { instanceId: "work", model: "small" },
92 exact: true,
93 });
94 expect(
95 resolvePromptLinkModelSelection(providers, { provider: "claudeagent", model: "opus" }),
96 ).toEqual({
97 selection: { instanceId: "work", model: "big" },
98 exact: true,
99 });
100 });
101
102 it("uses the instance default for an unknown model, and nothing for a missing provider", () => {
103 const providers = [provider("codex", "codex")];
104 expect(
105 resolvePromptLinkModelSelection(providers, { provider: "codex", model: "nope" }),
106 ).toEqual({
107 selection: { instanceId: "codex", model: "big" },
108 exact: false,
109 });
110 expect(
111 resolvePromptLinkModelSelection(providers, { provider: "cursor", model: null }),
112 ).toBeNull();
113 });
114});