test(工具链): ★★★ stripComments 两趟正则把真代码当注释吃掉 137 行 —— 改单趟扫描
**现象**:harmony-admin 那条「服务端要注册 GET /auth/me」报红,而
server/cmd/server/main.go:194 **明明写着** r.Get("/auth/me", handler.Me)。
**真因**(不是服务端写错,是读取器错了):
main.go:78 // 与 /api/v1/agent/* 完全同一份代码
↑ 这个 /* 在 // 里面
stripComments 原来是**两趟正则**(先块 {/\*[\s\S]*?\*\//g}、后行),
两趟**互相看不见对方** ⇒ 块注释那趟在**还没删行注释**的文本上看到那个 /*,
当块注释开头,一路找下一个 */(在 :214)⇒ **137 行 / 37 条路由注册**
被当注释抹掉,含它正在断言的 r.Get("/auth/me", …)。
全仓另有 12 处同样写法(/me/*、/assets/*、plugins/*…)。
⇒ 失效形状是「**读取器静默少给一段真代码**」(不抛错、不警告),
症状却出现在**被测对象**上 —— 看起来像"服务端把路由删了"。
**修法**:单趟字符扫描,且状态只用源码(注释内部不参与字符串状态)。
并**补上正则字面量**这一条 —— 漏认的方向是**假绿**:
harmony-device.mjs:59 的 /"bundleName"\s*:\s*"([^"]+)"/ 有 4 个引号(奇数),
打开的"字符串"永不闭合 ⇒ 后面所有注释被当字符串跳过。
五条性质逐条实测:① 行号不变 ② 'http://…' 字符串不被腰斩
③ 注释里的引号不污染状态 ④ 正则字面量被当正则 ⑤ 块注释连文本一起删。
变异测试:把旧实现放回去 ⇒ harmony-admin 确实变红(确认真修好了,
而不是"改的东西恰好没人用")。
**顺带修的三处判据自身缺陷**(都不是源码问题):
· inbox-fallback-poll:原断言钉的是**行尾注释里的字**
(删掉注释照样绿、塞进 await fetchInbox() 也照样绿)⇒ 改为取
if (lastTotal === null) { … } 整个分支做结构断言
· inbox-fallback-poll / sse-credentials:裸 readFileSync ⇒ 具名 code()
(换成更严格的读取后变红,暴露的是判据本来就在判错的对象)
· appearance-defaults:写死包名 ⇒ ourBundle()(deviceprobe 那条在盯这个)
· criteria-hygiene:自检样本「以 // 开头的字面量」被判成写死路径 ⇒
按**形状**排除,**不按文件/变量名豁免**(该文件自己的注释已写过
「豁免按名字或目录裁 = 给逃逸指路」)。变异验证:改成真路径仍红。
边界 / 未做:本函数**不区分模板串里的 ${…} 与字符类里的 /**,
失效方向是假绿(少剥注释),与 stripStrings 记的方向一致。
This commit is contained in:
@ -1,19 +1,18 @@
|
||||
import { test } from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { dirname, join } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import { readFileSync as read } from 'node:fs';
|
||||
import { code } from './lib/read.mjs';
|
||||
|
||||
const HERE = dirname(fileURLToPath(import.meta.url));
|
||||
const SRC = join(HERE, '..', 'src');
|
||||
const sse = readFileSync(join(SRC, 'api', 'sse.ts'), 'utf8');
|
||||
const app = readFileSync(join(SRC, 'App.tsx'), 'utf8');
|
||||
// ★ 服务端在**仓库根**(`server/`),而 `code()` 的相对基准是**包目录**
|
||||
// (`test/lib/read.mjs` 的 `PKG_ROOT`)⇒ 跨到包外必须给绝对路径。
|
||||
// 绝对路径由 `code()` 原样透传(见该文件 `abspath` 的注释),所以这样是安全的。
|
||||
const REPO_ROOT = join(dirname(fileURLToPath(import.meta.url)), '..', '..', '..');
|
||||
const sse = code('src/api/sse.ts');
|
||||
const app = code('src/App.tsx');
|
||||
// 服务端在另一个仓(../../../../server),读它是为了钉「两侧同步」这条 ——
|
||||
// 只改客户端不改服务端时,重放永远不生效,而**没有任何东西会红**。
|
||||
const sseMgr = read(
|
||||
join(SRC, '..', '..', '..', 'server', 'internal', 'sse', 'manager.go'),
|
||||
'utf8');
|
||||
const sseMgr = code(join(REPO_ROOT, 'server/internal/sse/manager.go'));
|
||||
|
||||
/**
|
||||
* WebUI 实时性:SSE 订阅必须**跟着账号凭证**走 + 断线重连要能补回漏掉的事件。
|
||||
|
||||
Reference in New Issue
Block a user