|
1 | 1 | import { afterEach, describe, expect, it, vi } from "vitest"; |
2 | 2 | import { runLoopOverLinkedIssueSatisfaction, type LinkedIssueSatisfactionRunInput } from "../../src/services/linked-issue-satisfaction-run"; |
| 3 | +import { MAX_BODY_CHARS, MAX_DIFF_CHARS } from "../../src/services/linked-issue-satisfaction"; |
3 | 4 | import { BEST_REVIEW_MODELS, RELIABLE_FALLBACK_MODELS } from "../../src/services/ai-review"; |
4 | 5 | import { buildAiReviewDiff, processJob, runLinkedIssueSatisfactionForAdvisory } from "../../src/queue/processors"; |
5 | 6 | import { evaluateGateCheck } from "../../src/rules/advisory"; |
@@ -481,6 +482,38 @@ describe("runLinkedIssueSatisfactionForAdvisory (processor wiring, #1961/#3906)" |
481 | 482 | expect(history.overrides).toEqual([]); // firing alone is never an override |
482 | 483 | }); |
483 | 484 |
|
| 485 | + it("captures the bounded raw context (issueText/prTitle/prBody/diff) on the fired signal, truncating an over-limit body (#8129)", async () => { |
| 486 | + stubIssueFetch(); |
| 487 | + const run = vi.fn(async () => ({ response: satisfactionJson({ status: "unaddressed", confidence: 0.9 }) })); |
| 488 | + const env = enabledEnv(run); |
| 489 | + const longBody = "x".repeat(MAX_BODY_CHARS + 500); |
| 490 | + const longBodyPr = { ...pr, body: longBody }; |
| 491 | + await runLinkedIssueSatisfactionForAdvisory(env, { mode: "live", settings: blockMode, advisory: advisory(), repoFullName: "acme/widgets", pr: longBodyPr, author: "alice", files, confirmedContributor: true, installationId: 1 }); |
| 492 | + |
| 493 | + const history = await createSignalStore(env).queryRuleHistory("linked_issue_scope_mismatch", 0); |
| 494 | + expect(history.fired).toHaveLength(1); |
| 495 | + const metadata = history.fired[0]?.metadata as Record<string, string>; |
| 496 | + // The stored issue text is the same title+body composite the assessment itself consumed. |
| 497 | + expect(metadata.issueText).toContain("Enrich SN74 Gittensor — add SSE stream"); |
| 498 | + expect(metadata.issueText).toContain("We need a live SSE stream surface for SN74 Gittensor."); |
| 499 | + expect(metadata.prTitle).toBe("Add SSE stream endpoint"); |
| 500 | + // The over-limit body is truncated to the assessment's OWN bound, never stored raw. |
| 501 | + expect(metadata.prBody).toBe(longBody.slice(0, MAX_BODY_CHARS)); |
| 502 | + expect(metadata.prBody).toHaveLength(MAX_BODY_CHARS); |
| 503 | + expect(metadata.diff).toBe(buildAiReviewDiff(files).slice(0, MAX_DIFF_CHARS)); |
| 504 | + }); |
| 505 | + |
| 506 | + it("stores an empty prBody (not the string 'null'/'undefined') for a body-less PR (#8129)", async () => { |
| 507 | + stubIssueFetch(); |
| 508 | + const run = vi.fn(async () => ({ response: satisfactionJson({ status: "unaddressed", confidence: 0.9 }) })); |
| 509 | + const env = enabledEnv(run); |
| 510 | + const bodylessPr = { ...pr, body: null as unknown as string }; |
| 511 | + await runLinkedIssueSatisfactionForAdvisory(env, { mode: "live", settings: blockMode, advisory: advisory(), repoFullName: "acme/widgets", pr: bodylessPr, author: "alice", files, confirmedContributor: true, installationId: 1 }); |
| 512 | + |
| 513 | + const history = await createSignalStore(env).queryRuleHistory("linked_issue_scope_mismatch", 0); |
| 514 | + expect((history.fired[0]?.metadata as Record<string, string>).prBody).toBe(""); |
| 515 | + }); |
| 516 | + |
484 | 517 | it("ADVISORY mode records NO fired signal for the same 'unaddressed' verdict (#8101 — no finding, no signal)", async () => { |
485 | 518 | stubIssueFetch(); |
486 | 519 | const run = vi.fn(async () => ({ response: satisfactionJson({ status: "unaddressed", confidence: 0.9 }) })); |
|
0 commit comments