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,446 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flow Diagram Generator Tests
|
|
3
|
+
* Generate Mermaid diagrams showing data flow between repos
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
7
|
+
|
|
8
|
+
describe('FlowDiagramGenerator', () => {
|
|
9
|
+
describe('workspace imports detection', () => {
|
|
10
|
+
it('detects workspace:* imports between repos', async () => {
|
|
11
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
12
|
+
const generator = new FlowDiagramGenerator();
|
|
13
|
+
|
|
14
|
+
const files = {
|
|
15
|
+
'/workspace/api-service/src/index.js': `
|
|
16
|
+
import { UserModel } from 'workspace:shared-models';
|
|
17
|
+
import { logger } from 'workspace:common-utils';
|
|
18
|
+
`,
|
|
19
|
+
'/workspace/web-app/src/api.js': `
|
|
20
|
+
import { apiClient } from 'workspace:api-client';
|
|
21
|
+
`,
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const result = generator.analyzeFiles(files);
|
|
25
|
+
|
|
26
|
+
expect(result.crossRepoImports).toHaveLength(3);
|
|
27
|
+
expect(result.crossRepoImports).toContainEqual(
|
|
28
|
+
expect.objectContaining({
|
|
29
|
+
from: 'api-service',
|
|
30
|
+
to: 'shared-models',
|
|
31
|
+
type: 'import',
|
|
32
|
+
})
|
|
33
|
+
);
|
|
34
|
+
expect(result.crossRepoImports).toContainEqual(
|
|
35
|
+
expect.objectContaining({
|
|
36
|
+
from: 'api-service',
|
|
37
|
+
to: 'common-utils',
|
|
38
|
+
type: 'import',
|
|
39
|
+
})
|
|
40
|
+
);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('handles repos with no cross-repo imports', async () => {
|
|
44
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
45
|
+
const generator = new FlowDiagramGenerator();
|
|
46
|
+
|
|
47
|
+
const files = {
|
|
48
|
+
'/workspace/isolated-service/src/index.js': `
|
|
49
|
+
import { helper } from './utils';
|
|
50
|
+
import express from 'express';
|
|
51
|
+
`,
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const result = generator.analyzeFiles(files);
|
|
55
|
+
|
|
56
|
+
expect(result.crossRepoImports).toHaveLength(0);
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
describe('HTTP calls detection', () => {
|
|
61
|
+
it('detects fetch calls to other services', async () => {
|
|
62
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
63
|
+
const generator = new FlowDiagramGenerator();
|
|
64
|
+
|
|
65
|
+
const files = {
|
|
66
|
+
'/workspace/frontend/src/api.js': `
|
|
67
|
+
const users = await fetch('http://user-service:3000/api/users');
|
|
68
|
+
const orders = await fetch(\`\${USER_SERVICE_URL}/orders\`);
|
|
69
|
+
`,
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const result = generator.analyzeFiles(files);
|
|
73
|
+
|
|
74
|
+
expect(result.httpCalls).toHaveLength(2);
|
|
75
|
+
expect(result.httpCalls).toContainEqual(
|
|
76
|
+
expect.objectContaining({
|
|
77
|
+
from: 'frontend',
|
|
78
|
+
to: 'user-service',
|
|
79
|
+
type: 'http',
|
|
80
|
+
})
|
|
81
|
+
);
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it('detects axios calls to other services', async () => {
|
|
85
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
86
|
+
const generator = new FlowDiagramGenerator();
|
|
87
|
+
|
|
88
|
+
const files = {
|
|
89
|
+
'/workspace/gateway/src/proxy.js': `
|
|
90
|
+
const response = await axios.get('http://inventory-service/items');
|
|
91
|
+
await axios.post('http://payment-service/charge', data);
|
|
92
|
+
`,
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const result = generator.analyzeFiles(files);
|
|
96
|
+
|
|
97
|
+
expect(result.httpCalls).toHaveLength(2);
|
|
98
|
+
expect(result.httpCalls).toContainEqual(
|
|
99
|
+
expect.objectContaining({
|
|
100
|
+
from: 'gateway',
|
|
101
|
+
to: 'inventory-service',
|
|
102
|
+
type: 'http',
|
|
103
|
+
})
|
|
104
|
+
);
|
|
105
|
+
expect(result.httpCalls).toContainEqual(
|
|
106
|
+
expect.objectContaining({
|
|
107
|
+
from: 'gateway',
|
|
108
|
+
to: 'payment-service',
|
|
109
|
+
type: 'http',
|
|
110
|
+
})
|
|
111
|
+
);
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('detects http/https module calls', async () => {
|
|
115
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
116
|
+
const generator = new FlowDiagramGenerator();
|
|
117
|
+
|
|
118
|
+
const files = {
|
|
119
|
+
'/workspace/backend/src/client.js': `
|
|
120
|
+
http.request({ hostname: 'auth-service', port: 3000, path: '/verify' });
|
|
121
|
+
https.get('https://external-api.com/data');
|
|
122
|
+
`,
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
const result = generator.analyzeFiles(files);
|
|
126
|
+
|
|
127
|
+
expect(result.httpCalls).toContainEqual(
|
|
128
|
+
expect.objectContaining({
|
|
129
|
+
from: 'backend',
|
|
130
|
+
to: 'auth-service',
|
|
131
|
+
type: 'http',
|
|
132
|
+
})
|
|
133
|
+
);
|
|
134
|
+
});
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
describe('message queue detection', () => {
|
|
138
|
+
it('detects publish/emit patterns', async () => {
|
|
139
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
140
|
+
const generator = new FlowDiagramGenerator();
|
|
141
|
+
|
|
142
|
+
const files = {
|
|
143
|
+
'/workspace/order-service/src/events.js': `
|
|
144
|
+
eventBus.publish('order.created', orderData);
|
|
145
|
+
queue.emit('payment.requested', { orderId });
|
|
146
|
+
channel.sendToQueue('notifications', Buffer.from(message));
|
|
147
|
+
`,
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
const result = generator.analyzeFiles(files);
|
|
151
|
+
|
|
152
|
+
expect(result.messageQueue).toContainEqual(
|
|
153
|
+
expect.objectContaining({
|
|
154
|
+
from: 'order-service',
|
|
155
|
+
event: 'order.created',
|
|
156
|
+
type: 'producer',
|
|
157
|
+
})
|
|
158
|
+
);
|
|
159
|
+
expect(result.messageQueue).toContainEqual(
|
|
160
|
+
expect.objectContaining({
|
|
161
|
+
from: 'order-service',
|
|
162
|
+
event: 'payment.requested',
|
|
163
|
+
type: 'producer',
|
|
164
|
+
})
|
|
165
|
+
);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('detects subscribe/consume patterns', async () => {
|
|
169
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
170
|
+
const generator = new FlowDiagramGenerator();
|
|
171
|
+
|
|
172
|
+
const files = {
|
|
173
|
+
'/workspace/notification-service/src/handlers.js': `
|
|
174
|
+
eventBus.subscribe('order.created', handleOrderCreated);
|
|
175
|
+
queue.on('payment.completed', handlePayment);
|
|
176
|
+
channel.consume('notifications', processNotification);
|
|
177
|
+
`,
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
const result = generator.analyzeFiles(files);
|
|
181
|
+
|
|
182
|
+
expect(result.messageQueue).toContainEqual(
|
|
183
|
+
expect.objectContaining({
|
|
184
|
+
from: 'notification-service',
|
|
185
|
+
event: 'order.created',
|
|
186
|
+
type: 'consumer',
|
|
187
|
+
})
|
|
188
|
+
);
|
|
189
|
+
expect(result.messageQueue).toContainEqual(
|
|
190
|
+
expect.objectContaining({
|
|
191
|
+
from: 'notification-service',
|
|
192
|
+
event: 'payment.completed',
|
|
193
|
+
type: 'consumer',
|
|
194
|
+
})
|
|
195
|
+
);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
198
|
+
|
|
199
|
+
describe('database access detection', () => {
|
|
200
|
+
it('detects database access patterns', async () => {
|
|
201
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
202
|
+
const generator = new FlowDiagramGenerator();
|
|
203
|
+
|
|
204
|
+
const files = {
|
|
205
|
+
'/workspace/user-service/src/repository.js': `
|
|
206
|
+
const users = await db.collection('users').find({});
|
|
207
|
+
await prisma.user.findMany();
|
|
208
|
+
const result = await sequelize.query('SELECT * FROM users');
|
|
209
|
+
`,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
const result = generator.analyzeFiles(files);
|
|
213
|
+
|
|
214
|
+
expect(result.databaseAccess).toContainEqual(
|
|
215
|
+
expect.objectContaining({
|
|
216
|
+
from: 'user-service',
|
|
217
|
+
resource: 'users',
|
|
218
|
+
type: 'database',
|
|
219
|
+
})
|
|
220
|
+
);
|
|
221
|
+
});
|
|
222
|
+
|
|
223
|
+
it('detects Redis cache access', async () => {
|
|
224
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
225
|
+
const generator = new FlowDiagramGenerator();
|
|
226
|
+
|
|
227
|
+
const files = {
|
|
228
|
+
'/workspace/cache-service/src/cache.js': `
|
|
229
|
+
await redis.get('session:123');
|
|
230
|
+
await redis.set('user:456', userData);
|
|
231
|
+
`,
|
|
232
|
+
};
|
|
233
|
+
|
|
234
|
+
const result = generator.analyzeFiles(files);
|
|
235
|
+
|
|
236
|
+
expect(result.databaseAccess).toContainEqual(
|
|
237
|
+
expect.objectContaining({
|
|
238
|
+
from: 'cache-service',
|
|
239
|
+
resource: 'session',
|
|
240
|
+
type: 'cache',
|
|
241
|
+
})
|
|
242
|
+
);
|
|
243
|
+
});
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
describe('Mermaid diagram generation', () => {
|
|
247
|
+
it('generates valid Mermaid flowchart syntax', async () => {
|
|
248
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
249
|
+
const generator = new FlowDiagramGenerator();
|
|
250
|
+
|
|
251
|
+
const analysisResult = {
|
|
252
|
+
crossRepoImports: [
|
|
253
|
+
{ from: 'api', to: 'shared', type: 'import' },
|
|
254
|
+
],
|
|
255
|
+
httpCalls: [
|
|
256
|
+
{ from: 'web', to: 'api', type: 'http' },
|
|
257
|
+
],
|
|
258
|
+
messageQueue: [],
|
|
259
|
+
databaseAccess: [],
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const mermaid = generator.generateMermaid(analysisResult);
|
|
263
|
+
|
|
264
|
+
expect(mermaid).toContain('flowchart');
|
|
265
|
+
expect(mermaid).toContain('->'); // Any arrow style
|
|
266
|
+
expect(mermaid).toMatch(/api.*-+>.*shared/); // Import uses -->
|
|
267
|
+
expect(mermaid).toMatch(/web.*-+>.*api/); // HTTP uses -.-> or -->
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('groups nodes by repo', async () => {
|
|
271
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
272
|
+
const generator = new FlowDiagramGenerator({ groupByRepo: true });
|
|
273
|
+
|
|
274
|
+
const analysisResult = {
|
|
275
|
+
crossRepoImports: [
|
|
276
|
+
{ from: 'service-a', to: 'shared-lib', type: 'import' },
|
|
277
|
+
{ from: 'service-b', to: 'shared-lib', type: 'import' },
|
|
278
|
+
],
|
|
279
|
+
httpCalls: [],
|
|
280
|
+
messageQueue: [],
|
|
281
|
+
databaseAccess: [],
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
const mermaid = generator.generateMermaid(analysisResult);
|
|
285
|
+
|
|
286
|
+
expect(mermaid).toContain('service_a');
|
|
287
|
+
expect(mermaid).toContain('service_b');
|
|
288
|
+
expect(mermaid).toContain('shared_lib');
|
|
289
|
+
});
|
|
290
|
+
|
|
291
|
+
it('shows direction of data flow', async () => {
|
|
292
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
293
|
+
const generator = new FlowDiagramGenerator();
|
|
294
|
+
|
|
295
|
+
const analysisResult = {
|
|
296
|
+
crossRepoImports: [],
|
|
297
|
+
httpCalls: [
|
|
298
|
+
{ from: 'client', to: 'server', type: 'http', method: 'POST' },
|
|
299
|
+
],
|
|
300
|
+
messageQueue: [
|
|
301
|
+
{ from: 'producer', event: 'order.created', type: 'producer' },
|
|
302
|
+
{ from: 'consumer', event: 'order.created', type: 'consumer' },
|
|
303
|
+
],
|
|
304
|
+
databaseAccess: [],
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
const mermaid = generator.generateMermaid(analysisResult);
|
|
308
|
+
|
|
309
|
+
// HTTP flows from client to server (uses dashed line -.->)
|
|
310
|
+
expect(mermaid).toMatch(/client.*-+\.?-*>.*server/);
|
|
311
|
+
// Message queue: producer to event, event to consumer (uses thick line ==>)
|
|
312
|
+
expect(mermaid).toMatch(/producer.*=+>.*order/i);
|
|
313
|
+
expect(mermaid).toMatch(/order.*=+>.*consumer/i);
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
it('handles repos with no cross-repo communication', async () => {
|
|
317
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
318
|
+
const generator = new FlowDiagramGenerator();
|
|
319
|
+
|
|
320
|
+
const analysisResult = {
|
|
321
|
+
crossRepoImports: [],
|
|
322
|
+
httpCalls: [],
|
|
323
|
+
messageQueue: [],
|
|
324
|
+
databaseAccess: [],
|
|
325
|
+
};
|
|
326
|
+
|
|
327
|
+
const mermaid = generator.generateMermaid(analysisResult);
|
|
328
|
+
|
|
329
|
+
expect(mermaid).toContain('flowchart');
|
|
330
|
+
expect(mermaid).toContain('No cross-repo communication detected');
|
|
331
|
+
});
|
|
332
|
+
|
|
333
|
+
it('uses different edge styles for different connection types', async () => {
|
|
334
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
335
|
+
const generator = new FlowDiagramGenerator();
|
|
336
|
+
|
|
337
|
+
const analysisResult = {
|
|
338
|
+
crossRepoImports: [
|
|
339
|
+
{ from: 'a', to: 'b', type: 'import' },
|
|
340
|
+
],
|
|
341
|
+
httpCalls: [
|
|
342
|
+
{ from: 'c', to: 'd', type: 'http' },
|
|
343
|
+
],
|
|
344
|
+
messageQueue: [
|
|
345
|
+
{ from: 'e', event: 'evt', type: 'producer' },
|
|
346
|
+
],
|
|
347
|
+
databaseAccess: [
|
|
348
|
+
{ from: 'f', resource: 'table', type: 'database' },
|
|
349
|
+
],
|
|
350
|
+
};
|
|
351
|
+
|
|
352
|
+
const mermaid = generator.generateMermaid(analysisResult);
|
|
353
|
+
|
|
354
|
+
// Should have legend or different line styles
|
|
355
|
+
expect(mermaid).toContain('classDef');
|
|
356
|
+
});
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
describe('full integration', () => {
|
|
360
|
+
it('analyzes files and generates complete diagram', async () => {
|
|
361
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
362
|
+
const generator = new FlowDiagramGenerator();
|
|
363
|
+
|
|
364
|
+
const files = {
|
|
365
|
+
'/workspace/api-gateway/src/routes.js': `
|
|
366
|
+
import { auth } from 'workspace:auth-service';
|
|
367
|
+
const users = await fetch('http://user-service/api/users');
|
|
368
|
+
eventBus.publish('request.logged', { path });
|
|
369
|
+
`,
|
|
370
|
+
'/workspace/user-service/src/index.js': `
|
|
371
|
+
import { db } from 'workspace:database-utils';
|
|
372
|
+
eventBus.subscribe('user.created', syncCache);
|
|
373
|
+
await prisma.user.findMany();
|
|
374
|
+
`,
|
|
375
|
+
};
|
|
376
|
+
|
|
377
|
+
const result = generator.analyzeFiles(files);
|
|
378
|
+
const mermaid = generator.generateMermaid(result);
|
|
379
|
+
|
|
380
|
+
// Should detect all types of connections
|
|
381
|
+
expect(result.crossRepoImports.length).toBeGreaterThan(0);
|
|
382
|
+
expect(result.httpCalls.length).toBeGreaterThan(0);
|
|
383
|
+
expect(result.messageQueue.length).toBeGreaterThan(0);
|
|
384
|
+
expect(result.databaseAccess.length).toBeGreaterThan(0);
|
|
385
|
+
|
|
386
|
+
// Should generate valid mermaid
|
|
387
|
+
expect(mermaid).toContain('flowchart');
|
|
388
|
+
expect(mermaid).toContain('api_gateway');
|
|
389
|
+
expect(mermaid).toContain('user_service');
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
describe('edge cases', () => {
|
|
394
|
+
it('handles empty file map', async () => {
|
|
395
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
396
|
+
const generator = new FlowDiagramGenerator();
|
|
397
|
+
|
|
398
|
+
const result = generator.analyzeFiles({});
|
|
399
|
+
const mermaid = generator.generateMermaid(result);
|
|
400
|
+
|
|
401
|
+
expect(result.crossRepoImports).toHaveLength(0);
|
|
402
|
+
expect(mermaid).toContain('No cross-repo communication');
|
|
403
|
+
});
|
|
404
|
+
|
|
405
|
+
it('handles files with syntax errors gracefully', async () => {
|
|
406
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
407
|
+
const generator = new FlowDiagramGenerator();
|
|
408
|
+
|
|
409
|
+
const files = {
|
|
410
|
+
'/workspace/broken/src/index.js': `
|
|
411
|
+
import { broken from 'workspace:other';
|
|
412
|
+
const x = {{{
|
|
413
|
+
`,
|
|
414
|
+
'/workspace/working/src/index.js': `
|
|
415
|
+
import { valid } from 'workspace:shared';
|
|
416
|
+
`,
|
|
417
|
+
};
|
|
418
|
+
|
|
419
|
+
// Should not throw, should process what it can
|
|
420
|
+
const result = generator.analyzeFiles(files);
|
|
421
|
+
|
|
422
|
+
expect(result.crossRepoImports.length).toBeGreaterThanOrEqual(1);
|
|
423
|
+
});
|
|
424
|
+
|
|
425
|
+
it('sanitizes repo names for Mermaid IDs', async () => {
|
|
426
|
+
const { FlowDiagramGenerator } = await import('./flow-diagram-generator.js');
|
|
427
|
+
const generator = new FlowDiagramGenerator();
|
|
428
|
+
|
|
429
|
+
const analysisResult = {
|
|
430
|
+
crossRepoImports: [
|
|
431
|
+
{ from: 'my-service.v2', to: '@scope/package', type: 'import' },
|
|
432
|
+
],
|
|
433
|
+
httpCalls: [],
|
|
434
|
+
messageQueue: [],
|
|
435
|
+
databaseAccess: [],
|
|
436
|
+
};
|
|
437
|
+
|
|
438
|
+
const mermaid = generator.generateMermaid(analysisResult);
|
|
439
|
+
|
|
440
|
+
// Should not contain invalid characters
|
|
441
|
+
expect(mermaid).not.toMatch(/@scope\/package/);
|
|
442
|
+
expect(mermaid).not.toMatch(/my-service\.v2/);
|
|
443
|
+
expect(mermaid).toContain('my_service_v2');
|
|
444
|
+
});
|
|
445
|
+
});
|
|
446
|
+
});
|