@unrdf/kgn 5.0.1

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.
Files changed (68) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +210 -0
  3. package/package.json +90 -0
  4. package/src/MIGRATION_COMPLETE.md +186 -0
  5. package/src/PORT-MAP.md +302 -0
  6. package/src/base/filter-templates.js +479 -0
  7. package/src/base/index.js +92 -0
  8. package/src/base/injection-targets.js +583 -0
  9. package/src/base/macro-templates.js +298 -0
  10. package/src/base/macro-templates.js.bak +461 -0
  11. package/src/base/shacl-templates.js +617 -0
  12. package/src/base/template-base.js +388 -0
  13. package/src/core/attestor.js +381 -0
  14. package/src/core/filters.js +518 -0
  15. package/src/core/index.js +21 -0
  16. package/src/core/kgen-engine.js +372 -0
  17. package/src/core/parser.js +447 -0
  18. package/src/core/post-processor.js +313 -0
  19. package/src/core/renderer.js +469 -0
  20. package/src/doc-generator/cli.mjs +122 -0
  21. package/src/doc-generator/index.mjs +28 -0
  22. package/src/doc-generator/mdx-generator.mjs +71 -0
  23. package/src/doc-generator/nav-generator.mjs +136 -0
  24. package/src/doc-generator/parser.mjs +291 -0
  25. package/src/doc-generator/rdf-builder.mjs +306 -0
  26. package/src/doc-generator/scanner.mjs +189 -0
  27. package/src/engine/index.js +42 -0
  28. package/src/engine/pipeline.js +448 -0
  29. package/src/engine/renderer.js +604 -0
  30. package/src/engine/template-engine.js +566 -0
  31. package/src/filters/array.js +436 -0
  32. package/src/filters/data.js +479 -0
  33. package/src/filters/index.js +270 -0
  34. package/src/filters/rdf.js +264 -0
  35. package/src/filters/text.js +369 -0
  36. package/src/index.js +109 -0
  37. package/src/inheritance/index.js +40 -0
  38. package/src/injection/api.js +260 -0
  39. package/src/injection/atomic-writer.js +327 -0
  40. package/src/injection/constants.js +136 -0
  41. package/src/injection/idempotency-manager.js +295 -0
  42. package/src/injection/index.js +28 -0
  43. package/src/injection/injection-engine.js +378 -0
  44. package/src/injection/integration.js +339 -0
  45. package/src/injection/modes/index.js +341 -0
  46. package/src/injection/rollback-manager.js +373 -0
  47. package/src/injection/target-resolver.js +323 -0
  48. package/src/injection/tests/atomic-writer.test.js +382 -0
  49. package/src/injection/tests/injection-engine.test.js +611 -0
  50. package/src/injection/tests/integration.test.js +392 -0
  51. package/src/injection/tests/run-tests.js +283 -0
  52. package/src/injection/validation-engine.js +547 -0
  53. package/src/linter/determinism-linter.js +473 -0
  54. package/src/linter/determinism.js +410 -0
  55. package/src/linter/index.js +6 -0
  56. package/src/linter/test-doubles.js +475 -0
  57. package/src/parser/frontmatter.js +228 -0
  58. package/src/parser/variables.js +344 -0
  59. package/src/renderer/deterministic.js +245 -0
  60. package/src/renderer/index.js +6 -0
  61. package/src/templates/latex/academic-paper.njk +186 -0
  62. package/src/templates/latex/index.js +104 -0
  63. package/src/templates/nextjs/app-page.njk +66 -0
  64. package/src/templates/nextjs/index.js +80 -0
  65. package/src/templates/office/docx/document.njk +368 -0
  66. package/src/templates/office/index.js +79 -0
  67. package/src/templates/office/word-report.njk +129 -0
  68. package/src/utils/template-utils.js +426 -0
@@ -0,0 +1,392 @@
1
+ /**
2
+ * KGEN Injection Integration Tests
3
+ *
4
+ * Tests for integration with the main KGEN template engine,
5
+ * including template processing with injection support.
6
+ */
7
+
8
+ import { describe, test, expect, beforeEach, afterEach } from 'vitest';
9
+ import { promises as fs } from 'fs';
10
+ import { join } from 'path';
11
+ import { tmpdir } from 'os';
12
+
13
+ import { inject, dryRun, processTemplate, initializeInjection } from '../api.js';
14
+ import { enhanceKgenWithInjection } from '../integration.js';
15
+ import { INJECTION_MODES } from '../constants.js';
16
+
17
+ describe('KGEN Injection Integration', () => {
18
+ let tempDir;
19
+ let testFiles;
20
+
21
+ beforeEach(async () => {
22
+ tempDir = await fs.mkdtemp(join(tmpdir(), 'kgen-integration-test-'));
23
+ await fs.mkdir(join(tempDir, 'src'), { recursive: true });
24
+
25
+ testFiles = {
26
+ routes: join(tempDir, 'src', 'routes.ts'),
27
+ config: join(tempDir, 'src', 'config.ts'),
28
+ template: join(tempDir, 'templates', 'route.kgen')
29
+ };
30
+
31
+ // Initialize injection system
32
+ initializeInjection({
33
+ projectRoot: tempDir,
34
+ backupEnabled: true
35
+ });
36
+ });
37
+
38
+ afterEach(async () => {
39
+ await fs.rm(tempDir, { recursive: true, force: true });
40
+ });
41
+
42
+ describe('API Functions', () => {
43
+ test('should inject content using API', async () => {
44
+ // Setup
45
+ const initialContent = 'export const config = { port: 3000 };';
46
+ await fs.writeFile(testFiles.config, initialContent);
47
+
48
+ const templateConfig = {
49
+ to: 'src/config.ts',
50
+ inject: true,
51
+ mode: 'append'
52
+ };
53
+
54
+ const content = '\nexport const database = "mongodb://localhost";';
55
+ const variables = {};
56
+
57
+ // Execute
58
+ const result = await inject(templateConfig, content, variables);
59
+
60
+ // Verify
61
+ expect(result.success).toBe(true);
62
+ expect(result.operationId).toBeDefined();
63
+
64
+ const finalContent = await fs.readFile(testFiles.config, 'utf8');
65
+ expect(finalContent).toContain('export const database');
66
+ });
67
+
68
+ test('should perform dry run without modifications', async () => {
69
+ // Setup
70
+ const initialContent = 'const original = true;';
71
+ await fs.writeFile(testFiles.config, initialContent);
72
+
73
+ const templateConfig = {
74
+ to: 'src/config.ts',
75
+ inject: true,
76
+ mode: 'append'
77
+ };
78
+
79
+ const content = 'const added = true;';
80
+
81
+ // Execute dry run
82
+ const result = await dryRun(templateConfig, content, {});
83
+
84
+ // Verify
85
+ expect(result.targets).toHaveLength(1);
86
+ expect(result.targets[0].valid).toBe(true);
87
+
88
+ // File should not be modified
89
+ const finalContent = await fs.readFile(testFiles.config, 'utf8');
90
+ expect(finalContent).toBe(initialContent);
91
+ });
92
+
93
+ test('should process template with frontmatter', async () => {
94
+ // Setup template with frontmatter
95
+ const template = `---
96
+ to: src/routes.ts
97
+ inject: true
98
+ mode: before
99
+ target: "export default"
100
+ ---
101
+ router.{{method}}('{{path}}', {{handler}});`;
102
+
103
+ // Create target file
104
+ const initialContent = `const router = Router();
105
+
106
+ export default router;`;
107
+ await fs.writeFile(testFiles.routes, initialContent);
108
+
109
+ const data = {
110
+ method: 'get',
111
+ path: '/users',
112
+ handler: 'getUsers'
113
+ };
114
+
115
+ // Execute
116
+ const result = await processTemplate(template, data);
117
+
118
+ // Verify
119
+ expect(result.success).toBe(true);
120
+ expect(result.operationId).toBeDefined();
121
+
122
+ const finalContent = await fs.readFile(testFiles.routes, 'utf8');
123
+ expect(finalContent).toContain("router.get('/users', getUsers);");
124
+ });
125
+
126
+ test('should handle template without frontmatter', async () => {
127
+ const template = 'Simple template without frontmatter';
128
+ const data = {};
129
+
130
+ // Should throw error since only injection templates are supported
131
+ await expect(processTemplate(template, data))
132
+ .rejects.toThrow('Regular template processing not implemented');
133
+ });
134
+ });
135
+
136
+ describe('Template Engine Enhancement', () => {
137
+ test('should enhance existing KGEN engine with injection', async () => {
138
+ // Mock KGEN engine
139
+ const mockKgenEngine = {
140
+ render: async (templatePath, data) => `Rendered: ${templatePath}`,
141
+ renderString: async (templateString, data) => `Rendered: ${templateString}`,
142
+ getTemplate: async (templatePath) => ({
143
+ frontmatter: { inject: true, to: 'src/test.ts', mode: 'append' },
144
+ content: 'test content {{name}}'
145
+ })
146
+ };
147
+
148
+ // Enhance with injection
149
+ const enhancedEngine = enhanceKgenWithInjection(mockKgenEngine, {
150
+ projectRoot: tempDir
151
+ });
152
+
153
+ // Verify new methods are added
154
+ expect(enhancedEngine.inject).toBeDefined();
155
+ expect(enhancedEngine.dryRunInjection).toBeDefined();
156
+ expect(enhancedEngine.getInjectionHistory).toBeDefined();
157
+ expect(enhancedEngine.undoInjection).toBeDefined();
158
+ expect(enhancedEngine.processBatch).toBeDefined();
159
+
160
+ // Test injection method
161
+ await fs.writeFile(join(tempDir, 'src', 'test.ts'), 'original content');
162
+
163
+ const result = await enhancedEngine.inject(
164
+ { to: 'src/test.ts', inject: true, mode: 'append' },
165
+ 'injected content',
166
+ {}
167
+ );
168
+
169
+ expect(result.success).toBe(true);
170
+ });
171
+
172
+ test('should process batch templates with mixed injection/regular', async () => {
173
+ const mockKgenEngine = {
174
+ render: async () => 'Regular render result',
175
+ renderString: async () => 'Regular renderString result',
176
+ getTemplate: async (templatePath) => {
177
+ if (templatePath.includes('injection')) {
178
+ return {
179
+ frontmatter: { inject: true, to: 'src/test.ts', mode: 'append' },
180
+ content: 'injected {{content}}'
181
+ };
182
+ }
183
+ return { frontmatter: {}, content: 'regular template' };
184
+ }
185
+ };
186
+
187
+ const enhancedEngine = enhanceKgenWithInjection(mockKgenEngine, {
188
+ projectRoot: tempDir
189
+ });
190
+
191
+ // Setup target file
192
+ await fs.writeFile(join(tempDir, 'src', 'test.ts'), 'original');
193
+
194
+ const templates = [
195
+ {
196
+ path: 'injection-template.kgen',
197
+ data: { content: 'test' }
198
+ },
199
+ {
200
+ path: 'regular-template.kgen',
201
+ data: { name: 'test' }
202
+ }
203
+ ];
204
+
205
+ const result = await enhancedEngine.processBatch(templates);
206
+
207
+ expect(result.total).toBe(2);
208
+ expect(result.successful).toBeGreaterThan(0);
209
+ });
210
+ });
211
+
212
+ describe('Error Handling', () => {
213
+ test('should handle missing template variables', async () => {
214
+ const templateConfig = {
215
+ to: 'src/{{filename}}.ts', // Missing filename variable
216
+ inject: true,
217
+ mode: 'create'
218
+ };
219
+
220
+ const content = 'test content';
221
+ const variables = {}; // Missing 'filename'
222
+
223
+ await expect(inject(templateConfig, content, variables))
224
+ .rejects.toThrow(/Variable 'filename' not provided/);
225
+ });
226
+
227
+ test('should handle invalid injection mode', async () => {
228
+ const templateConfig = {
229
+ to: 'src/test.ts',
230
+ inject: true,
231
+ mode: 'invalid-mode'
232
+ };
233
+
234
+ const content = 'test content';
235
+
236
+ await expect(inject(templateConfig, content, variables = {}))
237
+ .rejects.toThrow(/Unknown injection mode/);
238
+ });
239
+
240
+ test('should handle path traversal attempts', async () => {
241
+ const templateConfig = {
242
+ to: '../../../etc/passwd', // Path traversal attempt
243
+ inject: true,
244
+ mode: 'create'
245
+ };
246
+
247
+ const content = 'malicious content';
248
+
249
+ await expect(inject(templateConfig, content, {}))
250
+ .rejects.toThrow(/Path traversal blocked/);
251
+ });
252
+ });
253
+
254
+ describe('Complex Scenarios', () => {
255
+ test('should handle multi-target injection', async () => {
256
+ // Setup multiple target files
257
+ await fs.writeFile(join(tempDir, 'src', 'routes.ts'), 'export const routes = [];');
258
+ await fs.writeFile(join(tempDir, 'src', 'handlers.ts'), 'export const handlers = {};');
259
+
260
+ const templateConfig = {
261
+ targets: [
262
+ {
263
+ to: 'src/routes.ts',
264
+ inject: true,
265
+ mode: 'before',
266
+ target: 'export const routes'
267
+ },
268
+ {
269
+ to: 'src/handlers.ts',
270
+ inject: true,
271
+ mode: 'before',
272
+ target: 'export const handlers'
273
+ }
274
+ ]
275
+ };
276
+
277
+ const content = '// Auto-generated code';
278
+
279
+ const result = await inject(templateConfig, content, {});
280
+
281
+ expect(result.success).toBe(true);
282
+ expect(result.targets).toBe(2);
283
+
284
+ // Verify both files were modified
285
+ const routesContent = await fs.readFile(join(tempDir, 'src', 'routes.ts'), 'utf8');
286
+ const handlersContent = await fs.readFile(join(tempDir, 'src', 'handlers.ts'), 'utf8');
287
+
288
+ expect(routesContent).toContain('// Auto-generated code');
289
+ expect(handlersContent).toContain('// Auto-generated code');
290
+ });
291
+
292
+ test('should handle glob patterns with exclusions', async () => {
293
+ // Setup multiple TypeScript files
294
+ await fs.mkdir(join(tempDir, 'src', 'components'), { recursive: true });
295
+ await fs.writeFile(join(tempDir, 'src', 'components', 'Button.ts'), 'export class Button {}');
296
+ await fs.writeFile(join(tempDir, 'src', 'components', 'Input.ts'), 'export class Input {}');
297
+ await fs.writeFile(join(tempDir, 'src', 'components', 'Button.test.ts'), 'test code');
298
+ await fs.writeFile(join(tempDir, 'src', 'components', 'Input.spec.ts'), 'spec code');
299
+
300
+ const templateConfig = {
301
+ to: 'src/components/*.ts',
302
+ inject: true,
303
+ mode: 'prepend',
304
+ exclude: ['*.test.ts', '*.spec.ts']
305
+ };
306
+
307
+ const content = '// Auto-generated imports';
308
+
309
+ const result = await inject(templateConfig, content, {});
310
+
311
+ expect(result.success).toBe(true);
312
+ expect(result.targets).toBe(2); // Only Button.ts and Input.ts, not test files
313
+
314
+ // Verify only non-test files were modified
315
+ const buttonContent = await fs.readFile(join(tempDir, 'src', 'components', 'Button.ts'), 'utf8');
316
+ const inputContent = await fs.readFile(join(tempDir, 'src', 'components', 'Input.ts'), 'utf8');
317
+ const testContent = await fs.readFile(join(tempDir, 'src', 'components', 'Button.test.ts'), 'utf8');
318
+
319
+ expect(buttonContent).toContain('// Auto-generated imports');
320
+ expect(inputContent).toContain('// Auto-generated imports');
321
+ expect(testContent).not.toContain('// Auto-generated imports');
322
+ });
323
+
324
+ test('should handle conditional injection with complex skipIf', async () => {
325
+ // Setup file with existing content
326
+ const existingContent = `import express from 'express';
327
+ import cors from 'cors';
328
+
329
+ const app = express();
330
+ app.use(cors());`;
331
+
332
+ await fs.writeFile(testFiles.routes, existingContent);
333
+
334
+ const templateConfig = {
335
+ to: 'src/routes.ts',
336
+ inject: true,
337
+ mode: 'prepend',
338
+ skipIf: ['/cors/', 'express'],
339
+ skipIfLogic: 'OR'
340
+ };
341
+
342
+ const content = 'import helmet from "helmet";';
343
+
344
+ const result = await inject(templateConfig, content, {});
345
+
346
+ // Should be skipped because file contains 'cors'
347
+ expect(result.success).toBe(true);
348
+ expect(result.skipped).toBe(true);
349
+
350
+ // File should not be modified
351
+ const finalContent = await fs.readFile(testFiles.routes, 'utf8');
352
+ expect(finalContent).toBe(existingContent);
353
+ });
354
+
355
+ test('should handle rollback on partial multi-target failure', async () => {
356
+ // Setup target files
357
+ await fs.writeFile(join(tempDir, 'src', 'file1.ts'), 'original content 1');
358
+ await fs.writeFile(join(tempDir, 'src', 'file2.ts'), 'original content 2');
359
+
360
+ // Make file2 read-only to cause failure
361
+ await fs.chmod(join(tempDir, 'src', 'file2.ts'), 0o444);
362
+
363
+ const templateConfig = {
364
+ targets: [
365
+ {
366
+ to: 'src/file1.ts',
367
+ inject: true,
368
+ mode: 'append'
369
+ },
370
+ {
371
+ to: 'src/file2.ts',
372
+ inject: true,
373
+ mode: 'append'
374
+ }
375
+ ]
376
+ };
377
+
378
+ const content = 'new content';
379
+
380
+ // Should fail due to read-only file2
381
+ await expect(inject(templateConfig, content, {}))
382
+ .rejects.toThrow();
383
+
384
+ // file1 should be rolled back to original content
385
+ const file1Content = await fs.readFile(join(tempDir, 'src', 'file1.ts'), 'utf8');
386
+ expect(file1Content).toBe('original content 1');
387
+
388
+ // Restore permissions for cleanup
389
+ await fs.chmod(join(tempDir, 'src', 'file2.ts'), 0o644);
390
+ });
391
+ });
392
+ });
@@ -0,0 +1,283 @@
1
+ /**
2
+ * KGEN Injection Tests Runner
3
+ *
4
+ * Comprehensive test runner for the injection system with
5
+ * coverage reporting and BDD scenario validation.
6
+ */
7
+
8
+ import { exec } from 'child_process';
9
+ import { promisify } from 'util';
10
+ import { promises as fs } from 'fs';
11
+ import { join, dirname } from 'path';
12
+ import { fileURLToPath } from 'url';
13
+
14
+ const execAsync = promisify(exec);
15
+ const __filename = fileURLToPath(import.meta.url);
16
+ const __dirname = dirname(__filename);
17
+ const rootDir = join(__dirname, '../../..');
18
+
19
+ async function runTests() {
20
+ console.log('šŸš€ Starting KGEN Injection System Tests');
21
+ console.log('=' .repeat(50));
22
+
23
+ const results = {
24
+ unit: null,
25
+ integration: null,
26
+ bdd: null,
27
+ coverage: null,
28
+ errors: []
29
+ };
30
+
31
+ try {
32
+ // 1. Run unit tests
33
+ console.log('\nšŸ“¦ Running Unit Tests...');
34
+ try {
35
+ const unitResult = await execAsync('npm test', {
36
+ cwd: rootDir,
37
+ env: { ...process.env, NODE_ENV: 'test' }
38
+ });
39
+
40
+ results.unit = {
41
+ success: true,
42
+ output: unitResult.stdout,
43
+ duration: extractTestDuration(unitResult.stdout)
44
+ };
45
+
46
+ console.log('āœ… Unit tests passed');
47
+ } catch (error) {
48
+ results.unit = {
49
+ success: false,
50
+ error: error.message,
51
+ output: error.stdout || error.stderr
52
+ };
53
+ results.errors.push('Unit tests failed');
54
+ console.log('āŒ Unit tests failed:', error.message);
55
+ }
56
+
57
+ // 2. Run integration tests
58
+ console.log('\nšŸ”— Running Integration Tests...');
59
+ try {
60
+ const integrationResult = await execAsync('npm run test:integration', {
61
+ cwd: rootDir,
62
+ env: { ...process.env, NODE_ENV: 'test' }
63
+ });
64
+
65
+ results.integration = {
66
+ success: true,
67
+ output: integrationResult.stdout,
68
+ duration: extractTestDuration(integrationResult.stdout)
69
+ };
70
+
71
+ console.log('āœ… Integration tests passed');
72
+ } catch (error) {
73
+ results.integration = {
74
+ success: false,
75
+ error: error.message,
76
+ output: error.stdout || error.stderr
77
+ };
78
+ results.errors.push('Integration tests failed');
79
+ console.log('āŒ Integration tests failed:', error.message);
80
+ }
81
+
82
+ // 3. Run BDD feature tests
83
+ console.log('\nšŸ„’ Running BDD Feature Tests...');
84
+ try {
85
+ const bddResult = await execAsync('npm run test:cucumber', {
86
+ cwd: rootDir,
87
+ env: { ...process.env, NODE_ENV: 'test' }
88
+ });
89
+
90
+ results.bdd = {
91
+ success: true,
92
+ output: bddResult.stdout,
93
+ scenarios: extractBddScenarios(bddResult.stdout)
94
+ };
95
+
96
+ console.log('āœ… BDD feature tests passed');
97
+ } catch (error) {
98
+ results.bdd = {
99
+ success: false,
100
+ error: error.message,
101
+ output: error.stdout || error.stderr
102
+ };
103
+ results.errors.push('BDD tests failed');
104
+ console.log('āŒ BDD tests failed:', error.message);
105
+ }
106
+
107
+ // 4. Generate coverage report
108
+ console.log('\nšŸ“Š Generating Coverage Report...');
109
+ try {
110
+ const coverageResult = await execAsync('npm run test:coverage', {
111
+ cwd: rootDir,
112
+ env: { ...process.env, NODE_ENV: 'test' }
113
+ });
114
+
115
+ const coverage = extractCoverageStats(coverageResult.stdout);
116
+ results.coverage = {
117
+ success: true,
118
+ ...coverage
119
+ };
120
+
121
+ console.log(`āœ… Coverage: ${coverage.statements}% statements, ${coverage.branches}% branches`);
122
+ } catch (error) {
123
+ results.coverage = {
124
+ success: false,
125
+ error: error.message
126
+ };
127
+ console.log('āš ļø Coverage generation failed:', error.message);
128
+ }
129
+
130
+ // 5. Generate final report
131
+ await generateTestReport(results);
132
+
133
+ } catch (error) {
134
+ console.error('āŒ Test runner failed:', error);
135
+ results.errors.push(`Runner error: ${error.message}`);
136
+ }
137
+
138
+ // 6. Print summary
139
+ printTestSummary(results);
140
+
141
+ // Exit with appropriate code
142
+ const hasFailures = results.errors.length > 0;
143
+ process.exit(hasFailures ? 1 : 0);
144
+ }
145
+
146
+ function extractTestDuration(output) {
147
+ const match = output.match(/Tests?.*?(\d+(?:\.\d+)?(?:m?s))/i);
148
+ return match ? match[1] : 'unknown';
149
+ }
150
+
151
+ function extractBddScenarios(output) {
152
+ const scenarioMatch = output.match(/(\d+) scenarios?/i);
153
+ const passedMatch = output.match(/(\d+) passed/i);
154
+ const failedMatch = output.match(/(\d+) failed/i);
155
+
156
+ return {
157
+ total: scenarioMatch ? parseInt(scenarioMatch[1]) : 0,
158
+ passed: passedMatch ? parseInt(passedMatch[1]) : 0,
159
+ failed: failedMatch ? parseInt(failedMatch[1]) : 0
160
+ };
161
+ }
162
+
163
+ function extractCoverageStats(output) {
164
+ const statementsMatch = output.match(/Statements\s*:\s*(\d+(?:\.\d+)?)%/);
165
+ const branchesMatch = output.match(/Branches\s*:\s*(\d+(?:\.\d+)?)%/);
166
+ const functionsMatch = output.match(/Functions\s*:\s*(\d+(?:\.\d+)?)%/);
167
+ const linesMatch = output.match(/Lines\s*:\s*(\d+(?:\.\d+)?)%/);
168
+
169
+ return {
170
+ statements: statementsMatch ? parseFloat(statementsMatch[1]) : 0,
171
+ branches: branchesMatch ? parseFloat(branchesMatch[1]) : 0,
172
+ functions: functionsMatch ? parseFloat(functionsMatch[1]) : 0,
173
+ lines: linesMatch ? parseFloat(linesMatch[1]) : 0
174
+ };
175
+ }
176
+
177
+ async function generateTestReport(results) {
178
+ const report = {
179
+ timestamp: new Date().toISOString(),
180
+ component: 'KGEN Injection System',
181
+ version: '1.0.0',
182
+ summary: {
183
+ total_test_suites: 0,
184
+ passed_test_suites: 0,
185
+ failed_test_suites: 0,
186
+ coverage_percentage: results.coverage?.statements || 0,
187
+ errors: results.errors
188
+ },
189
+ results: {
190
+ unit_tests: results.unit,
191
+ integration_tests: results.integration,
192
+ bdd_tests: results.bdd,
193
+ coverage: results.coverage
194
+ }
195
+ };
196
+
197
+ // Count test suites
198
+ [results.unit, results.integration, results.bdd].forEach(result => {
199
+ if (result) {
200
+ report.summary.total_test_suites++;
201
+ if (result.success) {
202
+ report.summary.passed_test_suites++;
203
+ } else {
204
+ report.summary.failed_test_suites++;
205
+ }
206
+ }
207
+ });
208
+
209
+ // Write report to file
210
+ const reportPath = join(rootDir, 'test-results', 'injection-test-report.json');
211
+ await fs.mkdir(dirname(reportPath), { recursive: true });
212
+ await fs.writeFile(reportPath, JSON.stringify(report, null, 2));
213
+
214
+ console.log(`\nšŸ“„ Test report written to: ${reportPath}`);
215
+ }
216
+
217
+ function printTestSummary(results) {
218
+ console.log('\n' + '='.repeat(50));
219
+ console.log('šŸ Test Summary');
220
+ console.log('='.repeat(50));
221
+
222
+ const suites = [
223
+ { name: 'Unit Tests', result: results.unit },
224
+ { name: 'Integration Tests', result: results.integration },
225
+ { name: 'BDD Feature Tests', result: results.bdd }
226
+ ];
227
+
228
+ suites.forEach(suite => {
229
+ const status = suite.result?.success ? 'āœ… PASS' : 'āŒ FAIL';
230
+ console.log(`${suite.name}: ${status}`);
231
+ });
232
+
233
+ if (results.coverage?.success) {
234
+ console.log(`\nCode Coverage:`);
235
+ console.log(` Statements: ${results.coverage.statements}%`);
236
+ console.log(` Branches: ${results.coverage.branches}%`);
237
+ console.log(` Functions: ${results.coverage.functions}%`);
238
+ console.log(` Lines: ${results.coverage.lines}%`);
239
+
240
+ // Check if coverage meets requirements
241
+ const meetsRequirements = results.coverage.statements >= 90 &&
242
+ results.coverage.branches >= 85 &&
243
+ results.coverage.functions >= 90;
244
+
245
+ console.log(`\nCoverage Requirements: ${meetsRequirements ? 'āœ… MET' : 'āŒ NOT MET'}`);
246
+ console.log(` Target: 90% statements, 85% branches, 90% functions`);
247
+ }
248
+
249
+ if (results.bdd?.scenarios) {
250
+ console.log(`\nBDD Scenarios:`);
251
+ console.log(` Total: ${results.bdd.scenarios.total}`);
252
+ console.log(` Passed: ${results.bdd.scenarios.passed}`);
253
+ console.log(` Failed: ${results.bdd.scenarios.failed}`);
254
+ }
255
+
256
+ if (results.errors.length > 0) {
257
+ console.log(`\nāŒ Errors (${results.errors.length}):`);
258
+ results.errors.forEach((error, index) => {
259
+ console.log(` ${index + 1}. ${error}`);
260
+ });
261
+ }
262
+
263
+ const overallStatus = results.errors.length === 0 ? 'āœ… SUCCESS' : 'āŒ FAILURE';
264
+ console.log(`\nOverall Result: ${overallStatus}`);
265
+
266
+ // Recommendations
267
+ if (results.coverage?.statements < 90) {
268
+ console.log('\nšŸ’” Recommendation: Increase test coverage to meet 90% target');
269
+ }
270
+
271
+ if (results.errors.length > 0) {
272
+ console.log('\nšŸ’” Recommendation: Fix failing tests before deployment');
273
+ }
274
+
275
+ console.log('='.repeat(50));
276
+ }
277
+
278
+ // Run tests if this file is executed directly
279
+ if (process.argv[1] === __filename) {
280
+ runTests().catch(console.error);
281
+ }
282
+
283
+ export { runTests };