@luolingp/request-doctor 0.16.1 → 0.16.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/docs/01-/350/275/257/344/273/266/351/234/200/346/261/202/350/247/204/346/240/274/350/257/264/346/230/216.md +1 -1
- package/docs/02-/350/275/257/344/273/266/350/256/276/350/256/241/350/257/264/346/230/216.md +1 -1
- package/docs/03-/350/275/257/344/273/266/346/265/213/350/257/225/346/212/245/345/221/212.md +4 -3
- package/docs/04-/345/256/211/350/243/205/351/203/250/347/275/262/350/257/264/346/230/216.md +1 -1
- package/docs/05-/347/224/250/346/210/267/346/211/213/345/206/214.md +1 -1
- package/docs/06-/350/275/257/344/273/266/350/264/250/351/207/217/345/272/246/351/207/217/346/212/245/345/221/212.md +1 -1
- package/docs/quality-report.json +9 -9
- package/lib/diagnostics.js +83 -81
- package/lib/skip-log.js +5 -1
- package/lib/status-restore.js +20 -0
- package/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
# 软件测试报告
|
|
2
2
|
|
|
3
3
|
**产品名称**:request-doctor(请求急救员)
|
|
4
|
-
**版本**:0.16.
|
|
4
|
+
**版本**:0.16.2
|
|
5
5
|
**编制依据**:GB/T 8567-2006《计算机软件文档编制规范》6.6;GB/T 8566-2007 验证与确认过程
|
|
6
6
|
|
|
7
7
|
---
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
| `test/client-half.test.mjs` | 11 | 客户端半:模块身份、注入声明、apply 注册、组件能渲染 |
|
|
63
63
|
| `test/diagnostics.test.mjs` | 3 | 快照可选写入:不配置不写盘、突发合并、坏路径不致命 |
|
|
64
64
|
| `test/restart-notice.test.mjs` | 3 |
|
|
65
|
+
| `test/status-restore.test.mjs` | 7 | 跨重启恢复:快照可读、缺失/损坏/旧格式均当无历史、非法条目丢弃 |
|
|
65
66
|
| `test/status-path.test.mjs` | 6 | 诊断落盘路径:默认兜底、空串表示不写、非字符串当未设 |
|
|
66
67
|
| `test/skip-path.test.mjs` | 2 | 端到端:真实 TLS 失败走到记账,同类累加 |
|
|
67
68
|
| `test/skipped-summary.test.mjs` | 3 | 诊断收拢 skip 记录:按类归并、其它类别不入账 |
|
|
@@ -75,7 +76,7 @@
|
|
|
75
76
|
| `test/panel-blocked.test.mjs` | 7 | 冲突判定:审计兼容却被拒装、清单形状解析、给 allow 动作 |
|
|
76
77
|
| `test/panel-briefs.test.mjs` | 6 | 界面摘要:按名索引、只送可见行、缺数据不抛、不污染诊断 |
|
|
77
78
|
| `test/verdict-report.test.mjs` | 29 | why 与 list:判定、理由、环境摘要、自动扫描、分组、缺口、按形态给建议 | 缺导出判为需要重启;其它失败不误判;多次累积不覆盖 |
|
|
78
|
-
| **合计** | **
|
|
79
|
+
| **合计** | **259** | **全部通过** |
|
|
79
80
|
|
|
80
81
|
执行结果:
|
|
81
82
|
|
|
@@ -229,7 +230,7 @@
|
|
|
229
230
|
|
|
230
231
|
### 7.2 分文件执行结果
|
|
231
232
|
|
|
232
|
-
|
|
233
|
+
43 个测试文件逐个单独执行,每个都有用例且全部通过,不存在因导入失败而静默跳过的文件。用例数分布:compat.test.mjs 17、plugin.test.mjs 15、compat-env.test.mjs 14、classify.test.mjs 13,其余按职能分散,合计 259。
|
|
233
234
|
## 8 覆盖率实测
|
|
234
235
|
|
|
235
236
|
用 Node 自带的 `--experimental-test-coverage` 跑一遍,作为「分支有没有被真的走一遍」的证据。这不替代对断言质量的判断,只说明哪些代码从未被执行。
|
package/docs/quality-report.json
CHANGED
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
{
|
|
2
2
|
"standard": "GB/T 45717-2025 信息技术 软件测量 软件质量测量 自动化的源代码质量测度",
|
|
3
|
-
"generatedAt": "2026-10-29T17:
|
|
3
|
+
"generatedAt": "2026-10-29T17:11:56.729Z",
|
|
4
4
|
"scope": {
|
|
5
|
-
"files":
|
|
6
|
-
"totalLines":
|
|
7
|
-
"sourceLines":
|
|
8
|
-
"commentLines":
|
|
5
|
+
"files": 41,
|
|
6
|
+
"totalLines": 5538,
|
|
7
|
+
"sourceLines": 3984,
|
|
8
|
+
"commentLines": 1034
|
|
9
9
|
},
|
|
10
10
|
"measures": {
|
|
11
|
-
"commentDensity": 25.
|
|
12
|
-
"duplicationPercent": 4.
|
|
13
|
-
"functionCount":
|
|
11
|
+
"commentDensity": 25.95,
|
|
12
|
+
"duplicationPercent": 4.93,
|
|
13
|
+
"functionCount": 425,
|
|
14
14
|
"maxComplexity": 10,
|
|
15
15
|
"maxFunctionLines": 58,
|
|
16
16
|
"maxFileLines": 500,
|
|
17
|
-
"averageComplexity": 3.
|
|
17
|
+
"averageComplexity": 3.84
|
|
18
18
|
},
|
|
19
19
|
"limits": {
|
|
20
20
|
"maxComplexity": 10,
|
package/lib/diagnostics.js
CHANGED
|
@@ -1,81 +1,83 @@
|
|
|
1
|
-
// 诊断记录。
|
|
2
|
-
//
|
|
3
|
-
// 管三件事:各项修复计数、最近若干条事件、可选的快照文件。单独拎出来是因为组合根要
|
|
4
|
-
// 关心的是"怎么修",不该顺带管状态怎么记。
|
|
5
|
-
import { writeFileSync } from 'node:fs';
|
|
6
|
-
import { createSkipLog } from './skip-log.js';
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
...
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
1
|
+
// 诊断记录。
|
|
2
|
+
//
|
|
3
|
+
// 管三件事:各项修复计数、最近若干条事件、可选的快照文件。单独拎出来是因为组合根要
|
|
4
|
+
// 关心的是"怎么修",不该顺带管状态怎么记。
|
|
5
|
+
import { writeFileSync } from 'node:fs';
|
|
6
|
+
import { createSkipLog } from './skip-log.js';
|
|
7
|
+
import { restoreSkipped } from './status-restore.js';
|
|
8
|
+
|
|
9
|
+
// 最近事件留多少条给 /doctor 看
|
|
10
|
+
const MAX_LOG = 30;
|
|
11
|
+
|
|
12
|
+
// 突发重试时按这个间隔合并写盘
|
|
13
|
+
const WRITE_COALESCE_MS = 250;
|
|
14
|
+
|
|
15
|
+
export function createDiagnostics(input) {
|
|
16
|
+
const stats = {
|
|
17
|
+
startedAt: Date.now(),
|
|
18
|
+
modelRepairs: 0,
|
|
19
|
+
modelSkips: 0,
|
|
20
|
+
toolRepairs: 0,
|
|
21
|
+
connectionRepairs: 0,
|
|
22
|
+
versionRepairs: 0,
|
|
23
|
+
last: [],
|
|
24
|
+
};
|
|
25
|
+
const diagnostics = { versionScan: null };
|
|
26
|
+
const skipped = createSkipLog(restoreSkipped(input.statusFile));
|
|
27
|
+
let pending;
|
|
28
|
+
|
|
29
|
+
const snapshot = () => ({
|
|
30
|
+
skipped: skipped.read(),
|
|
31
|
+
pid: process.pid,
|
|
32
|
+
...stats,
|
|
33
|
+
diagnostics,
|
|
34
|
+
...(input.describe === undefined ? {} : { config: input.describe() }),
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
// 立刻写。诊断不是正确性数据,写失败就算了——磁盘满了也不该把请求修复带崩。
|
|
38
|
+
const writeNow = () => {
|
|
39
|
+
if (input.statusFile === '') return;
|
|
40
|
+
try {
|
|
41
|
+
writeFileSync(input.statusFile, `${JSON.stringify(snapshot(), null, 2)}\n`, 'utf8');
|
|
42
|
+
} catch {
|
|
43
|
+
// 只有诊断,丢了就丢了
|
|
44
|
+
}
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
// 合并写。record 每次修复都会调,而修复是会扎堆的——网络抖一下一秒能来好几次,
|
|
48
|
+
// 每次都同步写盘等于在 agent 最忙的时候卡住事件循环。
|
|
49
|
+
const scheduleWrite = () => {
|
|
50
|
+
if (input.statusFile === '' || pending !== undefined) return;
|
|
51
|
+
pending = setTimeout(() => {
|
|
52
|
+
pending = undefined;
|
|
53
|
+
writeNow();
|
|
54
|
+
}, WRITE_COALESCE_MS);
|
|
55
|
+
pending.unref?.();
|
|
56
|
+
input.trackTimer?.(pending);
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const record = (kind, detail) => {
|
|
60
|
+
stats.last.push({ at: new Date().toISOString(), kind, detail: String(detail).slice(0, 240) });
|
|
61
|
+
if (stats.last.length > MAX_LOG) stats.last.shift();
|
|
62
|
+
input.log('info', `request-doctor: ${kind}: ${detail}`);
|
|
63
|
+
// 没救回来的失败按类别归并:日志是流式的,翻起来费事。
|
|
64
|
+
if (kind === 'skip') skipped.add(detail);
|
|
65
|
+
scheduleWrite();
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
// 把扫描错误挂到当前这轮结果上,别让调用方去翻嵌套对象。
|
|
69
|
+
const scanError = (detail) => {
|
|
70
|
+
diagnostics.versionScan = { ...(diagnostics.versionScan ?? {}), ok: false, error: detail };
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
// 收尾时把攒着的写掉
|
|
74
|
+
const flush = () => {
|
|
75
|
+
if (pending !== undefined) {
|
|
76
|
+
clearTimeout(pending);
|
|
77
|
+
pending = undefined;
|
|
78
|
+
}
|
|
79
|
+
writeNow();
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
return { record, scheduleWrite, writeNow, flush, scanError, stats, skipped: () => skipped.read(), diagnostics, snapshot };
|
|
83
|
+
}
|
package/lib/skip-log.js
CHANGED
|
@@ -6,8 +6,12 @@
|
|
|
6
6
|
const MAX_KINDS = 12;
|
|
7
7
|
|
|
8
8
|
// 可读的汇总对象:add 记账,read 取回按次数排序的列表。
|
|
9
|
-
|
|
9
|
+
// 初始条目来自上次落盘的快照,让「没能修复的失败」跨重启可见。
|
|
10
|
+
export function createSkipLog(initial = []) {
|
|
10
11
|
const entries = new Map();
|
|
12
|
+
for (const entry of initial) {
|
|
13
|
+
if (entry !== null && typeof entry === 'object') entries.set(entry.kind, { ...entry });
|
|
14
|
+
}
|
|
11
15
|
return {
|
|
12
16
|
add(detail) {
|
|
13
17
|
const kind = String(detail).split(':')[0].trim();
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// 从上次的快照里恢复 skip 汇总。
|
|
2
|
+
//
|
|
3
|
+
// 诊断不是正确性数据:文件缺失、损坏、字段变了,都只当没有历史,
|
|
4
|
+
// 绝不能让插件因为读不到旧文件而启动失败。
|
|
5
|
+
import { readFileSync } from 'node:fs';
|
|
6
|
+
|
|
7
|
+
// @param {string} statusFile - 快照路径;空串表示不读。
|
|
8
|
+
// @returns {object[]} 上次记录的 skip 条目;读不到就是空数组。
|
|
9
|
+
export function restoreSkipped(statusFile) {
|
|
10
|
+
if (typeof statusFile !== 'string' || statusFile === '') return [];
|
|
11
|
+
let parsed;
|
|
12
|
+
try {
|
|
13
|
+
parsed = JSON.parse(readFileSync(statusFile, 'utf8'));
|
|
14
|
+
} catch {
|
|
15
|
+
return [];
|
|
16
|
+
}
|
|
17
|
+
const entries = parsed?.skipped;
|
|
18
|
+
if (!Array.isArray(entries)) return [];
|
|
19
|
+
return entries.filter((entry) => typeof entry?.kind === 'string' && typeof entry?.count === 'number');
|
|
20
|
+
}
|