CodeIssuesDiscussionsWikiPull RequestsProjectsCommitsActionsReleasesContributorsPulse● GatesSecuritySettingsDeploymentsPipelineInsightsAgents✨ Explain✨ Ask AI✨ Workspace✨ Spec✨ Tests▓ Debt Map✨ NL Search🏛 Archaeology
Blame · Line-by-line history
community.test.ts
Each line is annotated with the commit that last touched it. Click any SHA to jump to that commit and see the surrounding change.
| f9e7c01 | 1 | /** |
| 2 | * Block J12 — Community health scorecard tests. | |
| 3 | * | |
| 4 | * Pure helpers + route smoke. | |
| 5 | */ | |
| 6 | ||
| 7 | import { describe, it, expect } from "bun:test"; | |
| 8 | import app from "../app"; | |
| 9 | import { | |
| 10 | CHECKLIST, | |
| 11 | buildReport, | |
| 12 | checklistFromInputs, | |
| 13 | isCodeOfConduct, | |
| 14 | isContributing, | |
| 15 | isLicense, | |
| 16 | isPrTemplate, | |
| 17 | isReadme, | |
| 18 | } from "../lib/community"; | |
| 19 | ||
| 20 | describe("community — name matchers", () => { | |
| 21 | it("isReadme matches all common spellings", () => { | |
| 22 | expect(isReadme("README")).toBe(true); | |
| 23 | expect(isReadme("readme.md")).toBe(true); | |
| 24 | expect(isReadme("Readme.MD")).toBe(true); | |
| 25 | expect(isReadme("README.txt")).toBe(true); | |
| 26 | expect(isReadme("README.rst")).toBe(true); | |
| 27 | expect(isReadme("readme.markdown")).toBe(true); | |
| 28 | }); | |
| 29 | ||
| 30 | it("isReadme rejects look-alikes", () => { | |
| 31 | expect(isReadme("readme.html")).toBe(false); | |
| 32 | expect(isReadme("not-readme.md")).toBe(false); | |
| 33 | expect(isReadme("READMEFIRST.md")).toBe(false); | |
| 34 | }); | |
| 35 | ||
| 36 | it("isLicense matches LICENSE / LICENCE / COPYING variants", () => { | |
| 37 | expect(isLicense("LICENSE")).toBe(true); | |
| 38 | expect(isLicense("License.md")).toBe(true); | |
| 39 | expect(isLicense("LICENCE.txt")).toBe(true); | |
| 40 | expect(isLicense("COPYING")).toBe(true); | |
| 41 | }); | |
| 42 | ||
| 43 | it("isLicense rejects unrelated files", () => { | |
| 44 | expect(isLicense("licensing.md")).toBe(false); | |
| 45 | expect(isLicense("license-check.md")).toBe(false); | |
| 46 | }); | |
| 47 | ||
| 48 | it("isCodeOfConduct matches common separators", () => { | |
| 49 | expect(isCodeOfConduct("CODE_OF_CONDUCT.md")).toBe(true); | |
| 50 | expect(isCodeOfConduct("code-of-conduct.md")).toBe(true); | |
| 51 | expect(isCodeOfConduct("CodeOfConduct")).toBe(true); | |
| 52 | }); | |
| 53 | ||
| 54 | it("isContributing matches case-insensitively", () => { | |
| 55 | expect(isContributing("CONTRIBUTING.md")).toBe(true); | |
| 56 | expect(isContributing("contributing")).toBe(true); | |
| 57 | expect(isContributing("contributing.txt")).toBe(true); | |
| 58 | expect(isContributing("contributors.md")).toBe(false); | |
| 59 | }); | |
| 60 | ||
| 61 | it("isPrTemplate matches expected spellings", () => { | |
| 62 | expect(isPrTemplate("pull_request_template.md")).toBe(true); | |
| 63 | expect(isPrTemplate("PULL_REQUEST_TEMPLATE")).toBe(true); | |
| 64 | expect(isPrTemplate("pull-request-template.md")).toBe(true); | |
| 65 | expect(isPrTemplate("PR_TEMPLATE.md")).toBe(false); | |
| 66 | }); | |
| 67 | }); | |
| 68 | ||
| 69 | describe("community — CHECKLIST table", () => { | |
| 70 | it("exposes eight items with unique keys", () => { | |
| 71 | expect(CHECKLIST.length).toBe(8); | |
| 72 | const keys = new Set(CHECKLIST.map((c) => c.key)); | |
| 73 | expect(keys.size).toBe(8); | |
| 74 | }); | |
| 75 | ||
| 76 | it("marks description/readme/license required; others recommended", () => { | |
| 77 | const required = new Set( | |
| 78 | CHECKLIST.filter((i) => i.required).map((i) => i.key) | |
| 79 | ); | |
| 80 | expect(required.has("description")).toBe(true); | |
| 81 | expect(required.has("readme")).toBe(true); | |
| 82 | expect(required.has("license")).toBe(true); | |
| 83 | expect(required.has("code_of_conduct")).toBe(false); | |
| 84 | expect(required.has("pr_template")).toBe(false); | |
| 85 | }); | |
| 86 | }); | |
| 87 | ||
| 88 | describe("community — checklistFromInputs", () => { | |
| 89 | const empty = { | |
| 90 | rootEntries: [], | |
| 91 | githubEntries: [], | |
| 92 | issueTemplateDirExists: false, | |
| 93 | description: null, | |
| 94 | topics: [], | |
| 95 | }; | |
| 96 | ||
| 97 | it("all-zero input → all checks false", () => { | |
| 98 | const r = checklistFromInputs(empty); | |
| 99 | for (const key of Object.keys(r) as Array<keyof typeof r>) { | |
| 100 | expect(r[key]).toBe(false); | |
| 101 | } | |
| 102 | }); | |
| 103 | ||
| 104 | it("detects README at root + README in .github", () => { | |
| 105 | expect( | |
| 106 | checklistFromInputs({ ...empty, rootEntries: ["README.md"] }).readme | |
| 107 | ).toBe(true); | |
| 108 | expect( | |
| 109 | checklistFromInputs({ ...empty, githubEntries: ["README.md"] }).readme | |
| 110 | ).toBe(true); | |
| 111 | }); | |
| 112 | ||
| 113 | it("LICENSE variants all count", () => { | |
| 114 | for (const n of ["LICENSE", "License.md", "LICENCE.txt", "COPYING"]) { | |
| 115 | expect( | |
| 116 | checklistFromInputs({ ...empty, rootEntries: [n] }).license | |
| 117 | ).toBe(true); | |
| 118 | } | |
| 119 | }); | |
| 120 | ||
| 121 | it("description requires non-empty trimmed text", () => { | |
| 122 | expect(checklistFromInputs({ ...empty, description: "" }).description).toBe(false); | |
| 123 | expect( | |
| 124 | checklistFromInputs({ ...empty, description: " " }).description | |
| 125 | ).toBe(false); | |
| 126 | expect(checklistFromInputs({ ...empty, description: "x" }).description).toBe(true); | |
| 127 | }); | |
| 128 | ||
| 129 | it("topics requires at least one", () => { | |
| 130 | expect(checklistFromInputs({ ...empty, topics: [] }).topics).toBe(false); | |
| 131 | expect(checklistFromInputs({ ...empty, topics: ["ai"] }).topics).toBe(true); | |
| 132 | }); | |
| 133 | ||
| 134 | it("issue_template is present if dir exists OR a single file", () => { | |
| 135 | expect( | |
| 136 | checklistFromInputs({ ...empty, issueTemplateDirExists: true }) | |
| 137 | .issue_template | |
| 138 | ).toBe(true); | |
| 139 | expect( | |
| 140 | checklistFromInputs({ | |
| 141 | ...empty, | |
| 142 | rootEntries: ["ISSUE_TEMPLATE.md"], | |
| 143 | }).issue_template | |
| 144 | ).toBe(true); | |
| 145 | expect( | |
| 146 | checklistFromInputs({ | |
| 147 | ...empty, | |
| 148 | githubEntries: ["ISSUE_TEMPLATE.md"], | |
| 149 | }).issue_template | |
| 150 | ).toBe(true); | |
| 151 | }); | |
| 152 | ||
| 153 | it("pr_template — accepts .github/ and root spellings", () => { | |
| 154 | expect( | |
| 155 | checklistFromInputs({ | |
| 156 | ...empty, | |
| 157 | githubEntries: ["pull_request_template.md"], | |
| 158 | }).pr_template | |
| 159 | ).toBe(true); | |
| 160 | expect( | |
| 161 | checklistFromInputs({ | |
| 162 | ...empty, | |
| 163 | rootEntries: ["PULL_REQUEST_TEMPLATE.md"], | |
| 164 | }).pr_template | |
| 165 | ).toBe(true); | |
| 166 | }); | |
| 167 | }); | |
| 168 | ||
| 169 | describe("community — buildReport", () => { | |
| 170 | it("all-false → 0% / meetsRequired false", () => { | |
| 171 | const r = buildReport({ | |
| 172 | description: false, | |
| 173 | readme: false, | |
| 174 | license: false, | |
| 175 | code_of_conduct: false, | |
| 176 | contributing: false, | |
| 177 | issue_template: false, | |
| 178 | pr_template: false, | |
| 179 | topics: false, | |
| 180 | }); | |
| 181 | expect(r.score).toBe(0); | |
| 182 | expect(r.passed).toBe(0); | |
| 183 | expect(r.total).toBe(8); | |
| 184 | expect(r.requiredPassed).toBe(0); | |
| 185 | expect(r.requiredTotal).toBe(3); | |
| 186 | expect(r.meetsRequired).toBe(false); | |
| 187 | }); | |
| 188 | ||
| 189 | it("all-true → 100% / meetsRequired true", () => { | |
| 190 | const r = buildReport({ | |
| 191 | description: true, | |
| 192 | readme: true, | |
| 193 | license: true, | |
| 194 | code_of_conduct: true, | |
| 195 | contributing: true, | |
| 196 | issue_template: true, | |
| 197 | pr_template: true, | |
| 198 | topics: true, | |
| 199 | }); | |
| 200 | expect(r.score).toBe(100); | |
| 201 | expect(r.meetsRequired).toBe(true); | |
| 202 | }); | |
| 203 | ||
| 204 | it("required-only → meetsRequired true, score partial", () => { | |
| 205 | const r = buildReport({ | |
| 206 | description: true, | |
| 207 | readme: true, | |
| 208 | license: true, | |
| 209 | code_of_conduct: false, | |
| 210 | contributing: false, | |
| 211 | issue_template: false, | |
| 212 | pr_template: false, | |
| 213 | topics: false, | |
| 214 | }); | |
| 215 | expect(r.meetsRequired).toBe(true); | |
| 216 | expect(r.score).toBe(Math.round((3 / 8) * 100)); | |
| 217 | }); | |
| 218 | ||
| 219 | it("preserves CHECKLIST ordering + annotates `present`", () => { | |
| 220 | const r = buildReport({ | |
| 221 | description: true, | |
| 222 | readme: false, | |
| 223 | license: false, | |
| 224 | code_of_conduct: false, | |
| 225 | contributing: false, | |
| 226 | issue_template: false, | |
| 227 | pr_template: false, | |
| 228 | topics: false, | |
| 229 | }); | |
| 230 | expect(r.items[0].key).toBe("description"); | |
| 231 | expect(r.items[0].present).toBe(true); | |
| 232 | expect(r.items[1].key).toBe("readme"); | |
| 233 | expect(r.items[1].present).toBe(false); | |
| 234 | }); | |
| 235 | }); | |
| 236 | ||
| 237 | describe("community — route", () => { | |
| 238 | it("GET /:o/:r/community on missing repo returns 404", async () => { | |
| 239 | const res = await app.request("/alice/nope/community"); | |
| 240 | expect(res.status).toBe(404); | |
| 241 | }); | |
| 242 | }); |