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,474 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpoint Manager Tests
|
|
3
|
+
* Task 5: Create and manage git-based checkpoints for safe refactoring
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
7
|
+
|
|
8
|
+
describe('CheckpointManager', () => {
|
|
9
|
+
let mockExec;
|
|
10
|
+
let mockFs;
|
|
11
|
+
|
|
12
|
+
beforeEach(() => {
|
|
13
|
+
vi.resetAllMocks();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
describe('createCheckpoint', () => {
|
|
17
|
+
it('creates stash with uncommitted changes', async () => {
|
|
18
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
19
|
+
|
|
20
|
+
const execMock = vi.fn()
|
|
21
|
+
.mockResolvedValueOnce({ stdout: 'main\n' }) // git branch --show-current
|
|
22
|
+
.mockResolvedValueOnce({ stdout: ' M file.js\n' }) // git status
|
|
23
|
+
.mockResolvedValueOnce({ stdout: '' }) // git stash
|
|
24
|
+
.mockResolvedValueOnce({ stdout: '' }) // git checkout -b
|
|
25
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }); // git rev-parse HEAD
|
|
26
|
+
|
|
27
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
28
|
+
const writeFileMock = vi.fn().mockResolvedValue();
|
|
29
|
+
|
|
30
|
+
const manager = new CheckpointManager({
|
|
31
|
+
exec: execMock,
|
|
32
|
+
readFile: readFileMock,
|
|
33
|
+
writeFile: writeFileMock,
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
const checkpoint = await manager.create();
|
|
37
|
+
|
|
38
|
+
expect(execMock).toHaveBeenCalledWith('git status --porcelain');
|
|
39
|
+
expect(execMock).toHaveBeenCalledWith(expect.stringContaining('git stash push'));
|
|
40
|
+
expect(checkpoint.hasStash).toBe(true);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
it('creates branch with naming pattern refactor/{timestamp}', async () => {
|
|
44
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
45
|
+
|
|
46
|
+
const execMock = vi.fn()
|
|
47
|
+
.mockResolvedValueOnce({ stdout: 'main\n' }) // git branch --show-current
|
|
48
|
+
.mockResolvedValueOnce({ stdout: '' }) // git status (clean)
|
|
49
|
+
.mockResolvedValueOnce({ stdout: '' }) // git checkout -b
|
|
50
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }); // git rev-parse HEAD
|
|
51
|
+
|
|
52
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
53
|
+
const writeFileMock = vi.fn().mockResolvedValue();
|
|
54
|
+
|
|
55
|
+
const manager = new CheckpointManager({
|
|
56
|
+
exec: execMock,
|
|
57
|
+
readFile: readFileMock,
|
|
58
|
+
writeFile: writeFileMock,
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
const checkpoint = await manager.create();
|
|
62
|
+
|
|
63
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
64
|
+
expect.stringMatching(/git checkout -b refactor\/\d+/)
|
|
65
|
+
);
|
|
66
|
+
expect(checkpoint.branch).toMatch(/^refactor\/\d+$/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('stores original branch for rollback', async () => {
|
|
70
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
71
|
+
|
|
72
|
+
const execMock = vi.fn()
|
|
73
|
+
.mockResolvedValueOnce({ stdout: 'feature/my-feature\n' }) // git branch --show-current
|
|
74
|
+
.mockResolvedValueOnce({ stdout: '' }) // git status
|
|
75
|
+
.mockResolvedValueOnce({ stdout: '' }) // git checkout -b
|
|
76
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }); // git rev-parse HEAD
|
|
77
|
+
|
|
78
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
79
|
+
const writeFileMock = vi.fn().mockResolvedValue();
|
|
80
|
+
|
|
81
|
+
const manager = new CheckpointManager({
|
|
82
|
+
exec: execMock,
|
|
83
|
+
readFile: readFileMock,
|
|
84
|
+
writeFile: writeFileMock,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
const checkpoint = await manager.create();
|
|
88
|
+
|
|
89
|
+
expect(checkpoint.originalBranch).toBe('feature/my-feature');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('handles already clean working directory', async () => {
|
|
93
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
94
|
+
|
|
95
|
+
const execMock = vi.fn()
|
|
96
|
+
.mockResolvedValueOnce({ stdout: 'main\n' }) // git branch --show-current
|
|
97
|
+
.mockResolvedValueOnce({ stdout: '' }) // git status (clean)
|
|
98
|
+
.mockResolvedValueOnce({ stdout: '' }) // git checkout -b
|
|
99
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }); // git rev-parse HEAD
|
|
100
|
+
|
|
101
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
102
|
+
const writeFileMock = vi.fn().mockResolvedValue();
|
|
103
|
+
|
|
104
|
+
const manager = new CheckpointManager({
|
|
105
|
+
exec: execMock,
|
|
106
|
+
readFile: readFileMock,
|
|
107
|
+
writeFile: writeFileMock,
|
|
108
|
+
});
|
|
109
|
+
|
|
110
|
+
const checkpoint = await manager.create();
|
|
111
|
+
|
|
112
|
+
expect(checkpoint.hasStash).toBe(false);
|
|
113
|
+
expect(execMock).not.toHaveBeenCalledWith(expect.stringContaining('git stash'));
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
it('handles existing branch with same name', async () => {
|
|
117
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
118
|
+
|
|
119
|
+
const execMock = vi.fn()
|
|
120
|
+
.mockResolvedValueOnce({ stdout: 'main\n' }) // git branch --show-current
|
|
121
|
+
.mockResolvedValueOnce({ stdout: '' }) // git status
|
|
122
|
+
.mockRejectedValueOnce(new Error('branch already exists')) // first checkout -b fails
|
|
123
|
+
.mockResolvedValueOnce({ stdout: '' }) // second checkout -b with suffix
|
|
124
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }); // git rev-parse HEAD
|
|
125
|
+
|
|
126
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
127
|
+
const writeFileMock = vi.fn().mockResolvedValue();
|
|
128
|
+
|
|
129
|
+
const manager = new CheckpointManager({
|
|
130
|
+
exec: execMock,
|
|
131
|
+
readFile: readFileMock,
|
|
132
|
+
writeFile: writeFileMock,
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
const checkpoint = await manager.create();
|
|
136
|
+
|
|
137
|
+
// Should have tried with a different name
|
|
138
|
+
expect(checkpoint.branch).toBeDefined();
|
|
139
|
+
});
|
|
140
|
+
|
|
141
|
+
it('reports checkpoint state accurately', async () => {
|
|
142
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
143
|
+
|
|
144
|
+
const execMock = vi.fn()
|
|
145
|
+
.mockResolvedValueOnce({ stdout: 'main\n' }) // git branch --show-current
|
|
146
|
+
.mockResolvedValueOnce({ stdout: ' M file.js\n' }) // git status
|
|
147
|
+
.mockResolvedValueOnce({ stdout: '' }) // git stash
|
|
148
|
+
.mockResolvedValueOnce({ stdout: '' }) // git checkout -b
|
|
149
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }); // git rev-parse HEAD
|
|
150
|
+
|
|
151
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
152
|
+
const writeFileMock = vi.fn().mockResolvedValue();
|
|
153
|
+
|
|
154
|
+
const manager = new CheckpointManager({
|
|
155
|
+
exec: execMock,
|
|
156
|
+
readFile: readFileMock,
|
|
157
|
+
writeFile: writeFileMock,
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
const checkpoint = await manager.create();
|
|
161
|
+
|
|
162
|
+
expect(checkpoint).toMatchObject({
|
|
163
|
+
id: expect.any(String),
|
|
164
|
+
branch: expect.stringMatching(/^refactor\//),
|
|
165
|
+
originalBranch: 'main',
|
|
166
|
+
hasStash: true,
|
|
167
|
+
commitHash: 'abc123',
|
|
168
|
+
createdAt: expect.any(Date),
|
|
169
|
+
});
|
|
170
|
+
});
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
describe('rollback', () => {
|
|
174
|
+
it('deletes branch and pops stash on rollback', async () => {
|
|
175
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
176
|
+
|
|
177
|
+
const execMock = vi.fn()
|
|
178
|
+
.mockResolvedValue({ stdout: '' });
|
|
179
|
+
|
|
180
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
181
|
+
|
|
182
|
+
const checkpoint = {
|
|
183
|
+
id: 'test-123',
|
|
184
|
+
branch: 'refactor/12345',
|
|
185
|
+
originalBranch: 'main',
|
|
186
|
+
hasStash: true,
|
|
187
|
+
stashRef: 'stash@{0}',
|
|
188
|
+
};
|
|
189
|
+
|
|
190
|
+
await manager.rollback(checkpoint);
|
|
191
|
+
|
|
192
|
+
expect(execMock).toHaveBeenCalledWith('git checkout main');
|
|
193
|
+
expect(execMock).toHaveBeenCalledWith('git branch -D refactor/12345');
|
|
194
|
+
expect(execMock).toHaveBeenCalledWith('git stash pop');
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
it('skips stash pop if no stash was created', async () => {
|
|
198
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
199
|
+
|
|
200
|
+
const execMock = vi.fn()
|
|
201
|
+
.mockResolvedValue({ stdout: '' });
|
|
202
|
+
|
|
203
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
204
|
+
|
|
205
|
+
const checkpoint = {
|
|
206
|
+
id: 'test-123',
|
|
207
|
+
branch: 'refactor/12345',
|
|
208
|
+
originalBranch: 'main',
|
|
209
|
+
hasStash: false,
|
|
210
|
+
};
|
|
211
|
+
|
|
212
|
+
await manager.rollback(checkpoint);
|
|
213
|
+
|
|
214
|
+
expect(execMock).toHaveBeenCalledWith('git checkout main');
|
|
215
|
+
expect(execMock).toHaveBeenCalledWith('git branch -D refactor/12345');
|
|
216
|
+
expect(execMock).not.toHaveBeenCalledWith(expect.stringContaining('git stash pop'));
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
it('handles rollback when already on original branch', async () => {
|
|
220
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
221
|
+
|
|
222
|
+
const execMock = vi.fn()
|
|
223
|
+
.mockResolvedValueOnce({ stdout: 'main\n' }) // current branch
|
|
224
|
+
.mockResolvedValue({ stdout: '' });
|
|
225
|
+
|
|
226
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
227
|
+
|
|
228
|
+
const checkpoint = {
|
|
229
|
+
id: 'test-123',
|
|
230
|
+
branch: 'refactor/12345',
|
|
231
|
+
originalBranch: 'main',
|
|
232
|
+
hasStash: false,
|
|
233
|
+
};
|
|
234
|
+
|
|
235
|
+
await manager.rollback(checkpoint);
|
|
236
|
+
|
|
237
|
+
// Should still delete the refactor branch
|
|
238
|
+
expect(execMock).toHaveBeenCalledWith('git branch -D refactor/12345');
|
|
239
|
+
});
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
describe('commit', () => {
|
|
243
|
+
it('commits checkpoint changes to refactor branch', async () => {
|
|
244
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
245
|
+
|
|
246
|
+
const execMock = vi.fn()
|
|
247
|
+
.mockResolvedValue({ stdout: '' });
|
|
248
|
+
|
|
249
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
250
|
+
|
|
251
|
+
await manager.commit('Refactored validateEmail function');
|
|
252
|
+
|
|
253
|
+
expect(execMock).toHaveBeenCalledWith('git add -A');
|
|
254
|
+
expect(execMock).toHaveBeenCalledWith(
|
|
255
|
+
expect.stringContaining('git commit -m "Refactored validateEmail function"')
|
|
256
|
+
);
|
|
257
|
+
});
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
describe('merge', () => {
|
|
261
|
+
it('merges refactor branch back to original', async () => {
|
|
262
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
263
|
+
|
|
264
|
+
const execMock = vi.fn()
|
|
265
|
+
.mockResolvedValue({ stdout: '' });
|
|
266
|
+
|
|
267
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
268
|
+
|
|
269
|
+
const checkpoint = {
|
|
270
|
+
id: 'test-123',
|
|
271
|
+
branch: 'refactor/12345',
|
|
272
|
+
originalBranch: 'main',
|
|
273
|
+
hasStash: false,
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
await manager.merge(checkpoint);
|
|
277
|
+
|
|
278
|
+
expect(execMock).toHaveBeenCalledWith('git checkout main');
|
|
279
|
+
expect(execMock).toHaveBeenCalledWith('git merge refactor/12345');
|
|
280
|
+
});
|
|
281
|
+
|
|
282
|
+
it('cleans up refactor branch after merge', async () => {
|
|
283
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
284
|
+
|
|
285
|
+
const execMock = vi.fn()
|
|
286
|
+
.mockResolvedValue({ stdout: '' });
|
|
287
|
+
|
|
288
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
289
|
+
|
|
290
|
+
const checkpoint = {
|
|
291
|
+
id: 'test-123',
|
|
292
|
+
branch: 'refactor/12345',
|
|
293
|
+
originalBranch: 'main',
|
|
294
|
+
hasStash: false,
|
|
295
|
+
};
|
|
296
|
+
|
|
297
|
+
await manager.merge(checkpoint, { cleanup: true });
|
|
298
|
+
|
|
299
|
+
expect(execMock).toHaveBeenCalledWith('git branch -d refactor/12345');
|
|
300
|
+
});
|
|
301
|
+
});
|
|
302
|
+
|
|
303
|
+
describe('state tracking', () => {
|
|
304
|
+
it('saves checkpoint state to file', async () => {
|
|
305
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
306
|
+
|
|
307
|
+
const writeFileMock = vi.fn();
|
|
308
|
+
const execMock = vi.fn()
|
|
309
|
+
.mockResolvedValueOnce({ stdout: 'main\n' })
|
|
310
|
+
.mockResolvedValueOnce({ stdout: '' })
|
|
311
|
+
.mockResolvedValueOnce({ stdout: '' })
|
|
312
|
+
.mockResolvedValueOnce({ stdout: 'abc123' });
|
|
313
|
+
|
|
314
|
+
const manager = new CheckpointManager({
|
|
315
|
+
exec: execMock,
|
|
316
|
+
writeFile: writeFileMock,
|
|
317
|
+
stateFile: '.tlc/checkpoint.json',
|
|
318
|
+
});
|
|
319
|
+
|
|
320
|
+
await manager.create();
|
|
321
|
+
|
|
322
|
+
expect(writeFileMock).toHaveBeenCalledWith(
|
|
323
|
+
'.tlc/checkpoint.json',
|
|
324
|
+
expect.any(String)
|
|
325
|
+
);
|
|
326
|
+
});
|
|
327
|
+
|
|
328
|
+
it('loads existing checkpoint state', async () => {
|
|
329
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
330
|
+
|
|
331
|
+
const readFileMock = vi.fn().mockResolvedValue(JSON.stringify({
|
|
332
|
+
id: 'existing-123',
|
|
333
|
+
branch: 'refactor/99999',
|
|
334
|
+
originalBranch: 'develop',
|
|
335
|
+
hasStash: true,
|
|
336
|
+
}));
|
|
337
|
+
|
|
338
|
+
const manager = new CheckpointManager({
|
|
339
|
+
readFile: readFileMock,
|
|
340
|
+
stateFile: '.tlc/checkpoint.json',
|
|
341
|
+
});
|
|
342
|
+
|
|
343
|
+
const checkpoint = await manager.load();
|
|
344
|
+
|
|
345
|
+
expect(checkpoint.id).toBe('existing-123');
|
|
346
|
+
expect(checkpoint.branch).toBe('refactor/99999');
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
it('returns null when no checkpoint exists', async () => {
|
|
350
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
351
|
+
|
|
352
|
+
const readFileMock = vi.fn().mockRejectedValue(new Error('ENOENT'));
|
|
353
|
+
|
|
354
|
+
const manager = new CheckpointManager({
|
|
355
|
+
readFile: readFileMock,
|
|
356
|
+
stateFile: '.tlc/checkpoint.json',
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const checkpoint = await manager.load();
|
|
360
|
+
|
|
361
|
+
expect(checkpoint).toBeNull();
|
|
362
|
+
});
|
|
363
|
+
});
|
|
364
|
+
|
|
365
|
+
describe('edge cases', () => {
|
|
366
|
+
it('handles conflicts during merge', async () => {
|
|
367
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
368
|
+
|
|
369
|
+
const execMock = vi.fn()
|
|
370
|
+
.mockResolvedValueOnce({ stdout: '' }) // checkout
|
|
371
|
+
.mockRejectedValueOnce(new Error('CONFLICT')); // merge fails
|
|
372
|
+
|
|
373
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
374
|
+
|
|
375
|
+
const checkpoint = {
|
|
376
|
+
id: 'test-123',
|
|
377
|
+
branch: 'refactor/12345',
|
|
378
|
+
originalBranch: 'main',
|
|
379
|
+
hasStash: false,
|
|
380
|
+
};
|
|
381
|
+
|
|
382
|
+
await expect(manager.merge(checkpoint)).rejects.toThrow('CONFLICT');
|
|
383
|
+
});
|
|
384
|
+
|
|
385
|
+
it('handles detached HEAD state', async () => {
|
|
386
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
387
|
+
|
|
388
|
+
const execMock = vi.fn()
|
|
389
|
+
.mockResolvedValueOnce({ stdout: '' }) // git branch --show-current returns empty
|
|
390
|
+
.mockResolvedValueOnce({ stdout: 'abc123' }) // git rev-parse HEAD
|
|
391
|
+
.mockResolvedValueOnce({ stdout: '' }) // git status
|
|
392
|
+
.mockResolvedValueOnce({ stdout: '' }) // git checkout -b
|
|
393
|
+
.mockResolvedValueOnce({ stdout: 'def456' }); // git rev-parse HEAD
|
|
394
|
+
|
|
395
|
+
const manager = new CheckpointManager({ exec: execMock });
|
|
396
|
+
|
|
397
|
+
const checkpoint = await manager.create();
|
|
398
|
+
|
|
399
|
+
// Should store commit hash as "original" for detached HEAD
|
|
400
|
+
expect(checkpoint.originalBranch).toBe('abc123');
|
|
401
|
+
expect(checkpoint.wasDetached).toBe(true);
|
|
402
|
+
});
|
|
403
|
+
|
|
404
|
+
it('prevents creating checkpoint when one already exists', async () => {
|
|
405
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
406
|
+
|
|
407
|
+
const readFileMock = vi.fn().mockResolvedValue(JSON.stringify({
|
|
408
|
+
id: 'existing-123',
|
|
409
|
+
branch: 'refactor/99999',
|
|
410
|
+
}));
|
|
411
|
+
|
|
412
|
+
const manager = new CheckpointManager({
|
|
413
|
+
readFile: readFileMock,
|
|
414
|
+
stateFile: '.tlc/checkpoint.json',
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
await expect(manager.create()).rejects.toThrow('Checkpoint already exists');
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
it('allows force create to override existing checkpoint', async () => {
|
|
421
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
422
|
+
|
|
423
|
+
const readFileMock = vi.fn().mockResolvedValue(JSON.stringify({
|
|
424
|
+
id: 'existing-123',
|
|
425
|
+
branch: 'refactor/99999',
|
|
426
|
+
}));
|
|
427
|
+
|
|
428
|
+
const execMock = vi.fn()
|
|
429
|
+
.mockResolvedValue({ stdout: '' });
|
|
430
|
+
|
|
431
|
+
const writeFileMock = vi.fn();
|
|
432
|
+
|
|
433
|
+
const manager = new CheckpointManager({
|
|
434
|
+
readFile: readFileMock,
|
|
435
|
+
writeFile: writeFileMock,
|
|
436
|
+
exec: execMock,
|
|
437
|
+
stateFile: '.tlc/checkpoint.json',
|
|
438
|
+
});
|
|
439
|
+
|
|
440
|
+
const checkpoint = await manager.create({ force: true });
|
|
441
|
+
|
|
442
|
+
expect(checkpoint).toBeDefined();
|
|
443
|
+
});
|
|
444
|
+
});
|
|
445
|
+
|
|
446
|
+
describe('status', () => {
|
|
447
|
+
it('returns current checkpoint status', async () => {
|
|
448
|
+
const { CheckpointManager } = await import('./checkpoint-manager.js');
|
|
449
|
+
|
|
450
|
+
const execMock = vi.fn()
|
|
451
|
+
.mockResolvedValueOnce({ stdout: 'refactor/12345\n' }) // current branch
|
|
452
|
+
.mockResolvedValueOnce({ stdout: ' M file.js\n' }); // status
|
|
453
|
+
|
|
454
|
+
const readFileMock = vi.fn().mockResolvedValue(JSON.stringify({
|
|
455
|
+
id: 'test-123',
|
|
456
|
+
branch: 'refactor/12345',
|
|
457
|
+
originalBranch: 'main',
|
|
458
|
+
hasStash: true,
|
|
459
|
+
createdAt: new Date().toISOString(),
|
|
460
|
+
}));
|
|
461
|
+
|
|
462
|
+
const manager = new CheckpointManager({
|
|
463
|
+
exec: execMock,
|
|
464
|
+
readFile: readFileMock,
|
|
465
|
+
});
|
|
466
|
+
|
|
467
|
+
const status = await manager.status();
|
|
468
|
+
|
|
469
|
+
expect(status.active).toBe(true);
|
|
470
|
+
expect(status.branch).toBe('refactor/12345');
|
|
471
|
+
expect(status.hasUncommittedChanges).toBe(true);
|
|
472
|
+
});
|
|
473
|
+
});
|
|
474
|
+
});
|