fix production issues
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 51s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
Build & Push / Pipeline Tests (push) Failing after 59s
Build & Push / Build & Push Docker Image (push) Has been skipped
Test / Type Check (all packages) (push) Failing after 51s
Test / API Unit Tests (push) Has been skipped
Test / Homepage Unit Tests (push) Has been skipped
Test / Carplace Unit Tests (push) Has been skipped
Test / Admin Unit Tests (push) Has been skipped
Test / Dashboard Unit Tests (push) Has been skipped
Test / API Integration Tests (push) Has been skipped
This commit is contained in:
@@ -0,0 +1,73 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Compares runtime Express method registrations against OpenAPI path keys.
|
||||
* Fails if coverage drops below OPENAPI_MIN_COVERAGE (default 55) or if
|
||||
* required ops endpoints are missing from the OpenAPI document.
|
||||
*/
|
||||
import fs from 'node:fs'
|
||||
import path from 'node:path'
|
||||
|
||||
const root = path.resolve(process.cwd())
|
||||
const apiSrc = path.join(root, 'apps', 'api', 'src')
|
||||
const minCoverage = Number(process.env.OPENAPI_MIN_COVERAGE ?? 55)
|
||||
|
||||
function walk(dir, out = []) {
|
||||
for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
|
||||
const p = path.join(dir, entry.name)
|
||||
if (entry.isDirectory()) {
|
||||
if (entry.name === 'node_modules' || entry.name === 'dist') continue
|
||||
walk(p, out)
|
||||
} else if (/\.(ts|js)$/.test(entry.name) && !/\.test\./.test(entry.name)) {
|
||||
out.push(p)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
const routeCall = /\b(router|app)\.(get|post|put|patch|delete)\(\s*['"`]([^'"`]+)['"`]/gi
|
||||
const files = walk(apiSrc)
|
||||
const runtime = new Set()
|
||||
|
||||
for (const file of files) {
|
||||
const text = fs.readFileSync(file, 'utf8')
|
||||
let match
|
||||
while ((match = routeCall.exec(text))) {
|
||||
const method = match[2].toUpperCase()
|
||||
let routePath = match[3]
|
||||
if (!routePath.startsWith('/')) routePath = `/${routePath}`
|
||||
runtime.add(`${method} ${routePath}`)
|
||||
}
|
||||
}
|
||||
|
||||
const openapiPath = path.join(apiSrc, 'swagger', 'openapi.ts')
|
||||
const openapiText = fs.readFileSync(openapiPath, 'utf8')
|
||||
const pathKeys = [...openapiText.matchAll(/^\s*'(\/[^']+)':\s*\{/gm)].map((m) => m[1])
|
||||
const openapiPaths = new Set(pathKeys)
|
||||
|
||||
const required = ['/health', '/ready', '/metrics']
|
||||
const missingRequired = required.filter((p) => !openapiPaths.has(p))
|
||||
|
||||
// Rough coverage: unique OpenAPI path keys vs unique runtime path strings
|
||||
const runtimePaths = new Set([...runtime].map((entry) => entry.split(' ').slice(1).join(' ')))
|
||||
const covered = [...openapiPaths].filter((p) => {
|
||||
if (runtimePaths.has(p)) return true
|
||||
// parameterised OpenAPI paths often match runtime `/:id` style
|
||||
return [...runtimePaths].some((rp) => rp.split('/').length === p.split('/').length)
|
||||
})
|
||||
const coverage = openapiPaths.size === 0 ? 0 : Math.round((covered.length / Math.max(runtimePaths.size, 1)) * 100)
|
||||
|
||||
console.log(`OpenAPI paths: ${openapiPaths.size}`)
|
||||
console.log(`Runtime route registrations: ${runtime.size} (${runtimePaths.size} unique path patterns)`)
|
||||
console.log(`Approximate path coverage: ${coverage}% (min ${minCoverage}%)`)
|
||||
|
||||
if (missingRequired.length) {
|
||||
console.error(`Missing required OpenAPI paths: ${missingRequired.join(', ')}`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
if (coverage < minCoverage) {
|
||||
console.error(`OpenAPI coverage ${coverage}% is below minimum ${minCoverage}%`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
console.log('OpenAPI coverage check passed')
|
||||
Reference in New Issue
Block a user