@unrdf/diataxis-kit 26.4.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/README.md +425 -0
- package/bin/report.mjs +529 -0
- package/bin/run.mjs +114 -0
- package/bin/verify.mjs +356 -0
- package/capability-map.md +92 -0
- package/package.json +42 -0
- package/src/classify.mjs +584 -0
- package/src/diataxis-schema.mjs +425 -0
- package/src/evidence.mjs +268 -0
- package/src/hash.mjs +37 -0
- package/src/inventory.mjs +280 -0
- package/src/reference-extractor.mjs +324 -0
- package/src/scaffold.mjs +458 -0
- package/src/stable-json.mjs +113 -0
- package/src/verify-implementation.mjs +131 -0
- package/test/determinism.test.mjs +321 -0
- package/test/evidence.test.mjs +145 -0
- package/test/fixtures/scaffold-det1/explanation/explanation.md +35 -0
- package/test/fixtures/scaffold-det1/index.md +29 -0
- package/test/fixtures/scaffold-det1/reference/reference.md +34 -0
- package/test/fixtures/scaffold-det1/tutorials/tutorial-test-tutorial.md +37 -0
- package/test/fixtures/scaffold-det2/explanation/explanation.md +35 -0
- package/test/fixtures/scaffold-det2/index.md +29 -0
- package/test/fixtures/scaffold-det2/reference/reference.md +34 -0
- package/test/fixtures/scaffold-det2/tutorials/tutorial-test-tutorial.md +37 -0
- package/test/fixtures/scaffold-empty/explanation/explanation.md +35 -0
- package/test/fixtures/scaffold-empty/index.md +25 -0
- package/test/fixtures/scaffold-empty/reference/reference.md +34 -0
- package/test/fixtures/scaffold-escape/explanation/explanation.md +35 -0
- package/test/fixtures/scaffold-escape/index.md +29 -0
- package/test/fixtures/scaffold-escape/reference/reference.md +36 -0
- package/test/fixtures/scaffold-output/explanation/explanation.md +39 -0
- package/test/fixtures/scaffold-output/how-to/howto-configure-options.md +39 -0
- package/test/fixtures/scaffold-output/index.md +41 -0
- package/test/fixtures/scaffold-output/reference/reference.md +36 -0
- package/test/fixtures/scaffold-output/tutorials/tutorial-getting-started.md +41 -0
- package/test/fixtures/test-artifacts/ARTIFACTS/diataxis/test-pkg-1.inventory.json +115 -0
- package/test/fixtures/test-artifacts/ARTIFACTS/diataxis/test-pkg-2.inventory.json +93 -0
- package/test/fixtures/test-artifacts/ARTIFACTS/diataxis/test-pkg-3.inventory.json +97 -0
- package/test/fixtures/test-package/LICENSE +1 -0
- package/test/fixtures/test-package/README.md +15 -0
- package/test/fixtures/test-package/docs/guide.md +3 -0
- package/test/fixtures/test-package/examples/basic.mjs +3 -0
- package/test/fixtures/test-package/src/index.mjs +3 -0
- package/test/inventory.test.mjs +199 -0
- package/test/reference-extractor.test.mjs +187 -0
- package/test/report.test.mjs +503 -0
- package/test/scaffold.test.mjs +242 -0
- package/test/verify-gate.test.mjs +634 -0
|
@@ -0,0 +1,503 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file report.test.mjs
|
|
3
|
+
* @description Tests for report CLI tool
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, beforeEach } from 'node:test';
|
|
7
|
+
import assert from 'node:assert/strict';
|
|
8
|
+
import { exec } from 'node:child_process';
|
|
9
|
+
import { promisify } from 'node:util';
|
|
10
|
+
import { mkdir, writeFile, rm } from 'node:fs/promises';
|
|
11
|
+
import { join } from 'node:path';
|
|
12
|
+
import { fileURLToPath } from 'node:url';
|
|
13
|
+
import { dirname } from 'node:path';
|
|
14
|
+
|
|
15
|
+
const execAsync = promisify(exec);
|
|
16
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
17
|
+
const __dirname = dirname(__filename);
|
|
18
|
+
const binPath = join(__dirname, '..', 'bin', 'report.mjs');
|
|
19
|
+
const testArtifactsDir = join(__dirname, 'fixtures', 'test-artifacts');
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create test inventory files
|
|
23
|
+
*/
|
|
24
|
+
async function setupTestInventories() {
|
|
25
|
+
const diataxisDir = join(testArtifactsDir, 'ARTIFACTS', 'diataxis');
|
|
26
|
+
await mkdir(diataxisDir, { recursive: true });
|
|
27
|
+
|
|
28
|
+
// Inventory 1: High confidence package
|
|
29
|
+
const inventory1 = {
|
|
30
|
+
packageName: '@unrdf/test-pkg-1',
|
|
31
|
+
version: '1.0.0',
|
|
32
|
+
generatedAt: '2025-01-01T00:00:00.000Z',
|
|
33
|
+
confidence: {
|
|
34
|
+
tutorials: 0.9,
|
|
35
|
+
howtos: 0.85,
|
|
36
|
+
reference: 1.0,
|
|
37
|
+
explanation: 0.8
|
|
38
|
+
},
|
|
39
|
+
tutorials: [
|
|
40
|
+
{
|
|
41
|
+
id: 'tutorial-getting-started',
|
|
42
|
+
title: 'Getting Started',
|
|
43
|
+
goal: 'Learn basics',
|
|
44
|
+
prerequisites: [],
|
|
45
|
+
stepsOutline: ['Install', 'Import', 'Use'],
|
|
46
|
+
confidenceScore: 0.9,
|
|
47
|
+
source: ['readme', 'examples']
|
|
48
|
+
}
|
|
49
|
+
],
|
|
50
|
+
howtos: [
|
|
51
|
+
{
|
|
52
|
+
id: 'howto-configure',
|
|
53
|
+
title: 'Configure Package',
|
|
54
|
+
task: 'Set up configuration',
|
|
55
|
+
context: 'When customizing',
|
|
56
|
+
steps: ['Create config', 'Set options'],
|
|
57
|
+
confidenceScore: 0.85,
|
|
58
|
+
source: ['readme']
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
id: 'howto-use-cli',
|
|
62
|
+
title: 'Use CLI',
|
|
63
|
+
task: 'Use command line',
|
|
64
|
+
context: 'When running CLI',
|
|
65
|
+
steps: ['Install', 'Run'],
|
|
66
|
+
confidenceScore: 1.0,
|
|
67
|
+
source: ['bin']
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
reference: {
|
|
71
|
+
id: 'reference-api',
|
|
72
|
+
title: 'API Reference',
|
|
73
|
+
items: [
|
|
74
|
+
{ name: 'export1', type: 'export', description: 'Export 1', example: 'import x' },
|
|
75
|
+
{ name: 'cli-tool', type: 'bin', description: 'CLI tool', example: 'cli-tool' }
|
|
76
|
+
],
|
|
77
|
+
confidenceScore: 1.0,
|
|
78
|
+
source: ['exports', 'bin']
|
|
79
|
+
},
|
|
80
|
+
explanation: {
|
|
81
|
+
id: 'explanation-overview',
|
|
82
|
+
title: 'Understanding Package',
|
|
83
|
+
concepts: ['concept1', 'concept2'],
|
|
84
|
+
architecture: 'This is the architecture',
|
|
85
|
+
tradeoffs: ['Tradeoff 1', 'Tradeoff 2'],
|
|
86
|
+
confidenceScore: 0.8,
|
|
87
|
+
source: ['readme', 'keywords']
|
|
88
|
+
},
|
|
89
|
+
evidence: {
|
|
90
|
+
readmeHeadings: ['Usage', 'API', 'Examples'],
|
|
91
|
+
docsFiles: ['guide.md'],
|
|
92
|
+
examplesFiles: ['example1.mjs', 'example2.mjs'],
|
|
93
|
+
fingerprint: 'abc123'
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Inventory 2: Low confidence package
|
|
98
|
+
const inventory2 = {
|
|
99
|
+
packageName: '@unrdf/test-pkg-2',
|
|
100
|
+
version: '0.5.0',
|
|
101
|
+
generatedAt: '2025-01-01T00:00:00.000Z',
|
|
102
|
+
confidence: {
|
|
103
|
+
tutorials: 0.3,
|
|
104
|
+
howtos: 0.4,
|
|
105
|
+
reference: 0.5,
|
|
106
|
+
explanation: 0.4
|
|
107
|
+
},
|
|
108
|
+
tutorials: [
|
|
109
|
+
{
|
|
110
|
+
id: 'tutorial-basic',
|
|
111
|
+
title: 'Basic Tutorial',
|
|
112
|
+
goal: 'Learn',
|
|
113
|
+
prerequisites: [],
|
|
114
|
+
stepsOutline: ['Step 1'],
|
|
115
|
+
confidenceScore: 0.3,
|
|
116
|
+
source: ['inferred']
|
|
117
|
+
}
|
|
118
|
+
],
|
|
119
|
+
howtos: [
|
|
120
|
+
{
|
|
121
|
+
id: 'howto-use',
|
|
122
|
+
title: 'Use Package',
|
|
123
|
+
task: 'Use it',
|
|
124
|
+
context: 'When needed',
|
|
125
|
+
steps: ['Import', 'Use'],
|
|
126
|
+
confidenceScore: 0.4,
|
|
127
|
+
source: ['inferred']
|
|
128
|
+
},
|
|
129
|
+
{
|
|
130
|
+
id: 'howto-troubleshoot',
|
|
131
|
+
title: 'Troubleshoot',
|
|
132
|
+
task: 'Fix issues',
|
|
133
|
+
context: 'When broken',
|
|
134
|
+
steps: ['Check', 'Fix'],
|
|
135
|
+
confidenceScore: 0.4,
|
|
136
|
+
source: ['inferred']
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
reference: {
|
|
140
|
+
id: 'reference-api',
|
|
141
|
+
title: 'API Reference',
|
|
142
|
+
items: [
|
|
143
|
+
{ name: 'unknown', type: 'unknown', description: 'To be added', example: null }
|
|
144
|
+
],
|
|
145
|
+
confidenceScore: 0.5,
|
|
146
|
+
source: ['inferred']
|
|
147
|
+
},
|
|
148
|
+
explanation: {
|
|
149
|
+
id: 'explanation-overview',
|
|
150
|
+
title: 'Understanding Package',
|
|
151
|
+
concepts: ['core'],
|
|
152
|
+
architecture: 'Basic package',
|
|
153
|
+
tradeoffs: ['Generic tradeoff'],
|
|
154
|
+
confidenceScore: 0.4,
|
|
155
|
+
source: ['inferred']
|
|
156
|
+
},
|
|
157
|
+
evidence: {
|
|
158
|
+
readmeHeadings: [],
|
|
159
|
+
docsFiles: [],
|
|
160
|
+
examplesFiles: [],
|
|
161
|
+
fingerprint: 'def456'
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
|
|
165
|
+
// Inventory 3: Medium confidence package
|
|
166
|
+
const inventory3 = {
|
|
167
|
+
packageName: '@unrdf/test-pkg-3',
|
|
168
|
+
version: '2.1.0',
|
|
169
|
+
generatedAt: '2025-01-01T00:00:00.000Z',
|
|
170
|
+
confidence: {
|
|
171
|
+
tutorials: 0.6,
|
|
172
|
+
howtos: 0.7,
|
|
173
|
+
reference: 0.8,
|
|
174
|
+
explanation: 0.65
|
|
175
|
+
},
|
|
176
|
+
tutorials: [
|
|
177
|
+
{
|
|
178
|
+
id: 'tutorial-start',
|
|
179
|
+
title: 'Start Here',
|
|
180
|
+
goal: 'Get started',
|
|
181
|
+
prerequisites: [],
|
|
182
|
+
stepsOutline: ['Install', 'Use'],
|
|
183
|
+
confidenceScore: 0.6,
|
|
184
|
+
source: ['readme']
|
|
185
|
+
}
|
|
186
|
+
],
|
|
187
|
+
howtos: [
|
|
188
|
+
{
|
|
189
|
+
id: 'howto-config',
|
|
190
|
+
title: 'Configure',
|
|
191
|
+
task: 'Configure',
|
|
192
|
+
context: 'Setup',
|
|
193
|
+
steps: ['Edit config'],
|
|
194
|
+
confidenceScore: 0.7,
|
|
195
|
+
source: ['readme']
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'howto-integrate',
|
|
199
|
+
title: 'Integrate',
|
|
200
|
+
task: 'Integrate',
|
|
201
|
+
context: 'Integration',
|
|
202
|
+
steps: ['Setup'],
|
|
203
|
+
confidenceScore: 0.7,
|
|
204
|
+
source: ['keywords']
|
|
205
|
+
}
|
|
206
|
+
],
|
|
207
|
+
reference: {
|
|
208
|
+
id: 'reference-api',
|
|
209
|
+
title: 'API Reference',
|
|
210
|
+
items: [
|
|
211
|
+
{ name: 'export1', type: 'export', description: 'Export', example: 'import' }
|
|
212
|
+
],
|
|
213
|
+
confidenceScore: 0.8,
|
|
214
|
+
source: ['exports']
|
|
215
|
+
},
|
|
216
|
+
explanation: {
|
|
217
|
+
id: 'explanation-overview',
|
|
218
|
+
title: 'Understanding Package',
|
|
219
|
+
concepts: ['concept'],
|
|
220
|
+
architecture: 'Architecture description',
|
|
221
|
+
tradeoffs: ['Tradeoff'],
|
|
222
|
+
confidenceScore: 0.65,
|
|
223
|
+
source: ['readme']
|
|
224
|
+
},
|
|
225
|
+
evidence: {
|
|
226
|
+
readmeHeadings: ['Getting Started', 'Usage'],
|
|
227
|
+
docsFiles: [],
|
|
228
|
+
examplesFiles: ['example.mjs'],
|
|
229
|
+
fingerprint: 'ghi789'
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
|
|
233
|
+
// Write inventory files
|
|
234
|
+
await writeFile(
|
|
235
|
+
join(diataxisDir, 'test-pkg-1.inventory.json'),
|
|
236
|
+
JSON.stringify(inventory1, null, 2)
|
|
237
|
+
);
|
|
238
|
+
await writeFile(
|
|
239
|
+
join(diataxisDir, 'test-pkg-2.inventory.json'),
|
|
240
|
+
JSON.stringify(inventory2, null, 2)
|
|
241
|
+
);
|
|
242
|
+
await writeFile(
|
|
243
|
+
join(diataxisDir, 'test-pkg-3.inventory.json'),
|
|
244
|
+
JSON.stringify(inventory3, null, 2)
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
/**
|
|
249
|
+
* Clean up test artifacts
|
|
250
|
+
*/
|
|
251
|
+
async function cleanupTestInventories() {
|
|
252
|
+
try {
|
|
253
|
+
await rm(testArtifactsDir, { recursive: true, force: true });
|
|
254
|
+
} catch (error) {
|
|
255
|
+
// Ignore cleanup errors
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
describe('report.mjs', () => {
|
|
260
|
+
beforeEach(async () => {
|
|
261
|
+
await cleanupTestInventories();
|
|
262
|
+
await setupTestInventories();
|
|
263
|
+
});
|
|
264
|
+
|
|
265
|
+
describe('Test 1: Coverage summary calculation', () => {
|
|
266
|
+
it('should calculate correct coverage percentages', async () => {
|
|
267
|
+
const { stdout } = await execAsync(
|
|
268
|
+
`cd "${testArtifactsDir}" && node "${binPath}"`
|
|
269
|
+
);
|
|
270
|
+
|
|
271
|
+
// Check summary section
|
|
272
|
+
assert.ok(stdout.includes('Total packages: 3'));
|
|
273
|
+
assert.ok(stdout.includes('With tutorials: 3 (100%)'));
|
|
274
|
+
assert.ok(stdout.includes('With 2+ how-tos: 3 (100%)'));
|
|
275
|
+
assert.ok(stdout.includes('With reference: 3 (100%)'));
|
|
276
|
+
assert.ok(stdout.includes('With explanation: 3 (100%)'));
|
|
277
|
+
});
|
|
278
|
+
});
|
|
279
|
+
|
|
280
|
+
describe('Test 2: Confidence stats (avg, min, max)', () => {
|
|
281
|
+
it('should calculate correct confidence statistics', async () => {
|
|
282
|
+
const { stdout } = await execAsync(
|
|
283
|
+
`cd "${testArtifactsDir}" && node "${binPath}"`
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
// Check confidence section exists
|
|
287
|
+
assert.ok(stdout.includes('CONFIDENCE'));
|
|
288
|
+
|
|
289
|
+
// Check format (avg=X.XX, min=X.XX, max=X.XX)
|
|
290
|
+
assert.match(stdout, /Tutorials\s+: avg=\d+\.\d{2}, min=\d+\.\d{2}, max=\d+\.\d{2}/);
|
|
291
|
+
assert.match(stdout, /Howtos\s+: avg=\d+\.\d{2}, min=\d+\.\d{2}, max=\d+\.\d{2}/);
|
|
292
|
+
assert.match(stdout, /Reference\s+: avg=\d+\.\d{2}, min=\d+\.\d{2}, max=\d+\.\d{2}/);
|
|
293
|
+
assert.match(stdout, /Explanation\s+: avg=\d+\.\d{2}, min=\d+\.\d{2}, max=\d+\.\d{2}/);
|
|
294
|
+
|
|
295
|
+
// Tutorials: avg=(0.9+0.3+0.6)/3=0.60, min=0.30, max=0.90
|
|
296
|
+
assert.ok(stdout.includes('Tutorials : avg=0.60, min=0.30, max=0.90'));
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('Test 3: Lowest confidence packages ranking', () => {
|
|
301
|
+
it('should rank packages by lowest confidence', async () => {
|
|
302
|
+
const { stdout } = await execAsync(
|
|
303
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --top 3`
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
// Check lowest confidence section
|
|
307
|
+
assert.ok(stdout.includes('LOWEST CONFIDENCE (3 packages)'));
|
|
308
|
+
|
|
309
|
+
// Package 2 should be first (avg=0.40)
|
|
310
|
+
assert.ok(stdout.includes('@unrdf/test-pkg-2'));
|
|
311
|
+
assert.ok(stdout.includes('(0.40)'));
|
|
312
|
+
|
|
313
|
+
// Package 3 should be second (avg=0.69)
|
|
314
|
+
assert.ok(stdout.includes('@unrdf/test-pkg-3'));
|
|
315
|
+
assert.ok(stdout.includes('(0.69)'));
|
|
316
|
+
|
|
317
|
+
// Package 1 should be last (avg=0.89)
|
|
318
|
+
assert.ok(stdout.includes('@unrdf/test-pkg-1'));
|
|
319
|
+
assert.ok(stdout.includes('(0.89)'));
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('should respect --top N option', async () => {
|
|
323
|
+
const { stdout } = await execAsync(
|
|
324
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --top 1`
|
|
325
|
+
);
|
|
326
|
+
|
|
327
|
+
assert.ok(stdout.includes('LOWEST CONFIDENCE (1 packages)'));
|
|
328
|
+
|
|
329
|
+
// Should only show one package
|
|
330
|
+
const lines = stdout.split('\n').filter(line => line.match(/^\s*\d+\./));
|
|
331
|
+
assert.equal(lines.length, 1);
|
|
332
|
+
});
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
describe('Test 4: Missing evidence detection', () => {
|
|
336
|
+
it('should detect and count missing evidence', async () => {
|
|
337
|
+
const { stdout } = await execAsync(
|
|
338
|
+
`cd "${testArtifactsDir}" && node "${binPath}"`
|
|
339
|
+
);
|
|
340
|
+
|
|
341
|
+
// Check missing evidence section
|
|
342
|
+
assert.ok(stdout.includes('MISSING EVIDENCE'));
|
|
343
|
+
|
|
344
|
+
// Package 2 has all evidence missing
|
|
345
|
+
// Package 3 has no docs/
|
|
346
|
+
// Package 1 has all evidence present
|
|
347
|
+
|
|
348
|
+
// no examples/: 1 package (pkg-2)
|
|
349
|
+
assert.match(stdout, /no examples\/\s*:\s*1 packages?/);
|
|
350
|
+
|
|
351
|
+
// no docs/: 2 packages (pkg-2, pkg-3)
|
|
352
|
+
assert.match(stdout, /no docs\/\s*:\s*2 packages?/);
|
|
353
|
+
|
|
354
|
+
// empty README: 1 package (pkg-2)
|
|
355
|
+
assert.match(stdout, /empty README\s*:\s*1 packages?/);
|
|
356
|
+
|
|
357
|
+
// no bin entries: 2 packages (pkg-2, pkg-3)
|
|
358
|
+
assert.match(stdout, /no bin entries\s*:\s*2 packages?/);
|
|
359
|
+
});
|
|
360
|
+
});
|
|
361
|
+
|
|
362
|
+
describe('Test 5: JSON output format', () => {
|
|
363
|
+
it('should generate valid JSON output', async () => {
|
|
364
|
+
const { stdout } = await execAsync(
|
|
365
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --json`
|
|
366
|
+
);
|
|
367
|
+
|
|
368
|
+
// Parse JSON
|
|
369
|
+
const report = JSON.parse(stdout);
|
|
370
|
+
|
|
371
|
+
// Check structure
|
|
372
|
+
assert.ok(report.summary);
|
|
373
|
+
assert.equal(report.summary.total, 3);
|
|
374
|
+
assert.equal(report.summary.withTutorials, 3);
|
|
375
|
+
assert.equal(report.summary.with2PlusHowtos, 3);
|
|
376
|
+
|
|
377
|
+
// Check confidence
|
|
378
|
+
assert.ok(report.confidence);
|
|
379
|
+
assert.ok(report.confidence.tutorials);
|
|
380
|
+
assert.equal(report.confidence.tutorials.avg.toFixed(2), '0.60');
|
|
381
|
+
assert.equal(report.confidence.tutorials.min, 0.3);
|
|
382
|
+
assert.equal(report.confidence.tutorials.max, 0.9);
|
|
383
|
+
|
|
384
|
+
// Check lowestConfidence
|
|
385
|
+
assert.ok(Array.isArray(report.lowestConfidence));
|
|
386
|
+
assert.equal(report.lowestConfidence.length, 3); // Only 3 packages total
|
|
387
|
+
|
|
388
|
+
// First should be lowest (pkg-2)
|
|
389
|
+
assert.equal(report.lowestConfidence[0].packageName, '@unrdf/test-pkg-2');
|
|
390
|
+
assert.equal(report.lowestConfidence[0].avgConfidence, 0.40);
|
|
391
|
+
|
|
392
|
+
// Check missingEvidence
|
|
393
|
+
assert.ok(report.missingEvidence);
|
|
394
|
+
assert.equal(report.missingEvidence['no docs/'], 2);
|
|
395
|
+
assert.equal(report.missingEvidence['no examples/'], 1);
|
|
396
|
+
|
|
397
|
+
// Check generatedAt
|
|
398
|
+
assert.ok(report.generatedAt);
|
|
399
|
+
assert.match(report.generatedAt, /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/);
|
|
400
|
+
});
|
|
401
|
+
});
|
|
402
|
+
|
|
403
|
+
describe('Test 6: CSV output format', () => {
|
|
404
|
+
it('should generate valid CSV output', async () => {
|
|
405
|
+
const { stdout } = await execAsync(
|
|
406
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --csv`
|
|
407
|
+
);
|
|
408
|
+
|
|
409
|
+
const lines = stdout.trim().split('\n');
|
|
410
|
+
|
|
411
|
+
// Check header
|
|
412
|
+
assert.equal(
|
|
413
|
+
lines[0],
|
|
414
|
+
'Package,Tutorials,HowTos,HasReference,HasExplanation,AvgConfidence,TutorialsConf,HowtosConf,ReferenceConf,ExplanationConf,MissingEvidence'
|
|
415
|
+
);
|
|
416
|
+
|
|
417
|
+
// Check data rows (3 packages)
|
|
418
|
+
assert.equal(lines.length, 4); // Header + 3 rows
|
|
419
|
+
|
|
420
|
+
// Check pkg-1 row
|
|
421
|
+
const pkg1Row = lines.find(line => line.startsWith('@unrdf/test-pkg-1'));
|
|
422
|
+
assert.ok(pkg1Row);
|
|
423
|
+
assert.ok(pkg1Row.includes(',1,2,yes,yes,0.89,0.90,0.85,1.00,0.80,'));
|
|
424
|
+
|
|
425
|
+
// Check pkg-2 row (low confidence)
|
|
426
|
+
const pkg2Row = lines.find(line => line.startsWith('@unrdf/test-pkg-2'));
|
|
427
|
+
assert.ok(pkg2Row);
|
|
428
|
+
assert.ok(pkg2Row.includes(',1,2,yes,yes,0.40,0.30,0.40,0.50,0.40,'));
|
|
429
|
+
assert.ok(pkg2Row.includes('"no examples/; no docs/; empty README; no bin entries"'));
|
|
430
|
+
});
|
|
431
|
+
});
|
|
432
|
+
|
|
433
|
+
describe('Test 7: Filter option', () => {
|
|
434
|
+
it('should filter packages by keyword', async () => {
|
|
435
|
+
const { stdout } = await execAsync(
|
|
436
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --filter "pkg-1"`
|
|
437
|
+
);
|
|
438
|
+
|
|
439
|
+
// Should only show pkg-1
|
|
440
|
+
assert.ok(stdout.includes('Total packages: 1'));
|
|
441
|
+
assert.ok(stdout.includes('@unrdf/test-pkg-1'));
|
|
442
|
+
assert.ok(!stdout.includes('@unrdf/test-pkg-2'));
|
|
443
|
+
assert.ok(!stdout.includes('@unrdf/test-pkg-3'));
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
|
|
447
|
+
describe('Test 8: Sort option', () => {
|
|
448
|
+
it('should sort by tutorials count', async () => {
|
|
449
|
+
const { stdout } = await execAsync(
|
|
450
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --sort tutorials --top 3 --csv`
|
|
451
|
+
);
|
|
452
|
+
|
|
453
|
+
const lines = stdout.trim().split('\n').slice(1); // Skip header
|
|
454
|
+
|
|
455
|
+
// All have 1 tutorial, so order should be stable (alphabetical)
|
|
456
|
+
assert.ok(lines[0].startsWith('@unrdf/test-pkg-1'));
|
|
457
|
+
assert.ok(lines[1].startsWith('@unrdf/test-pkg-2'));
|
|
458
|
+
assert.ok(lines[2].startsWith('@unrdf/test-pkg-3'));
|
|
459
|
+
});
|
|
460
|
+
|
|
461
|
+
it('should sort by confidence (default)', async () => {
|
|
462
|
+
const { stdout } = await execAsync(
|
|
463
|
+
`cd "${testArtifactsDir}" && node "${binPath}" --sort confidence --csv`
|
|
464
|
+
);
|
|
465
|
+
|
|
466
|
+
const lines = stdout.trim().split('\n').slice(1); // Skip header
|
|
467
|
+
|
|
468
|
+
// Should be sorted by avgConfidence descending
|
|
469
|
+
// pkg-1: 0.89, pkg-3: 0.69, pkg-2: 0.40
|
|
470
|
+
assert.ok(lines[0].startsWith('@unrdf/test-pkg-1'));
|
|
471
|
+
assert.ok(lines[1].startsWith('@unrdf/test-pkg-3'));
|
|
472
|
+
assert.ok(lines[2].startsWith('@unrdf/test-pkg-2'));
|
|
473
|
+
});
|
|
474
|
+
});
|
|
475
|
+
|
|
476
|
+
describe('Test 9: Empty inventory handling', () => {
|
|
477
|
+
it('should handle missing ARTIFACTS directory gracefully', async () => {
|
|
478
|
+
await cleanupTestInventories();
|
|
479
|
+
// Create test directory again so cd works, but without ARTIFACTS
|
|
480
|
+
await mkdir(testArtifactsDir, { recursive: true });
|
|
481
|
+
|
|
482
|
+
const { stdout } = await execAsync(
|
|
483
|
+
`cd "${testArtifactsDir}" && node "${binPath}"`
|
|
484
|
+
);
|
|
485
|
+
|
|
486
|
+
assert.ok(stdout.includes('No inventory generated'));
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
|
|
490
|
+
describe('Test 10: Help option', () => {
|
|
491
|
+
it('should display help message', async () => {
|
|
492
|
+
const { stdout } = await execAsync(`node "${binPath}" --help`);
|
|
493
|
+
|
|
494
|
+
assert.ok(stdout.includes('Diátaxis Coverage Report Generator'));
|
|
495
|
+
assert.ok(stdout.includes('Usage:'));
|
|
496
|
+
assert.ok(stdout.includes('--json'));
|
|
497
|
+
assert.ok(stdout.includes('--csv'));
|
|
498
|
+
assert.ok(stdout.includes('--top'));
|
|
499
|
+
assert.ok(stdout.includes('--filter'));
|
|
500
|
+
assert.ok(stdout.includes('--sort'));
|
|
501
|
+
});
|
|
502
|
+
});
|
|
503
|
+
});
|