| 1 | import { SourceControlProviderKind } from "@t3tools/contracts"; |
| 2 | import { expect, it } from "@effect/vitest"; |
| 3 | import * as Effect from "effect/Effect"; |
| 4 | import * as Schema from "effect/Schema"; |
| 5 | import * as Layer from "effect/Layer"; |
| 6 | import * as TestClock from "effect/testing/TestClock"; |
| 7 | import * as Fiber from "effect/Fiber"; |
| 8 | import * as Deferred from "effect/Deferred"; |
| 9 | import { SourceControlProviderError } from "@t3tools/contracts"; |
| 10 | import { resolveThreadTitleLinks } from "./ThreadTitleLinks.ts"; |
| 11 | import * as SourceControlProviderRegistry from "../sourceControl/SourceControlProviderRegistry.ts"; |
| 12 | |
| 13 | const layerRegistry = Layer.mock(SourceControlProviderRegistry.SourceControlProviderRegistry); |
| 14 | const encodeSubject = Schema.encodeSync( |
| 15 | Schema.fromJsonString(Schema.Struct({ title: Schema.String, body: Schema.String })), |
| 16 | ); |
| 17 | const success = { title: "Fix QR pairing expiry", body: "Keep remote connections working." }; |
| 18 | |
| 19 | it.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 | |
| 54 | it.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 | |
| 79 | it.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 | ); |