Add OpenAPI parity guard for pull-number routes

swadhinbiswas committed 8 months ago
f8e3815
1 parent d817506
commit f8e3815
2 files changed
+47 -1
▼doc/feature_audit.md
+1-1
200
200
| CLI | ✅ | `cli/` package - 21+ commands |
201
201
| Plugin/extension system | ⚠️ | `plugins.ts` infrastructure exists, but basic |
202
202
| Webhook events | ✅ | Full implementation |
203
-| API documentation | ⚠️ | OpenAPI coverage now includes AI callback and AI review trigger/read routes, PR detail/update routes, review/comment flows (including path-scoped comment visibility metadata), stack approvals/rebase/auto-update/merge routes, PR bulk-merge/auto-merge/checks/merge/impact/rewrite routes, PR issue-links and file-approvals routes, merge-gate reporting metadata, notifications/email test endpoints, and analytics merge-frequency/workload routes; broader endpoint parity still incomplete |
203
+| API documentation | ⚠️ | OpenAPI coverage now includes AI callback and AI review trigger/read routes, PR detail/update routes, review/comment flows (including path-scoped comment visibility metadata), stack approvals/rebase/auto-update/merge routes, PR bulk-merge/auto-merge/checks/merge/impact/rewrite routes, PR issue-links and file-approvals routes, merge-gate reporting metadata, notifications/email test endpoints, and analytics merge-frequency/workload routes, plus parity guard tests for `pulls/{number}` route documentation; broader endpoint parity still incomplete |
204
204
205
205
## 12. Self-Hosted & Deployment
206
206
| Feature | Status | Implementation Notes |
207
207
▼tests/unit/openapi-pulls-parity.test.ts
+46-0
1
+import { describe, expect, it } from "vitest";
2
+import { readdirSync, statSync } from "node:fs";
3
+import { join, relative } from "node:path";
4
+import { openApiSpec } from "@/lib/openapi";
5
+
6
+function collectRouteFiles(root: string): string[] {
7
+ const files: string[] = [];
8
+ const stack = [root];
9
+
10
+ while (stack.length > 0) {
11
+ const dir = stack.pop()!;
12
+ for (const entry of readdirSync(dir)) {
13
+ const fullPath = join(dir, entry);
14
+ const stats = statSync(fullPath);
15
+ if (stats.isDirectory()) {
16
+ stack.push(fullPath);
17
+ } else if (entry.endsWith(".ts")) {
18
+ files.push(relative(root, fullPath).replaceAll("\\", "/"));
19
+ }
20
+ }
21
+ }
22
+
23
+ return files;
24
+}
25
+
26
+function toOpenApiPath(routeFile: string): string {
27
+ const withoutTs = routeFile.replace(/\.ts$/, "");
28
+ if (withoutTs === "index") {
29
+ return "/repos/{owner}/{repo}/pulls/{number}";
30
+ }
31
+ const normalized = withoutTs.replace(/\[([^\]]+)\]/g, "{$1}");
32
+ return `/repos/{owner}/{repo}/pulls/{number}/${normalized}`;
33
+}
34
+
35
+describe("openapi pulls[number] parity", () => {
36
+ it("documents all implemented pull-number routes", () => {
37
+ const routeRoot = "src/pages/api/repos/[owner]/[repo]/pulls/[number]";
38
+ const routeFiles = collectRouteFiles(routeRoot);
39
+ const expectedPaths = routeFiles.map(toOpenApiPath);
40
+
41
+ const openApiPaths = new Set(Object.keys(openApiSpec.paths));
42
+ const missing = expectedPaths.filter((path) => !openApiPaths.has(path));
43
+
44
+ expect(missing).toEqual([]);
45
+ });
46
+});
0
47