| #!/usr/bin/env node
|
| |
| |
| |
| |
| |
| |
|
|
|
|
| import fs from "node:fs";
|
| import path from "node:path";
|
| import yaml from "js-yaml";
|
|
|
| const ROOT = process.cwd();
|
| const API_ROOT = path.join(ROOT, "src", "app", "api");
|
| const OPENAPI_PATH = path.join(ROOT, "docs", "reference", "openapi.yaml");
|
|
|
|
|
|
|
|
|
|
|
| const THRESHOLD = 36;
|
|
|
| function collectRoutePaths(dir) {
|
| const entries = fs.readdirSync(dir, { withFileTypes: true });
|
| const paths = [];
|
| for (const entry of entries) {
|
| const fullPath = path.join(dir, entry.name);
|
| if (entry.isDirectory()) {
|
| paths.push(...collectRoutePaths(fullPath));
|
| continue;
|
| }
|
| if (entry.isFile() && entry.name === "route.ts") {
|
| const apiPath = path
|
| .dirname(fullPath)
|
| .replace(API_ROOT, "")
|
| .replace(/\[([^\]]+)\]/g, "{$1}");
|
| paths.push(`/api${apiPath}`);
|
| }
|
| }
|
| return paths;
|
| }
|
|
|
| function normalizePath(p) {
|
| return p.replace(/\/\[\.\.\.([^\]]+)\]/g, "/{$1}").replace(/\[([^\]]+)\]/g, "{$1}");
|
| }
|
|
|
| if (!fs.existsSync(API_ROOT)) {
|
| console.error(`[openapi-coverage] FAIL β API root not found: ${API_ROOT}`);
|
| process.exit(1);
|
| }
|
|
|
| if (!fs.existsSync(OPENAPI_PATH)) {
|
| console.error(`[openapi-coverage] FAIL β openapi.yaml not found: ${OPENAPI_PATH}`);
|
| process.exit(1);
|
| }
|
|
|
| const implementedPaths = collectRoutePaths(API_ROOT).map(normalizePath).sort();
|
| const raw = yaml.load(fs.readFileSync(OPENAPI_PATH, "utf-8"));
|
| const documentedPaths = new Set(Object.keys(raw.paths || {}));
|
|
|
| let covered = 0;
|
| const missing = [];
|
|
|
| for (const p of implementedPaths) {
|
| if (documentedPaths.has(p)) {
|
| covered++;
|
| } else {
|
| missing.push(p);
|
| }
|
| }
|
|
|
| const total = implementedPaths.length;
|
| const coverage = (covered / total) * 100;
|
|
|
| if (coverage >= THRESHOLD) {
|
| console.log(
|
| `[openapi-coverage] PASS β ${coverage.toFixed(1)}% (${covered}/${total} routes documented)`
|
| );
|
| process.exit(0);
|
| } else {
|
| console.error(`[openapi-coverage] FAIL β coverage ${coverage.toFixed(1)}% < ${THRESHOLD}%`);
|
| console.error(`Missing routes (${missing.length}):`);
|
| missing.forEach((p) => console.error(` - ${p}`));
|
| process.exit(1);
|
| }
|
|
|