diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1f9076c65a..2626472422 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -43,8 +43,18 @@ jobs: run: npm run typecheck - name: Test with coverage + id: coverage run: npm run test:coverage + - name: Coverage gate guidance + if: ${{ failure() && steps.coverage.conclusion == 'failure' }} + run: | + echo "::error title=Coverage gate::Tests or the 97% coverage gate failed." + echo "The repo enforces 97% global coverage for lines, statements, functions, and branches." + echo "Review the per-file coverage table printed above to find undercovered files and branch arms." + echo "Run 'npm run test:coverage' locally and add tests for the missing branches, fallback paths, and sanitizer rules." + echo "See CONTRIBUTING.md (Testing & coverage): aim for 98%+ branch coverage locally so small CI variance does not fail near the threshold." + - name: Worker runtime tests run: npm run test:workers diff --git a/test/unit/pending-pr-scenarios.test.ts b/test/unit/pending-pr-scenarios.test.ts index 0b74ad1ac9..e05dc63035 100644 --- a/test/unit/pending-pr-scenarios.test.ts +++ b/test/unit/pending-pr-scenarios.test.ts @@ -84,6 +84,33 @@ describe("pending PR scenario detection", () => { expect(detection?.classified.find((entry) => entry.number === 12)?.classification).toBe("blocked"); }); + it("treats timed-out checks as failing work that blocks merge readiness", () => { + const classified = classifyOpenPullRequest({ + pr: pr({ number: 77 }), + roleContext: outsideContributorRole, + reviews: [approvedReview(77)], + checks: [{ id: "c77", repoFullName: "entrius/allways-ui", pullNumber: 77, name: "ci", status: "completed", conclusion: "timed_out", payload: {} }], + }); + expect(classified.classification).toBe("blocked"); + expect(classified.reasons.join(" ")).toMatch(/failing or cancelled check/i); + }); + + it("counts stale approved PRs as pending closes and projects the post-cleanup open count", () => { + const staleDate = new Date(Date.now() - 30 * 86_400_000).toISOString(); + const detection = detectPendingPrScenario({ + login: "miner-a", + repoFullName: "entrius/allways-ui", + pullRequests: [pr({ number: 21, updatedAt: staleDate, createdAt: staleDate })], + roleContext: outsideContributorRole, + openPrCount: 2, + reviewsByPullNumber: new Map([[21, [approvedReview(21)]]]), + checksByPullNumber: new Map([[21, []]]), + }); + expect(detection?.pendingMergedPrCount).toBe(0); + expect(detection?.pendingClosedPrCount).toBe(1); + expect(detection?.expectedOpenPrCountAfterMerge).toBe(1); + }); + it("does not treat draft, stale, or maintainer-lane PRs as likely-to-land", () => { const staleDate = new Date(Date.now() - 20 * 86_400_000).toISOString(); const classified = [ diff --git a/test/unit/settings-preview.test.ts b/test/unit/settings-preview.test.ts index c61332be3d..7dd1d3f1a7 100644 --- a/test/unit/settings-preview.test.ts +++ b/test/unit/settings-preview.test.ts @@ -263,4 +263,30 @@ describe("buildRepoSettingsPreview", () => { expect(preview.previewComment).not.toBeNull(); expect(preview.previewComment ?? "").not.toMatch(/wallet|hotkey|trust score|raw trust|scoreability|payout|reward|farming|\/100|reviewability\s*\d/i); }); + + it("reports a generic needs-attention summary when health is degraded but no permission or event is missing", () => { + const preview = buildRepoSettingsPreview({ + ...base, + settings: settings(), + installation: { ...healthyInstall, status: "needs_attention", missingPermissions: [], missingEvents: [] }, + sample: { authorLogin: "miner", minerStatus: "confirmed" }, + }); + expect(preview.installPreview.status).toBe("needs_attention"); + expect(preview.installPreview.permissions.summary).toMatch(/needs attention; review remediation/i); + expect(preview.installPreview.permissions.missing).toEqual([]); + }); + + it("requires no issues/checks write scope and lists a no-output sample when every public action is disabled", () => { + const preview = buildRepoSettingsPreview({ + ...base, + settings: settings({ publicSurface: "label_only", autoLabelEnabled: false, commentMode: "off", checkRunMode: "off" }), + installation: healthyInstall, + sample: { authorLogin: "miner", minerStatus: "confirmed" }, + }); + expect(preview.decision).toMatchObject({ skipped: false, willComment: false, willLabel: false, willCheckRun: false, actions: ["none"] }); + // requiredInstallPermissions keeps only read scopes when no public write action is enabled. + expect(preview.installPreview.permissions.required).toEqual(["metadata: read", "pull_requests: read"]); + expect(preview.installPreview.publicOutputs).toEqual(["No public comment, label, or check run for this sample."]); + expect(preview.installPreview.checklist.find((item) => item.id === "public-outputs")?.summary).toMatch(/no public output action is enabled/i); + }); });