Archived
route-coverage 补下界断言防「数不到路由也绿」;跳过告警点名哪道保险没跑
评审 Important:missing 为空有两种可能——真的不漏,或者根本没数到路由 (app.routes 形状变了、createServer 早退)。空集是任何集合的子集,不设 下界这条 fail-closed 测试会从「会红」退化成「永远绿」。补 registered.length > 50 的下界,反向验证过(临时调到 1000 确认真的报红)。 无库跳过时的 stderr 告警原来只说连不上库,没说清「这道保险这次没跑、 漏的是哪一类」——按评审裁决不改 skipIf 行为(仓库级惯例),只改措辞 点名到位。
This commit is contained in:
@@ -35,7 +35,9 @@ async function provisionDatabase(): Promise<{ url: string; stop(): Promise<void>
|
||||
return { url: started.getConnectionUri(), stop: () => started.stop().then(() => undefined) };
|
||||
} catch (error) {
|
||||
process.stderr.write(
|
||||
`⚠️ 路由覆盖 it 跳过:连不上开发实例也起不了 Testcontainers(${error instanceof Error ? error.message : String(error)})\n`,
|
||||
`⚠️ 路由覆盖 it 跳过 —— **这道 fail-closed 的安全保险这一次没有跑**:` +
|
||||
`「写了路由处理器却没有契约声明 access」这一类漏网,本次构建没有检查。\n` +
|
||||
` 连不上开发实例也起不了 Testcontainers(${error instanceof Error ? error.message : String(error)})\n`,
|
||||
);
|
||||
return undefined;
|
||||
}
|
||||
@@ -78,10 +80,17 @@ describe.skipIf(database === undefined)('路由覆盖', () => {
|
||||
it('Hono 里每条注册的路由都在归属注册表里', () => {
|
||||
const app = createServer(container!);
|
||||
const registry = appRouteRegistry();
|
||||
const missing = app.routes
|
||||
.filter((r) => r.method !== 'ALL')
|
||||
.filter((r) => !registry.has(r.method, r.path))
|
||||
.map((r) => `${r.method} ${r.path}`);
|
||||
const registered = app.routes.filter((r) => r.method !== 'ALL');
|
||||
|
||||
/*
|
||||
* 先钉下界:`missing` 为空有两种可能 —— 真的一条不漏,或者**根本没数到路由**
|
||||
* (createServer 改用子应用组合、Hono 换了 routes 的形状、建 server 中途早退)。
|
||||
* 空集是任何集合的子集,不设下界的话后一种情况会伪装成前一种,
|
||||
* 这道保险就从「会红」变成「永远绿」。
|
||||
*/
|
||||
expect(registered.length, '一条路由都没数到 —— 先查 createServer 与 app.routes 的形状').toBeGreaterThan(50);
|
||||
|
||||
const missing = registered.filter((r) => !registry.has(r.method, r.path)).map((r) => `${r.method} ${r.path}`);
|
||||
expect(missing, '这些路由没有声明归哪个端(契约加 access,或 EXTRA_ROUTES 补入)').toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user