@aigne/doc-smith 0.7.2 → 0.8.0

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.
@@ -1,686 +0,0 @@
1
- import { afterAll, describe, expect, test } from "bun:test";
2
- import checkDetailResult from "../agents/check-detail-result.mjs";
3
- import { checkMarkdown } from "../utils/markdown-checker.mjs";
4
- import { shutdownValidation } from "../utils/mermaid-validator.mjs";
5
-
6
- // Mock structure plan for link validation
7
- const mockStructurePlan = [
8
- { path: "./getting-started" },
9
- { path: "/api/overview" },
10
- { path: "./advanced/configuration" },
11
- { path: "./tutorials/basic" },
12
- { path: "/reference/api" },
13
- ];
14
-
15
- // Create allowed links set
16
- const allowedLinks = new Set();
17
- mockStructurePlan.forEach((item) => {
18
- allowedLinks.add(item.path);
19
- // Add processed .md path
20
- let processedPath = item.path;
21
- if (processedPath.startsWith(".")) {
22
- processedPath = processedPath.replace(/^\./, "");
23
- }
24
- let flatPath = processedPath.replace(/^\//, "").replace(/\//g, "-");
25
- flatPath = `./${flatPath}.md`;
26
- allowedLinks.add(flatPath);
27
- });
28
-
29
- const testCases = [
30
- // ========== PASSING CASES ==========
31
- {
32
- category: "✅ PASSING CASES",
33
- name: "Perfect valid document",
34
- expectPass: true,
35
- content: `# Getting Started Guide
36
-
37
- This is a complete document with proper structure and formatting.
38
-
39
- ## Introduction
40
-
41
- Welcome to our comprehensive guide. This document follows all markdown best practices.
42
-
43
- ## Code Examples
44
-
45
- Here's a properly formatted code block:
46
-
47
- \`\`\`javascript
48
- function validateInput(data) {
49
- if (!data) {
50
- throw new Error("Data is required");
51
- }
52
- return data.trim();
53
- }
54
-
55
- // Export the function
56
- export { validateInput };
57
- \`\`\`
58
-
59
- ## Data Tables
60
-
61
- Our API supports the following data types:
62
-
63
- |Type|Description|Example|
64
- |----|-----------|-------|
65
- |String|Text data|"Hello"|
66
- |Number|Numeric values|42|
67
- |Boolean|True/false|true|
68
-
69
- ## Process Flow
70
-
71
- The following diagram shows our validation process:
72
-
73
- \`\`\`mermaid
74
- flowchart TD
75
- A[Start] --> B{Valid Input?}
76
- B -->|Yes| C[Process Data]
77
- B -->|No| D[Return Error]
78
- C --> E[Save Results]
79
- D --> F[Log Error]
80
- E --> G[End]
81
- F --> G
82
- \`\`\`
83
-
84
- ## Related Documentation
85
-
86
- For more information, see our [API reference](/reference/api) and [advanced configuration](./advanced/configuration).
87
-
88
- This document ends with proper punctuation and formatting.
89
- `,
90
- },
91
-
92
- {
93
- category: "✅ PASSING CASES",
94
- name: "Simple valid content",
95
- expectPass: true,
96
- content: `# Simple Document
97
-
98
- This is a simple but valid document.
99
-
100
- It has multiple paragraphs and proper structure.
101
-
102
- The content ends with a period.
103
- `,
104
- },
105
-
106
- {
107
- category: "✅ PASSING CASES",
108
- name: "Valid content with Chinese punctuation",
109
- expectPass: true,
110
- content: `# 中文文档
111
-
112
- 这是一个中文文档的示例。
113
-
114
- ## 内容说明
115
-
116
- 文档内容使用中文标点符号。
117
-
118
- 这个文档以中文句号结尾。
119
- `,
120
- },
121
-
122
- // ========== LINK VALIDATION CASES ==========
123
- {
124
- category: "🔗 LINK VALIDATION",
125
- name: "Dead internal link",
126
- expectPass: false,
127
- expectedErrors: ["dead link"],
128
- content: `# Test Document
129
-
130
- Check out this [broken link](./non-existent-page) for more info.
131
-
132
- This content ends properly.
133
- `,
134
- },
135
-
136
- {
137
- category: "🔗 LINK VALIDATION",
138
- name: "Multiple dead links",
139
- expectPass: false,
140
- expectedErrors: ["dead link"],
141
- content: `# Test Document
142
-
143
- See [invalid page](./invalid) and [another broken link](/broken/path).
144
-
145
- External links like [Google](https://google.com) should be ignored.
146
-
147
- This content ends properly.
148
- `,
149
- },
150
-
151
- {
152
- category: "🔗 LINK VALIDATION",
153
- name: "Valid internal links",
154
- expectPass: true,
155
- content: `# Test Document
156
-
157
- Check out our [getting started guide](./getting-started) and [API overview](/api/overview).
158
-
159
- External links like [GitHub](https://github.com) and [email](mailto:test@example.com) are fine.
160
-
161
- This content ends properly.
162
- `,
163
- },
164
-
165
- // ========== CODE BLOCK VALIDATION CASES ==========
166
- {
167
- category: "💻 CODE BLOCK VALIDATION",
168
- name: "Incomplete code block",
169
- expectPass: false,
170
- expectedErrors: ["incomplete code block"],
171
- content: `# Test Document
172
-
173
- Here's incomplete code:
174
-
175
- \`\`\`javascript
176
- function incomplete() {
177
- console.log("missing closing");
178
- `,
179
- },
180
-
181
- {
182
- category: "💻 CODE BLOCK VALIDATION",
183
- name: "Valid indented code block",
184
- expectPass: true,
185
- content: `# Test Document
186
-
187
- Here's properly indented code:
188
-
189
- \`\`\`javascript
190
- function test() {
191
- return "properly indented";
192
- }
193
- \`\`\`
194
-
195
- This content ends properly.
196
- `,
197
- },
198
-
199
- {
200
- category: "💻 CODE BLOCK VALIDATION",
201
- name: "Code block with inconsistent indentation (user case)",
202
- expectPass: false,
203
- expectedErrors: ["code block with inconsistent indentation"],
204
- content: `# API Response Handling
205
-
206
- You can retrieve the response body in various formats:
207
-
208
- * **\`response.content\`**: Accesses the raw response body as bytes. This is useful for non-text data like images or binary files.
209
- \`\`\`python
210
- import requests
211
-
212
- r = requests.get('https://httpbin.org/image/png')
213
- print(type(r.content))
214
- # Expected output: <class 'bytes'>
215
- \`\`\`
216
-
217
- * **\`response.text\`**: Accesses the response body as Unicode text. Requests automatically guesses the encoding, or you can explicitly set \`response.encoding\`.
218
- \`\`\`python
219
- import requests
220
-
221
- r = requests.get('https://httpbin.org/get')
222
- print(type(r.text))
223
- # Expected output: <class 'str'>
224
- print(r.text)
225
- # Expected output: {"args": {}, "headers": ..., "origin": "...", "url": "https://httpbin.org/get"}
226
- \`\`\`
227
-
228
- * **\`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.
229
- \`\`\`python
230
- import requests
231
-
232
- r = requests.get('https://httpbin.org/json')
233
- print(type(r.json()))
234
- # Expected output: <class 'dict'>
235
- print(r.json())
236
- # Expected output: {'slideshow': {'author': 'Yours Truly', 'date': 'date of publication', 'slides': [...], 'title': 'Sample Slide Show'}}
237
- \`\`\`
238
-
239
- **Status and Error Handling**
240
-
241
- * **\`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\`.
242
-
243
- * **\`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.
244
-
245
- \`\`\`python
246
- import requests
247
- from requests.exceptions import HTTPError
248
-
249
- try:
250
- r = requests.get('https://httpbin.org/status/404')
251
- r.raise_for_status() # This will raise an HTTPError for 404
252
- except HTTPError as e:
253
- print(f"HTTP Error occurred: {e}")
254
- # Expected output: HTTP Error occurred: 404 Client Error: NOT FOUND for url: https://httpbin.org/status/404
255
- \`\`\`
256
-
257
- This document ends properly.
258
- `,
259
- },
260
-
261
- // ========== CONTENT STRUCTURE CASES ==========
262
- {
263
- category: "📝 CONTENT STRUCTURE",
264
- name: "Single line content",
265
- expectPass: false,
266
- expectedErrors: ["single line content"],
267
- content: `This is just one line without any line breaks or proper structure`,
268
- },
269
-
270
- {
271
- category: "📝 CONTENT STRUCTURE",
272
- name: "Missing punctuation ending",
273
- expectPass: false,
274
- expectedErrors: ["incomplete content"],
275
- content: `# Test Document
276
-
277
- This content doesn't end with proper punctuation`,
278
- },
279
-
280
- {
281
- category: "📝 CONTENT STRUCTURE",
282
- name: "Content with proper line breaks",
283
- expectPass: true,
284
- content: `# Test Document
285
-
286
- This content has proper line breaks.
287
-
288
- Multiple paragraphs are formatted correctly.
289
-
290
- The document ends with proper punctuation.
291
- `,
292
- },
293
-
294
- // ========== TABLE VALIDATION CASES ==========
295
- {
296
- category: "📊 TABLE VALIDATION",
297
- name: "Table separator with fewer columns",
298
- expectPass: false,
299
- expectedErrors: ["table separator with mismatched column count"],
300
- content: `# Test Document
301
-
302
- | Column 1 | Column 2 | Column 3 |
303
- | - | - |
304
- | Data 1 | Data 2 | Data 3 |
305
-
306
- This content ends properly.
307
- `,
308
- },
309
-
310
- {
311
- category: "📊 TABLE VALIDATION",
312
- name: "Table separator with more columns",
313
- expectPass: false,
314
- expectedErrors: ["table separator with mismatched column count"],
315
- content: `# Test Document
316
-
317
- | Column 1 | Column 2 |
318
- |----------|----------|----------|
319
- | Data 1 | Data 2 |
320
-
321
- This content ends properly.
322
- `,
323
- },
324
-
325
- {
326
- category: "📊 TABLE VALIDATION",
327
- name: "Table data row with mismatched columns",
328
- expectPass: false,
329
- expectedErrors: ["table data row with mismatched column count"],
330
- content: `# Test Document
331
-
332
- | Column 1 | Column 2 |
333
- |----------|----------|
334
- | Data 1 | Data 2 | Data 3 |
335
-
336
- This content ends properly.
337
- `,
338
- },
339
-
340
- {
341
- category: "📊 TABLE VALIDATION",
342
- name: "Valid table with consistent columns",
343
- expectPass: true,
344
- content: `# Test Document
345
-
346
- |Column 1|Column 2|Column 3|
347
- |--------|--------|--------|
348
- |Data 1|Data 2|Data 3|
349
- |Row 2|More|Data|
350
-
351
- This content ends properly.
352
-
353
- | 参数 | 类型 | 描述 |
354
- |---|---|---|
355
- | callback | () => void \\| Promise<void> | Payment Kit 组件运行后要执行的函数。这可以是一个异步函数。 |
356
- | wait | boolean | 可选。如果为 ,稍后在组件启动时执行回调。 |
357
-
358
- This document demonstrates escaped pipe handling.
359
- `,
360
- },
361
-
362
- // ========== MERMAID VALIDATION CASES ==========
363
- {
364
- category: "🧩 MERMAID VALIDATION",
365
- name: "Invalid Mermaid syntax",
366
- expectPass: false,
367
- expectedErrors: ["Mermaid syntax error"],
368
- content: `# Test Document
369
-
370
- \`\`\`mermaid
371
- invalid diagram type
372
- A --> B
373
- \`\`\`
374
-
375
- This content ends properly.
376
- `,
377
- },
378
-
379
- {
380
- category: "🧩 MERMAID VALIDATION",
381
- name: "Mermaid with backticks in node labels",
382
- expectPass: false,
383
- expectedErrors: ["backticks in Mermaid node label"],
384
- content: `# Test Document
385
-
386
- \`\`\`mermaid
387
- flowchart TD
388
- A["label with \`backticks\`"] --> B[End]
389
- C{"another \`label\` with backticks"} --> D[Final]
390
- \`\`\`
391
-
392
- This content ends properly.
393
- `,
394
- },
395
-
396
- {
397
- category: "🧩 MERMAID VALIDATION",
398
- name: "Mermaid with numbered edge descriptions",
399
- expectPass: false,
400
- expectedErrors: ["numbered list format in Mermaid edge description"],
401
- content: `# Test Document
402
-
403
- \`\`\`mermaid
404
- flowchart TD
405
- A[Start] -- "1. First step" --> B[Middle]
406
- B -- "2. Second step" --> C[End]
407
- \`\`\`
408
-
409
- This content ends properly.
410
- `,
411
- },
412
-
413
- {
414
- category: "🧩 MERMAID VALIDATION",
415
- name: "Mermaid with unquoted special characters",
416
- expectPass: false,
417
- expectedErrors: ["unquoted special characters"],
418
- content: `# Test Document
419
-
420
- \`\`\`mermaid
421
- flowchart LR
422
- A[Start] --> B[Response.raw (file-like) is available]
423
- B --> C[End]
424
- \`\`\`
425
-
426
- This content ends properly.
427
- `,
428
- },
429
-
430
- {
431
- category: "🧩 MERMAID VALIDATION",
432
- name: "Valid Mermaid diagrams",
433
- expectPass: true,
434
- content: `# Test Document
435
-
436
- \`\`\`mermaid
437
- flowchart TD
438
- A[Start] --> B{Decision}
439
- B -->|Yes| C[Process]
440
- B -->|No| D[End]
441
- C --> E[Save]
442
- E --> D
443
- \`\`\`
444
-
445
- \`\`\`mermaid
446
- sequenceDiagram
447
- participant A as Alice
448
- participant B as Bob
449
- A->>B: Hello Bob
450
- B-->>A: Hello Alice
451
- \`\`\`
452
-
453
- This content ends properly.
454
- `,
455
- },
456
-
457
- {
458
- category: "🧩 MERMAID VALIDATION",
459
- name: "Mermaid with subgraph reference issues (rendering failure)",
460
- expectPass: false,
461
- expectedErrors: ["Mermaid syntax error"],
462
- content: `# Test Document
463
-
464
- \`\`\`mermaid
465
- flowchart TD
466
- A["FastAPI Application"] --> B["Security & Authentication"];
467
- A --> C["Error Handling"];
468
- A --> D["WebSockets"];
469
- A --> E["Middleware"];
470
- A --> F["Lifespan Events"];
471
- A --> G["Database Integration"];
472
- H["Project Structure"] -- "Organizes" --> A;
473
- I["Application Settings"] -- "Configures" --> A;
474
- J["Testing FastAPI Applications"] -- "Ensures Reliability" --> A;
475
- A --> K["Deployment"];
476
-
477
- subgraph Advanced Capabilities
478
- B
479
- C
480
- D
481
- E
482
- F
483
- G
484
- end
485
-
486
- subgraph Operational Excellence
487
- H
488
- I
489
- J
490
- K
491
- end
492
-
493
- AdvancedCapabilities --> "Robustness" --> L["Production-Ready API"];
494
- OperationalExcellence --> "Maintainability & Scalability" --> L;
495
- \`\`\`
496
-
497
- This content ends properly.
498
- `,
499
- },
500
-
501
- {
502
- category: "🧩 MERMAID VALIDATION",
503
- name: "Mermaid with numbered list format in node labels",
504
- expectPass: false,
505
- expectedErrors: ["numbered list format in Mermaid node label"],
506
- content: `# Test Document
507
-
508
- \`\`\`mermaid
509
- flowchart TD
510
- A["1. Create Backend Implementation<br>api/src/providers/"]
511
- B["2. Add Backend Configuration<br>api/src/providers/models.ts"]
512
- C["3. Update Frontend Selector<br>src/pages/config/ai-providers/"]
513
- D["4. Add Provider Logo<br>public/logo/"]
514
- E["5. Update Documentation"]
515
-
516
- A --> B --> C --> D --> E
517
- \`\`\`
518
-
519
- This content ends properly.
520
- `,
521
- },
522
-
523
- // ========== COMPLEX MIXED CASES ==========
524
- {
525
- category: "🔄 COMPLEX MIXED CASES",
526
- name: "Multiple issues in one document",
527
- expectPass: false,
528
- expectedErrors: [
529
- "dead link",
530
- "table separator with mismatched column count",
531
- "incomplete content",
532
- ],
533
- content: `# Complex Test Document
534
-
535
- This document has [multiple issues](./broken-link).
536
-
537
- | Column 1 | Column 2 | Column 3 |
538
- | - | - |
539
- | Data 1 | Data 2 | Data 3 |
540
-
541
- \`\`\`mermaid
542
- flowchart TD
543
- A["node with \`backticks\`"] --> B[End]
544
- \`\`\`
545
-
546
- This content doesn't end properly`,
547
- },
548
-
549
- {
550
- category: "🔄 COMPLEX MIXED CASES",
551
- name: "Complex valid document with all elements",
552
- expectPass: true,
553
- content: `# Comprehensive Test Document
554
-
555
- This document contains all supported elements in their correct form.
556
-
557
- ## Links Section
558
-
559
- Internal links: [Getting Started](./getting-started) and [API Reference](/api/overview).
560
- External links: [GitHub](https://github.com) and [Email](mailto:support@example.com).
561
-
562
- ## Code Examples
563
-
564
- Standard code block:
565
-
566
- \`\`\`javascript
567
- function processData(input) {
568
- const result = input.map(item => ({
569
- id: item.id,
570
- value: item.value * 2
571
- }));
572
- return result;
573
- }
574
- \`\`\`
575
-
576
- ## Data Tables
577
-
578
- |Feature|Status|Notes|
579
- |-------|------|-----|
580
- |API v1|Active|Current version|
581
- |API v2|Beta|Testing phase|
582
- |Dashboard|Complete|Ready for use|
583
-
584
- ## Process Diagrams
585
-
586
- Main workflow:
587
-
588
- \`\`\`mermaid
589
- flowchart TD
590
- A[User Request] --> B{Validate Input}
591
- B -->|Valid| C[Process Request]
592
- B -->|Invalid| D[Return Error]
593
- C --> E[Generate Response]
594
- D --> F[Log Error]
595
- E --> G[Send Response]
596
- F --> G
597
- \`\`\`
598
-
599
- Sequence diagram:
600
-
601
- \`\`\`mermaid
602
- sequenceDiagram
603
- participant U as User
604
- participant A as API
605
- participant D as Database
606
-
607
- U->>A: Send Request
608
- A->>D: Query Data
609
- D-->>A: Return Results
610
- A-->>U: Send Response
611
- \`\`\`
612
-
613
- ## Conclusion
614
-
615
- This comprehensive document demonstrates all validation rules in their correct usage.
616
- `,
617
- },
618
- ];
619
-
620
- describe("Markdown Validation Test Suite", () => {
621
- afterAll(async () => {
622
- // Shutdown worker pool to ensure clean exit
623
- try {
624
- await shutdownValidation();
625
- } catch {
626
- // Ignore shutdown errors in tests
627
- }
628
- });
629
-
630
- // Group tests by category
631
- const testsByCategory = testCases.reduce((acc, testCase) => {
632
- if (!acc[testCase.category]) {
633
- acc[testCase.category] = [];
634
- }
635
- acc[testCase.category].push(testCase);
636
- return acc;
637
- }, {});
638
-
639
- Object.entries(testsByCategory).forEach(([category, categoryTests]) => {
640
- describe(category, () => {
641
- categoryTests.forEach((testCase) => {
642
- test(testCase.name, async () => {
643
- // Test with checkMarkdown directly
644
- const errors = await checkMarkdown(testCase.content, "test", {
645
- allowedLinks,
646
- });
647
-
648
- // Test with checkDetailResult wrapper
649
- const wrapperResult = await checkDetailResult({
650
- structurePlan: mockStructurePlan,
651
- reviewContent: testCase.content,
652
- });
653
-
654
- const hasErrors = errors.length > 0;
655
- const expectPass = testCase.expectPass;
656
-
657
- // Verify test expectation
658
- if (expectPass) {
659
- expect(hasErrors).toBe(false);
660
- } else {
661
- expect(hasErrors).toBe(true);
662
-
663
- // Check if expected error types are present
664
- if (testCase.expectedErrors) {
665
- const foundExpectedErrors = testCase.expectedErrors.every((expectedError) =>
666
- errors.some((error) => error.toLowerCase().includes(expectedError.toLowerCase())),
667
- );
668
- expect(foundExpectedErrors).toBe(true);
669
- }
670
- }
671
-
672
- // Verify consistency between direct call and wrapper
673
- const wrapperErrors = wrapperResult.detailFeedback
674
- ? wrapperResult.detailFeedback.split("\n").filter((line) => line.trim())
675
- : [];
676
-
677
- // Note: We don't enforce exact equality as wrapper may format differently
678
- expect(wrapperErrors.length > 0).toBe(hasErrors);
679
- });
680
- });
681
- });
682
- });
683
- });
684
-
685
- // Export test cases for external use
686
- export { testCases, mockStructurePlan, allowedLinks };