dsh-flakefinder 0.2.2 → 0.3.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 stardustlc
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md CHANGED
@@ -20,12 +20,25 @@ A DeepSeek Harness plugin for test stability: run tests repeatedly, classify fla
20
20
 
21
21
  Frameworks: vitest / jest / pytest (setup.cfg / pyproject / pytest.ini / tox.ini) / node:test, auto-detected in that order. JUnit XML entities and TAP SKIP directives are parsed.
22
22
 
23
+ ## Compatibility
24
+
25
+ Verified against `@deepseek-ai/dsh@0.1.1-rc.2` on 2026-08-26. Built for the cordis patch-bundle plugin model (`cordis.patch.yml` + `dsh.bundle.patch`). No runtime imports of `@deepseek-ai/*` internals.
26
+
23
27
  ## Install
24
28
 
25
29
  ```bash
26
30
  dsh plugin --profile web add dsh-flakefinder
27
31
  ```
28
32
 
33
+ ## Uninstall
34
+
35
+ ```bash
36
+ dsh plugin --profile web remove dsh-flakefinder
37
+ ```
38
+
39
+ Then restart the web service. To clean up fully, also remove the plugin entry from your profile `cordis.patch.yml` if you overrode it.
40
+
41
+
29
42
  ## Example
30
43
 
31
44
  ```text
@@ -47,4 +60,4 @@ pnpm test
47
60
  pnpm typecheck
48
61
  ```
49
62
 
50
- MIT
63
+ MIT
package/README.md CHANGED
@@ -26,6 +26,10 @@ DeepSeek Harness 测试稳定性插件:支持 vitest / jest / pytest / node:te
26
26
  - node:test:`node --test --test-reporter=tap`,解析 TAP(含 SKIP 指令归类)
27
27
  - `framework: auto` 按 vitest → jest → pytest → node:test 自动探测
28
28
 
29
+ ## 兼容性
30
+
31
+ 在 `@deepseek-ai/dsh@0.1.1-rc.2` 上验证(2026-08-26)。遵循 cordis 组合包补丁模型(`cordis.patch.yml` + `dsh.bundle.patch`),运行时不 import 任何 `@deepseek-ai/*` 内部模块。
32
+
29
33
  ## 安装
30
34
 
31
35
  ```bash
@@ -42,6 +46,15 @@ dsh plugin --profile web add dsh-flakefinder
42
46
  writeApproval: true
43
47
  ```
44
48
 
49
+ ## 卸载
50
+
51
+ ```bash
52
+ dsh plugin --profile web remove dsh-flakefinder
53
+ ```
54
+
55
+ 卸载后重启 Web 服务。如需彻底清理,可再手动删除自己 profile `cordis.patch.yml` 中覆盖的插件行。
56
+
57
+
45
58
  ## 使用示例
46
59
 
47
60
  ```text
@@ -100,4 +113,4 @@ Agent:
100
113
 
101
114
  ## License
102
115
 
103
- MIT
116
+ MIT
package/cordis.patch.yml CHANGED
@@ -10,7 +10,7 @@
10
10
  # writeApproval: true # quarantine/clear 是否走审批门(默认 true)
11
11
  # dataDir: '' # 历史存储目录(默认 DSH_HOME/.dsh-flakefinder)
12
12
  # quarantineFile: '' # 隔离清单(默认项目根 .flakefinder.json)
13
- pythonPath: '' # pytest 使用的 Python 解释器(默认 python/python3)
13
+ # pythonPath: '' # pytest 使用的 Python 解释器(默认 python/python3)
14
14
  #
15
15
  - insert:
16
16
  - id: flakefinder
package/lib/classify.js CHANGED
@@ -71,7 +71,10 @@ export function aggregate(runs) {
71
71
  }
72
72
  let verdict;
73
73
  const passRuns = records.filter(run => run.success).length;
74
- if (tests.every(test => test.verdict === 'skipped')) {
74
+ if (tests.length === 0) {
75
+ verdict = 'empty';
76
+ }
77
+ else if (tests.every(test => test.verdict === 'skipped')) {
75
78
  verdict = 'skipped';
76
79
  }
77
80
  else if (passRuns === records.length) {
@@ -83,6 +86,24 @@ export function aggregate(runs) {
83
86
  else {
84
87
  verdict = 'flaky';
85
88
  }
89
+ // 运行级 flaky 但无具体 flaky 用例:通常是某次运行崩溃(report=null)未产出报告。
90
+ // 补一条合成记录,避免 flaky 信号丢失(历史写入 verdict=flaky 但 flakyCount=0)。
91
+ if (verdict === 'flaky' && flakyCount === 0) {
92
+ const crashRuns = runs.filter(run => run.report === null).length;
93
+ if (crashRuns > 0) {
94
+ tests.push({
95
+ id: '(crash)',
96
+ file: '(crash)',
97
+ name: '运行崩溃(未产出报告)',
98
+ passes: 0,
99
+ failures: crashRuns,
100
+ skips: 0,
101
+ failureRate: 100,
102
+ verdict: 'flaky',
103
+ });
104
+ flakyCount += 1;
105
+ }
106
+ }
86
107
  tests.sort((a, b) => a.id.localeCompare(b.id));
87
108
  return {
88
109
  runs: records,
package/lib/index.d.ts CHANGED
@@ -11,6 +11,7 @@ import { type FlakeConfig } from './config.js';
11
11
  import { type SubprocessSpawnLike } from './runner.js';
12
12
  import { type FlakeToolDefinition } from './tools.js';
13
13
  /** cordis 服务注入:apply 里要用 ctx.subprocess 与 ctx.tools。 */
14
+ export declare const name = "flakefinder";
14
15
  export declare const inject: string[];
15
16
  /** 插件所需的最小 ctx 面。 */
16
17
  export interface FlakePluginContext {
package/lib/index.js CHANGED
@@ -12,6 +12,7 @@ import { createSubprocessRunner } from './runner.js';
12
12
  import { createStore } from './store.js';
13
13
  import { buildFlakeTools } from './tools.js';
14
14
  /** cordis 服务注入:apply 里要用 ctx.subprocess 与 ctx.tools。 */
15
+ export const name = 'flakefinder';
15
16
  export const inject = ['subprocess', 'tools'];
16
17
  /**
17
18
  * 插件入口:解析配置、封装执行器与存储、注册五个工具。
package/lib/tools.js CHANGED
@@ -301,7 +301,60 @@ export function buildFlakeTools(cfg, runner, store) {
301
301
  quarantine.gate = approvalGate('flaky_quarantine', '把测试用例写入 .flakefinder.json 隔离清单');
302
302
  clear.gate = approvalGate('flaky_clear', '从 .flakefinder.json 隔离清单移除测试用例');
303
303
  }
304
- return [detect, history, quarantine, clear, report];
304
+ const flakyHealth = {
305
+ name: 'flaky_health',
306
+ description: 'dsh-flakefinder 自检:验证 Python 解释器与 pytest 是否可用(pytest 框架必需),并汇总默认配置。遇到问题时先运行本工具定位。',
307
+ parameters: compileParameters({}),
308
+ output: {
309
+ schema: { type: 'object', additionalProperties: true },
310
+ render: (_args, value) => {
311
+ const rec = asRecord(value);
312
+ const checks = Array.isArray(rec.checks) ? rec.checks : [];
313
+ const lines = ['dsh-flakefinder 自检' + (rec.ok === true ? ':正常。' : ':发现问题。')];
314
+ for (const item of checks) {
315
+ const c = asRecord(item);
316
+ lines.push('- ' + c.name + ':' + (c.ok === true ? '✅' : '❌ ' + String(c.detail ?? '')));
317
+ }
318
+ return [{ type: 'text', text: lines.join('\n') }];
319
+ },
320
+ },
321
+ async execute() {
322
+ const checks = [];
323
+ let ok = true;
324
+ try {
325
+ const result = await runner.run([cfg.pythonPath, '--version'], { timeoutMs: 15000 });
326
+ const version = (result.stdout + result.stderr).split(/\r?\n/)[0]?.trim() ?? '';
327
+ if (result.exitCode === 0)
328
+ checks.push({ name: 'python', ok: true, detail: version + '(' + cfg.pythonPath + ')' });
329
+ else {
330
+ ok = false;
331
+ checks.push({ name: 'python', ok: false, detail: '退出码 ' + String(result.exitCode) });
332
+ }
333
+ }
334
+ catch (error) {
335
+ ok = false;
336
+ checks.push({ name: 'python', ok: false, detail: error instanceof Error ? error.message : String(error) });
337
+ }
338
+ try {
339
+ const result = await runner.run([cfg.pythonPath, '-m', 'pytest', '--version'], { timeoutMs: 20000 });
340
+ const version = (result.stdout + result.stderr).split(/\r?\n/)[0]?.trim() ?? '';
341
+ if (result.exitCode === 0)
342
+ checks.push({ name: 'pytest', ok: true, detail: version });
343
+ else {
344
+ ok = false;
345
+ checks.push({ name: 'pytest', ok: false, detail: '未安装或不可用(pip install pytest)' });
346
+ }
347
+ }
348
+ catch (error) {
349
+ ok = false;
350
+ checks.push({ name: 'pytest', ok: false, detail: error instanceof Error ? error.message : String(error) });
351
+ }
352
+ checks.push({ name: '默认配置', ok: true, detail: 'defaultRuns=' + cfg.defaultRuns + ', timeoutMs=' + cfg.timeoutMs });
353
+ return { ok, plugin: 'dsh-flakefinder', checks };
354
+ },
355
+ timeoutMs: 40000,
356
+ };
357
+ return [detect, history, quarantine, clear, report, flakyHealth];
305
358
  }
306
359
  function runsTimeout(cfg) {
307
360
  return Math.min(10 * 60 * 1000, cfg.maxRuns * cfg.timeoutMs + 15000);
package/package.json CHANGED
@@ -1,61 +1,60 @@
1
- {
2
- "name": "dsh-flakefinder",
3
- "version": "0.2.2",
4
- "description": "DSH 测试稳定性插件:flaky_detect/history/quarantine/clear/report 五工具,支持 vitest/jest/pytest/node:test,重复运行测试并识别不稳定用例,JUnit/TAP 完整解析,JSON 历史与隔离清单,零运行时依赖。",
5
- "type": "module",
6
- "main": "lib/index.js",
7
- "types": "lib/index.d.ts",
8
- "exports": {
9
- ".": {
10
- "types": "./lib/index.d.ts",
11
- "default": "./lib/index.js"
12
- },
13
- "./cordis.patch.yml": "./cordis.patch.yml",
14
- "./package.json": "./package.json"
15
- },
16
- "files": [
17
- "lib",
18
- "cordis.patch.yml",
19
- "README.md",
20
- "README.en.md"
21
- ],
22
- "scripts": {
23
- "build": "tsc -p tsconfig.json",
24
- "prepare": "tsc -p tsconfig.json",
25
- "typecheck": "tsc -p tsconfig.json --noEmit",
26
- "test": "pnpm run build && node --test \"test/*.test.mjs\"",
27
- "prepublishOnly": "pnpm run build"
28
- },
29
- "dsh": {
30
- "bundle": {
31
- "patch": "./cordis.patch.yml"
32
- }
33
- },
34
- "keywords": [
35
- "dsh",
36
- "deepseek-harness",
37
- "plugin",
38
- "testing",
39
- "flaky-test",
40
- "quality",
41
- "dsh-plugin"
42
- ],
43
- "license": "MIT",
44
- "engines": {
45
- "node": ">=22.13"
46
- },
47
- "devDependencies": {
48
- "@types/node": "^24.0.0",
49
- "typescript": "^5.6.0"
50
- },
51
- "repository": {
52
- "type": "git",
53
- "url": "git+https://github.com/STARDUSTLC666/dsh-flakefinder.git"
54
- },
55
- "bugs": {
56
- "url": "https://github.com/STARDUSTLC666/dsh-flakefinder/issues"
57
- },
58
- "homepage": "https://github.com/STARDUSTLC666/dsh-flakefinder#readme",
59
- "author": "stardustlc",
60
- "packageManager": "pnpm@11.7.0"
61
- }
1
+ {
2
+ "name": "dsh-flakefinder",
3
+ "version": "0.3.0",
4
+ "description": "DSH 测试稳定性插件:flaky_detect/history/quarantine/clear/report 五工具,支持 vitest/jest/pytest/node:test,重复运行测试并识别不稳定用例,JUnit/TAP 完整解析,JSON 历史与隔离清单,零运行时依赖。",
5
+ "type": "module",
6
+ "main": "lib/index.js",
7
+ "types": "lib/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "types": "./lib/index.d.ts",
11
+ "default": "./lib/index.js"
12
+ },
13
+ "./cordis.patch.yml": "./cordis.patch.yml",
14
+ "./package.json": "./package.json"
15
+ },
16
+ "files": [
17
+ "lib",
18
+ "cordis.patch.yml",
19
+ "README.md",
20
+ "README.en.md"
21
+ ],
22
+ "scripts": {
23
+ "build": "tsc -p tsconfig.json",
24
+ "typecheck": "tsc -p tsconfig.json --noEmit",
25
+ "test": "pnpm run build && node --test \"test/*.test.mjs\"",
26
+ "prepublishOnly": "pnpm run build"
27
+ },
28
+ "dsh": {
29
+ "bundle": {
30
+ "patch": "./cordis.patch.yml"
31
+ }
32
+ },
33
+ "keywords": [
34
+ "dsh",
35
+ "deepseek-harness",
36
+ "plugin",
37
+ "testing",
38
+ "flaky-test",
39
+ "quality",
40
+ "dsh-plugin"
41
+ ],
42
+ "license": "MIT",
43
+ "engines": {
44
+ "node": ">=22"
45
+ },
46
+ "devDependencies": {
47
+ "@types/node": "^24.0.0",
48
+ "typescript": "^5.6.0"
49
+ },
50
+ "repository": {
51
+ "type": "git",
52
+ "url": "git+https://github.com/STARDUSTLC666/dsh-flakefinder.git"
53
+ },
54
+ "bugs": {
55
+ "url": "https://github.com/STARDUSTLC666/dsh-flakefinder/issues"
56
+ },
57
+ "homepage": "https://github.com/STARDUSTLC666/dsh-flakefinder#readme",
58
+ "author": "stardustlc",
59
+ "packageManager": "pnpm@11.7.0"
60
+ }