@aiready/testability 0.6.0 → 0.6.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/.aiready/aiready-report-20260314-225228.json +270 -0
- package/.turbo/turbo-build.log +8 -8
- package/.turbo/turbo-test.log +9 -8
- package/dist/index.d.mts +3 -2
- package/dist/index.d.ts +3 -2
- package/dist/index.js +19 -20
- package/dist/index.mjs +21 -22
- package/package.json +2 -2
- package/src/__tests__/provider.test.ts +1 -1
- package/src/__tests__/types.test.ts +72 -0
- package/src/analyzer.ts +3 -1
- package/src/provider.ts +20 -30
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
{
|
|
2
|
+
"summary": {
|
|
3
|
+
"totalIssues": 0,
|
|
4
|
+
"criticalIssues": 0,
|
|
5
|
+
"majorIssues": 0,
|
|
6
|
+
"totalFiles": 0,
|
|
7
|
+
"toolsRun": ["testability-index"],
|
|
8
|
+
"executionTime": 41,
|
|
9
|
+
"config": {
|
|
10
|
+
"scan": {
|
|
11
|
+
"tools": ["testability-index"]
|
|
12
|
+
},
|
|
13
|
+
"tools": {
|
|
14
|
+
"testability-index": {}
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"toolConfigs": {
|
|
18
|
+
"testability-index": {}
|
|
19
|
+
}
|
|
20
|
+
},
|
|
21
|
+
"testability-index": {
|
|
22
|
+
"results": [],
|
|
23
|
+
"summary": {
|
|
24
|
+
"score": 47,
|
|
25
|
+
"sourceFiles": 6,
|
|
26
|
+
"testFiles": 4,
|
|
27
|
+
"coverageRatio": 0.67,
|
|
28
|
+
"rating": "poor",
|
|
29
|
+
"aiChangeSafetyRating": "high-risk",
|
|
30
|
+
"dimensions": {
|
|
31
|
+
"testCoverageRatio": 67,
|
|
32
|
+
"purityScore": 50,
|
|
33
|
+
"dependencyInjectionScore": 0,
|
|
34
|
+
"interfaceFocusScore": 73,
|
|
35
|
+
"observabilityScore": 50
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"metadata": {
|
|
39
|
+
"toolName": "testability-index",
|
|
40
|
+
"version": "0.2.5",
|
|
41
|
+
"timestamp": "2026-03-14T11:52:28.204Z",
|
|
42
|
+
"rawData": {
|
|
43
|
+
"sourceFiles": 6,
|
|
44
|
+
"testFiles": 4,
|
|
45
|
+
"pureFunctions": 1,
|
|
46
|
+
"totalFunctions": 2,
|
|
47
|
+
"injectionPatterns": 0,
|
|
48
|
+
"totalClasses": 0,
|
|
49
|
+
"bloatedInterfaces": 1,
|
|
50
|
+
"totalInterfaces": 3,
|
|
51
|
+
"externalStateMutations": 1,
|
|
52
|
+
"hasTestFramework": true
|
|
53
|
+
},
|
|
54
|
+
"config": {}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"testability": {
|
|
58
|
+
"results": [],
|
|
59
|
+
"summary": {
|
|
60
|
+
"score": 47,
|
|
61
|
+
"sourceFiles": 6,
|
|
62
|
+
"testFiles": 4,
|
|
63
|
+
"coverageRatio": 0.67,
|
|
64
|
+
"rating": "poor",
|
|
65
|
+
"aiChangeSafetyRating": "high-risk",
|
|
66
|
+
"dimensions": {
|
|
67
|
+
"testCoverageRatio": 67,
|
|
68
|
+
"purityScore": 50,
|
|
69
|
+
"dependencyInjectionScore": 0,
|
|
70
|
+
"interfaceFocusScore": 73,
|
|
71
|
+
"observabilityScore": 50
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"metadata": {
|
|
75
|
+
"toolName": "testability-index",
|
|
76
|
+
"version": "0.2.5",
|
|
77
|
+
"timestamp": "2026-03-14T11:52:28.204Z",
|
|
78
|
+
"rawData": {
|
|
79
|
+
"sourceFiles": 6,
|
|
80
|
+
"testFiles": 4,
|
|
81
|
+
"pureFunctions": 1,
|
|
82
|
+
"totalFunctions": 2,
|
|
83
|
+
"injectionPatterns": 0,
|
|
84
|
+
"totalClasses": 0,
|
|
85
|
+
"bloatedInterfaces": 1,
|
|
86
|
+
"totalInterfaces": 3,
|
|
87
|
+
"externalStateMutations": 1,
|
|
88
|
+
"hasTestFramework": true
|
|
89
|
+
},
|
|
90
|
+
"config": {}
|
|
91
|
+
}
|
|
92
|
+
},
|
|
93
|
+
"tests": {
|
|
94
|
+
"results": [],
|
|
95
|
+
"summary": {
|
|
96
|
+
"score": 47,
|
|
97
|
+
"sourceFiles": 6,
|
|
98
|
+
"testFiles": 4,
|
|
99
|
+
"coverageRatio": 0.67,
|
|
100
|
+
"rating": "poor",
|
|
101
|
+
"aiChangeSafetyRating": "high-risk",
|
|
102
|
+
"dimensions": {
|
|
103
|
+
"testCoverageRatio": 67,
|
|
104
|
+
"purityScore": 50,
|
|
105
|
+
"dependencyInjectionScore": 0,
|
|
106
|
+
"interfaceFocusScore": 73,
|
|
107
|
+
"observabilityScore": 50
|
|
108
|
+
}
|
|
109
|
+
},
|
|
110
|
+
"metadata": {
|
|
111
|
+
"toolName": "testability-index",
|
|
112
|
+
"version": "0.2.5",
|
|
113
|
+
"timestamp": "2026-03-14T11:52:28.204Z",
|
|
114
|
+
"rawData": {
|
|
115
|
+
"sourceFiles": 6,
|
|
116
|
+
"testFiles": 4,
|
|
117
|
+
"pureFunctions": 1,
|
|
118
|
+
"totalFunctions": 2,
|
|
119
|
+
"injectionPatterns": 0,
|
|
120
|
+
"totalClasses": 0,
|
|
121
|
+
"bloatedInterfaces": 1,
|
|
122
|
+
"totalInterfaces": 3,
|
|
123
|
+
"externalStateMutations": 1,
|
|
124
|
+
"hasTestFramework": true
|
|
125
|
+
},
|
|
126
|
+
"config": {}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
"verification": {
|
|
130
|
+
"results": [],
|
|
131
|
+
"summary": {
|
|
132
|
+
"score": 47,
|
|
133
|
+
"sourceFiles": 6,
|
|
134
|
+
"testFiles": 4,
|
|
135
|
+
"coverageRatio": 0.67,
|
|
136
|
+
"rating": "poor",
|
|
137
|
+
"aiChangeSafetyRating": "high-risk",
|
|
138
|
+
"dimensions": {
|
|
139
|
+
"testCoverageRatio": 67,
|
|
140
|
+
"purityScore": 50,
|
|
141
|
+
"dependencyInjectionScore": 0,
|
|
142
|
+
"interfaceFocusScore": 73,
|
|
143
|
+
"observabilityScore": 50
|
|
144
|
+
}
|
|
145
|
+
},
|
|
146
|
+
"metadata": {
|
|
147
|
+
"toolName": "testability-index",
|
|
148
|
+
"version": "0.2.5",
|
|
149
|
+
"timestamp": "2026-03-14T11:52:28.204Z",
|
|
150
|
+
"rawData": {
|
|
151
|
+
"sourceFiles": 6,
|
|
152
|
+
"testFiles": 4,
|
|
153
|
+
"pureFunctions": 1,
|
|
154
|
+
"totalFunctions": 2,
|
|
155
|
+
"injectionPatterns": 0,
|
|
156
|
+
"totalClasses": 0,
|
|
157
|
+
"bloatedInterfaces": 1,
|
|
158
|
+
"totalInterfaces": 3,
|
|
159
|
+
"externalStateMutations": 1,
|
|
160
|
+
"hasTestFramework": true
|
|
161
|
+
},
|
|
162
|
+
"config": {}
|
|
163
|
+
}
|
|
164
|
+
},
|
|
165
|
+
"testabilityIndex": {
|
|
166
|
+
"results": [],
|
|
167
|
+
"summary": {
|
|
168
|
+
"score": 47,
|
|
169
|
+
"sourceFiles": 6,
|
|
170
|
+
"testFiles": 4,
|
|
171
|
+
"coverageRatio": 0.67,
|
|
172
|
+
"rating": "poor",
|
|
173
|
+
"aiChangeSafetyRating": "high-risk",
|
|
174
|
+
"dimensions": {
|
|
175
|
+
"testCoverageRatio": 67,
|
|
176
|
+
"purityScore": 50,
|
|
177
|
+
"dependencyInjectionScore": 0,
|
|
178
|
+
"interfaceFocusScore": 73,
|
|
179
|
+
"observabilityScore": 50
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
"metadata": {
|
|
183
|
+
"toolName": "testability-index",
|
|
184
|
+
"version": "0.2.5",
|
|
185
|
+
"timestamp": "2026-03-14T11:52:28.204Z",
|
|
186
|
+
"rawData": {
|
|
187
|
+
"sourceFiles": 6,
|
|
188
|
+
"testFiles": 4,
|
|
189
|
+
"pureFunctions": 1,
|
|
190
|
+
"totalFunctions": 2,
|
|
191
|
+
"injectionPatterns": 0,
|
|
192
|
+
"totalClasses": 0,
|
|
193
|
+
"bloatedInterfaces": 1,
|
|
194
|
+
"totalInterfaces": 3,
|
|
195
|
+
"externalStateMutations": 1,
|
|
196
|
+
"hasTestFramework": true
|
|
197
|
+
},
|
|
198
|
+
"config": {}
|
|
199
|
+
}
|
|
200
|
+
},
|
|
201
|
+
"results": [],
|
|
202
|
+
"scoring": {
|
|
203
|
+
"overall": 47,
|
|
204
|
+
"rating": "Needs Work",
|
|
205
|
+
"timestamp": "2026-03-14T11:52:28.205Z",
|
|
206
|
+
"toolsUsed": ["testability-index"],
|
|
207
|
+
"breakdown": [
|
|
208
|
+
{
|
|
209
|
+
"toolName": "testability-index",
|
|
210
|
+
"score": 47,
|
|
211
|
+
"rawMetrics": {
|
|
212
|
+
"sourceFiles": 6,
|
|
213
|
+
"testFiles": 4,
|
|
214
|
+
"pureFunctions": 1,
|
|
215
|
+
"totalFunctions": 2,
|
|
216
|
+
"injectionPatterns": 0,
|
|
217
|
+
"totalClasses": 0,
|
|
218
|
+
"bloatedInterfaces": 1,
|
|
219
|
+
"totalInterfaces": 3,
|
|
220
|
+
"externalStateMutations": 1,
|
|
221
|
+
"hasTestFramework": true,
|
|
222
|
+
"rating": "poor",
|
|
223
|
+
"aiChangeSafetyRating": "high-risk",
|
|
224
|
+
"coverageRatio": 0.67
|
|
225
|
+
},
|
|
226
|
+
"factors": [
|
|
227
|
+
{
|
|
228
|
+
"name": "Test Coverage",
|
|
229
|
+
"impact": 17,
|
|
230
|
+
"description": "4 test files / 6 source files (67%)"
|
|
231
|
+
},
|
|
232
|
+
{
|
|
233
|
+
"name": "Function Purity",
|
|
234
|
+
"impact": 0,
|
|
235
|
+
"description": "1/2 functions are pure"
|
|
236
|
+
},
|
|
237
|
+
{
|
|
238
|
+
"name": "Dependency Injection",
|
|
239
|
+
"impact": -50,
|
|
240
|
+
"description": "0/0 classes use DI"
|
|
241
|
+
},
|
|
242
|
+
{
|
|
243
|
+
"name": "Interface Focus",
|
|
244
|
+
"impact": 23,
|
|
245
|
+
"description": "1 interfaces have >10 methods"
|
|
246
|
+
},
|
|
247
|
+
{
|
|
248
|
+
"name": "Observability",
|
|
249
|
+
"impact": 0,
|
|
250
|
+
"description": "1 functions mutate external state"
|
|
251
|
+
}
|
|
252
|
+
],
|
|
253
|
+
"recommendations": []
|
|
254
|
+
}
|
|
255
|
+
],
|
|
256
|
+
"calculation": {
|
|
257
|
+
"formula": "[(47 × 10)] / 10 = 47",
|
|
258
|
+
"weights": {
|
|
259
|
+
"testability-index": 10
|
|
260
|
+
},
|
|
261
|
+
"normalized": "[(47 × 10)] / 10 = 47"
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
"repository": {
|
|
265
|
+
"url": "https://github.com/caopengau/aiready.git",
|
|
266
|
+
"branch": "main",
|
|
267
|
+
"commit": "3be4936e1c8aa50d6324b014ca8fcf791c6dcfaf",
|
|
268
|
+
"author": "caopengau@gmail.com"
|
|
269
|
+
}
|
|
270
|
+
}
|
package/.turbo/turbo-build.log
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/testability@0.
|
|
3
|
+
> @aiready/testability@0.6.2 build /Users/pengcao/projects/aiready/packages/testability
|
|
4
4
|
> tsup src/index.ts src/cli.ts --format cjs,esm --dts
|
|
5
5
|
|
|
6
6
|
[34mCLI[39m Building entry: src/cli.ts, src/index.ts
|
|
@@ -10,15 +10,15 @@
|
|
|
10
10
|
[34mCJS[39m Build start
|
|
11
11
|
[34mESM[39m Build start
|
|
12
12
|
[32mCJS[39m [1mdist/cli.js [22m[32m15.61 KB[39m
|
|
13
|
-
[32mCJS[39m [1mdist/index.js [22m[32m10.
|
|
14
|
-
[32mCJS[39m ⚡️ Build success in
|
|
13
|
+
[32mCJS[39m [1mdist/index.js [22m[32m10.61 KB[39m
|
|
14
|
+
[32mCJS[39m ⚡️ Build success in 82ms
|
|
15
15
|
[32mESM[39m [1mdist/cli.mjs [22m[32m5.75 KB[39m
|
|
16
|
-
[32mESM[39m [1mdist/index.mjs [22m[32m1.28 KB[39m
|
|
17
16
|
[32mESM[39m [1mdist/chunk-QOIBI5E7.mjs [22m[32m8.31 KB[39m
|
|
18
|
-
[32mESM[39m
|
|
17
|
+
[32mESM[39m [1mdist/index.mjs [22m[32m1.17 KB[39m
|
|
18
|
+
[32mESM[39m ⚡️ Build success in 83ms
|
|
19
19
|
DTS Build start
|
|
20
|
-
DTS ⚡️ Build success in
|
|
20
|
+
DTS ⚡️ Build success in 4018ms
|
|
21
21
|
DTS dist/cli.d.ts 20.00 B
|
|
22
|
-
DTS dist/index.d.ts 2.
|
|
22
|
+
DTS dist/index.d.ts 2.45 KB
|
|
23
23
|
DTS dist/cli.d.mts 20.00 B
|
|
24
|
-
DTS dist/index.d.mts 2.
|
|
24
|
+
DTS dist/index.d.mts 2.45 KB
|
package/.turbo/turbo-test.log
CHANGED
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
|
|
2
2
|
|
|
3
|
-
> @aiready/testability@0.
|
|
3
|
+
> @aiready/testability@0.6.1 test /Users/pengcao/projects/aiready/packages/testability
|
|
4
4
|
> vitest run
|
|
5
5
|
|
|
6
6
|
[?25l
|
|
7
7
|
[1m[46m RUN [49m[22m [36mv4.0.18 [39m[90m/Users/pengcao/projects/aiready/packages/testability[39m
|
|
8
8
|
|
|
9
|
-
[32m✓[39m src/__tests__/
|
|
9
|
+
[32m✓[39m src/__tests__/types.test.ts [2m([22m[2m4 tests[22m[2m)[22m[32m 7[2mms[22m[39m
|
|
10
10
|
[32m✓[39m src/__tests__/provider.test.ts [2m([22m[2m2 tests[22m[2m)[22m[32m 3[2mms[22m[39m
|
|
11
|
-
[32m✓[39m src/__tests__/
|
|
12
|
-
|
|
11
|
+
[32m✓[39m src/__tests__/scoring.test.ts [2m([22m[2m3 tests[22m[2m)[22m[32m 2[2mms[22m[39m
|
|
12
|
+
[32m✓[39m src/__tests__/analyzer.test.ts [2m([22m[2m5 tests[22m[2m)[22m[33m 1716[2mms[22m[39m
|
|
13
|
+
[33m[2m✓[22m[39m detects test frameworks in multiple languages [33m 1688[2mms[22m[39m
|
|
13
14
|
|
|
14
|
-
[2m Test Files [22m [1m[
|
|
15
|
-
[2m Tests [22m [1m[
|
|
16
|
-
[2m Start at [22m
|
|
17
|
-
[2m Duration [22m
|
|
15
|
+
[2m Test Files [22m [1m[32m4 passed[39m[22m[90m (4)[39m
|
|
16
|
+
[2m Tests [22m [1m[32m14 passed[39m[22m[90m (14)[39m
|
|
17
|
+
[2m Start at [22m 02:00:17
|
|
18
|
+
[2m Duration [22m 4.74s[2m (transform 1.12s, setup 0ms, import 6.16s, tests 1.73s, environment 0ms)[22m
|
|
18
19
|
|
|
19
20
|
[?25h
|
package/dist/index.d.mts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _aiready_core from '@aiready/core';
|
|
2
|
+
import { Issue, IssueType, ToolScoringOutput } from '@aiready/core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Testability Tool Provider
|
|
5
6
|
*/
|
|
6
|
-
declare const TestabilityProvider: ToolProvider;
|
|
7
|
+
declare const TestabilityProvider: _aiready_core.ToolProvider;
|
|
7
8
|
|
|
8
9
|
interface TestabilityOptions {
|
|
9
10
|
/** Root directory to scan */
|
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import
|
|
1
|
+
import * as _aiready_core from '@aiready/core';
|
|
2
|
+
import { Issue, IssueType, ToolScoringOutput } from '@aiready/core';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Testability Tool Provider
|
|
5
6
|
*/
|
|
6
|
-
declare const TestabilityProvider: ToolProvider;
|
|
7
|
+
declare const TestabilityProvider: _aiready_core.ToolProvider;
|
|
7
8
|
|
|
8
9
|
interface TestabilityOptions {
|
|
9
10
|
/** Root directory to scan */
|
package/dist/index.js
CHANGED
|
@@ -282,39 +282,38 @@ function calculateTestabilityScore(report) {
|
|
|
282
282
|
}
|
|
283
283
|
|
|
284
284
|
// src/provider.ts
|
|
285
|
-
var TestabilityProvider = {
|
|
285
|
+
var TestabilityProvider = (0, import_core3.createProvider)({
|
|
286
286
|
id: import_core3.ToolName.TestabilityIndex,
|
|
287
287
|
alias: ["testability", "tests", "verification"],
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
288
|
+
version: "0.2.5",
|
|
289
|
+
defaultWeight: 10,
|
|
290
|
+
async analyzeReport(options) {
|
|
291
|
+
return analyzeTestability(options);
|
|
292
|
+
},
|
|
293
|
+
getResults(report) {
|
|
294
|
+
return report.issues.map((issue) => ({
|
|
295
|
+
fileName: issue.location.file,
|
|
296
|
+
issues: [issue],
|
|
293
297
|
metrics: {
|
|
294
298
|
testabilityScore: report.summary.score
|
|
295
299
|
}
|
|
296
300
|
}));
|
|
297
|
-
return import_core3.SpokeOutputSchema.parse({
|
|
298
|
-
results,
|
|
299
|
-
summary: report.summary,
|
|
300
|
-
metadata: {
|
|
301
|
-
toolName: import_core3.ToolName.TestabilityIndex,
|
|
302
|
-
version: "0.2.5",
|
|
303
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
304
|
-
rawData: report.rawData
|
|
305
|
-
}
|
|
306
|
-
});
|
|
307
301
|
},
|
|
308
|
-
|
|
302
|
+
getSummary(report) {
|
|
303
|
+
return report.summary;
|
|
304
|
+
},
|
|
305
|
+
getMetadata(report) {
|
|
306
|
+
return { rawData: report.rawData };
|
|
307
|
+
},
|
|
308
|
+
score(output) {
|
|
309
309
|
const report = {
|
|
310
310
|
summary: output.summary,
|
|
311
311
|
rawData: output.metadata.rawData,
|
|
312
312
|
recommendations: output.summary.recommendations || []
|
|
313
313
|
};
|
|
314
314
|
return calculateTestabilityScore(report);
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
};
|
|
315
|
+
}
|
|
316
|
+
});
|
|
318
317
|
|
|
319
318
|
// src/index.ts
|
|
320
319
|
import_core4.ToolRegistry.register(TestabilityProvider);
|
package/dist/index.mjs
CHANGED
|
@@ -8,42 +8,41 @@ import { ToolRegistry } from "@aiready/core";
|
|
|
8
8
|
|
|
9
9
|
// src/provider.ts
|
|
10
10
|
import {
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
createProvider,
|
|
12
|
+
ToolName
|
|
13
13
|
} from "@aiready/core";
|
|
14
|
-
var TestabilityProvider = {
|
|
14
|
+
var TestabilityProvider = createProvider({
|
|
15
15
|
id: ToolName.TestabilityIndex,
|
|
16
16
|
alias: ["testability", "tests", "verification"],
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
version: "0.2.5",
|
|
18
|
+
defaultWeight: 10,
|
|
19
|
+
async analyzeReport(options) {
|
|
20
|
+
return analyzeTestability(options);
|
|
21
|
+
},
|
|
22
|
+
getResults(report) {
|
|
23
|
+
return report.issues.map((issue) => ({
|
|
24
|
+
fileName: issue.location.file,
|
|
25
|
+
issues: [issue],
|
|
22
26
|
metrics: {
|
|
23
27
|
testabilityScore: report.summary.score
|
|
24
28
|
}
|
|
25
29
|
}));
|
|
26
|
-
return SpokeOutputSchema.parse({
|
|
27
|
-
results,
|
|
28
|
-
summary: report.summary,
|
|
29
|
-
metadata: {
|
|
30
|
-
toolName: ToolName.TestabilityIndex,
|
|
31
|
-
version: "0.2.5",
|
|
32
|
-
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
33
|
-
rawData: report.rawData
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
30
|
},
|
|
37
|
-
|
|
31
|
+
getSummary(report) {
|
|
32
|
+
return report.summary;
|
|
33
|
+
},
|
|
34
|
+
getMetadata(report) {
|
|
35
|
+
return { rawData: report.rawData };
|
|
36
|
+
},
|
|
37
|
+
score(output) {
|
|
38
38
|
const report = {
|
|
39
39
|
summary: output.summary,
|
|
40
40
|
rawData: output.metadata.rawData,
|
|
41
41
|
recommendations: output.summary.recommendations || []
|
|
42
42
|
};
|
|
43
43
|
return calculateTestabilityScore(report);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
};
|
|
44
|
+
}
|
|
45
|
+
});
|
|
47
46
|
|
|
48
47
|
// src/index.ts
|
|
49
48
|
ToolRegistry.register(TestabilityProvider);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aiready/testability",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.2",
|
|
4
4
|
"description": "Measures how safely and verifiably AI-generated changes can be made to your codebase",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.mjs",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"chalk": "^5.3.0",
|
|
41
41
|
"commander": "^14.0.0",
|
|
42
42
|
"glob": "^13.0.0",
|
|
43
|
-
"@aiready/core": "0.23.
|
|
43
|
+
"@aiready/core": "0.23.2"
|
|
44
44
|
},
|
|
45
45
|
"devDependencies": {
|
|
46
46
|
"@types/node": "^24.0.0",
|
|
@@ -18,7 +18,7 @@ describe('Testability Provider', () => {
|
|
|
18
18
|
const output = await TestabilityProvider.analyze({ rootDir: '.' });
|
|
19
19
|
|
|
20
20
|
expect(output.summary.score).toBe(90);
|
|
21
|
-
expect(output.metadata
|
|
21
|
+
expect(output.metadata!.toolName).toBe('testability-index');
|
|
22
22
|
});
|
|
23
23
|
|
|
24
24
|
it('should score an output', () => {
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
import { describe, it, expect } from 'vitest';
|
|
2
|
+
import type { TestabilityOptions, TestabilityReport } from '../types';
|
|
3
|
+
|
|
4
|
+
describe('Testability Types', () => {
|
|
5
|
+
describe('TestabilityOptions', () => {
|
|
6
|
+
it('should allow required rootDir', () => {
|
|
7
|
+
const options: TestabilityOptions = {
|
|
8
|
+
rootDir: '/test/project',
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
expect(options.rootDir).toBe('/test/project');
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
it('should allow optional minCoverageRatio', () => {
|
|
15
|
+
const options: TestabilityOptions = {
|
|
16
|
+
rootDir: '/test/project',
|
|
17
|
+
minCoverageRatio: 0.5,
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
expect(options.minCoverageRatio).toBe(0.5);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('should allow custom test patterns', () => {
|
|
24
|
+
const options: TestabilityOptions = {
|
|
25
|
+
rootDir: '/test/project',
|
|
26
|
+
testPatterns: ['**/*.test.ts', '**/*.spec.ts', '**/*.e2e.ts'],
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
expect(options.testPatterns).toHaveLength(3);
|
|
30
|
+
});
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
describe('TestabilityReport', () => {
|
|
34
|
+
it('should structure summary correctly', () => {
|
|
35
|
+
const report: TestabilityReport = {
|
|
36
|
+
summary: {
|
|
37
|
+
sourceFiles: 100,
|
|
38
|
+
testFiles: 30,
|
|
39
|
+
coverageRatio: 0.3,
|
|
40
|
+
score: 60,
|
|
41
|
+
rating: 'moderate',
|
|
42
|
+
aiChangeSafetyRating: 'moderate-risk',
|
|
43
|
+
dimensions: {
|
|
44
|
+
testCoverageRatio: 30,
|
|
45
|
+
purityScore: 70,
|
|
46
|
+
dependencyInjectionScore: 50,
|
|
47
|
+
interfaceFocusScore: 80,
|
|
48
|
+
observabilityScore: 60,
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
issues: [],
|
|
52
|
+
rawData: {
|
|
53
|
+
sourceFiles: 100,
|
|
54
|
+
testFiles: 30,
|
|
55
|
+
pureFunctions: 80,
|
|
56
|
+
totalFunctions: 100,
|
|
57
|
+
injectionPatterns: 5,
|
|
58
|
+
totalClasses: 20,
|
|
59
|
+
bloatedInterfaces: 10,
|
|
60
|
+
totalInterfaces: 50,
|
|
61
|
+
externalStateMutations: 15,
|
|
62
|
+
hasTestFramework: true,
|
|
63
|
+
},
|
|
64
|
+
recommendations: ['Add more tests to reach 30% coverage'],
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
expect(report.summary.sourceFiles).toBe(100);
|
|
68
|
+
expect(report.summary.coverageRatio).toBe(0.3);
|
|
69
|
+
expect(report.rawData.hasTestFramework).toBe(true);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
});
|
package/src/analyzer.ts
CHANGED
|
@@ -115,7 +115,9 @@ function detectTestFramework(rootDir: string): boolean {
|
|
|
115
115
|
try {
|
|
116
116
|
const content = readFileSync(p, 'utf-8');
|
|
117
117
|
if (m.deps.some((d) => content.includes(d))) return true;
|
|
118
|
-
} catch {
|
|
118
|
+
} catch {
|
|
119
|
+
// Ignore file read errors
|
|
120
|
+
}
|
|
119
121
|
}
|
|
120
122
|
}
|
|
121
123
|
return false;
|
package/src/provider.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
2
|
+
AnalysisResult,
|
|
3
|
+
createProvider,
|
|
3
4
|
ToolName,
|
|
4
|
-
SpokeOutput,
|
|
5
5
|
ScanOptions,
|
|
6
|
-
ToolScoringOutput,
|
|
7
|
-
AnalysisResult,
|
|
8
|
-
SpokeOutputSchema,
|
|
9
6
|
} from '@aiready/core';
|
|
10
7
|
import { analyzeTestability } from './analyzer';
|
|
11
8
|
import { calculateTestabilityScore } from './scoring';
|
|
@@ -14,42 +11,35 @@ import { TestabilityOptions, TestabilityReport } from './types';
|
|
|
14
11
|
/**
|
|
15
12
|
* Testability Tool Provider
|
|
16
13
|
*/
|
|
17
|
-
export const TestabilityProvider
|
|
14
|
+
export const TestabilityProvider = createProvider({
|
|
18
15
|
id: ToolName.TestabilityIndex,
|
|
19
16
|
alias: ['testability', 'tests', 'verification'],
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
17
|
+
version: '0.2.5',
|
|
18
|
+
defaultWeight: 10,
|
|
19
|
+
async analyzeReport(options: ScanOptions) {
|
|
20
|
+
return analyzeTestability(options as TestabilityOptions);
|
|
21
|
+
},
|
|
22
|
+
getResults(report): AnalysisResult[] {
|
|
23
|
+
return report.issues.map((issue) => ({
|
|
24
|
+
fileName: issue.location.file,
|
|
25
|
+
issues: [issue] as any[],
|
|
27
26
|
metrics: {
|
|
28
27
|
testabilityScore: report.summary.score,
|
|
29
28
|
},
|
|
30
29
|
}));
|
|
31
|
-
|
|
32
|
-
return SpokeOutputSchema.parse({
|
|
33
|
-
results,
|
|
34
|
-
summary: report.summary,
|
|
35
|
-
metadata: {
|
|
36
|
-
toolName: ToolName.TestabilityIndex,
|
|
37
|
-
version: '0.2.5',
|
|
38
|
-
timestamp: new Date().toISOString(),
|
|
39
|
-
rawData: report.rawData,
|
|
40
|
-
},
|
|
41
|
-
});
|
|
42
30
|
},
|
|
43
|
-
|
|
44
|
-
|
|
31
|
+
getSummary(report) {
|
|
32
|
+
return report.summary;
|
|
33
|
+
},
|
|
34
|
+
getMetadata(report) {
|
|
35
|
+
return { rawData: report.rawData };
|
|
36
|
+
},
|
|
37
|
+
score(output) {
|
|
45
38
|
const report = {
|
|
46
39
|
summary: output.summary,
|
|
47
40
|
rawData: (output.metadata as any).rawData,
|
|
48
41
|
recommendations: (output.summary as any).recommendations || [],
|
|
49
42
|
} as unknown as TestabilityReport;
|
|
50
|
-
|
|
51
43
|
return calculateTestabilityScore(report);
|
|
52
44
|
},
|
|
53
|
-
|
|
54
|
-
defaultWeight: 10,
|
|
55
|
-
};
|
|
45
|
+
});
|