@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.
- package/CHANGELOG.md +26 -0
- package/README.md +1 -0
- package/agents/check-detail-result.mjs +13 -139
- package/agents/check-detail.mjs +4 -6
- package/agents/check-structure-plan.mjs +56 -12
- package/agents/detail-generator-and-translate.yaml +7 -1
- package/agents/detail-regenerator.yaml +3 -1
- package/agents/docs-generator.yaml +2 -1
- package/agents/find-item-by-path.mjs +64 -15
- package/agents/input-generator.mjs +31 -11
- package/agents/language-selector.mjs +89 -0
- package/agents/load-config.mjs +2 -2
- package/agents/load-sources.mjs +13 -40
- package/agents/publish-docs.mjs +47 -161
- package/agents/retranslate.yaml +74 -0
- package/agents/save-docs.mjs +19 -21
- package/agents/save-output.mjs +2 -9
- package/agents/save-single-doc.mjs +20 -1
- package/agents/schema/structure-plan.yaml +1 -1
- package/agents/structure-planning.yaml +6 -0
- package/agents/transform-detail-datasources.mjs +2 -5
- package/agents/translate.yaml +3 -0
- package/aigne.yaml +5 -1
- package/biome.json +13 -3
- package/docs-mcp/get-docs-structure.mjs +1 -1
- package/docs-mcp/read-doc-content.mjs +1 -4
- package/package.json +20 -7
- package/prompts/check-structure-planning-result.md +4 -7
- package/prompts/content-detail-generator.md +1 -2
- package/prompts/structure-planning.md +7 -2
- package/prompts/translator.md +4 -0
- package/tests/check-detail-result.test.mjs +8 -19
- package/tests/load-sources.test.mjs +65 -161
- package/tests/test-all-validation-cases.mjs +741 -0
- package/tests/test-save-docs.mjs +6 -17
- package/utils/constants.mjs +1 -2
- package/utils/markdown-checker.mjs +453 -0
- package/utils/mermaid-validator.mjs +153 -0
- package/utils/mermaid-worker-pool.mjs +250 -0
- package/utils/mermaid-worker.mjs +233 -0
- 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
|
+
}
|