| ... | ... | @@ -199,6 +199,87 @@ it.effect("reads Forgejo checks without repository or viewer requests", () => { |
| 199 | 199 | ); |
| 200 | 200 | }); |
| 201 | 201 | |
| 202 | it.effect.each([ |
| 203 | ["Ready", false, true, false, "mergeable"], |
| 204 | ["Draft", true, false, true, "unknown"], |
| 205 | ["Blocked", false, false, false, "unknown"], |
| 206 | ["Unchecked", false, undefined, false, "unknown"], |
| 207 | ["WIP: Legacy draft", undefined, false, true, "unknown"], |
| 208 | ["[WIP] Legacy draft", undefined, false, true, "unknown"], |
| 209 | ["WIP: Explicitly ready", false, false, false, "unknown"], |
| 210 | ] as const)( |
| 211 | "reads Forgejo mergeability for %s (draft=%s, mergeable=%s) across list, summary and detail", |
| 212 | ([title, draft, mergeable, isDraft, mergeability]) => { |
| 213 | const pr = { |
| 214 | number: 42, |
| 215 | title, |
| 216 | body: "", |
| 217 | html_url: "https://forgejo.test/maria/project/pulls/42", |
| 218 | user: { login: "maria" }, |
| 219 | state: "open", |
| 220 | merged: false, |
| 221 | ...(draft === undefined ? {} : { draft }), |
| 222 | ...(mergeable === undefined ? {} : { mergeable }), |
| 223 | head: { ref: "feature", sha: "head", repo: null }, |
| 224 | base: { ref: "main", sha: "base", repo: null }, |
| 225 | created_at: "2026-09-16T00:00:00Z", |
| 226 | updated_at: "2026-09-16T00:00:00Z", |
| 227 | closed_at: null, |
| 228 | merged_at: null, |
| 229 | labels: [], |
| 230 | }; |
| 231 | return Effect.gen(function* () { |
| 232 | const provider = yield* ForgejoPullRequestProvider.make; |
| 233 | const readSummary = provider.getChangeRequestSummary; |
| 234 | if (readSummary === undefined) return yield* Effect.die("summary read missing"); |
| 235 | const input = { cwd: "/repo", repository: "maria/project", host: "forgejo.test", number: 42 }; |
| 236 | const list = yield* provider.listChangeRequests({ |
| 237 | ...input, |
| 238 | state: "open", |
| 239 | involvement: "all", |
| 240 | viewer: "maria", |
| 241 | limit: 10, |
| 242 | }); |
| 243 | const summary = yield* readSummary(input); |
| 244 | const detail = yield* provider.getChangeRequest(input); |
| 245 | assert.strictEqual(list.items.length, 1); |
| 246 | for (const result of [list.items[0]!, summary, detail]) { |
| 247 | assert.strictEqual(result.mergeability, mergeability); |
| 248 | assert.strictEqual(result.isDraft, isDraft); |
| 249 | } |
| 250 | }).pipe( |
| 251 | Effect.provide( |
| 252 | Layer.mock(ForgejoCli.ForgejoCli)({ |
| 253 | api: (input) => { |
| 254 | const [path, query] = input.path.split("?"); |
| 255 | let response: unknown; |
| 256 | switch (path) { |
| 257 | case "repos/maria/project/pulls": |
| 258 | response = new URLSearchParams(query).get("page") === "1" ? [pr] : []; |
| 259 | break; |
| 260 | case "repos/maria/project/pulls/42": |
| 261 | response = pr; |
| 262 | break; |
| 263 | case "repos/maria/project": |
| 264 | response = { full_name: "maria/project", permissions: { push: true, admin: true } }; |
| 265 | break; |
| 266 | case "user": |
| 267 | response = pr.user; |
| 268 | break; |
| 269 | case "repos/maria/project/statuses/head": |
| 270 | response = []; |
| 271 | break; |
| 272 | default: |
| 273 | return Effect.die(`Unexpected Forgejo request: ${input.path}`); |
| 274 | } |
| 275 | return encodeJsonEffect(response).pipe(Effect.orDie, Effect.map(processOutput)); |
| 276 | }, |
| 277 | }), |
| 278 | ), |
| 279 | ); |
| 280 | }, |
| 281 | ); |
| 282 | |
| 202 | 283 | it.effect("loads Forgejo pull request references from files and commits views", () => |
| 203 | 284 | Effect.gen(function* () { |
| 204 | 285 | const provider = yield* ForgejoSourceControlProvider.make; |