-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathintegration_test.go
More file actions
372 lines (308 loc) · 11.2 KB
/
integration_test.go
File metadata and controls
372 lines (308 loc) · 11.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
package main
import (
"os"
"os/exec"
"path/filepath"
"strings"
"testing"
cpk "github.com/dmoose/checkpoint/internal/app/checkpoint"
"github.com/dmoose/checkpoint/internal/file"
"github.com/dmoose/checkpoint/pkg/config"
)
// TestCompleteWorkflow tests the full check -> edit -> commit cycle
func TestCompleteWorkflow(t *testing.T) {
// Create temporary directory for test
tmpDir, err := os.MkdirTemp("", "checkpoint-integration")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
// Initialize git repository
setupGitRepo(t, tmpDir)
// Create some initial content to commit
testFile := filepath.Join(tmpDir, "README.md")
initialContent := "# Test Project\n\nThis is a test project for checkpoint integration testing.\n"
if err := os.WriteFile(testFile, []byte(initialContent), 0644); err != nil {
t.Fatalf("failed to create test file: %v", err)
}
// Make initial commit
runGitCmd(t, tmpDir, "add", "README.md")
runGitCmd(t, tmpDir, "commit", "-m", "Initial commit")
// Modify the file to create a change for checkpoint
modifiedContent := initialContent + "\n## New Section\n\nAdded some new content.\n"
if err := os.WriteFile(testFile, []byte(modifiedContent), 0644); err != nil {
t.Fatalf("failed to modify test file: %v", err)
}
// Step 1: Run checkpoint check
cpk.Check(tmpDir)
// Verify input file was created
inputPath := filepath.Join(tmpDir, config.InputFileName)
if !file.Exists(inputPath) {
t.Fatalf("input file not created at %s", inputPath)
}
// Verify diff file was created
diffPath := filepath.Join(tmpDir, config.DiffFileName)
if !file.Exists(diffPath) {
t.Fatalf("diff file not created at %s", diffPath)
}
// Step 2: Edit the input file (simulate user/LLM editing)
inputContent, err := file.ReadFile(inputPath)
if err != nil {
t.Fatalf("failed to read input file: %v", err)
}
// Replace placeholder with actual content
editedContent := strings.ReplaceAll(inputContent, ` - summary: "[FILL IN: what changed]"
details: "[OPTIONAL: longer description]"
change_type: "[FILL IN: feature|fix|refactor|docs|perf|other]"
scope: "[FILL IN: affected component]"`, ` - summary: "Add new section to README"
details: "Added a new section with additional content for testing"
change_type: "docs"
scope: "documentation"`)
if err := file.WriteFile(inputPath, editedContent); err != nil {
t.Fatalf("failed to edit input file: %v", err)
}
// Step 3: Run checkpoint commit
cpk.Commit(tmpDir, "test-version")
// Verify changelog was created and contains our change
changelogPath := filepath.Join(tmpDir, config.ChangelogFileName)
if !file.Exists(changelogPath) {
t.Fatalf("changelog file not created at %s", changelogPath)
}
changelogContent, err := file.ReadFile(changelogPath)
if err != nil {
t.Fatalf("failed to read changelog: %v", err)
}
expectedEntries := []string{
"Add new section to README",
"change_type: docs",
"scope: documentation",
"schema_version: \"1\"",
}
for _, entry := range expectedEntries {
if !strings.Contains(changelogContent, entry) {
t.Errorf("changelog missing expected entry '%s'. Content:\n%s", entry, changelogContent)
}
}
// Verify git commit was created
output := runGitCmd(t, tmpDir, "log", "-1", "--pretty=format:%s")
if !strings.Contains(output, "Checkpoint:") {
t.Errorf("expected git commit with 'Checkpoint:' prefix, got: %s", output)
}
// Verify status file was created
statusPath := filepath.Join(tmpDir, config.StatusFileName)
if !file.Exists(statusPath) {
t.Fatalf("status file not created at %s", statusPath)
}
// Verify temporary files were cleaned up
if file.Exists(inputPath) {
t.Errorf("input file should have been cleaned up")
}
if file.Exists(diffPath) {
t.Errorf("diff file should have been cleaned up")
}
}
// TestDryRunWorkflow tests the dry-run functionality
func TestDryRunWorkflow(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "checkpoint-dryrun")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
setupGitRepo(t, tmpDir)
// Create and commit initial content
testFile := filepath.Join(tmpDir, "test.txt")
if err := os.WriteFile(testFile, []byte("initial\n"), 0644); err != nil {
t.Fatalf("failed to create test file: %v", err)
}
runGitCmd(t, tmpDir, "add", "test.txt")
runGitCmd(t, tmpDir, "commit", "-m", "Initial commit")
// Modify file
if err := os.WriteFile(testFile, []byte("initial\nmodified\n"), 0644); err != nil {
t.Fatalf("failed to modify test file: %v", err)
}
// Run check
cpk.Check(tmpDir)
// Edit input file
inputPath := filepath.Join(tmpDir, config.InputFileName)
inputContent, _ := file.ReadFile(inputPath)
editedContent := strings.ReplaceAll(inputContent, ` - summary: "[FILL IN: what changed]"
details: "[OPTIONAL: longer description]"
change_type: "[FILL IN: feature|fix|refactor|docs|perf|other]"
scope: "[FILL IN: affected component]"`, ` - summary: "Update test file"
change_type: "feature"
scope: "core"`)
_ = file.WriteFile(inputPath, editedContent)
// Run commit with dry-run
cpk.CommitWithOptions(tmpDir, cpk.CommitOptions{DryRun: true}, "test-version")
// Verify no actual commit was made
output := runGitCmd(t, tmpDir, "log", "--oneline")
lines := strings.Split(strings.TrimSpace(output), "\n")
if len(lines) != 1 {
t.Errorf("expected only 1 commit (initial), got %d commits", len(lines))
}
// Verify changelog was not created
changelogPath := filepath.Join(tmpDir, config.ChangelogFileName)
if file.Exists(changelogPath) {
t.Errorf("changelog should not be created in dry-run mode")
}
// Verify input files still exist (not cleaned up in dry-run)
if !file.Exists(inputPath) {
t.Errorf("input file should still exist after dry-run")
}
}
// TestCleanWorkflow tests the clean command
func TestCleanWorkflow(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "checkpoint-clean")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
setupGitRepo(t, tmpDir)
// Create test file and initial commit
testFile := filepath.Join(tmpDir, "test.txt")
if err := os.WriteFile(testFile, []byte("content\n"), 0644); err != nil {
t.Fatalf("failed to create test file: %v", err)
}
runGitCmd(t, tmpDir, "add", "test.txt")
runGitCmd(t, tmpDir, "commit", "-m", "Initial commit")
// Modify file and run check to create temporary files
if err := os.WriteFile(testFile, []byte("content\nmodified\n"), 0644); err != nil {
t.Fatalf("failed to modify test file: %v", err)
}
cpk.Check(tmpDir)
// Verify temporary files exist
inputPath := filepath.Join(tmpDir, config.InputFileName)
diffPath := filepath.Join(tmpDir, config.DiffFileName)
if !file.Exists(inputPath) {
t.Fatalf("input file should exist before clean")
}
if !file.Exists(diffPath) {
t.Fatalf("diff file should exist before clean")
}
// Run clean command
cpk.Clean(tmpDir)
// Verify temporary files are removed
if file.Exists(inputPath) {
t.Errorf("input file should be removed after clean")
}
if file.Exists(diffPath) {
t.Errorf("diff file should be removed after clean")
}
}
// TestConcurrentCheckpointPrevention tests that concurrent checkpoints are prevented
func TestConcurrentCheckpointPrevention(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "checkpoint-concurrent")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
setupGitRepo(t, tmpDir)
// Create test file
testFile := filepath.Join(tmpDir, "test.txt")
if err := os.WriteFile(testFile, []byte("content\n"), 0644); err != nil {
t.Fatalf("failed to create test file: %v", err)
}
runGitCmd(t, tmpDir, "add", "test.txt")
runGitCmd(t, tmpDir, "commit", "-m", "Initial commit")
// Modify file
if err := os.WriteFile(testFile, []byte("content\nmodified\n"), 0644); err != nil {
t.Fatalf("failed to modify test file: %v", err)
}
// Run first check
cpk.Check(tmpDir)
// Verify lock file exists
lockPath := filepath.Join(tmpDir, config.LockFileName)
if !file.Exists(lockPath) {
t.Fatalf("lock file should exist after check")
}
// Verify input file also exists
inputPath := filepath.Join(tmpDir, config.InputFileName)
if !file.Exists(inputPath) {
t.Fatalf("input file should exist after check")
}
// Attempt second check - should be prevented by lock file
// Capture stderr to verify proper error message
originalStderr := os.Stderr
_, w, _ := os.Pipe()
os.Stderr = w
// This should exit due to lock file, but we can't easily test exit behavior
// So we'll just verify the lock file still exists as evidence of protection
if !file.Exists(lockPath) {
t.Errorf("lock file should still exist to prevent concurrent checkpoints")
}
_ = w.Close()
os.Stderr = originalStderr
// Clean up
cpk.Clean(tmpDir)
}
// TestInitWorkflow tests the init command
func TestInitWorkflow(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "checkpoint-init")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
setupGitRepo(t, tmpDir)
// Run init command
cpk.InitWithOptions(tmpDir, "test-version")
// Verify changelog was created (checkpoint init now only creates changelog + gitignore)
changelogPath := filepath.Join(tmpDir, config.ChangelogFileName)
if !file.Exists(changelogPath) {
t.Fatalf("changelog file not created at %s", changelogPath)
}
// Verify .gitignore was updated
gitignorePath := filepath.Join(tmpDir, ".gitignore")
if !file.Exists(gitignorePath) {
t.Fatalf(".gitignore not created at %s", gitignorePath)
}
gitignoreContent, err := file.ReadFile(gitignorePath)
if err != nil {
t.Fatalf("failed to read .gitignore: %v", err)
}
if !strings.Contains(gitignoreContent, "checkpoint-input") {
t.Errorf(".gitignore should contain checkpoint-input")
}
}
// TestErrorHandling tests various error conditions
func TestErrorHandling(t *testing.T) {
tmpDir, err := os.MkdirTemp("", "checkpoint-errors")
if err != nil {
t.Fatalf("failed to create temp dir: %v", err)
}
defer func() { _ = os.RemoveAll(tmpDir) }()
// Test check in non-git directory
originalStderr := os.Stderr
_, w, _ := os.Pipe()
os.Stderr = w
// This should fail because it's not a git repository
// We'll skip actually testing the exit behavior since it's hard to mock
_ = w.Close()
os.Stderr = originalStderr
// For now, just verify the directory structure
setupGitRepo(t, tmpDir)
// Verify git repo was set up correctly
gitDir := filepath.Join(tmpDir, ".git")
if _, err := os.Stat(gitDir); os.IsNotExist(err) {
t.Errorf("expected .git directory to exist after setupGitRepo")
}
}
// Helper function to set up a git repository
func setupGitRepo(t *testing.T, dir string) {
gitCmd := exec.Command("git", "init")
gitCmd.Dir = dir
if err := gitCmd.Run(); err != nil {
t.Skipf("git not available, skipping test: %v", err)
}
runGitCmd(t, dir, "config", "user.email", "test@example.com")
runGitCmd(t, dir, "config", "user.name", "Test User")
}
// Helper function to run git commands
func runGitCmd(t *testing.T, dir string, args ...string) string {
gitCmd := exec.Command("git", args...)
gitCmd.Dir = dir
output, err := gitCmd.Output()
if err != nil {
t.Fatalf("git command failed: %v\nOutput: %s", err, output)
}
return string(output)
}