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.
- package/package.json +1 -1
- package/server/index.js +229 -14
- package/server/lib/compliance/control-mapper.js +401 -0
- package/server/lib/compliance/control-mapper.test.js +117 -0
- package/server/lib/compliance/evidence-linker.js +296 -0
- package/server/lib/compliance/evidence-linker.test.js +121 -0
- package/server/lib/compliance/gdpr-checklist.js +416 -0
- package/server/lib/compliance/gdpr-checklist.test.js +131 -0
- package/server/lib/compliance/hipaa-checklist.js +277 -0
- package/server/lib/compliance/hipaa-checklist.test.js +101 -0
- package/server/lib/compliance/iso27001-checklist.js +287 -0
- package/server/lib/compliance/iso27001-checklist.test.js +99 -0
- package/server/lib/compliance/multi-framework-reporter.js +284 -0
- package/server/lib/compliance/multi-framework-reporter.test.js +127 -0
- package/server/lib/compliance/pci-dss-checklist.js +214 -0
- package/server/lib/compliance/pci-dss-checklist.test.js +95 -0
- package/server/lib/compliance/trust-centre.js +187 -0
- package/server/lib/compliance/trust-centre.test.js +93 -0
- package/server/lib/dashboard/api-server.js +155 -0
- package/server/lib/dashboard/api-server.test.js +155 -0
- package/server/lib/dashboard/health-api.js +199 -0
- package/server/lib/dashboard/health-api.test.js +122 -0
- package/server/lib/dashboard/notes-api.js +234 -0
- package/server/lib/dashboard/notes-api.test.js +134 -0
- package/server/lib/dashboard/router-api.js +176 -0
- package/server/lib/dashboard/router-api.test.js +132 -0
- package/server/lib/dashboard/tasks-api.js +289 -0
- package/server/lib/dashboard/tasks-api.test.js +161 -0
- package/server/lib/dashboard/tlc-introspection.js +197 -0
- package/server/lib/dashboard/tlc-introspection.test.js +138 -0
- package/server/lib/dashboard/version-api.js +222 -0
- package/server/lib/dashboard/version-api.test.js +112 -0
- package/server/lib/dashboard/websocket-server.js +104 -0
- package/server/lib/dashboard/websocket-server.test.js +118 -0
- package/server/lib/deploy/branch-classifier.js +163 -0
- package/server/lib/deploy/branch-classifier.test.js +164 -0
- package/server/lib/deploy/deployment-approval.js +299 -0
- package/server/lib/deploy/deployment-approval.test.js +296 -0
- package/server/lib/deploy/deployment-audit.js +374 -0
- package/server/lib/deploy/deployment-audit.test.js +307 -0
- package/server/lib/deploy/deployment-executor.js +335 -0
- package/server/lib/deploy/deployment-executor.test.js +329 -0
- package/server/lib/deploy/deployment-rules.js +163 -0
- package/server/lib/deploy/deployment-rules.test.js +188 -0
- package/server/lib/deploy/rollback-manager.js +379 -0
- package/server/lib/deploy/rollback-manager.test.js +321 -0
- package/server/lib/deploy/security-gates.js +236 -0
- package/server/lib/deploy/security-gates.test.js +222 -0
- package/server/lib/k8s/gitops-config.js +188 -0
- package/server/lib/k8s/gitops-config.test.js +59 -0
- package/server/lib/k8s/helm-generator.js +196 -0
- package/server/lib/k8s/helm-generator.test.js +59 -0
- package/server/lib/k8s/kustomize-generator.js +176 -0
- package/server/lib/k8s/kustomize-generator.test.js +58 -0
- package/server/lib/k8s/network-policy.js +114 -0
- package/server/lib/k8s/network-policy.test.js +53 -0
- package/server/lib/k8s/pod-security.js +114 -0
- package/server/lib/k8s/pod-security.test.js +55 -0
- package/server/lib/k8s/rbac-generator.js +132 -0
- package/server/lib/k8s/rbac-generator.test.js +57 -0
- package/server/lib/k8s/resource-manager.js +172 -0
- package/server/lib/k8s/resource-manager.test.js +60 -0
- package/server/lib/k8s/secrets-encryption.js +168 -0
- package/server/lib/k8s/secrets-encryption.test.js +49 -0
- package/server/lib/monitoring/alert-manager.js +238 -0
- package/server/lib/monitoring/alert-manager.test.js +106 -0
- package/server/lib/monitoring/health-check.js +226 -0
- package/server/lib/monitoring/health-check.test.js +176 -0
- package/server/lib/monitoring/incident-manager.js +230 -0
- package/server/lib/monitoring/incident-manager.test.js +98 -0
- package/server/lib/monitoring/log-aggregator.js +147 -0
- package/server/lib/monitoring/log-aggregator.test.js +89 -0
- package/server/lib/monitoring/metrics-collector.js +337 -0
- package/server/lib/monitoring/metrics-collector.test.js +172 -0
- package/server/lib/monitoring/status-page.js +214 -0
- package/server/lib/monitoring/status-page.test.js +105 -0
- package/server/lib/monitoring/uptime-monitor.js +194 -0
- package/server/lib/monitoring/uptime-monitor.test.js +109 -0
- package/server/lib/network/fail2ban-config.js +294 -0
- package/server/lib/network/fail2ban-config.test.js +275 -0
- package/server/lib/network/firewall-manager.js +252 -0
- package/server/lib/network/firewall-manager.test.js +254 -0
- package/server/lib/network/geoip-filter.js +282 -0
- package/server/lib/network/geoip-filter.test.js +264 -0
- package/server/lib/network/rate-limiter.js +229 -0
- package/server/lib/network/rate-limiter.test.js +293 -0
- package/server/lib/network/request-validator.js +351 -0
- package/server/lib/network/request-validator.test.js +345 -0
- package/server/lib/network/security-headers.js +251 -0
- package/server/lib/network/security-headers.test.js +283 -0
- package/server/lib/network/tls-config.js +210 -0
- package/server/lib/network/tls-config.test.js +248 -0
- package/server/lib/security/auth-security.js +369 -0
- package/server/lib/security/auth-security.test.js +448 -0
- package/server/lib/security/cis-benchmark.js +152 -0
- package/server/lib/security/cis-benchmark.test.js +137 -0
- package/server/lib/security/compose-templates.js +312 -0
- package/server/lib/security/compose-templates.test.js +229 -0
- package/server/lib/security/container-runtime.js +456 -0
- package/server/lib/security/container-runtime.test.js +503 -0
- package/server/lib/security/cors-validator.js +278 -0
- package/server/lib/security/cors-validator.test.js +310 -0
- package/server/lib/security/crypto-utils.js +253 -0
- package/server/lib/security/crypto-utils.test.js +409 -0
- package/server/lib/security/dockerfile-linter.js +459 -0
- package/server/lib/security/dockerfile-linter.test.js +483 -0
- package/server/lib/security/dockerfile-templates.js +278 -0
- package/server/lib/security/dockerfile-templates.test.js +164 -0
- package/server/lib/security/error-sanitizer.js +426 -0
- package/server/lib/security/error-sanitizer.test.js +331 -0
- package/server/lib/security/headers-generator.js +368 -0
- package/server/lib/security/headers-generator.test.js +398 -0
- package/server/lib/security/image-scanner.js +83 -0
- package/server/lib/security/image-scanner.test.js +106 -0
- package/server/lib/security/input-validator.js +352 -0
- package/server/lib/security/input-validator.test.js +330 -0
- package/server/lib/security/network-policy.js +174 -0
- package/server/lib/security/network-policy.test.js +164 -0
- package/server/lib/security/output-encoder.js +237 -0
- package/server/lib/security/output-encoder.test.js +276 -0
- package/server/lib/security/path-validator.js +359 -0
- package/server/lib/security/path-validator.test.js +293 -0
- package/server/lib/security/query-builder.js +421 -0
- package/server/lib/security/query-builder.test.js +318 -0
- package/server/lib/security/secret-detector.js +290 -0
- package/server/lib/security/secret-detector.test.js +354 -0
- package/server/lib/security/secrets-validator.js +137 -0
- package/server/lib/security/secrets-validator.test.js +120 -0
- package/server/lib/security-testing/dast-runner.js +154 -0
- package/server/lib/security-testing/dast-runner.test.js +62 -0
- package/server/lib/security-testing/dependency-scanner.js +172 -0
- package/server/lib/security-testing/dependency-scanner.test.js +64 -0
- package/server/lib/security-testing/pentest-runner.js +230 -0
- package/server/lib/security-testing/pentest-runner.test.js +60 -0
- package/server/lib/security-testing/sast-runner.js +136 -0
- package/server/lib/security-testing/sast-runner.test.js +62 -0
- package/server/lib/security-testing/secret-scanner.js +153 -0
- package/server/lib/security-testing/secret-scanner.test.js +66 -0
- package/server/lib/security-testing/security-gate.js +216 -0
- package/server/lib/security-testing/security-gate.test.js +115 -0
- package/server/lib/security-testing/security-reporter.js +303 -0
- package/server/lib/security-testing/security-reporter.test.js +114 -0
- package/server/lib/standards/audit-checker.js +546 -0
- package/server/lib/standards/audit-checker.test.js +415 -0
- package/server/lib/standards/cleanup-executor.js +452 -0
- package/server/lib/standards/cleanup-executor.test.js +293 -0
- package/server/lib/standards/refactor-stepper.js +425 -0
- package/server/lib/standards/refactor-stepper.test.js +298 -0
- package/server/lib/standards/standards-injector.js +167 -0
- package/server/lib/standards/standards-injector.test.js +232 -0
- package/server/lib/user-management.test.js +284 -0
- package/server/lib/vps/backup-manager.js +157 -0
- package/server/lib/vps/backup-manager.test.js +59 -0
- package/server/lib/vps/caddy-config.js +159 -0
- package/server/lib/vps/caddy-config.test.js +48 -0
- package/server/lib/vps/compose-orchestrator.js +219 -0
- package/server/lib/vps/compose-orchestrator.test.js +50 -0
- package/server/lib/vps/database-config.js +208 -0
- package/server/lib/vps/database-config.test.js +47 -0
- package/server/lib/vps/deploy-script.js +211 -0
- package/server/lib/vps/deploy-script.test.js +53 -0
- package/server/lib/vps/secrets-manager.js +148 -0
- package/server/lib/vps/secrets-manager.test.js +58 -0
- package/server/lib/vps/server-hardening.js +174 -0
- package/server/lib/vps/server-hardening.test.js +70 -0
- package/server/package-lock.json +19 -0
- package/server/package.json +1 -0
- package/server/templates/CLAUDE.md +37 -0
- 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
|
+
});
|