@aigne/doc-smith 0.2.5 โ†’ 0.2.8

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 (41) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +1 -0
  3. package/agents/check-detail-result.mjs +13 -139
  4. package/agents/check-detail.mjs +4 -6
  5. package/agents/check-structure-plan.mjs +56 -12
  6. package/agents/detail-generator-and-translate.yaml +7 -1
  7. package/agents/detail-regenerator.yaml +3 -1
  8. package/agents/docs-generator.yaml +2 -1
  9. package/agents/find-item-by-path.mjs +64 -15
  10. package/agents/input-generator.mjs +31 -11
  11. package/agents/language-selector.mjs +89 -0
  12. package/agents/load-config.mjs +2 -2
  13. package/agents/load-sources.mjs +13 -40
  14. package/agents/publish-docs.mjs +47 -161
  15. package/agents/retranslate.yaml +74 -0
  16. package/agents/save-docs.mjs +19 -21
  17. package/agents/save-output.mjs +2 -9
  18. package/agents/save-single-doc.mjs +20 -1
  19. package/agents/schema/structure-plan.yaml +1 -1
  20. package/agents/structure-planning.yaml +6 -0
  21. package/agents/transform-detail-datasources.mjs +2 -5
  22. package/agents/translate.yaml +3 -0
  23. package/aigne.yaml +5 -1
  24. package/biome.json +13 -3
  25. package/docs-mcp/get-docs-structure.mjs +1 -1
  26. package/docs-mcp/read-doc-content.mjs +1 -4
  27. package/package.json +20 -7
  28. package/prompts/check-structure-planning-result.md +4 -7
  29. package/prompts/content-detail-generator.md +1 -2
  30. package/prompts/structure-planning.md +7 -2
  31. package/prompts/translator.md +4 -0
  32. package/tests/check-detail-result.test.mjs +8 -19
  33. package/tests/load-sources.test.mjs +65 -161
  34. package/tests/test-all-validation-cases.mjs +741 -0
  35. package/tests/test-save-docs.mjs +6 -17
  36. package/utils/constants.mjs +1 -2
  37. package/utils/markdown-checker.mjs +453 -0
  38. package/utils/mermaid-validator.mjs +153 -0
  39. package/utils/mermaid-worker-pool.mjs +250 -0
  40. package/utils/mermaid-worker.mjs +233 -0
  41. package/utils/utils.mjs +162 -114
@@ -0,0 +1,741 @@
1
+ #!/usr/bin/env node
2
+
3
+ import checkDetailResult from "../agents/check-detail-result.mjs";
4
+ import { checkMarkdown } from "../utils/markdown-checker.mjs";
5
+ import { shutdownValidation } from "../utils/mermaid-validator.mjs";
6
+
7
+ // Mock structure plan for link validation
8
+ const mockStructurePlan = [
9
+ { path: "./getting-started" },
10
+ { path: "/api/overview" },
11
+ { path: "./advanced/configuration" },
12
+ { path: "./tutorials/basic" },
13
+ { path: "/reference/api" },
14
+ ];
15
+
16
+ // Create allowed links set
17
+ const allowedLinks = new Set();
18
+ mockStructurePlan.forEach((item) => {
19
+ allowedLinks.add(item.path);
20
+ // Add processed .md path
21
+ let processedPath = item.path;
22
+ if (processedPath.startsWith(".")) {
23
+ processedPath = processedPath.replace(/^\./, "");
24
+ }
25
+ let flatPath = processedPath.replace(/^\//, "").replace(/\//g, "-");
26
+ flatPath = `./${flatPath}.md`;
27
+ allowedLinks.add(flatPath);
28
+ });
29
+
30
+ const testCases = [
31
+ // ========== PASSING CASES ==========
32
+ {
33
+ category: "โœ… PASSING CASES",
34
+ name: "Perfect valid document",
35
+ expectPass: true,
36
+ content: `# Getting Started Guide
37
+
38
+ This is a complete document with proper structure and formatting.
39
+
40
+ ## Introduction
41
+
42
+ Welcome to our comprehensive guide. This document follows all markdown best practices.
43
+
44
+ ## Code Examples
45
+
46
+ Here's a properly formatted code block:
47
+
48
+ \`\`\`javascript
49
+ function validateInput(data) {
50
+ if (!data) {
51
+ throw new Error("Data is required");
52
+ }
53
+ return data.trim();
54
+ }
55
+
56
+ // Export the function
57
+ export { validateInput };
58
+ \`\`\`
59
+
60
+ ## Data Tables
61
+
62
+ Our API supports the following data types:
63
+
64
+ |Type|Description|Example|
65
+ |----|-----------|-------|
66
+ |String|Text data|"Hello"|
67
+ |Number|Numeric values|42|
68
+ |Boolean|True/false|true|
69
+
70
+ ## Process Flow
71
+
72
+ The following diagram shows our validation process:
73
+
74
+ \`\`\`mermaid
75
+ flowchart TD
76
+ A[Start] --> B{Valid Input?}
77
+ B -->|Yes| C[Process Data]
78
+ B -->|No| D[Return Error]
79
+ C --> E[Save Results]
80
+ D --> F[Log Error]
81
+ E --> G[End]
82
+ F --> G
83
+ \`\`\`
84
+
85
+ ## Related Documentation
86
+
87
+ For more information, see our [API reference](/reference/api) and [advanced configuration](./advanced/configuration).
88
+
89
+ This document ends with proper punctuation and formatting.
90
+ `,
91
+ },
92
+
93
+ {
94
+ category: "โœ… PASSING CASES",
95
+ name: "Simple valid content",
96
+ expectPass: true,
97
+ content: `# Simple Document
98
+
99
+ This is a simple but valid document.
100
+
101
+ It has multiple paragraphs and proper structure.
102
+
103
+ The content ends with a period.
104
+ `,
105
+ },
106
+
107
+ {
108
+ category: "โœ… PASSING CASES",
109
+ name: "Valid content with Chinese punctuation",
110
+ expectPass: true,
111
+ content: `# ไธญๆ–‡ๆ–‡ๆกฃ
112
+
113
+ ่ฟ™ๆ˜ฏไธ€ไธชไธญๆ–‡ๆ–‡ๆกฃ็š„็คบไพ‹ใ€‚
114
+
115
+ ## ๅ†…ๅฎน่ฏดๆ˜Ž
116
+
117
+ ๆ–‡ๆกฃๅ†…ๅฎนไฝฟ็”จไธญๆ–‡ๆ ‡็‚น็ฌฆๅทใ€‚
118
+
119
+ ่ฟ™ไธชๆ–‡ๆกฃไปฅไธญๆ–‡ๅฅๅท็ป“ๅฐพใ€‚
120
+ `,
121
+ },
122
+
123
+ // ========== LINK VALIDATION CASES ==========
124
+ {
125
+ category: "๐Ÿ”— LINK VALIDATION",
126
+ name: "Dead internal link",
127
+ expectPass: false,
128
+ expectedErrors: ["dead link"],
129
+ content: `# Test Document
130
+
131
+ Check out this [broken link](./non-existent-page) for more info.
132
+
133
+ This content ends properly.
134
+ `,
135
+ },
136
+
137
+ {
138
+ category: "๐Ÿ”— LINK VALIDATION",
139
+ name: "Multiple dead links",
140
+ expectPass: false,
141
+ expectedErrors: ["dead link"],
142
+ content: `# Test Document
143
+
144
+ See [invalid page](./invalid) and [another broken link](/broken/path).
145
+
146
+ External links like [Google](https://google.com) should be ignored.
147
+
148
+ This content ends properly.
149
+ `,
150
+ },
151
+
152
+ {
153
+ category: "๐Ÿ”— LINK VALIDATION",
154
+ name: "Valid internal links",
155
+ expectPass: true,
156
+ content: `# Test Document
157
+
158
+ Check out our [getting started guide](./getting-started) and [API overview](/api/overview).
159
+
160
+ External links like [GitHub](https://github.com) and [email](mailto:test@example.com) are fine.
161
+
162
+ This content ends properly.
163
+ `,
164
+ },
165
+
166
+ // ========== CODE BLOCK VALIDATION CASES ==========
167
+ {
168
+ category: "๐Ÿ’ป CODE BLOCK VALIDATION",
169
+ name: "Incomplete code block",
170
+ expectPass: false,
171
+ expectedErrors: ["incomplete code block"],
172
+ content: `# Test Document
173
+
174
+ Here's incomplete code:
175
+
176
+ \`\`\`javascript
177
+ function incomplete() {
178
+ console.log("missing closing");
179
+ `,
180
+ },
181
+
182
+ {
183
+ category: "๐Ÿ’ป CODE BLOCK VALIDATION",
184
+ name: "Valid indented code block",
185
+ expectPass: true,
186
+ content: `# Test Document
187
+
188
+ Here's properly indented code:
189
+
190
+ \`\`\`javascript
191
+ function test() {
192
+ return "properly indented";
193
+ }
194
+ \`\`\`
195
+
196
+ This content ends properly.
197
+ `,
198
+ },
199
+
200
+ {
201
+ category: "๐Ÿ’ป CODE BLOCK VALIDATION",
202
+ name: "Code block with inconsistent indentation (user case)",
203
+ expectPass: false,
204
+ expectedErrors: ["code block with inconsistent indentation"],
205
+ content: `# API Response Handling
206
+
207
+ You can retrieve the response body in various formats:
208
+
209
+ * **\`response.content\`**: Accesses the raw response body as bytes. This is useful for non-text data like images or binary files.
210
+ \`\`\`python
211
+ import requests
212
+
213
+ r = requests.get('https://httpbin.org/image/png')
214
+ print(type(r.content))
215
+ # Expected output: <class 'bytes'>
216
+ \`\`\`
217
+
218
+ * **\`response.text\`**: Accesses the response body as Unicode text. Requests automatically guesses the encoding, or you can explicitly set \`response.encoding\`.
219
+ \`\`\`python
220
+ import requests
221
+
222
+ r = requests.get('https://httpbin.org/get')
223
+ print(type(r.text))
224
+ # Expected output: <class 'str'>
225
+ print(r.text)
226
+ # Expected output: {"args": {}, "headers": ..., "origin": "...", "url": "https://httpbin.org/get"}
227
+ \`\`\`
228
+
229
+ * **\`response.json(**kwargs)\`**: Decodes the response body as JSON into a Python object (dictionary, list, etc.). This method raises \`requests.exceptions.JSONDecodeError\` if the content is not valid JSON.
230
+ \`\`\`python
231
+ import requests
232
+
233
+ r = requests.get('https://httpbin.org/json')
234
+ print(type(r.json()))
235
+ # Expected output: <class 'dict'>
236
+ print(r.json())
237
+ # Expected output: {'slideshow': {'author': 'Yours Truly', 'date': 'date of publication', 'slides': [...], 'title': 'Sample Slide Show'}}
238
+ \`\`\`
239
+
240
+ **Status and Error Handling**
241
+
242
+ * **\`response.ok\`**: A boolean property that returns \`True\` if \`status_code\` is less than 400, indicating no client or server error. It does *not* necessarily mean \`200 OK\`.
243
+
244
+ * **\`response.raise_for_status()\`**: Raises an \`HTTPError\` if the HTTP request returned an unsuccessful status code (4xx or 5xx). This is a convenient way to check for errors and is typically used after a request to ensure it was successful.
245
+
246
+ \`\`\`python
247
+ import requests
248
+ from requests.exceptions import HTTPError
249
+
250
+ try:
251
+ r = requests.get('https://httpbin.org/status/404')
252
+ r.raise_for_status() # This will raise an HTTPError for 404
253
+ except HTTPError as e:
254
+ print(f"HTTP Error occurred: {e}")
255
+ # Expected output: HTTP Error occurred: 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404
256
+ \`\`\`
257
+
258
+ This document ends properly.
259
+ `,
260
+ },
261
+
262
+ // ========== CONTENT STRUCTURE CASES ==========
263
+ {
264
+ category: "๐Ÿ“ CONTENT STRUCTURE",
265
+ name: "Single line content",
266
+ expectPass: false,
267
+ expectedErrors: ["single line content"],
268
+ content: `This is just one line without any line breaks or proper structure`,
269
+ },
270
+
271
+ {
272
+ category: "๐Ÿ“ CONTENT STRUCTURE",
273
+ name: "Missing punctuation ending",
274
+ expectPass: false,
275
+ expectedErrors: ["incomplete content"],
276
+ content: `# Test Document
277
+
278
+ This content doesn't end with proper punctuation`,
279
+ },
280
+
281
+ {
282
+ category: "๐Ÿ“ CONTENT STRUCTURE",
283
+ name: "Content with proper line breaks",
284
+ expectPass: true,
285
+ content: `# Test Document
286
+
287
+ This content has proper line breaks.
288
+
289
+ Multiple paragraphs are formatted correctly.
290
+
291
+ The document ends with proper punctuation.
292
+ `,
293
+ },
294
+
295
+ // ========== TABLE VALIDATION CASES ==========
296
+ {
297
+ category: "๐Ÿ“Š TABLE VALIDATION",
298
+ name: "Table separator with fewer columns",
299
+ expectPass: false,
300
+ expectedErrors: ["table separator with mismatched column count"],
301
+ content: `# Test Document
302
+
303
+ | Column 1 | Column 2 | Column 3 |
304
+ | - | - |
305
+ | Data 1 | Data 2 | Data 3 |
306
+
307
+ This content ends properly.
308
+ `,
309
+ },
310
+
311
+ {
312
+ category: "๐Ÿ“Š TABLE VALIDATION",
313
+ name: "Table separator with more columns",
314
+ expectPass: false,
315
+ expectedErrors: ["table separator with mismatched column count"],
316
+ content: `# Test Document
317
+
318
+ | Column 1 | Column 2 |
319
+ |----------|----------|----------|
320
+ | Data 1 | Data 2 |
321
+
322
+ This content ends properly.
323
+ `,
324
+ },
325
+
326
+ {
327
+ category: "๐Ÿ“Š TABLE VALIDATION",
328
+ name: "Table data row with mismatched columns",
329
+ expectPass: false,
330
+ expectedErrors: ["table data row with mismatched column count"],
331
+ content: `# Test Document
332
+
333
+ | Column 1 | Column 2 |
334
+ |----------|----------|
335
+ | Data 1 | Data 2 | Data 3 |
336
+
337
+ This content ends properly.
338
+ `,
339
+ },
340
+
341
+ {
342
+ category: "๐Ÿ“Š TABLE VALIDATION",
343
+ name: "Valid table with consistent columns",
344
+ expectPass: true,
345
+ content: `# Test Document
346
+
347
+ |Column 1|Column 2|Column 3|
348
+ |--------|--------|--------|
349
+ |Data 1|Data 2|Data 3|
350
+ |Row 2|More|Data|
351
+
352
+ This content ends properly.
353
+
354
+ | ๅ‚ๆ•ฐ | ็ฑปๅž‹ | ๆ่ฟฐ |
355
+ |---|---|---|
356
+ | callback | () => void \\| Promise<void> | Payment Kit ็ป„ไปถ่ฟ่กŒๅŽ่ฆๆ‰ง่กŒ็š„ๅ‡ฝๆ•ฐใ€‚่ฟ™ๅฏไปฅๆ˜ฏไธ€ไธชๅผ‚ๆญฅๅ‡ฝๆ•ฐใ€‚ |
357
+ | wait | boolean | ๅฏ้€‰ใ€‚ๅฆ‚ๆžœไธบ ๏ผŒ็จๅŽๅœจ็ป„ไปถๅฏๅŠจๆ—ถๆ‰ง่กŒๅ›ž่ฐƒใ€‚ |
358
+
359
+ This document demonstrates escaped pipe handling.
360
+ `,
361
+ },
362
+
363
+ // ========== MERMAID VALIDATION CASES ==========
364
+ {
365
+ category: "๐Ÿงฉ MERMAID VALIDATION",
366
+ name: "Invalid Mermaid syntax",
367
+ expectPass: false,
368
+ expectedErrors: ["Mermaid syntax error"],
369
+ content: `# Test Document
370
+
371
+ \`\`\`mermaid
372
+ invalid diagram type
373
+ A --> B
374
+ \`\`\`
375
+
376
+ This content ends properly.
377
+ `,
378
+ },
379
+
380
+ {
381
+ category: "๐Ÿงฉ MERMAID VALIDATION",
382
+ name: "Mermaid with backticks in node labels",
383
+ expectPass: false,
384
+ expectedErrors: ["backticks in Mermaid node label"],
385
+ content: `# Test Document
386
+
387
+ \`\`\`mermaid
388
+ flowchart TD
389
+ A["label with \`backticks\`"] --> B[End]
390
+ C{"another \`label\` with backticks"} --> D[Final]
391
+ \`\`\`
392
+
393
+ This content ends properly.
394
+ `,
395
+ },
396
+
397
+ {
398
+ category: "๐Ÿงฉ MERMAID VALIDATION",
399
+ name: "Mermaid with numbered edge descriptions",
400
+ expectPass: false,
401
+ expectedErrors: ["numbered list format in Mermaid edge description"],
402
+ content: `# Test Document
403
+
404
+ \`\`\`mermaid
405
+ flowchart TD
406
+ A[Start] -- "1. First step" --> B[Middle]
407
+ B -- "2. Second step" --> C[End]
408
+ \`\`\`
409
+
410
+ This content ends properly.
411
+ `,
412
+ },
413
+
414
+ {
415
+ category: "๐Ÿงฉ MERMAID VALIDATION",
416
+ name: "Mermaid with unquoted special characters",
417
+ expectPass: false,
418
+ expectedErrors: ["unquoted special characters"],
419
+ content: `# Test Document
420
+
421
+ \`\`\`mermaid
422
+ flowchart LR
423
+ A[Start] --> B[Response.raw (file-like) is available]
424
+ B --> C[End]
425
+ \`\`\`
426
+
427
+ This content ends properly.
428
+ `,
429
+ },
430
+
431
+ {
432
+ category: "๐Ÿงฉ MERMAID VALIDATION",
433
+ name: "Valid Mermaid diagrams",
434
+ expectPass: true,
435
+ content: `# Test Document
436
+
437
+ \`\`\`mermaid
438
+ flowchart TD
439
+ A[Start] --> B{Decision}
440
+ B -->|Yes| C[Process]
441
+ B -->|No| D[End]
442
+ C --> E[Save]
443
+ E --> D
444
+ \`\`\`
445
+
446
+ \`\`\`mermaid
447
+ sequenceDiagram
448
+ participant A as Alice
449
+ participant B as Bob
450
+ A->>B: Hello Bob
451
+ B-->>A: Hello Alice
452
+ \`\`\`
453
+
454
+ This content ends properly.
455
+ `,
456
+ },
457
+
458
+ {
459
+ category: "๐Ÿงฉ MERMAID VALIDATION",
460
+ name: "Mermaid with subgraph reference issues (rendering failure)",
461
+ expectPass: false,
462
+ expectedErrors: ["subgraph reference"],
463
+ content: `# Test Document
464
+
465
+ \`\`\`mermaid
466
+ flowchart TD
467
+ A["FastAPI Application"] --> B["Security & Authentication"];
468
+ A --> C["Error Handling"];
469
+ A --> D["WebSockets"];
470
+ A --> E["Middleware"];
471
+ A --> F["Lifespan Events"];
472
+ A --> G["Database Integration"];
473
+ H["Project Structure"] -- "Organizes" --> A;
474
+ I["Application Settings"] -- "Configures" --> A;
475
+ J["Testing FastAPI Applications"] -- "Ensures Reliability" --> A;
476
+ A --> K["Deployment"];
477
+
478
+ subgraph Advanced Capabilities
479
+ B
480
+ C
481
+ D
482
+ E
483
+ F
484
+ G
485
+ end
486
+
487
+ subgraph Operational Excellence
488
+ H
489
+ I
490
+ J
491
+ K
492
+ end
493
+
494
+ AdvancedCapabilities --> "Robustness" --> L["Production-Ready API"];
495
+ OperationalExcellence --> "Maintainability & Scalability" --> L;
496
+ \`\`\`
497
+
498
+ This content ends properly.
499
+ `,
500
+ },
501
+
502
+ // ========== COMPLEX MIXED CASES ==========
503
+ {
504
+ category: "๐Ÿ”„ COMPLEX MIXED CASES",
505
+ name: "Multiple issues in one document",
506
+ expectPass: false,
507
+ expectedErrors: [
508
+ "dead link",
509
+ "table separator with mismatched column count",
510
+ "incomplete content",
511
+ ],
512
+ content: `# Complex Test Document
513
+
514
+ This document has [multiple issues](./broken-link).
515
+
516
+ | Column 1 | Column 2 | Column 3 |
517
+ | - | - |
518
+ | Data 1 | Data 2 | Data 3 |
519
+
520
+ \`\`\`mermaid
521
+ flowchart TD
522
+ A["node with \`backticks\`"] --> B[End]
523
+ \`\`\`
524
+
525
+ This content doesn't end properly`,
526
+ },
527
+
528
+ {
529
+ category: "๐Ÿ”„ COMPLEX MIXED CASES",
530
+ name: "Complex valid document with all elements",
531
+ expectPass: true,
532
+ content: `# Comprehensive Test Document
533
+
534
+ This document contains all supported elements in their correct form.
535
+
536
+ ## Links Section
537
+
538
+ Internal links: [Getting Started](./getting-started) and [API Reference](/api/overview).
539
+ External links: [GitHub](https://github.com) and [Email](mailto:support@example.com).
540
+
541
+ ## Code Examples
542
+
543
+ Standard code block:
544
+
545
+ \`\`\`javascript
546
+ function processData(input) {
547
+ const result = input.map(item => ({
548
+ id: item.id,
549
+ value: item.value * 2
550
+ }));
551
+ return result;
552
+ }
553
+ \`\`\`
554
+
555
+ ## Data Tables
556
+
557
+ |Feature|Status|Notes|
558
+ |-------|------|-----|
559
+ |API v1|Active|Current version|
560
+ |API v2|Beta|Testing phase|
561
+ |Dashboard|Complete|Ready for use|
562
+
563
+ ## Process Diagrams
564
+
565
+ Main workflow:
566
+
567
+ \`\`\`mermaid
568
+ flowchart TD
569
+ A[User Request] --> B{Validate Input}
570
+ B -->|Valid| C[Process Request]
571
+ B -->|Invalid| D[Return Error]
572
+ C --> E[Generate Response]
573
+ D --> F[Log Error]
574
+ E --> G[Send Response]
575
+ F --> G
576
+ \`\`\`
577
+
578
+ Sequence diagram:
579
+
580
+ \`\`\`mermaid
581
+ sequenceDiagram
582
+ participant U as User
583
+ participant A as API
584
+ participant D as Database
585
+
586
+ U->>A: Send Request
587
+ A->>D: Query Data
588
+ D-->>A: Return Results
589
+ A-->>U: Send Response
590
+ \`\`\`
591
+
592
+ ## Conclusion
593
+
594
+ This comprehensive document demonstrates all validation rules in their correct usage.
595
+ `,
596
+ },
597
+ ];
598
+
599
+ async function runValidationTests() {
600
+ console.log("๐Ÿงช Comprehensive Markdown Validation Test Suite\n");
601
+ console.log("=".repeat(80));
602
+
603
+ let totalTests = 0;
604
+ let passedTests = 0;
605
+ let failedTests = 0;
606
+
607
+ let currentCategory = "";
608
+
609
+ for (const testCase of testCases) {
610
+ // Print category header if it changed
611
+ if (testCase.category !== currentCategory) {
612
+ currentCategory = testCase.category;
613
+ console.log(`\n${currentCategory}`);
614
+ console.log("-".repeat(80));
615
+ }
616
+
617
+ console.log(`\n๐Ÿ“ Testing: ${testCase.name}`);
618
+ totalTests++;
619
+
620
+ try {
621
+ // Test with checkMarkdown directly
622
+ const errors = await checkMarkdown(testCase.content, "test", {
623
+ allowedLinks,
624
+ });
625
+
626
+ // Test with checkDetailResult wrapper
627
+ const wrapperResult = await checkDetailResult({
628
+ structurePlan: mockStructurePlan,
629
+ reviewContent: testCase.content,
630
+ });
631
+
632
+ const hasErrors = errors.length > 0;
633
+ const expectPass = testCase.expectPass;
634
+
635
+ // Verify test expectation
636
+ if (expectPass && !hasErrors) {
637
+ console.log("โœ… PASS - Content correctly passed validation");
638
+ passedTests++;
639
+ } else if (!expectPass && hasErrors) {
640
+ console.log("โœ… PASS - Content correctly failed validation");
641
+
642
+ // Check if expected error types are present
643
+ if (testCase.expectedErrors) {
644
+ const foundExpectedErrors = testCase.expectedErrors.every((expectedError) =>
645
+ errors.some((error) => error.toLowerCase().includes(expectedError.toLowerCase())),
646
+ );
647
+
648
+ if (foundExpectedErrors) {
649
+ console.log("โœ… Expected error types found");
650
+ } else {
651
+ console.log("โš ๏ธ Expected error types not all found");
652
+ console.log(` Expected: ${testCase.expectedErrors.join(", ")}`);
653
+ }
654
+ }
655
+
656
+ passedTests++;
657
+ } else {
658
+ console.log(
659
+ `โŒ FAIL - Expected ${expectPass ? "PASS" : "FAIL"} but got ${
660
+ hasErrors ? "FAIL" : "PASS"
661
+ }`,
662
+ );
663
+ failedTests++;
664
+ }
665
+
666
+ // Show error details for failing cases
667
+ if (hasErrors) {
668
+ console.log(` Found ${errors.length} issue(s):`);
669
+ errors.slice(0, 3).forEach((error) => {
670
+ console.log(` โ€ข ${error}`);
671
+ });
672
+ if (errors.length > 3) {
673
+ console.log(` ... and ${errors.length - 3} more issues`);
674
+ }
675
+ }
676
+
677
+ // Verify consistency between direct call and wrapper
678
+ const wrapperErrors = wrapperResult.detailFeedback
679
+ ? wrapperResult.detailFeedback.split("\n").filter((line) => line.trim())
680
+ : [];
681
+
682
+ if (errors.length === wrapperErrors.length) {
683
+ console.log("โœ… Direct call and wrapper consistent");
684
+ } else {
685
+ console.log(
686
+ `โš ๏ธ Inconsistent results: direct=${errors.length}, wrapper=${wrapperErrors.length}`,
687
+ );
688
+ }
689
+ } catch (error) {
690
+ console.log(`โŒ ERROR: ${error.message}`);
691
+ failedTests++;
692
+ }
693
+ }
694
+
695
+ // Final summary
696
+ console.log(`\n${"=".repeat(80)}`);
697
+ console.log("๐Ÿ“Š TEST SUMMARY");
698
+ console.log("=".repeat(80));
699
+ console.log(`Total Tests: ${totalTests}`);
700
+ console.log(`Passed: ${passedTests} โœ…`);
701
+ console.log(`Failed: ${failedTests} โŒ`);
702
+ console.log(`Success Rate: ${((passedTests / totalTests) * 100).toFixed(1)}%`);
703
+
704
+ console.log("\n๐Ÿ” VALIDATION COVERAGE:");
705
+ console.log("โœ… Link validation (dead links, allowed links)");
706
+ console.log("โœ… Code block validation (indentation, completeness)");
707
+ console.log("โœ… Content structure (line breaks, punctuation)");
708
+ console.log("โœ… Table validation (column count consistency)");
709
+ console.log("โœ… Mermaid validation (syntax, rendering issues)");
710
+ console.log("โœ… Standard markdown linting (formatting rules)");
711
+ console.log("โœ… Complex mixed scenarios");
712
+ console.log("โœ… Edge cases and error conditions");
713
+
714
+ if (failedTests === 0) {
715
+ console.log("\n๐ŸŽ‰ ALL TESTS PASSED! Validation system is working correctly.");
716
+ } else {
717
+ console.log(`\nโš ๏ธ ${failedTests} test(s) failed. Please review the validation logic.`);
718
+ }
719
+
720
+ // Shutdown worker pool to ensure clean exit
721
+ try {
722
+ await shutdownValidation();
723
+ } catch (error) {
724
+ console.error("Error shutting down validation:", error);
725
+ }
726
+ }
727
+
728
+ // Export test cases for external use
729
+ export { testCases, mockStructurePlan, allowedLinks };
730
+
731
+ // Run tests if this file is executed directly
732
+ if (import.meta.url === `file://${process.argv[1]}`) {
733
+ runValidationTests()
734
+ .then(() => {
735
+ process.exit(0);
736
+ })
737
+ .catch((error) => {
738
+ console.error("Test suite error:", error);
739
+ process.exit(1);
740
+ });
741
+ }