tlc-claude-code 1.2.29 → 1.4.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.
- package/dashboard/dist/components/AuditPane.d.ts +30 -0
- package/dashboard/dist/components/AuditPane.js +127 -0
- package/dashboard/dist/components/AuditPane.test.d.ts +1 -0
- package/dashboard/dist/components/AuditPane.test.js +339 -0
- package/dashboard/dist/components/CompliancePane.d.ts +39 -0
- package/dashboard/dist/components/CompliancePane.js +96 -0
- package/dashboard/dist/components/CompliancePane.test.d.ts +1 -0
- package/dashboard/dist/components/CompliancePane.test.js +183 -0
- package/dashboard/dist/components/SSOPane.d.ts +36 -0
- package/dashboard/dist/components/SSOPane.js +71 -0
- package/dashboard/dist/components/SSOPane.test.d.ts +1 -0
- package/dashboard/dist/components/SSOPane.test.js +155 -0
- package/dashboard/dist/components/UsagePane.d.ts +13 -0
- package/dashboard/dist/components/UsagePane.js +51 -0
- package/dashboard/dist/components/UsagePane.test.d.ts +1 -0
- package/dashboard/dist/components/UsagePane.test.js +142 -0
- package/dashboard/dist/components/WorkspaceDocsPane.d.ts +19 -0
- package/dashboard/dist/components/WorkspaceDocsPane.js +130 -0
- package/dashboard/dist/components/WorkspaceDocsPane.test.d.ts +1 -0
- package/dashboard/dist/components/WorkspaceDocsPane.test.js +242 -0
- package/dashboard/dist/components/WorkspacePane.d.ts +18 -0
- package/dashboard/dist/components/WorkspacePane.js +17 -0
- package/dashboard/dist/components/WorkspacePane.test.d.ts +1 -0
- package/dashboard/dist/components/WorkspacePane.test.js +84 -0
- package/dashboard/dist/components/ZeroRetentionPane.d.ts +44 -0
- package/dashboard/dist/components/ZeroRetentionPane.js +83 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.d.ts +1 -0
- package/dashboard/dist/components/ZeroRetentionPane.test.js +160 -0
- package/package.json +1 -1
- package/server/lib/access-control-doc.js +541 -0
- package/server/lib/access-control-doc.test.js +672 -0
- package/server/lib/adr-generator.js +423 -0
- package/server/lib/adr-generator.test.js +586 -0
- package/server/lib/agent-progress-monitor.js +223 -0
- package/server/lib/agent-progress-monitor.test.js +202 -0
- package/server/lib/architecture-command.js +450 -0
- package/server/lib/architecture-command.test.js +754 -0
- package/server/lib/ast-analyzer.js +324 -0
- package/server/lib/ast-analyzer.test.js +437 -0
- package/server/lib/audit-attribution.js +191 -0
- package/server/lib/audit-attribution.test.js +359 -0
- package/server/lib/audit-classifier.js +202 -0
- package/server/lib/audit-classifier.test.js +209 -0
- package/server/lib/audit-command.js +275 -0
- package/server/lib/audit-command.test.js +325 -0
- package/server/lib/audit-exporter.js +380 -0
- package/server/lib/audit-exporter.test.js +464 -0
- package/server/lib/audit-logger.js +236 -0
- package/server/lib/audit-logger.test.js +364 -0
- package/server/lib/audit-query.js +257 -0
- package/server/lib/audit-query.test.js +352 -0
- package/server/lib/audit-storage.js +269 -0
- package/server/lib/audit-storage.test.js +272 -0
- package/server/lib/auth-system.test.js +4 -1
- package/server/lib/boundary-detector.js +427 -0
- package/server/lib/boundary-detector.test.js +320 -0
- package/server/lib/budget-alerts.js +138 -0
- package/server/lib/budget-alerts.test.js +235 -0
- package/server/lib/bulk-repo-init.js +342 -0
- package/server/lib/bulk-repo-init.test.js +388 -0
- package/server/lib/candidates-tracker.js +210 -0
- package/server/lib/candidates-tracker.test.js +300 -0
- package/server/lib/checkpoint-manager.js +251 -0
- package/server/lib/checkpoint-manager.test.js +474 -0
- package/server/lib/circular-detector.js +337 -0
- package/server/lib/circular-detector.test.js +353 -0
- package/server/lib/cohesion-analyzer.js +310 -0
- package/server/lib/cohesion-analyzer.test.js +447 -0
- package/server/lib/compliance-checklist.js +866 -0
- package/server/lib/compliance-checklist.test.js +476 -0
- package/server/lib/compliance-command.js +616 -0
- package/server/lib/compliance-command.test.js +551 -0
- package/server/lib/compliance-reporter.js +692 -0
- package/server/lib/compliance-reporter.test.js +707 -0
- package/server/lib/contract-testing.js +625 -0
- package/server/lib/contract-testing.test.js +342 -0
- package/server/lib/conversion-planner.js +469 -0
- package/server/lib/conversion-planner.test.js +361 -0
- package/server/lib/convert-command.js +351 -0
- package/server/lib/convert-command.test.js +608 -0
- package/server/lib/coupling-calculator.js +189 -0
- package/server/lib/coupling-calculator.test.js +509 -0
- package/server/lib/data-flow-doc.js +665 -0
- package/server/lib/data-flow-doc.test.js +659 -0
- package/server/lib/dependency-graph.js +367 -0
- package/server/lib/dependency-graph.test.js +516 -0
- package/server/lib/duplication-detector.js +349 -0
- package/server/lib/duplication-detector.test.js +401 -0
- package/server/lib/ephemeral-storage.js +249 -0
- package/server/lib/ephemeral-storage.test.js +254 -0
- package/server/lib/evidence-collector.js +627 -0
- package/server/lib/evidence-collector.test.js +901 -0
- package/server/lib/example-service.js +616 -0
- package/server/lib/example-service.test.js +397 -0
- package/server/lib/flow-diagram-generator.js +474 -0
- package/server/lib/flow-diagram-generator.test.js +446 -0
- package/server/lib/idp-manager.js +626 -0
- package/server/lib/idp-manager.test.js +587 -0
- package/server/lib/impact-scorer.js +184 -0
- package/server/lib/impact-scorer.test.js +211 -0
- package/server/lib/memory-exclusion.js +326 -0
- package/server/lib/memory-exclusion.test.js +241 -0
- package/server/lib/mermaid-generator.js +358 -0
- package/server/lib/mermaid-generator.test.js +301 -0
- package/server/lib/messaging-patterns.js +750 -0
- package/server/lib/messaging-patterns.test.js +213 -0
- package/server/lib/mfa-handler.js +452 -0
- package/server/lib/mfa-handler.test.js +490 -0
- package/server/lib/microservice-template.js +386 -0
- package/server/lib/microservice-template.test.js +325 -0
- package/server/lib/new-project-microservice.js +450 -0
- package/server/lib/new-project-microservice.test.js +600 -0
- package/server/lib/oauth-flow.js +375 -0
- package/server/lib/oauth-flow.test.js +487 -0
- package/server/lib/oauth-registry.js +190 -0
- package/server/lib/oauth-registry.test.js +306 -0
- package/server/lib/readme-generator.js +490 -0
- package/server/lib/readme-generator.test.js +493 -0
- package/server/lib/refactor-command.js +326 -0
- package/server/lib/refactor-command.test.js +528 -0
- package/server/lib/refactor-executor.js +254 -0
- package/server/lib/refactor-executor.test.js +305 -0
- package/server/lib/refactor-observer.js +292 -0
- package/server/lib/refactor-observer.test.js +422 -0
- package/server/lib/refactor-progress.js +193 -0
- package/server/lib/refactor-progress.test.js +251 -0
- package/server/lib/refactor-reporter.js +237 -0
- package/server/lib/refactor-reporter.test.js +247 -0
- package/server/lib/repo-dependency-tracker.js +261 -0
- package/server/lib/repo-dependency-tracker.test.js +350 -0
- package/server/lib/retention-policy.js +281 -0
- package/server/lib/retention-policy.test.js +486 -0
- package/server/lib/role-mapper.js +236 -0
- package/server/lib/role-mapper.test.js +395 -0
- package/server/lib/saml-provider.js +765 -0
- package/server/lib/saml-provider.test.js +643 -0
- package/server/lib/security-policy-generator.js +682 -0
- package/server/lib/security-policy-generator.test.js +544 -0
- package/server/lib/semantic-analyzer.js +198 -0
- package/server/lib/semantic-analyzer.test.js +474 -0
- package/server/lib/sensitive-detector.js +112 -0
- package/server/lib/sensitive-detector.test.js +209 -0
- package/server/lib/service-interaction-diagram.js +700 -0
- package/server/lib/service-interaction-diagram.test.js +638 -0
- package/server/lib/service-scaffold.js +486 -0
- package/server/lib/service-scaffold.test.js +373 -0
- package/server/lib/service-summary.js +553 -0
- package/server/lib/service-summary.test.js +619 -0
- package/server/lib/session-purge.js +460 -0
- package/server/lib/session-purge.test.js +312 -0
- package/server/lib/shared-kernel.js +578 -0
- package/server/lib/shared-kernel.test.js +255 -0
- package/server/lib/sso-command.js +544 -0
- package/server/lib/sso-command.test.js +552 -0
- package/server/lib/sso-session.js +492 -0
- package/server/lib/sso-session.test.js +670 -0
- package/server/lib/traefik-config.js +282 -0
- package/server/lib/traefik-config.test.js +312 -0
- package/server/lib/usage-command.js +218 -0
- package/server/lib/usage-command.test.js +391 -0
- package/server/lib/usage-formatter.js +192 -0
- package/server/lib/usage-formatter.test.js +267 -0
- package/server/lib/usage-history.js +122 -0
- package/server/lib/usage-history.test.js +206 -0
- package/server/lib/workspace-command.js +249 -0
- package/server/lib/workspace-command.test.js +264 -0
- package/server/lib/workspace-config.js +270 -0
- package/server/lib/workspace-config.test.js +312 -0
- package/server/lib/workspace-docs-command.js +547 -0
- package/server/lib/workspace-docs-command.test.js +692 -0
- package/server/lib/workspace-memory.js +451 -0
- package/server/lib/workspace-memory.test.js +403 -0
- package/server/lib/workspace-scanner.js +452 -0
- package/server/lib/workspace-scanner.test.js +677 -0
- package/server/lib/workspace-test-runner.js +315 -0
- package/server/lib/workspace-test-runner.test.js +294 -0
- package/server/lib/zero-retention-command.js +439 -0
- package/server/lib/zero-retention-command.test.js +448 -0
- package/server/lib/zero-retention.js +322 -0
- package/server/lib/zero-retention.test.js +258 -0
- package/server/package-lock.json +14 -0
- package/server/package.json +1 -0
|
@@ -0,0 +1,551 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, vi, afterEach } from 'vitest';
|
|
2
|
+
|
|
3
|
+
describe('compliance-command', () => {
|
|
4
|
+
let ComplianceCommand;
|
|
5
|
+
let parseArgs;
|
|
6
|
+
let complianceCommand;
|
|
7
|
+
let mockChecklist;
|
|
8
|
+
let mockReporter;
|
|
9
|
+
let mockEvidenceCollector;
|
|
10
|
+
let mockPolicyGenerator;
|
|
11
|
+
|
|
12
|
+
beforeEach(async () => {
|
|
13
|
+
// Reset modules to ensure clean state
|
|
14
|
+
vi.resetModules();
|
|
15
|
+
|
|
16
|
+
// Create mock checklist
|
|
17
|
+
mockChecklist = {
|
|
18
|
+
controls: [
|
|
19
|
+
{
|
|
20
|
+
id: 'CC1.1',
|
|
21
|
+
category: 'Security',
|
|
22
|
+
name: 'Control Environment',
|
|
23
|
+
description: 'The entity demonstrates commitment to integrity.',
|
|
24
|
+
status: 'implemented',
|
|
25
|
+
evidence: ['ev-001'],
|
|
26
|
+
gapSeverity: 'high',
|
|
27
|
+
estimatedEffort: '2 weeks',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'CC6.1',
|
|
31
|
+
category: 'Security',
|
|
32
|
+
name: 'Access Control',
|
|
33
|
+
description: 'Logical access security software.',
|
|
34
|
+
status: 'not_implemented',
|
|
35
|
+
evidence: [],
|
|
36
|
+
gapSeverity: 'high',
|
|
37
|
+
estimatedEffort: '4 weeks',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'CC6.2',
|
|
41
|
+
category: 'Security',
|
|
42
|
+
name: 'User Registration',
|
|
43
|
+
description: 'Register and authorize new users.',
|
|
44
|
+
status: 'partial',
|
|
45
|
+
evidence: ['ev-002'],
|
|
46
|
+
gapSeverity: 'high',
|
|
47
|
+
estimatedEffort: '2 weeks',
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
id: 'A1.1',
|
|
51
|
+
category: 'Availability',
|
|
52
|
+
name: 'Capacity Planning',
|
|
53
|
+
description: 'Maintain and monitor capacity requirements.',
|
|
54
|
+
status: 'not_implemented',
|
|
55
|
+
evidence: [],
|
|
56
|
+
gapSeverity: 'high',
|
|
57
|
+
estimatedEffort: '3 weeks',
|
|
58
|
+
},
|
|
59
|
+
{
|
|
60
|
+
id: 'C1.1',
|
|
61
|
+
category: 'Confidentiality',
|
|
62
|
+
name: 'Information Classification',
|
|
63
|
+
description: 'Identify and maintain classifications.',
|
|
64
|
+
status: 'implemented',
|
|
65
|
+
evidence: ['ev-003'],
|
|
66
|
+
gapSeverity: 'high',
|
|
67
|
+
estimatedEffort: '2 weeks',
|
|
68
|
+
},
|
|
69
|
+
],
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
// Create mock reporter
|
|
73
|
+
mockReporter = {
|
|
74
|
+
generateReadinessReport: vi.fn().mockReturnValue({
|
|
75
|
+
title: 'SOC 2 Type II Readiness Report',
|
|
76
|
+
generatedAt: '2026-02-02T10:00:00.000Z',
|
|
77
|
+
period: { start: '2026-01-01', end: '2026-02-01' },
|
|
78
|
+
summary: {
|
|
79
|
+
overallScore: 85,
|
|
80
|
+
riskLevel: 'medium',
|
|
81
|
+
riskScore: 25,
|
|
82
|
+
totalControls: 5,
|
|
83
|
+
implemented: 2,
|
|
84
|
+
partial: 1,
|
|
85
|
+
gaps: 2,
|
|
86
|
+
},
|
|
87
|
+
categories: {
|
|
88
|
+
Security: { score: 50, implemented: 1, partial: 1, gaps: 1, total: 3 },
|
|
89
|
+
Availability: { score: 0, implemented: 0, partial: 0, gaps: 1, total: 1 },
|
|
90
|
+
Confidentiality: { score: 100, implemented: 1, partial: 0, gaps: 0, total: 1 },
|
|
91
|
+
},
|
|
92
|
+
findings: [],
|
|
93
|
+
recommendations: [],
|
|
94
|
+
}),
|
|
95
|
+
formatReportMarkdown: vi.fn().mockReturnValue('# SOC 2 Report\n\nContent here'),
|
|
96
|
+
formatReportHTML: vi.fn().mockReturnValue('<html><body>Report</body></html>'),
|
|
97
|
+
};
|
|
98
|
+
|
|
99
|
+
// Create mock evidence collector
|
|
100
|
+
mockEvidenceCollector = {
|
|
101
|
+
collectAll: vi.fn().mockResolvedValue({
|
|
102
|
+
items: [
|
|
103
|
+
{ id: 'ev-001', type: 'policy', title: 'Access Control Policy' },
|
|
104
|
+
{ id: 'ev-002', type: 'audit_log', title: 'User Access Logs' },
|
|
105
|
+
{ id: 'ev-003', type: 'config', title: 'Security Config' },
|
|
106
|
+
],
|
|
107
|
+
summary: {
|
|
108
|
+
totalItems: 3,
|
|
109
|
+
byType: { policy: 1, audit_log: 1, config: 1 },
|
|
110
|
+
},
|
|
111
|
+
}),
|
|
112
|
+
getAll: vi.fn().mockReturnValue([
|
|
113
|
+
{ id: 'ev-001', type: 'policy', title: 'Access Control Policy' },
|
|
114
|
+
{ id: 'ev-002', type: 'audit_log', title: 'User Access Logs' },
|
|
115
|
+
]),
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
// Create mock policy generator
|
|
119
|
+
mockPolicyGenerator = {
|
|
120
|
+
generateAccessControlPolicy: vi.fn().mockReturnValue({
|
|
121
|
+
title: 'Access Control Policy',
|
|
122
|
+
sections: [{ heading: 'Purpose', content: 'Test content' }],
|
|
123
|
+
}),
|
|
124
|
+
generateDataProtectionPolicy: vi.fn().mockReturnValue({
|
|
125
|
+
title: 'Data Protection Policy',
|
|
126
|
+
sections: [{ heading: 'Purpose', content: 'Test content' }],
|
|
127
|
+
}),
|
|
128
|
+
generateIncidentResponsePolicy: vi.fn().mockReturnValue({
|
|
129
|
+
title: 'Incident Response Policy',
|
|
130
|
+
sections: [{ heading: 'Purpose', content: 'Test content' }],
|
|
131
|
+
}),
|
|
132
|
+
generateAuthPolicy: vi.fn().mockReturnValue({
|
|
133
|
+
title: 'Authentication Policy',
|
|
134
|
+
sections: [{ heading: 'Purpose', content: 'Test content' }],
|
|
135
|
+
}),
|
|
136
|
+
generateAcceptableUsePolicy: vi.fn().mockReturnValue({
|
|
137
|
+
title: 'Acceptable Use Policy',
|
|
138
|
+
sections: [{ heading: 'Purpose', content: 'Test content' }],
|
|
139
|
+
}),
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
// Mock the checklist module
|
|
143
|
+
vi.doMock('./compliance-checklist.js', () => ({
|
|
144
|
+
createComplianceChecklist: vi.fn().mockReturnValue(mockChecklist),
|
|
145
|
+
getSOC2Checklist: vi.fn().mockReturnValue(mockChecklist.controls),
|
|
146
|
+
getCompliancePercentage: vi.fn().mockReturnValue({
|
|
147
|
+
percentage: 50,
|
|
148
|
+
implemented: 2,
|
|
149
|
+
partial: 1,
|
|
150
|
+
notImplemented: 2,
|
|
151
|
+
notApplicable: 0,
|
|
152
|
+
total: 5,
|
|
153
|
+
byCategory: {
|
|
154
|
+
Security: { percentage: 50, implemented: 1, total: 3 },
|
|
155
|
+
Availability: { percentage: 0, implemented: 0, total: 1 },
|
|
156
|
+
Confidentiality: { percentage: 100, implemented: 1, total: 1 },
|
|
157
|
+
},
|
|
158
|
+
}),
|
|
159
|
+
getComplianceGaps: vi.fn().mockReturnValue([
|
|
160
|
+
{
|
|
161
|
+
id: 'CC6.1',
|
|
162
|
+
category: 'Security',
|
|
163
|
+
name: 'Access Control',
|
|
164
|
+
status: 'not_implemented',
|
|
165
|
+
gapSeverity: 'high',
|
|
166
|
+
estimatedEffort: '4 weeks',
|
|
167
|
+
},
|
|
168
|
+
{
|
|
169
|
+
id: 'CC6.2',
|
|
170
|
+
category: 'Security',
|
|
171
|
+
name: 'User Registration',
|
|
172
|
+
status: 'partial',
|
|
173
|
+
gapSeverity: 'high',
|
|
174
|
+
estimatedEffort: '2 weeks',
|
|
175
|
+
},
|
|
176
|
+
{
|
|
177
|
+
id: 'A1.1',
|
|
178
|
+
category: 'Availability',
|
|
179
|
+
name: 'Capacity Planning',
|
|
180
|
+
status: 'not_implemented',
|
|
181
|
+
gapSeverity: 'high',
|
|
182
|
+
estimatedEffort: '3 weeks',
|
|
183
|
+
},
|
|
184
|
+
]),
|
|
185
|
+
getControlsByCategory: vi.fn().mockReturnValue({
|
|
186
|
+
Security: mockChecklist.controls.filter((c) => c.category === 'Security'),
|
|
187
|
+
Availability: mockChecklist.controls.filter((c) => c.category === 'Availability'),
|
|
188
|
+
Confidentiality: mockChecklist.controls.filter((c) => c.category === 'Confidentiality'),
|
|
189
|
+
}),
|
|
190
|
+
TSC_CATEGORIES: {
|
|
191
|
+
SECURITY: 'Security',
|
|
192
|
+
AVAILABILITY: 'Availability',
|
|
193
|
+
PROCESSING_INTEGRITY: 'Processing Integrity',
|
|
194
|
+
CONFIDENTIALITY: 'Confidentiality',
|
|
195
|
+
PRIVACY: 'Privacy',
|
|
196
|
+
},
|
|
197
|
+
}));
|
|
198
|
+
|
|
199
|
+
// Mock the reporter module
|
|
200
|
+
vi.doMock('./compliance-reporter.js', () => ({
|
|
201
|
+
createReporter: vi.fn().mockReturnValue(mockReporter),
|
|
202
|
+
generateReadinessReport: vi.fn().mockImplementation(() => mockReporter.generateReadinessReport()),
|
|
203
|
+
formatReportMarkdown: vi.fn().mockImplementation(() => mockReporter.formatReportMarkdown()),
|
|
204
|
+
formatReportHTML: vi.fn().mockImplementation(() => mockReporter.formatReportHTML()),
|
|
205
|
+
calculateCategoryScores: vi.fn().mockReturnValue({
|
|
206
|
+
Security: { score: 50, implemented: 1, partial: 1, gaps: 1, total: 3 },
|
|
207
|
+
Availability: { score: 0, implemented: 0, partial: 0, gaps: 1, total: 1 },
|
|
208
|
+
Confidentiality: { score: 100, implemented: 1, partial: 0, gaps: 0, total: 1 },
|
|
209
|
+
}),
|
|
210
|
+
}));
|
|
211
|
+
|
|
212
|
+
// Mock the evidence collector module
|
|
213
|
+
vi.doMock('./evidence-collector.js', () => ({
|
|
214
|
+
createEvidenceCollector: vi.fn().mockReturnValue(mockEvidenceCollector),
|
|
215
|
+
collectPolicyDocuments: vi.fn().mockResolvedValue({
|
|
216
|
+
type: 'policy',
|
|
217
|
+
content: { policies: [] },
|
|
218
|
+
}),
|
|
219
|
+
collectConfigSnapshot: vi.fn().mockResolvedValue({
|
|
220
|
+
type: 'config',
|
|
221
|
+
content: {},
|
|
222
|
+
}),
|
|
223
|
+
collectAuditLogs: vi.fn().mockResolvedValue({
|
|
224
|
+
type: 'audit_log',
|
|
225
|
+
content: { entries: [] },
|
|
226
|
+
}),
|
|
227
|
+
}));
|
|
228
|
+
|
|
229
|
+
// Mock the policy generator module
|
|
230
|
+
vi.doMock('./security-policy-generator.js', () => ({
|
|
231
|
+
generateAccessControlPolicy: mockPolicyGenerator.generateAccessControlPolicy,
|
|
232
|
+
generateDataProtectionPolicy: mockPolicyGenerator.generateDataProtectionPolicy,
|
|
233
|
+
generateIncidentResponsePolicy: mockPolicyGenerator.generateIncidentResponsePolicy,
|
|
234
|
+
generateAuthPolicy: mockPolicyGenerator.generateAuthPolicy,
|
|
235
|
+
generateAcceptableUsePolicy: mockPolicyGenerator.generateAcceptableUsePolicy,
|
|
236
|
+
exportAsMarkdown: vi.fn().mockReturnValue('# Policy\n\nContent'),
|
|
237
|
+
}));
|
|
238
|
+
|
|
239
|
+
// Import module after mocks are set up
|
|
240
|
+
const module = await import('./compliance-command.js');
|
|
241
|
+
ComplianceCommand = module.ComplianceCommand;
|
|
242
|
+
parseArgs = module.parseArgs;
|
|
243
|
+
|
|
244
|
+
// Create instance with mocks
|
|
245
|
+
complianceCommand = new ComplianceCommand({
|
|
246
|
+
checklist: mockChecklist,
|
|
247
|
+
reporter: mockReporter,
|
|
248
|
+
evidenceCollector: mockEvidenceCollector,
|
|
249
|
+
policyGenerator: mockPolicyGenerator,
|
|
250
|
+
});
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
afterEach(() => {
|
|
254
|
+
vi.resetAllMocks();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
describe('parseArgs', () => {
|
|
258
|
+
it('parses empty args', () => {
|
|
259
|
+
const result = parseArgs([]);
|
|
260
|
+
expect(result).toEqual({
|
|
261
|
+
subcommand: null,
|
|
262
|
+
category: null,
|
|
263
|
+
format: 'text',
|
|
264
|
+
output: null,
|
|
265
|
+
unknownSubcommand: null,
|
|
266
|
+
});
|
|
267
|
+
});
|
|
268
|
+
|
|
269
|
+
it('parses status subcommand', () => {
|
|
270
|
+
const result = parseArgs(['status']);
|
|
271
|
+
expect(result.subcommand).toBe('status');
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
it('parses checklist subcommand', () => {
|
|
275
|
+
const result = parseArgs(['checklist']);
|
|
276
|
+
expect(result.subcommand).toBe('checklist');
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('parses evidence subcommand', () => {
|
|
280
|
+
const result = parseArgs(['evidence']);
|
|
281
|
+
expect(result.subcommand).toBe('evidence');
|
|
282
|
+
});
|
|
283
|
+
|
|
284
|
+
it('parses report subcommand', () => {
|
|
285
|
+
const result = parseArgs(['report']);
|
|
286
|
+
expect(result.subcommand).toBe('report');
|
|
287
|
+
});
|
|
288
|
+
|
|
289
|
+
it('parses policies subcommand', () => {
|
|
290
|
+
const result = parseArgs(['policies']);
|
|
291
|
+
expect(result.subcommand).toBe('policies');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('parses gaps subcommand', () => {
|
|
295
|
+
const result = parseArgs(['gaps']);
|
|
296
|
+
expect(result.subcommand).toBe('gaps');
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
it('parses --category flag', () => {
|
|
300
|
+
const result = parseArgs(['checklist', '--category', 'Security']);
|
|
301
|
+
expect(result.category).toBe('Security');
|
|
302
|
+
});
|
|
303
|
+
|
|
304
|
+
it('parses --category= syntax', () => {
|
|
305
|
+
const result = parseArgs(['checklist', '--category=Availability']);
|
|
306
|
+
expect(result.category).toBe('Availability');
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
it('parses --format flag', () => {
|
|
310
|
+
const result = parseArgs(['report', '--format', 'markdown']);
|
|
311
|
+
expect(result.format).toBe('markdown');
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
it('parses --format= syntax', () => {
|
|
315
|
+
const result = parseArgs(['report', '--format=html']);
|
|
316
|
+
expect(result.format).toBe('html');
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('parses --output flag', () => {
|
|
320
|
+
const result = parseArgs(['report', '--output', '/path/to/report.md']);
|
|
321
|
+
expect(result.output).toBe('/path/to/report.md');
|
|
322
|
+
});
|
|
323
|
+
|
|
324
|
+
it('handles all subcommands', () => {
|
|
325
|
+
const subcommands = ['status', 'checklist', 'evidence', 'report', 'policies', 'gaps'];
|
|
326
|
+
for (const cmd of subcommands) {
|
|
327
|
+
const result = parseArgs([cmd]);
|
|
328
|
+
expect(result.subcommand).toBe(cmd);
|
|
329
|
+
}
|
|
330
|
+
});
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
describe('execute status', () => {
|
|
334
|
+
it('shows compliance percentage', async () => {
|
|
335
|
+
const result = await complianceCommand.execute(['status']);
|
|
336
|
+
|
|
337
|
+
expect(result.success).toBe(true);
|
|
338
|
+
expect(result.output).toContain('50%');
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
it('shows category breakdown', async () => {
|
|
342
|
+
const result = await complianceCommand.execute(['status']);
|
|
343
|
+
|
|
344
|
+
expect(result.success).toBe(true);
|
|
345
|
+
expect(result.output).toContain('Security');
|
|
346
|
+
expect(result.output).toContain('Availability');
|
|
347
|
+
expect(result.output).toContain('Confidentiality');
|
|
348
|
+
});
|
|
349
|
+
|
|
350
|
+
it('shows risk level', async () => {
|
|
351
|
+
const result = await complianceCommand.execute(['status']);
|
|
352
|
+
|
|
353
|
+
expect(result.success).toBe(true);
|
|
354
|
+
// Should contain some indicator of risk
|
|
355
|
+
expect(result.output.toLowerCase()).toMatch(/risk|gap/i);
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
it('shows gaps count', async () => {
|
|
359
|
+
const result = await complianceCommand.execute(['status']);
|
|
360
|
+
|
|
361
|
+
expect(result.success).toBe(true);
|
|
362
|
+
expect(result.output).toMatch(/\d+\s*(gap|control)/i);
|
|
363
|
+
});
|
|
364
|
+
});
|
|
365
|
+
|
|
366
|
+
describe('execute checklist', () => {
|
|
367
|
+
it('shows all controls', async () => {
|
|
368
|
+
const result = await complianceCommand.execute(['checklist']);
|
|
369
|
+
|
|
370
|
+
expect(result.success).toBe(true);
|
|
371
|
+
expect(result.output).toContain('CC1.1');
|
|
372
|
+
expect(result.output).toContain('CC6.1');
|
|
373
|
+
});
|
|
374
|
+
|
|
375
|
+
it('filters by category', async () => {
|
|
376
|
+
const result = await complianceCommand.execute(['checklist', '--category', 'Availability']);
|
|
377
|
+
|
|
378
|
+
expect(result.success).toBe(true);
|
|
379
|
+
expect(result.output).toContain('A1.1');
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
it('shows control status', async () => {
|
|
383
|
+
const result = await complianceCommand.execute(['checklist']);
|
|
384
|
+
|
|
385
|
+
expect(result.success).toBe(true);
|
|
386
|
+
// Should show status indicators
|
|
387
|
+
expect(result.output).toMatch(/implement|partial|not/i);
|
|
388
|
+
});
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
describe('execute evidence', () => {
|
|
392
|
+
it('collects all evidence', async () => {
|
|
393
|
+
const result = await complianceCommand.execute(['evidence']);
|
|
394
|
+
|
|
395
|
+
expect(result.success).toBe(true);
|
|
396
|
+
});
|
|
397
|
+
|
|
398
|
+
it('shows collection summary', async () => {
|
|
399
|
+
const result = await complianceCommand.execute(['evidence']);
|
|
400
|
+
|
|
401
|
+
expect(result.success).toBe(true);
|
|
402
|
+
expect(result.output).toMatch(/evidence|collected|item/i);
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('lists evidence items', async () => {
|
|
406
|
+
const result = await complianceCommand.execute(['evidence']);
|
|
407
|
+
|
|
408
|
+
expect(result.success).toBe(true);
|
|
409
|
+
// Should show evidence types or IDs
|
|
410
|
+
expect(result.output).toMatch(/policy|audit|config|ev-/i);
|
|
411
|
+
});
|
|
412
|
+
});
|
|
413
|
+
|
|
414
|
+
describe('execute report', () => {
|
|
415
|
+
it('generates full report', async () => {
|
|
416
|
+
const result = await complianceCommand.execute(['report']);
|
|
417
|
+
|
|
418
|
+
expect(result.success).toBe(true);
|
|
419
|
+
expect(result.output).toBeTruthy();
|
|
420
|
+
});
|
|
421
|
+
|
|
422
|
+
it('supports format flag for markdown', async () => {
|
|
423
|
+
const result = await complianceCommand.execute(['report', '--format', 'markdown']);
|
|
424
|
+
|
|
425
|
+
expect(result.success).toBe(true);
|
|
426
|
+
});
|
|
427
|
+
|
|
428
|
+
it('supports format flag for html', async () => {
|
|
429
|
+
const result = await complianceCommand.execute(['report', '--format', 'html']);
|
|
430
|
+
|
|
431
|
+
expect(result.success).toBe(true);
|
|
432
|
+
});
|
|
433
|
+
|
|
434
|
+
it('supports text format by default', async () => {
|
|
435
|
+
const result = await complianceCommand.execute(['report']);
|
|
436
|
+
|
|
437
|
+
expect(result.success).toBe(true);
|
|
438
|
+
});
|
|
439
|
+
});
|
|
440
|
+
|
|
441
|
+
describe('execute policies', () => {
|
|
442
|
+
it('generates all policies', async () => {
|
|
443
|
+
const result = await complianceCommand.execute(['policies']);
|
|
444
|
+
|
|
445
|
+
expect(result.success).toBe(true);
|
|
446
|
+
expect(result.output).toContain('Access Control');
|
|
447
|
+
});
|
|
448
|
+
|
|
449
|
+
it('lists available policy types', async () => {
|
|
450
|
+
const result = await complianceCommand.execute(['policies']);
|
|
451
|
+
|
|
452
|
+
expect(result.success).toBe(true);
|
|
453
|
+
// Should mention different policy types
|
|
454
|
+
expect(result.output).toMatch(/access|data|incident|auth|acceptable/i);
|
|
455
|
+
});
|
|
456
|
+
});
|
|
457
|
+
|
|
458
|
+
describe('execute gaps', () => {
|
|
459
|
+
it('shows unimplemented controls', async () => {
|
|
460
|
+
const result = await complianceCommand.execute(['gaps']);
|
|
461
|
+
|
|
462
|
+
expect(result.success).toBe(true);
|
|
463
|
+
expect(result.output).toContain('CC6.1');
|
|
464
|
+
});
|
|
465
|
+
|
|
466
|
+
it('shows partial controls', async () => {
|
|
467
|
+
const result = await complianceCommand.execute(['gaps']);
|
|
468
|
+
|
|
469
|
+
expect(result.success).toBe(true);
|
|
470
|
+
expect(result.output).toContain('CC6.2');
|
|
471
|
+
});
|
|
472
|
+
|
|
473
|
+
it('groups by priority', async () => {
|
|
474
|
+
const result = await complianceCommand.execute(['gaps']);
|
|
475
|
+
|
|
476
|
+
expect(result.success).toBe(true);
|
|
477
|
+
expect(result.output.toLowerCase()).toMatch(/high|priority/i);
|
|
478
|
+
});
|
|
479
|
+
|
|
480
|
+
it('shows gap severity', async () => {
|
|
481
|
+
const result = await complianceCommand.execute(['gaps']);
|
|
482
|
+
|
|
483
|
+
expect(result.success).toBe(true);
|
|
484
|
+
expect(result.output).toMatch(/not_implemented|partial|high/i);
|
|
485
|
+
});
|
|
486
|
+
});
|
|
487
|
+
|
|
488
|
+
describe('formatStatus', () => {
|
|
489
|
+
it('returns readable output', () => {
|
|
490
|
+
const status = {
|
|
491
|
+
percentage: 85,
|
|
492
|
+
implemented: 34,
|
|
493
|
+
partial: 4,
|
|
494
|
+
notImplemented: 2,
|
|
495
|
+
total: 40,
|
|
496
|
+
byCategory: {
|
|
497
|
+
Security: { percentage: 90, implemented: 20, total: 22 },
|
|
498
|
+
Availability: { percentage: 80, implemented: 3, total: 4 },
|
|
499
|
+
},
|
|
500
|
+
};
|
|
501
|
+
|
|
502
|
+
const output = complianceCommand.formatStatus(status);
|
|
503
|
+
|
|
504
|
+
expect(output).toContain('85%');
|
|
505
|
+
expect(output).toContain('Security');
|
|
506
|
+
expect(output).toContain('Availability');
|
|
507
|
+
});
|
|
508
|
+
|
|
509
|
+
it('includes progress bar', () => {
|
|
510
|
+
const status = {
|
|
511
|
+
percentage: 50,
|
|
512
|
+
implemented: 5,
|
|
513
|
+
partial: 0,
|
|
514
|
+
notImplemented: 5,
|
|
515
|
+
total: 10,
|
|
516
|
+
byCategory: {},
|
|
517
|
+
};
|
|
518
|
+
|
|
519
|
+
const output = complianceCommand.formatStatus(status);
|
|
520
|
+
|
|
521
|
+
// Should have some visual progress indicator
|
|
522
|
+
expect(output.length).toBeGreaterThan(0);
|
|
523
|
+
});
|
|
524
|
+
});
|
|
525
|
+
|
|
526
|
+
describe('error handling', () => {
|
|
527
|
+
it('handles unknown subcommand', async () => {
|
|
528
|
+
const result = await complianceCommand.execute(['unknown']);
|
|
529
|
+
|
|
530
|
+
expect(result.success).toBe(false);
|
|
531
|
+
expect(result.error).toContain('unknown');
|
|
532
|
+
});
|
|
533
|
+
|
|
534
|
+
it('handles missing subcommand gracefully', async () => {
|
|
535
|
+
const result = await complianceCommand.execute([]);
|
|
536
|
+
|
|
537
|
+
// Should either show help or status by default
|
|
538
|
+
expect(result.success).toBe(true);
|
|
539
|
+
});
|
|
540
|
+
|
|
541
|
+
it('handles errors gracefully', async () => {
|
|
542
|
+
// Override to throw an error
|
|
543
|
+
complianceCommand.handleStatus = vi.fn().mockRejectedValue(new Error('Test error'));
|
|
544
|
+
|
|
545
|
+
const result = await complianceCommand.execute(['status']);
|
|
546
|
+
|
|
547
|
+
expect(result.success).toBe(false);
|
|
548
|
+
expect(result.error).toContain('Test error');
|
|
549
|
+
});
|
|
550
|
+
});
|
|
551
|
+
});
|