tlc-claude-code 1.4.8 → 1.4.9

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 (169) hide show
  1. package/package.json +1 -1
  2. package/server/index.js +229 -14
  3. package/server/lib/compliance/control-mapper.js +401 -0
  4. package/server/lib/compliance/control-mapper.test.js +117 -0
  5. package/server/lib/compliance/evidence-linker.js +296 -0
  6. package/server/lib/compliance/evidence-linker.test.js +121 -0
  7. package/server/lib/compliance/gdpr-checklist.js +416 -0
  8. package/server/lib/compliance/gdpr-checklist.test.js +131 -0
  9. package/server/lib/compliance/hipaa-checklist.js +277 -0
  10. package/server/lib/compliance/hipaa-checklist.test.js +101 -0
  11. package/server/lib/compliance/iso27001-checklist.js +287 -0
  12. package/server/lib/compliance/iso27001-checklist.test.js +99 -0
  13. package/server/lib/compliance/multi-framework-reporter.js +284 -0
  14. package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
  15. package/server/lib/compliance/pci-dss-checklist.js +214 -0
  16. package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
  17. package/server/lib/compliance/trust-centre.js +187 -0
  18. package/server/lib/compliance/trust-centre.test.js +93 -0
  19. package/server/lib/dashboard/api-server.js +155 -0
  20. package/server/lib/dashboard/api-server.test.js +155 -0
  21. package/server/lib/dashboard/health-api.js +199 -0
  22. package/server/lib/dashboard/health-api.test.js +122 -0
  23. package/server/lib/dashboard/notes-api.js +234 -0
  24. package/server/lib/dashboard/notes-api.test.js +134 -0
  25. package/server/lib/dashboard/router-api.js +176 -0
  26. package/server/lib/dashboard/router-api.test.js +132 -0
  27. package/server/lib/dashboard/tasks-api.js +289 -0
  28. package/server/lib/dashboard/tasks-api.test.js +161 -0
  29. package/server/lib/dashboard/tlc-introspection.js +197 -0
  30. package/server/lib/dashboard/tlc-introspection.test.js +138 -0
  31. package/server/lib/dashboard/version-api.js +222 -0
  32. package/server/lib/dashboard/version-api.test.js +112 -0
  33. package/server/lib/dashboard/websocket-server.js +104 -0
  34. package/server/lib/dashboard/websocket-server.test.js +118 -0
  35. package/server/lib/deploy/branch-classifier.js +163 -0
  36. package/server/lib/deploy/branch-classifier.test.js +164 -0
  37. package/server/lib/deploy/deployment-approval.js +299 -0
  38. package/server/lib/deploy/deployment-approval.test.js +296 -0
  39. package/server/lib/deploy/deployment-audit.js +374 -0
  40. package/server/lib/deploy/deployment-audit.test.js +307 -0
  41. package/server/lib/deploy/deployment-executor.js +335 -0
  42. package/server/lib/deploy/deployment-executor.test.js +329 -0
  43. package/server/lib/deploy/deployment-rules.js +163 -0
  44. package/server/lib/deploy/deployment-rules.test.js +188 -0
  45. package/server/lib/deploy/rollback-manager.js +379 -0
  46. package/server/lib/deploy/rollback-manager.test.js +321 -0
  47. package/server/lib/deploy/security-gates.js +236 -0
  48. package/server/lib/deploy/security-gates.test.js +222 -0
  49. package/server/lib/k8s/gitops-config.js +188 -0
  50. package/server/lib/k8s/gitops-config.test.js +59 -0
  51. package/server/lib/k8s/helm-generator.js +196 -0
  52. package/server/lib/k8s/helm-generator.test.js +59 -0
  53. package/server/lib/k8s/kustomize-generator.js +176 -0
  54. package/server/lib/k8s/kustomize-generator.test.js +58 -0
  55. package/server/lib/k8s/network-policy.js +114 -0
  56. package/server/lib/k8s/network-policy.test.js +53 -0
  57. package/server/lib/k8s/pod-security.js +114 -0
  58. package/server/lib/k8s/pod-security.test.js +55 -0
  59. package/server/lib/k8s/rbac-generator.js +132 -0
  60. package/server/lib/k8s/rbac-generator.test.js +57 -0
  61. package/server/lib/k8s/resource-manager.js +172 -0
  62. package/server/lib/k8s/resource-manager.test.js +60 -0
  63. package/server/lib/k8s/secrets-encryption.js +168 -0
  64. package/server/lib/k8s/secrets-encryption.test.js +49 -0
  65. package/server/lib/monitoring/alert-manager.js +238 -0
  66. package/server/lib/monitoring/alert-manager.test.js +106 -0
  67. package/server/lib/monitoring/health-check.js +226 -0
  68. package/server/lib/monitoring/health-check.test.js +176 -0
  69. package/server/lib/monitoring/incident-manager.js +230 -0
  70. package/server/lib/monitoring/incident-manager.test.js +98 -0
  71. package/server/lib/monitoring/log-aggregator.js +147 -0
  72. package/server/lib/monitoring/log-aggregator.test.js +89 -0
  73. package/server/lib/monitoring/metrics-collector.js +337 -0
  74. package/server/lib/monitoring/metrics-collector.test.js +172 -0
  75. package/server/lib/monitoring/status-page.js +214 -0
  76. package/server/lib/monitoring/status-page.test.js +105 -0
  77. package/server/lib/monitoring/uptime-monitor.js +194 -0
  78. package/server/lib/monitoring/uptime-monitor.test.js +109 -0
  79. package/server/lib/network/fail2ban-config.js +294 -0
  80. package/server/lib/network/fail2ban-config.test.js +275 -0
  81. package/server/lib/network/firewall-manager.js +252 -0
  82. package/server/lib/network/firewall-manager.test.js +254 -0
  83. package/server/lib/network/geoip-filter.js +282 -0
  84. package/server/lib/network/geoip-filter.test.js +264 -0
  85. package/server/lib/network/rate-limiter.js +229 -0
  86. package/server/lib/network/rate-limiter.test.js +293 -0
  87. package/server/lib/network/request-validator.js +351 -0
  88. package/server/lib/network/request-validator.test.js +345 -0
  89. package/server/lib/network/security-headers.js +251 -0
  90. package/server/lib/network/security-headers.test.js +283 -0
  91. package/server/lib/network/tls-config.js +210 -0
  92. package/server/lib/network/tls-config.test.js +248 -0
  93. package/server/lib/security/auth-security.js +369 -0
  94. package/server/lib/security/auth-security.test.js +448 -0
  95. package/server/lib/security/cis-benchmark.js +152 -0
  96. package/server/lib/security/cis-benchmark.test.js +137 -0
  97. package/server/lib/security/compose-templates.js +312 -0
  98. package/server/lib/security/compose-templates.test.js +229 -0
  99. package/server/lib/security/container-runtime.js +456 -0
  100. package/server/lib/security/container-runtime.test.js +503 -0
  101. package/server/lib/security/cors-validator.js +278 -0
  102. package/server/lib/security/cors-validator.test.js +310 -0
  103. package/server/lib/security/crypto-utils.js +253 -0
  104. package/server/lib/security/crypto-utils.test.js +409 -0
  105. package/server/lib/security/dockerfile-linter.js +459 -0
  106. package/server/lib/security/dockerfile-linter.test.js +483 -0
  107. package/server/lib/security/dockerfile-templates.js +278 -0
  108. package/server/lib/security/dockerfile-templates.test.js +164 -0
  109. package/server/lib/security/error-sanitizer.js +426 -0
  110. package/server/lib/security/error-sanitizer.test.js +331 -0
  111. package/server/lib/security/headers-generator.js +368 -0
  112. package/server/lib/security/headers-generator.test.js +398 -0
  113. package/server/lib/security/image-scanner.js +83 -0
  114. package/server/lib/security/image-scanner.test.js +106 -0
  115. package/server/lib/security/input-validator.js +352 -0
  116. package/server/lib/security/input-validator.test.js +330 -0
  117. package/server/lib/security/network-policy.js +174 -0
  118. package/server/lib/security/network-policy.test.js +164 -0
  119. package/server/lib/security/output-encoder.js +237 -0
  120. package/server/lib/security/output-encoder.test.js +276 -0
  121. package/server/lib/security/path-validator.js +359 -0
  122. package/server/lib/security/path-validator.test.js +293 -0
  123. package/server/lib/security/query-builder.js +421 -0
  124. package/server/lib/security/query-builder.test.js +318 -0
  125. package/server/lib/security/secret-detector.js +290 -0
  126. package/server/lib/security/secret-detector.test.js +354 -0
  127. package/server/lib/security/secrets-validator.js +137 -0
  128. package/server/lib/security/secrets-validator.test.js +120 -0
  129. package/server/lib/security-testing/dast-runner.js +154 -0
  130. package/server/lib/security-testing/dast-runner.test.js +62 -0
  131. package/server/lib/security-testing/dependency-scanner.js +172 -0
  132. package/server/lib/security-testing/dependency-scanner.test.js +64 -0
  133. package/server/lib/security-testing/pentest-runner.js +230 -0
  134. package/server/lib/security-testing/pentest-runner.test.js +60 -0
  135. package/server/lib/security-testing/sast-runner.js +136 -0
  136. package/server/lib/security-testing/sast-runner.test.js +62 -0
  137. package/server/lib/security-testing/secret-scanner.js +153 -0
  138. package/server/lib/security-testing/secret-scanner.test.js +66 -0
  139. package/server/lib/security-testing/security-gate.js +216 -0
  140. package/server/lib/security-testing/security-gate.test.js +115 -0
  141. package/server/lib/security-testing/security-reporter.js +303 -0
  142. package/server/lib/security-testing/security-reporter.test.js +114 -0
  143. package/server/lib/standards/audit-checker.js +546 -0
  144. package/server/lib/standards/audit-checker.test.js +415 -0
  145. package/server/lib/standards/cleanup-executor.js +452 -0
  146. package/server/lib/standards/cleanup-executor.test.js +293 -0
  147. package/server/lib/standards/refactor-stepper.js +425 -0
  148. package/server/lib/standards/refactor-stepper.test.js +298 -0
  149. package/server/lib/standards/standards-injector.js +167 -0
  150. package/server/lib/standards/standards-injector.test.js +232 -0
  151. package/server/lib/user-management.test.js +284 -0
  152. package/server/lib/vps/backup-manager.js +157 -0
  153. package/server/lib/vps/backup-manager.test.js +59 -0
  154. package/server/lib/vps/caddy-config.js +159 -0
  155. package/server/lib/vps/caddy-config.test.js +48 -0
  156. package/server/lib/vps/compose-orchestrator.js +219 -0
  157. package/server/lib/vps/compose-orchestrator.test.js +50 -0
  158. package/server/lib/vps/database-config.js +208 -0
  159. package/server/lib/vps/database-config.test.js +47 -0
  160. package/server/lib/vps/deploy-script.js +211 -0
  161. package/server/lib/vps/deploy-script.test.js +53 -0
  162. package/server/lib/vps/secrets-manager.js +148 -0
  163. package/server/lib/vps/secrets-manager.test.js +58 -0
  164. package/server/lib/vps/server-hardening.js +174 -0
  165. package/server/lib/vps/server-hardening.test.js +70 -0
  166. package/server/package-lock.json +19 -0
  167. package/server/package.json +1 -0
  168. package/server/templates/CLAUDE.md +37 -0
  169. package/server/templates/CODING-STANDARDS.md +408 -0
@@ -0,0 +1,415 @@
1
+ /**
2
+ * Audit Checker Tests
3
+ */
4
+ import { describe, it, expect, vi } from 'vitest';
5
+ import {
6
+ auditProject,
7
+ checkStandardsFiles,
8
+ checkFlatFolders,
9
+ checkInlineInterfaces,
10
+ checkHardcodedUrls,
11
+ checkMagicStrings,
12
+ checkSeedOrganization,
13
+ checkJsDocCoverage,
14
+ checkImportStyle,
15
+ generateReport
16
+ } from './audit-checker.js';
17
+
18
+ describe('audit-checker', () => {
19
+ describe('checkStandardsFiles', () => {
20
+ it('passes when both files exist', async () => {
21
+ const mockFs = {
22
+ access: vi.fn().mockResolvedValue(undefined)
23
+ };
24
+
25
+ const result = await checkStandardsFiles('/test', { fs: mockFs });
26
+
27
+ expect(result.passed).toBe(true);
28
+ expect(result.issues).toHaveLength(0);
29
+ });
30
+
31
+ it('fails when CLAUDE.md missing', async () => {
32
+ const mockFs = {
33
+ access: vi.fn().mockImplementation((path) => {
34
+ if (path.includes('CLAUDE.md')) {
35
+ return Promise.reject(new Error('ENOENT'));
36
+ }
37
+ return Promise.resolve(undefined);
38
+ })
39
+ };
40
+
41
+ const result = await checkStandardsFiles('/test', { fs: mockFs });
42
+
43
+ expect(result.passed).toBe(false);
44
+ expect(result.issues).toContainEqual(expect.objectContaining({
45
+ file: 'CLAUDE.md',
46
+ type: 'missing'
47
+ }));
48
+ });
49
+
50
+ it('fails when CODING-STANDARDS.md missing', async () => {
51
+ const mockFs = {
52
+ access: vi.fn().mockImplementation((path) => {
53
+ if (path.includes('CODING-STANDARDS.md')) {
54
+ return Promise.reject(new Error('ENOENT'));
55
+ }
56
+ return Promise.resolve(undefined);
57
+ })
58
+ };
59
+
60
+ const result = await checkStandardsFiles('/test', { fs: mockFs });
61
+
62
+ expect(result.passed).toBe(false);
63
+ expect(result.issues).toContainEqual(expect.objectContaining({
64
+ file: 'CODING-STANDARDS.md',
65
+ type: 'missing'
66
+ }));
67
+ });
68
+ });
69
+
70
+ describe('checkFlatFolders', () => {
71
+ it('passes on clean project', async () => {
72
+ const mockGlob = vi.fn().mockResolvedValue([]);
73
+
74
+ const result = await checkFlatFolders('/test', { glob: mockGlob });
75
+
76
+ expect(result.passed).toBe(true);
77
+ });
78
+
79
+ it('fails on flat services/ folder', async () => {
80
+ const mockGlob = vi.fn().mockResolvedValue(['src/services/userService.js']);
81
+
82
+ const result = await checkFlatFolders('/test', { glob: mockGlob });
83
+
84
+ expect(result.passed).toBe(false);
85
+ expect(result.issues).toContainEqual(expect.objectContaining({
86
+ type: 'flat-folder',
87
+ folder: 'services'
88
+ }));
89
+ });
90
+
91
+ it('fails on flat interfaces/ folder', async () => {
92
+ const mockGlob = vi.fn().mockResolvedValue(['src/interfaces/IUser.ts']);
93
+
94
+ const result = await checkFlatFolders('/test', { glob: mockGlob });
95
+
96
+ expect(result.passed).toBe(false);
97
+ expect(result.issues).toContainEqual(expect.objectContaining({
98
+ type: 'flat-folder',
99
+ folder: 'interfaces'
100
+ }));
101
+ });
102
+
103
+ it('fails on flat controllers/ folder', async () => {
104
+ const mockGlob = vi.fn().mockResolvedValue(['src/controllers/userController.js']);
105
+
106
+ const result = await checkFlatFolders('/test', { glob: mockGlob });
107
+
108
+ expect(result.passed).toBe(false);
109
+ expect(result.issues).toContainEqual(expect.objectContaining({
110
+ type: 'flat-folder',
111
+ folder: 'controllers'
112
+ }));
113
+ });
114
+ });
115
+
116
+ describe('checkInlineInterfaces', () => {
117
+ it('passes on clean code', async () => {
118
+ const mockReadFile = vi.fn().mockResolvedValue(`
119
+ import { User } from './types/user';
120
+ export class UserService {
121
+ getUser(id: string): User { }
122
+ }
123
+ `);
124
+ const mockGlob = vi.fn().mockResolvedValue(['src/user/user.service.ts']);
125
+
126
+ const result = await checkInlineInterfaces('/test', {
127
+ glob: mockGlob,
128
+ readFile: mockReadFile
129
+ });
130
+
131
+ expect(result.passed).toBe(true);
132
+ });
133
+
134
+ it('fails on inline interface in service', async () => {
135
+ const mockReadFile = vi.fn().mockResolvedValue(`
136
+ interface UserData {
137
+ id: string;
138
+ name: string;
139
+ }
140
+ export class UserService {
141
+ getUser(id: string): UserData { }
142
+ }
143
+ `);
144
+ const mockGlob = vi.fn().mockResolvedValue(['src/services/user.service.ts']);
145
+
146
+ const result = await checkInlineInterfaces('/test', {
147
+ glob: mockGlob,
148
+ readFile: mockReadFile
149
+ });
150
+
151
+ expect(result.passed).toBe(false);
152
+ expect(result.issues).toContainEqual(expect.objectContaining({
153
+ type: 'inline-interface',
154
+ file: expect.stringContaining('user.service.ts')
155
+ }));
156
+ });
157
+ });
158
+
159
+ describe('checkHardcodedUrls', () => {
160
+ it('passes on clean code', async () => {
161
+ const mockReadFile = vi.fn().mockResolvedValue(`
162
+ const apiUrl = process.env.API_URL;
163
+ fetch(apiUrl + '/users');
164
+ `);
165
+ const mockGlob = vi.fn().mockResolvedValue(['src/api.js']);
166
+
167
+ const result = await checkHardcodedUrls('/test', {
168
+ glob: mockGlob,
169
+ readFile: mockReadFile
170
+ });
171
+
172
+ expect(result.passed).toBe(true);
173
+ });
174
+
175
+ it('fails on hardcoded URL', async () => {
176
+ const mockReadFile = vi.fn().mockResolvedValue(`
177
+ fetch('http://localhost:3000/api/users');
178
+ `);
179
+ const mockGlob = vi.fn().mockResolvedValue(['src/api.js']);
180
+
181
+ const result = await checkHardcodedUrls('/test', {
182
+ glob: mockGlob,
183
+ readFile: mockReadFile
184
+ });
185
+
186
+ expect(result.passed).toBe(false);
187
+ expect(result.issues).toContainEqual(expect.objectContaining({
188
+ type: 'hardcoded-url'
189
+ }));
190
+ });
191
+
192
+ it('fails on hardcoded port', async () => {
193
+ const mockReadFile = vi.fn().mockResolvedValue(`
194
+ const port = 3000;
195
+ app.listen(port);
196
+ `);
197
+ const mockGlob = vi.fn().mockResolvedValue(['src/server.js']);
198
+
199
+ const result = await checkHardcodedUrls('/test', {
200
+ glob: mockGlob,
201
+ readFile: mockReadFile
202
+ });
203
+
204
+ expect(result.passed).toBe(false);
205
+ expect(result.issues).toContainEqual(expect.objectContaining({
206
+ type: 'hardcoded-port'
207
+ }));
208
+ });
209
+ });
210
+
211
+ describe('checkMagicStrings', () => {
212
+ it('passes on clean code', async () => {
213
+ const mockReadFile = vi.fn().mockResolvedValue(`
214
+ import { STATUS_ACTIVE } from './constants';
215
+ if (user.status === STATUS_ACTIVE) { }
216
+ `);
217
+ const mockGlob = vi.fn().mockResolvedValue(['src/user.js']);
218
+
219
+ const result = await checkMagicStrings('/test', {
220
+ glob: mockGlob,
221
+ readFile: mockReadFile
222
+ });
223
+
224
+ expect(result.passed).toBe(true);
225
+ });
226
+
227
+ it('fails on magic string', async () => {
228
+ const mockReadFile = vi.fn().mockResolvedValue(`
229
+ if (user.status === 'active') {
230
+ if (user.role === 'admin') { }
231
+ }
232
+ `);
233
+ const mockGlob = vi.fn().mockResolvedValue(['src/user.js']);
234
+
235
+ const result = await checkMagicStrings('/test', {
236
+ glob: mockGlob,
237
+ readFile: mockReadFile
238
+ });
239
+
240
+ expect(result.passed).toBe(false);
241
+ expect(result.issues.length).toBeGreaterThan(0);
242
+ expect(result.issues[0].type).toBe('magic-string');
243
+ });
244
+ });
245
+
246
+ describe('checkSeedOrganization', () => {
247
+ it('passes when seeds are per-entity', async () => {
248
+ const mockGlob = vi.fn().mockResolvedValue([
249
+ 'src/user/seeds/user.seed.ts',
250
+ 'src/product/seeds/product.seed.ts'
251
+ ]);
252
+
253
+ const result = await checkSeedOrganization('/test', { glob: mockGlob });
254
+
255
+ expect(result.passed).toBe(true);
256
+ });
257
+
258
+ it('fails on flat seeds/ folder', async () => {
259
+ const mockGlob = vi.fn().mockResolvedValue([
260
+ 'src/seeds/userSeed.ts',
261
+ 'src/seeds/productSeed.ts'
262
+ ]);
263
+
264
+ const result = await checkSeedOrganization('/test', { glob: mockGlob });
265
+
266
+ expect(result.passed).toBe(false);
267
+ expect(result.issues).toContainEqual(expect.objectContaining({
268
+ type: 'flat-seeds'
269
+ }));
270
+ });
271
+ });
272
+
273
+ describe('checkJsDocCoverage', () => {
274
+ it('passes when functions have JSDoc', async () => {
275
+ const mockReadFile = vi.fn().mockResolvedValue(`
276
+ /**
277
+ * Gets a user by ID
278
+ * @param id - The user ID
279
+ * @returns The user object
280
+ */
281
+ export function getUser(id: string): User {
282
+ return users.find(u => u.id === id);
283
+ }
284
+ `);
285
+ const mockGlob = vi.fn().mockResolvedValue(['src/user.ts']);
286
+
287
+ const result = await checkJsDocCoverage('/test', {
288
+ glob: mockGlob,
289
+ readFile: mockReadFile
290
+ });
291
+
292
+ expect(result.passed).toBe(true);
293
+ });
294
+
295
+ it('fails when exported function lacks JSDoc', async () => {
296
+ const mockReadFile = vi.fn().mockResolvedValue(`
297
+ export function getUser(id: string): User {
298
+ return users.find(u => u.id === id);
299
+ }
300
+ `);
301
+ const mockGlob = vi.fn().mockResolvedValue(['src/user.ts']);
302
+
303
+ const result = await checkJsDocCoverage('/test', {
304
+ glob: mockGlob,
305
+ readFile: mockReadFile
306
+ });
307
+
308
+ expect(result.passed).toBe(false);
309
+ expect(result.issues).toContainEqual(expect.objectContaining({
310
+ type: 'missing-jsdoc'
311
+ }));
312
+ });
313
+ });
314
+
315
+ describe('checkImportStyle', () => {
316
+ it('passes on correct import style', async () => {
317
+ const mockReadFile = vi.fn().mockResolvedValue(`
318
+ import { User } from '@/user/types';
319
+ import { config } from '@/config';
320
+ `);
321
+ const mockGlob = vi.fn().mockResolvedValue(['src/service.ts']);
322
+
323
+ const result = await checkImportStyle('/test', {
324
+ glob: mockGlob,
325
+ readFile: mockReadFile
326
+ });
327
+
328
+ expect(result.passed).toBe(true);
329
+ });
330
+
331
+ it('warns on deep relative imports', async () => {
332
+ const mockReadFile = vi.fn().mockResolvedValue(`
333
+ import { User } from '../../../user/types';
334
+ `);
335
+ const mockGlob = vi.fn().mockResolvedValue(['src/deep/nested/service.ts']);
336
+
337
+ const result = await checkImportStyle('/test', {
338
+ glob: mockGlob,
339
+ readFile: mockReadFile
340
+ });
341
+
342
+ expect(result.passed).toBe(false);
343
+ expect(result.issues).toContainEqual(expect.objectContaining({
344
+ type: 'deep-import'
345
+ }));
346
+ });
347
+ });
348
+
349
+ describe('auditProject', () => {
350
+ it('runs all checks', async () => {
351
+ const mockOptions = {
352
+ fs: {
353
+ access: vi.fn().mockResolvedValue(undefined)
354
+ },
355
+ glob: vi.fn().mockResolvedValue([]),
356
+ readFile: vi.fn().mockResolvedValue('')
357
+ };
358
+
359
+ const result = await auditProject('/test', mockOptions);
360
+
361
+ expect(result).toHaveProperty('standardsFiles');
362
+ expect(result).toHaveProperty('flatFolders');
363
+ expect(result).toHaveProperty('inlineInterfaces');
364
+ expect(result).toHaveProperty('hardcodedUrls');
365
+ expect(result).toHaveProperty('magicStrings');
366
+ expect(result).toHaveProperty('seedOrganization');
367
+ expect(result).toHaveProperty('jsDocCoverage');
368
+ expect(result).toHaveProperty('importStyle');
369
+ expect(result).toHaveProperty('summary');
370
+ });
371
+
372
+ it('reports all issues found', async () => {
373
+ const mockOptions = {
374
+ fs: {
375
+ access: vi.fn().mockRejectedValue(new Error('ENOENT'))
376
+ },
377
+ glob: vi.fn().mockResolvedValue(['src/services/user.js']),
378
+ readFile: vi.fn().mockResolvedValue('fetch("http://localhost:3000")')
379
+ };
380
+
381
+ const result = await auditProject('/test', mockOptions);
382
+
383
+ expect(result.summary.totalIssues).toBeGreaterThan(0);
384
+ expect(result.summary.passed).toBe(false);
385
+ });
386
+ });
387
+
388
+ describe('generateReport', () => {
389
+ it('creates AUDIT-REPORT.md', () => {
390
+ const auditResults = {
391
+ standardsFiles: { passed: true, issues: [] },
392
+ flatFolders: { passed: false, issues: [{ type: 'flat-folder', folder: 'services' }] },
393
+ summary: { totalIssues: 1, passed: false }
394
+ };
395
+
396
+ const report = generateReport(auditResults);
397
+
398
+ expect(report).toContain('# Audit Report');
399
+ expect(report).toContain('flat-folder');
400
+ expect(report).toContain('services');
401
+ });
402
+
403
+ it('shows pass status', () => {
404
+ const auditResults = {
405
+ standardsFiles: { passed: true, issues: [] },
406
+ flatFolders: { passed: true, issues: [] },
407
+ summary: { totalIssues: 0, passed: true }
408
+ };
409
+
410
+ const report = generateReport(auditResults);
411
+
412
+ expect(report).toContain('PASSED');
413
+ });
414
+ });
415
+ });