1import { SourceControlProviderKind } from "@t3tools/contracts";
2import { expect, it } from "@effect/vitest";
3import * as Effect from "effect/Effect";
4import * as Schema from "effect/Schema";
5import * as Layer from "effect/Layer";
6import * as TestClock from "effect/testing/TestClock";
7import * as Fiber from "effect/Fiber";
8import * as Deferred from "effect/Deferred";
9import { SourceControlProviderError } from "@t3tools/contracts";
10import { resolveThreadTitleLinks } from "./ThreadTitleLinks.ts";
11import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts";
12
13const layerRegistry = Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry);
14const encodeSubject = Schema.encodeSync(
15 Schema.fromJsonString(Schema.Struct({ title: Schema.String, body: Schema.String })),
16);
17const success = { title: "Fix QR pairing expiry", body: "Keep remote connections working." };
18
19it.effect(
20 "uses provider-selected links, deduplicates anchors, and bounds lookups and summaries",
21 () =>
22 Effect.gen(function* () {
23 const calls: string[] = [];
24 const result = yield* resolveThreadTitleLinks({
25 cwd: "/tmp/project",
26 message:
27 "https://docs.test/guide [https://forge.test/change/1] https://forge.test/change/1#discussion https://forge.test/change/1?view=full `https://forge.test/change/2` https://forge.test/change/2. https://forge.test/change/3",
28 }).pipe(
29 Effect.provide(
30 layerRegistry({
31 resolveLink: ({ url, cwd }) =>
32 url.host === "forge.test"
33 ? Effect.sync(() => {
34 expect(cwd).toBe("/tmp/project");
35 calls.push(url.href);
36 return { title: "t".repeat(400), body: "b".repeat(2_000) };
37 })
38 : undefined,
39 }),
40 ),
41 );
42 expect(calls).toEqual(["https://forge.test/change/1", "https://forge.test/change/2"]);
43 expect(result).toBe(
44 calls
45 .map(
46 (url) =>
47 `${url}\n${encodeSubject({ title: "t".repeat(300), body: "b".repeat(1_200) })}`,
48 )
49 .join("\n\n"),
50 );
51 }),
52);
53
54it.effect("returns unavailable when a lookup times out while retaining successful subjects", () =>
55 Effect.gen(function* () {
56 const started = yield* Deferred.make<void>();
57 const fiber = yield* resolveThreadTitleLinks({
58 cwd: "/tmp/project",
59 message: "https://forge.test/change/1 https://forge.test/change/2",
60 }).pipe(
61 Effect.provide(
62 layerRegistry({
63 resolveLink: ({ url }) =>
64 url.pathname.endsWith("1")
65 ? Deferred.succeed(started, undefined).pipe(Effect.andThen(Effect.never))
66 : Effect.succeed(success),
67 }),
68 ),
69 Effect.forkChild,
70 );
71 yield* Deferred.await(started);
72 yield* TestClock.adjust("3 seconds");
73 expect(yield* Fiber.join(fiber)).toBe(
74 `https://forge.test/change/1: unavailable\n\nhttps://forge.test/change/2\n${encodeSubject(success)}`,
75 );
76 }),
77);
78
79it.effect("keeps lookup failure out of generation and skips unlinked messages", () =>
80 Effect.gen(function* () {
81 expect(yield* resolveThreadTitleLinks({ cwd: "/tmp", message: "Fix pairing" })).toBeUndefined();
82 expect(
83 yield* resolveThreadTitleLinks({ cwd: "/tmp", message: "https://forge.test/change/1" }),
84 ).toContain("unavailable");
85 }).pipe(
86 Effect.provide(
87 layerRegistry({
88 resolveLink: () =>
89 Effect.fail(
90 new SourceControlProviderError({
91 provider: SourceControlProviderKind.make("unknown"),
92 operation: "resolveLink",
93 cwd: "/tmp",
94 detail: "Unavailable",
95 }),
96 ),
97 }),
98 ),
99 ),
100);