code-abyss 1.5.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 (64) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +197 -0
  3. package/bin/install.js +193 -0
  4. package/bin/uninstall.js +42 -0
  5. package/config/AGENTS.md +247 -0
  6. package/config/CLAUDE.md +207 -0
  7. package/config/settings.example.json +27 -0
  8. package/output-styles/abyss-cultivator.md +399 -0
  9. package/package.json +41 -0
  10. package/skills/SKILL.md +115 -0
  11. package/skills/ai/SKILL.md +29 -0
  12. package/skills/ai/agent-dev.md +242 -0
  13. package/skills/ai/llm-security.md +288 -0
  14. package/skills/architecture/SKILL.md +41 -0
  15. package/skills/architecture/api-design.md +225 -0
  16. package/skills/architecture/caching.md +299 -0
  17. package/skills/architecture/cloud-native.md +285 -0
  18. package/skills/architecture/compliance.md +299 -0
  19. package/skills/architecture/data-security.md +184 -0
  20. package/skills/architecture/message-queue.md +329 -0
  21. package/skills/architecture/security-arch.md +210 -0
  22. package/skills/development/SKILL.md +43 -0
  23. package/skills/development/cpp.md +246 -0
  24. package/skills/development/go.md +323 -0
  25. package/skills/development/java.md +277 -0
  26. package/skills/development/python.md +288 -0
  27. package/skills/development/rust.md +313 -0
  28. package/skills/development/shell.md +313 -0
  29. package/skills/development/typescript.md +277 -0
  30. package/skills/devops/SKILL.md +36 -0
  31. package/skills/devops/cost-optimization.md +272 -0
  32. package/skills/devops/database.md +217 -0
  33. package/skills/devops/devsecops.md +198 -0
  34. package/skills/devops/git-workflow.md +181 -0
  35. package/skills/devops/observability.md +280 -0
  36. package/skills/devops/performance.md +273 -0
  37. package/skills/devops/testing.md +186 -0
  38. package/skills/gen-docs/SKILL.md +114 -0
  39. package/skills/gen-docs/scripts/doc_generator.py +491 -0
  40. package/skills/multi-agent/SKILL.md +268 -0
  41. package/skills/run_skill.py +88 -0
  42. package/skills/security/SKILL.md +51 -0
  43. package/skills/security/blue-team.md +379 -0
  44. package/skills/security/code-audit.md +265 -0
  45. package/skills/security/pentest.md +226 -0
  46. package/skills/security/red-team.md +321 -0
  47. package/skills/security/threat-intel.md +322 -0
  48. package/skills/security/vuln-research.md +369 -0
  49. package/skills/tests/README.md +225 -0
  50. package/skills/tests/SUMMARY.md +362 -0
  51. package/skills/tests/__init__.py +3 -0
  52. package/skills/tests/test_change_analyzer.py +558 -0
  53. package/skills/tests/test_doc_generator.py +538 -0
  54. package/skills/tests/test_module_scanner.py +376 -0
  55. package/skills/tests/test_quality_checker.py +516 -0
  56. package/skills/tests/test_security_scanner.py +426 -0
  57. package/skills/verify-change/SKILL.md +138 -0
  58. package/skills/verify-change/scripts/change_analyzer.py +529 -0
  59. package/skills/verify-module/SKILL.md +125 -0
  60. package/skills/verify-module/scripts/module_scanner.py +321 -0
  61. package/skills/verify-quality/SKILL.md +158 -0
  62. package/skills/verify-quality/scripts/quality_checker.py +481 -0
  63. package/skills/verify-security/SKILL.md +141 -0
  64. package/skills/verify-security/scripts/security_scanner.py +368 -0
@@ -0,0 +1,558 @@
1
+ #!/usr/bin/env python3
2
+ """
3
+ verify-change 单元测试
4
+ 测试变更分析器功能
5
+ """
6
+
7
+ import unittest
8
+ import tempfile
9
+ import sys
10
+ from pathlib import Path
11
+ from unittest.mock import patch, MagicMock
12
+
13
+ # 添加 skills 目录到 Python 路径
14
+ sys.path.insert(0, str(Path(__file__).parent.parent / "verify-change" / "scripts"))
15
+
16
+ from change_analyzer import (
17
+ ChangeType, Severity, FileChange, Issue, AnalysisResult,
18
+ classify_file, identify_affected_modules, check_doc_sync,
19
+ analyze_impact, analyze_changes, format_report,
20
+ get_working_changes, get_staged_changes, get_git_changes
21
+ )
22
+
23
+
24
+ class TestChangeAnalyzer(unittest.TestCase):
25
+ """变更分析器测试"""
26
+
27
+ def setUp(self):
28
+ """测试前准备"""
29
+ self.temp_dir = tempfile.TemporaryDirectory()
30
+ self.temp_path = Path(self.temp_dir.name)
31
+
32
+ def tearDown(self):
33
+ """测试后清理"""
34
+ self.temp_dir.cleanup()
35
+
36
+ def test_classify_file_python_code(self):
37
+ """测试 Python 代码文件分类"""
38
+ change = classify_file("src/main.py")
39
+
40
+ self.assertTrue(change.is_code)
41
+ self.assertFalse(change.is_doc)
42
+ self.assertFalse(change.is_test)
43
+
44
+ def test_classify_file_test(self):
45
+ """测试测试文件分类"""
46
+ change = classify_file("tests/test_main.py")
47
+
48
+ self.assertTrue(change.is_code)
49
+ self.assertTrue(change.is_test)
50
+
51
+ def test_classify_file_documentation(self):
52
+ """测试文档文件分类"""
53
+ change = classify_file("docs/README.md")
54
+
55
+ self.assertTrue(change.is_doc)
56
+ self.assertFalse(change.is_code)
57
+
58
+ def test_classify_file_config(self):
59
+ """测试配置文件分类"""
60
+ change = classify_file("package.json")
61
+
62
+ self.assertTrue(change.is_config)
63
+
64
+ def test_classify_file_yaml_config(self):
65
+ """测试 YAML 配置文件"""
66
+ change = classify_file("config.yaml")
67
+
68
+ self.assertTrue(change.is_config)
69
+
70
+ def test_classify_file_multiple_patterns(self):
71
+ """测试多个模式匹配"""
72
+ change = classify_file("tests/test_utils.py")
73
+
74
+ self.assertTrue(change.is_code)
75
+ self.assertTrue(change.is_test)
76
+
77
+ def test_classify_file_underscore_test(self):
78
+ """测试 _test 模式"""
79
+ change = classify_file("src/utils_test.go")
80
+
81
+ self.assertTrue(change.is_test)
82
+
83
+ def test_classify_file_spec_pattern(self):
84
+ """测试 spec 模式"""
85
+ change = classify_file("spec/main_spec.js")
86
+
87
+ self.assertTrue(change.is_test)
88
+
89
+ @patch('change_analyzer.subprocess.run')
90
+ def test_get_working_changes_keeps_dotfile_prefix(self, mock_run):
91
+ """测试工作区变更保留 dotfile 前缀"""
92
+ mock_run.return_value = MagicMock(stdout=' M .gitignore\n')
93
+
94
+ changes = get_working_changes()
95
+
96
+ self.assertEqual(len(changes), 1)
97
+ self.assertEqual(changes[0].path, '.gitignore')
98
+ self.assertEqual(changes[0].change_type, ChangeType.MODIFIED)
99
+
100
+ @patch('change_analyzer.subprocess.run')
101
+ def test_get_staged_changes_parses_status(self, mock_run):
102
+ """测试暂存区变更解析"""
103
+ mock_run.return_value = MagicMock(stdout='A\tsrc/new.py\nM\tREADME.md\n')
104
+
105
+ changes = get_staged_changes()
106
+
107
+ self.assertEqual(len(changes), 2)
108
+ self.assertEqual(changes[0].change_type, ChangeType.ADDED)
109
+ self.assertEqual(changes[0].path, 'src/new.py')
110
+ self.assertEqual(changes[1].change_type, ChangeType.MODIFIED)
111
+
112
+ @patch('change_analyzer.subprocess.run')
113
+ def test_get_git_changes_maps_numstat(self, mock_run):
114
+ """测试提交变更行数映射"""
115
+ mock_run.side_effect = [
116
+ MagicMock(stdout='M\tsrc/main.py\n'),
117
+ MagicMock(stdout='10\t2\tsrc/main.py\n')
118
+ ]
119
+
120
+ changes = get_git_changes('HEAD~1', 'HEAD')
121
+
122
+ self.assertEqual(len(changes), 1)
123
+ self.assertEqual(changes[0].path, 'src/main.py')
124
+ self.assertEqual(changes[0].additions, 10)
125
+ self.assertEqual(changes[0].deletions, 2)
126
+
127
+ @patch('change_analyzer.subprocess.run')
128
+ def test_get_working_changes_parses_rename(self, mock_run):
129
+ """测试工作区重命名解析"""
130
+ mock_run.return_value = MagicMock(stdout='R old_name.py -> new_name.py\n')
131
+
132
+ changes = get_working_changes()
133
+
134
+ self.assertEqual(len(changes), 1)
135
+ self.assertEqual(changes[0].path, 'new_name.py')
136
+ self.assertEqual(changes[0].change_type, ChangeType.RENAMED)
137
+
138
+ def test_identify_affected_modules_single_file(self):
139
+ """测试识别受影响的模块"""
140
+ # 创建模块结构
141
+ module_path = self.temp_path / "mymodule"
142
+ module_path.mkdir()
143
+ (module_path / "README.md").write_text("# Module")
144
+ (module_path / "src").mkdir()
145
+ (module_path / "src" / "main.py").write_text("x = 1")
146
+
147
+ changes = [
148
+ FileChange(path="mymodule/src/main.py", change_type=ChangeType.MODIFIED)
149
+ ]
150
+
151
+ modules = identify_affected_modules(changes)
152
+
153
+ self.assertIn("mymodule", modules)
154
+
155
+ def test_identify_affected_modules_multiple(self):
156
+ """测试识别多个受影响的模块"""
157
+ # 创建多个模块
158
+ for mod in ["module1", "module2"]:
159
+ mod_path = self.temp_path / mod
160
+ mod_path.mkdir()
161
+ (mod_path / "README.md").write_text("# Module")
162
+
163
+ changes = [
164
+ FileChange(path="module1/src/main.py", change_type=ChangeType.MODIFIED),
165
+ FileChange(path="module2/src/main.py", change_type=ChangeType.MODIFIED)
166
+ ]
167
+
168
+ modules = identify_affected_modules(changes)
169
+
170
+ self.assertIn("module1", modules)
171
+ self.assertIn("module2", modules)
172
+
173
+ def test_identify_affected_modules_root_file(self):
174
+ """测试根目录文件识别为当前模块"""
175
+ changes = [
176
+ FileChange(path="main.py", change_type=ChangeType.MODIFIED)
177
+ ]
178
+
179
+ modules = identify_affected_modules(changes)
180
+
181
+ self.assertIn(".", modules)
182
+
183
+ def test_check_doc_sync_root_module(self):
184
+ """测试根目录模块文档同步"""
185
+ changes = [
186
+ FileChange(
187
+ path="main.py",
188
+ change_type=ChangeType.MODIFIED,
189
+ is_code=True,
190
+ additions=80,
191
+ deletions=10
192
+ )
193
+ ]
194
+
195
+ doc_status, issues = check_doc_sync(changes, {"."})
196
+
197
+ self.assertFalse(doc_status.get("./DESIGN.md", True))
198
+ self.assertTrue(any("DESIGN.md" in i.message for i in issues))
199
+
200
+ def test_check_doc_sync_no_changes(self):
201
+ """测试无变更时的文档同步检查"""
202
+ doc_status, issues = check_doc_sync([], set())
203
+
204
+ self.assertEqual(len(doc_status), 0)
205
+ self.assertEqual(len(issues), 0)
206
+
207
+ def test_check_doc_sync_code_without_doc_update(self):
208
+ """测试代码变更但文档未更新"""
209
+ module_path = self.temp_path / "mymodule"
210
+ module_path.mkdir()
211
+ (module_path / "README.md").write_text("# Module")
212
+ (module_path / "DESIGN.md").write_text("# Design")
213
+
214
+ changes = [
215
+ FileChange(
216
+ path="mymodule/src/main.py",
217
+ change_type=ChangeType.MODIFIED,
218
+ is_code=True,
219
+ additions=100,
220
+ deletions=50
221
+ )
222
+ ]
223
+
224
+ doc_status, issues = check_doc_sync(changes, {"mymodule"})
225
+
226
+ # 大规模变更应该有警告
227
+ self.assertTrue(any("DESIGN.md" in i.message for i in issues))
228
+
229
+ def test_check_doc_sync_with_doc_update(self):
230
+ """测试代码和文档同时更新"""
231
+ module_path = self.temp_path / "mymodule"
232
+ module_path.mkdir()
233
+ (module_path / "README.md").write_text("# Module")
234
+ (module_path / "DESIGN.md").write_text("# Design")
235
+
236
+ changes = [
237
+ FileChange(
238
+ path="mymodule/src/main.py",
239
+ change_type=ChangeType.MODIFIED,
240
+ is_code=True,
241
+ additions=100,
242
+ deletions=50
243
+ ),
244
+ FileChange(
245
+ path="mymodule/DESIGN.md",
246
+ change_type=ChangeType.MODIFIED,
247
+ is_doc=True
248
+ )
249
+ ]
250
+
251
+ doc_status, issues = check_doc_sync(changes, {"mymodule"})
252
+
253
+ # 文档已更新,不应该有警告
254
+ self.assertFalse(any("DESIGN.md" in i.message for i in issues))
255
+
256
+ def test_analyze_impact_code_without_tests(self):
257
+ """测试代码变更但无测试更新"""
258
+ changes = [
259
+ FileChange(
260
+ path="src/main.py",
261
+ change_type=ChangeType.MODIFIED,
262
+ is_code=True,
263
+ additions=50,
264
+ deletions=20
265
+ )
266
+ ]
267
+
268
+ issues = analyze_impact(changes)
269
+
270
+ # 应该有关于缺少测试的警告
271
+ self.assertTrue(any("测试" in i.message for i in issues))
272
+
273
+ def test_analyze_impact_with_tests(self):
274
+ """测试代码和测试同时更新"""
275
+ changes = [
276
+ FileChange(
277
+ path="src/main.py",
278
+ change_type=ChangeType.MODIFIED,
279
+ is_code=True,
280
+ additions=50,
281
+ deletions=20
282
+ ),
283
+ FileChange(
284
+ path="tests/test_main.py",
285
+ change_type=ChangeType.MODIFIED,
286
+ is_code=True,
287
+ is_test=True
288
+ )
289
+ ]
290
+
291
+ issues = analyze_impact(changes)
292
+
293
+ # 不应该有关于缺少测试的警告
294
+ self.assertFalse(any("测试" in i.message for i in issues))
295
+
296
+ def test_analyze_impact_config_changes(self):
297
+ """测试配置文件变更"""
298
+ changes = [
299
+ FileChange(
300
+ path="package.json",
301
+ change_type=ChangeType.MODIFIED,
302
+ is_config=True
303
+ )
304
+ ]
305
+
306
+ issues = analyze_impact(changes)
307
+
308
+ # 应该有关于配置变更的提示
309
+ self.assertTrue(any("配置文件" in i.message for i in issues))
310
+
311
+ def test_analyze_impact_deleted_files(self):
312
+ """测试删除文件"""
313
+ changes = [
314
+ FileChange(
315
+ path="src/old_module.py",
316
+ change_type=ChangeType.DELETED,
317
+ is_code=True
318
+ )
319
+ ]
320
+
321
+ issues = analyze_impact(changes)
322
+
323
+ # 应该有关于删除的提示
324
+ self.assertTrue(any("删除" in i.message for i in issues))
325
+
326
+ def test_analysis_result_passed_property(self):
327
+ """测试 AnalysisResult.passed 属性"""
328
+ result = AnalysisResult()
329
+
330
+ # 没有错误时应该通过
331
+ self.assertTrue(result.passed)
332
+
333
+ # 添加警告
334
+ result.issues.append(Issue(
335
+ severity=Severity.WARNING,
336
+ message="test warning"
337
+ ))
338
+ self.assertTrue(result.passed)
339
+
340
+ # 添加错误
341
+ result.issues.append(Issue(
342
+ severity=Severity.ERROR,
343
+ message="test error"
344
+ ))
345
+ self.assertFalse(result.passed)
346
+
347
+ def test_analysis_result_additions_deletions(self):
348
+ """测试新增和删除行数统计"""
349
+ result = AnalysisResult()
350
+
351
+ result.changes.append(FileChange(
352
+ path="file1.py",
353
+ change_type=ChangeType.MODIFIED,
354
+ additions=10,
355
+ deletions=5
356
+ ))
357
+ result.changes.append(FileChange(
358
+ path="file2.py",
359
+ change_type=ChangeType.MODIFIED,
360
+ additions=20,
361
+ deletions=15
362
+ ))
363
+
364
+ self.assertEqual(result.total_additions, 30)
365
+ self.assertEqual(result.total_deletions, 20)
366
+
367
+ def test_file_change_dataclass(self):
368
+ """测试 FileChange 数据类"""
369
+ change = FileChange(
370
+ path="src/main.py",
371
+ change_type=ChangeType.MODIFIED,
372
+ additions=10,
373
+ deletions=5,
374
+ is_code=True
375
+ )
376
+
377
+ self.assertEqual(change.path, "src/main.py")
378
+ self.assertEqual(change.change_type, ChangeType.MODIFIED)
379
+ self.assertEqual(change.additions, 10)
380
+ self.assertTrue(change.is_code)
381
+
382
+ def test_change_type_enum(self):
383
+ """测试 ChangeType 枚举"""
384
+ self.assertEqual(ChangeType.ADDED.value, "added")
385
+ self.assertEqual(ChangeType.MODIFIED.value, "modified")
386
+ self.assertEqual(ChangeType.DELETED.value, "deleted")
387
+ self.assertEqual(ChangeType.RENAMED.value, "renamed")
388
+
389
+ def test_severity_enum(self):
390
+ """测试 Severity 枚举"""
391
+ self.assertEqual(Severity.ERROR.value, "error")
392
+ self.assertEqual(Severity.WARNING.value, "warning")
393
+ self.assertEqual(Severity.INFO.value, "info")
394
+
395
+ def test_format_report_basic(self):
396
+ """测试报告格式化"""
397
+ result = AnalysisResult()
398
+ result.changes.append(FileChange(
399
+ path="src/main.py",
400
+ change_type=ChangeType.MODIFIED,
401
+ additions=10,
402
+ deletions=5
403
+ ))
404
+
405
+ report = format_report(result)
406
+
407
+ self.assertIn("变更分析报告", report)
408
+ self.assertIn("1", report) # 1 file changed
409
+
410
+ def test_format_report_with_issues(self):
411
+ """测试包含问题的报告"""
412
+ result = AnalysisResult()
413
+ result.issues.append(Issue(
414
+ severity=Severity.WARNING,
415
+ message="代码变更但没有测试更新",
416
+ related_files=["src/main.py"]
417
+ ))
418
+
419
+ report = format_report(result, verbose=True)
420
+
421
+ self.assertIn("代码变更", report)
422
+ self.assertIn("src/main.py", report)
423
+
424
+ def test_format_report_with_modules(self):
425
+ """测试包含模块信息的报告"""
426
+ result = AnalysisResult()
427
+ result.affected_modules.add("module1")
428
+ result.affected_modules.add("module2")
429
+
430
+ report = format_report(result)
431
+
432
+ self.assertIn("module1", report)
433
+ self.assertIn("module2", report)
434
+
435
+ def test_issue_dataclass(self):
436
+ """测试 Issue 数据类"""
437
+ issue = Issue(
438
+ severity=Severity.WARNING,
439
+ message="test message",
440
+ related_files=["file1.py", "file2.py"]
441
+ )
442
+
443
+ self.assertEqual(issue.severity, Severity.WARNING)
444
+ self.assertEqual(issue.message, "test message")
445
+ self.assertEqual(len(issue.related_files), 2)
446
+
447
+
448
+ class TestChangeAnalyzerEdgeCases(unittest.TestCase):
449
+ """变更分析器边界条件测试"""
450
+
451
+ def setUp(self):
452
+ """测试前准备"""
453
+ self.temp_dir = tempfile.TemporaryDirectory()
454
+ self.temp_path = Path(self.temp_dir.name)
455
+
456
+ def tearDown(self):
457
+ """测试后清理"""
458
+ self.temp_dir.cleanup()
459
+
460
+ def test_classify_file_case_insensitive(self):
461
+ """测试文件分类大小写不敏感"""
462
+ change1 = classify_file("src/Main.PY")
463
+ change2 = classify_file("src/main.py")
464
+
465
+ self.assertEqual(change1.is_code, change2.is_code)
466
+
467
+ def test_identify_modules_with_nested_structure(self):
468
+ """测试嵌套模块识别"""
469
+ changes = [
470
+ FileChange(path="a/b/c/src/main.py", change_type=ChangeType.MODIFIED)
471
+ ]
472
+
473
+ modules = identify_affected_modules(changes)
474
+
475
+ self.assertIsInstance(modules, set)
476
+
477
+ def test_analyze_impact_small_changes(self):
478
+ """测试小规模变更"""
479
+ changes = [
480
+ FileChange(
481
+ path="src/main.py",
482
+ change_type=ChangeType.MODIFIED,
483
+ is_code=True,
484
+ additions=5,
485
+ deletions=2
486
+ )
487
+ ]
488
+
489
+ issues = analyze_impact(changes)
490
+
491
+ # 小规模变更不应该有测试警告
492
+ self.assertFalse(any("测试" in i.message for i in issues))
493
+
494
+ def test_analyze_impact_large_changes(self):
495
+ """测试大规模变更"""
496
+ changes = [
497
+ FileChange(
498
+ path="src/main.py",
499
+ change_type=ChangeType.MODIFIED,
500
+ is_code=True,
501
+ additions=100,
502
+ deletions=50
503
+ )
504
+ ]
505
+
506
+ issues = analyze_impact(changes)
507
+
508
+ # 大规模变更应该有测试警告
509
+ self.assertTrue(any("测试" in i.message for i in issues))
510
+
511
+ def test_check_doc_sync_small_changes(self):
512
+ """测试小规模变更的文档同步检查"""
513
+ module_path = self.temp_path / "mymodule"
514
+ module_path.mkdir()
515
+ (module_path / "README.md").write_text("# Module")
516
+ (module_path / "DESIGN.md").write_text("# Design")
517
+
518
+ changes = [
519
+ FileChange(
520
+ path="mymodule/src/main.py",
521
+ change_type=ChangeType.MODIFIED,
522
+ is_code=True,
523
+ additions=10,
524
+ deletions=5
525
+ )
526
+ ]
527
+
528
+ doc_status, issues = check_doc_sync(changes, {"mymodule"})
529
+
530
+ # 小规模变更不应该有文档警告
531
+ self.assertFalse(any("DESIGN.md" in i.message for i in issues))
532
+
533
+ def test_format_report_empty_changes(self):
534
+ """测试空变更报告"""
535
+ result = AnalysisResult()
536
+
537
+ report = format_report(result)
538
+
539
+ self.assertIn("变更分析报告", report)
540
+ self.assertIn("0", report)
541
+
542
+ def test_multiple_file_types(self):
543
+ """测试多种文件类型"""
544
+ changes = [
545
+ FileChange(path="src/main.py", change_type=ChangeType.MODIFIED, is_code=True),
546
+ FileChange(path="README.md", change_type=ChangeType.MODIFIED, is_doc=True),
547
+ FileChange(path="tests/test.py", change_type=ChangeType.MODIFIED, is_code=True, is_test=True),
548
+ FileChange(path="package.json", change_type=ChangeType.MODIFIED, is_config=True),
549
+ ]
550
+
551
+ result = AnalysisResult()
552
+ result.changes = changes
553
+
554
+ self.assertEqual(len(result.changes), 4)
555
+
556
+
557
+ if __name__ == '__main__':
558
+ unittest.main()