@7n/test 0.8.0 → 0.9.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/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## [0.9.0] - 2026-06-28
4
+
5
+ ### Changed
6
+
7
+ - В нормальному циклі `assessNeed` (LLM-оцінка) повністю прибрано: coverage % < порогу є достатнім сигналом для генерації тестів. У bootstrap-режимі тепер використовується тільки `quickClassify` (локально, без LLM). Таким чином нульова кількість LLM-викликів до `assessNeed` у будь-якому сценарії.
8
+
3
9
  ## [0.8.0] - 2026-06-28
4
10
 
5
11
  ### Added
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@7n/test",
3
- "version": "0.8.0",
3
+ "version": "0.9.0",
4
4
  "description": "CLI-утиліта @7n/test",
5
5
  "keywords": [
6
6
  "7n",
package/src/run.mjs CHANGED
@@ -1,10 +1,10 @@
1
1
  /**
2
- * Main auto-test loop: coverage → assess → gen-tests → loop until max → mutation + fix.
2
+ * Main auto-test loop: coverage → gen-tests → loop until max → mutation + fix.
3
3
  *
4
4
  * Phase 1 (loop, max 5 iterations):
5
5
  * 1. Measure per-file line coverage via vitest+lcov.
6
- * 2. pi CLI assesses which uncovered files actually need tests.
7
- * 3. pi agent generates tests for those files.
6
+ * 2. Generate tests for all files below threshold — coverage % is the signal.
7
+ * 3. Bootstrap: when no tests exist, scan sources and quickClassify locally.
8
8
  * 4. Repeat until coverage maxes out or no improvement.
9
9
  *
10
10
  * Phase 2:
@@ -12,11 +12,13 @@
12
12
  * 6. Auto-fix survived mutants via pi agent.
13
13
  */
14
14
  import { measureCoveragePerFile, getUncoveredFiles, findSourceFiles } from './coverage-per-file.mjs'
15
- import { assessNeed } from './assess-need.mjs'
15
+ import { quickClassify } from './assess-need.mjs'
16
16
  import { generateTests } from './gen-tests.mjs'
17
17
  import { fixFailingTests } from './fix-tests.mjs'
18
18
  import { runCoverageSteps } from './coverage/coverage.mjs'
19
19
  import { withLock } from './scripts/utils/with-lock.mjs'
20
+ import { readFileSync } from 'node:fs'
21
+ import { join } from 'node:path'
20
22
 
21
23
  const MAX_ITERATIONS = 5
22
24
  const COVERAGE_THRESHOLD = 80
@@ -53,17 +55,26 @@ export async function runAutoTest(dir, opts = {}) {
53
55
  console.log('⚠ Vitest coverage не повернула даних — перевір налаштування vitest.')
54
56
  break
55
57
  }
56
- console.log(`\n── Bootstrap: ${sourceFiles.length} файлів без тестів оцінюю потребу ──\n`)
57
- const bootstrapFiles = sourceFiles.map(f => ({ file: f, pct: 0 }))
58
- const assessed = await assessNeed(bootstrapFiles, dir)
59
- const needsTests = assessed.filter(f => f.needsTests)
60
- if (needsTests.length === 0) {
61
- console.log('✓ LLM вирішила: жоден файл не потребує unit-тестів.')
58
+ console.log(`\n── Bootstrap: ${sourceFiles.length} джерелкласифікую локально ──\n`)
59
+
60
+ const bootstrapFiles = sourceFiles
61
+ .map(f => {
62
+ try {
63
+ const q = quickClassify(readFileSync(join(dir, f), 'utf8'))
64
+ if (q?.needsTests === false) return null
65
+ } catch { /* include on read error */ }
66
+ return { file: f, pct: 0 }
67
+ })
68
+ .filter(Boolean)
69
+
70
+ if (bootstrapFiles.length === 0) {
71
+ console.log('✓ Жоден файл не потребує unit-тестів (реекспорти/типи).')
62
72
  break
63
73
  }
64
- console.log(`\n→ Bootstrap-тести для (${needsTests.length}):`)
65
- for (const f of needsTests) console.log(` • ${f.file} ${f.reason}`)
66
- await generateTests(needsTests, dir)
74
+
75
+ console.log(`\n→ Bootstrap-тести для (${bootstrapFiles.length}):`)
76
+ for (const f of bootstrapFiles) console.log(` • ${f.file}`)
77
+ await generateTests(bootstrapFiles, dir)
67
78
  continue
68
79
  }
69
80
 
@@ -84,21 +95,12 @@ export async function runAutoTest(dir, opts = {}) {
84
95
  }
85
96
  prevUncoveredCount = uncovered.length
86
97
 
87
- console.log(`\n── Оцінюю ${uncovered.length} непокритих файлів (LLM) ──\n`)
88
- const assessed = await assessNeed(uncovered, dir)
89
- const needsTests = assessed.filter(f => f.needsTests)
90
-
91
- if (needsTests.length === 0) {
92
- console.log('✓ LLM вирішила: жоден непокритий файл не потребує unit-тестів.')
93
- break
94
- }
95
-
96
- console.log(`\n→ Потребують тестів (${needsTests.length}):`)
97
- for (const f of needsTests) {
98
- console.log(` • ${f.file} (${f.pct.toFixed(1)}%) — ${f.reason}`)
98
+ console.log(`\n Генерую тести для ${uncovered.length} файлів:`)
99
+ for (const f of uncovered) {
100
+ console.log(` • ${f.file} (${f.pct.toFixed(1)}%)`)
99
101
  }
100
102
 
101
- await generateTests(needsTests, dir)
103
+ await generateTests(uncovered, dir)
102
104
  }
103
105
 
104
106
  if (opts.noMutation) {