Approval-based snapshot testing library for Go (mirror)
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

test: improve test coverage

+606 -1
+5
__snapshots__/test_accept.snap
··· 1 + --- 2 + version: 0.1.0 3 + test_name: TestAccept 4 + --- 5 + new content to accept
+13
freeze.go
··· 7 7 "github.com/ptdewey/freeze/internal/diff" 8 8 "github.com/ptdewey/freeze/internal/files" 9 9 "github.com/ptdewey/freeze/internal/pretty" 10 + "github.com/ptdewey/freeze/internal/review" 10 11 ) 11 12 12 13 const version = "0.1.0" ··· 126 127 // } 127 128 return utter.Sdump(v) 128 129 } 130 + 131 + func Review() error { 132 + return review.Review() 133 + } 134 + 135 + func AcceptAll() error { 136 + return review.AcceptAll() 137 + } 138 + 139 + func RejectAll() error { 140 + return review.RejectAll() 141 + }
+182
internal/diff/diff_test.go
··· 1 + package diff_test 2 + 3 + import ( 4 + "testing" 5 + 6 + "github.com/ptdewey/freeze/internal/diff" 7 + ) 8 + 9 + func TestHistogramEmpty(t *testing.T) { 10 + both := diff.Histogram("", "") 11 + if both != nil { 12 + t.Errorf("empty strings should return nil, got %v", both) 13 + } 14 + 15 + oldEmpty := diff.Histogram("", "hello") 16 + if oldEmpty == nil { 17 + t.Errorf("new content should return non-nil") 18 + } 19 + 20 + newEmpty := diff.Histogram("hello", "") 21 + if newEmpty == nil { 22 + t.Errorf("old content should return non-nil") 23 + } 24 + } 25 + 26 + func TestHistogramIdentical(t *testing.T) { 27 + old := "line1\nline2\nline3" 28 + new := "line1\nline2\nline3" 29 + 30 + result := diff.Histogram(old, new) 31 + 32 + if len(result) != 3 { 33 + t.Errorf("expected 3 diff lines, got %d", len(result)) 34 + } 35 + 36 + for i, dl := range result { 37 + if dl.Kind != diff.DiffShared { 38 + t.Errorf("line %d: expected DiffShared, got %v", i, dl.Kind) 39 + } 40 + if dl.Number != i+1 { 41 + t.Errorf("line %d: expected Number=%d, got %d", i, i+1, dl.Number) 42 + } 43 + } 44 + } 45 + 46 + func TestHistogramCompletelyDifferent(t *testing.T) { 47 + old := "old content" 48 + new := "new content" 49 + 50 + result := diff.Histogram(old, new) 51 + 52 + if len(result) != 2 { 53 + t.Errorf("expected 2 diff lines, got %d", len(result)) 54 + } 55 + 56 + hasOld := false 57 + hasNew := false 58 + for _, dl := range result { 59 + if dl.Kind == diff.DiffOld { 60 + hasOld = true 61 + } 62 + if dl.Kind == diff.DiffNew { 63 + hasNew = true 64 + } 65 + } 66 + 67 + if !hasOld || !hasNew { 68 + t.Error("expected both old and new diff kinds") 69 + } 70 + } 71 + 72 + func TestHistogramSingleLineChange(t *testing.T) { 73 + old := "line1\nline2\nline3" 74 + new := "line1\nmodified\nline3" 75 + 76 + result := diff.Histogram(old, new) 77 + 78 + if len(result) < 3 { 79 + t.Errorf("expected at least 3 diff lines, got %d", len(result)) 80 + } 81 + 82 + if result[0].Kind != diff.DiffShared || result[0].Line != "line1" { 83 + t.Errorf("line 0: expected shared 'line1', got %v %s", result[0].Kind, result[0].Line) 84 + } 85 + 86 + hasModified := false 87 + for _, dl := range result { 88 + if dl.Line == "modified" { 89 + hasModified = true 90 + if dl.Kind != diff.DiffNew { 91 + t.Errorf("'modified' should be marked as new, got %v", dl.Kind) 92 + } 93 + } 94 + } 95 + if !hasModified { 96 + t.Error("diff missing 'modified' line") 97 + } 98 + } 99 + 100 + func TestHistogramAddLine(t *testing.T) { 101 + old := "line1\nline2" 102 + new := "line1\nline1.5\nline2" 103 + 104 + result := diff.Histogram(old, new) 105 + 106 + newCount := 0 107 + for _, dl := range result { 108 + if dl.Kind == diff.DiffNew { 109 + newCount++ 110 + if dl.Line != "line1.5" { 111 + t.Errorf("expected new line 'line1.5', got '%s'", dl.Line) 112 + } 113 + } 114 + } 115 + 116 + if newCount != 1 { 117 + t.Errorf("expected 1 new line, got %d", newCount) 118 + } 119 + } 120 + 121 + func TestHistogramRemoveLine(t *testing.T) { 122 + old := "line1\nline2\nline3" 123 + new := "line1\nline3" 124 + 125 + result := diff.Histogram(old, new) 126 + 127 + oldCount := 0 128 + for _, dl := range result { 129 + if dl.Kind == diff.DiffOld { 130 + oldCount++ 131 + if dl.Line != "line2" { 132 + t.Errorf("expected old line 'line2', got '%s'", dl.Line) 133 + } 134 + } 135 + } 136 + 137 + if oldCount != 1 { 138 + t.Errorf("expected 1 old line, got %d", oldCount) 139 + } 140 + } 141 + 142 + func TestHistogramLineNumbers(t *testing.T) { 143 + old := "a\nb\nc" 144 + new := "a\nb\nc" 145 + 146 + result := diff.Histogram(old, new) 147 + 148 + for i, dl := range result { 149 + if dl.Number != i+1 { 150 + t.Errorf("line %d: expected Number=%d, got %d", i, i+1, dl.Number) 151 + } 152 + } 153 + } 154 + 155 + func TestHistogramMultilineChanges(t *testing.T) { 156 + old := "start\nmiddle\nend" 157 + new := "start\nnew1\nnew2\nend" 158 + 159 + result := diff.Histogram(old, new) 160 + 161 + newCount := 0 162 + for _, dl := range result { 163 + if dl.Kind == diff.DiffNew { 164 + newCount++ 165 + } 166 + } 167 + 168 + if newCount != 2 { 169 + t.Errorf("expected 2 new lines, got %d", newCount) 170 + } 171 + } 172 + 173 + func TestHistogramWithEmptyLines(t *testing.T) { 174 + old := "line1\n\nline3" 175 + new := "line1\nline2\nline3" 176 + 177 + result := diff.Histogram(old, new) 178 + 179 + if len(result) == 0 { 180 + t.Error("expected non-empty diff result") 181 + } 182 + }
+239
internal/files/files_test.go
··· 1 + package files_test 2 + 3 + import ( 4 + "os" 5 + "path/filepath" 6 + "testing" 7 + 8 + "github.com/ptdewey/freeze/internal/files" 9 + ) 10 + 11 + func TestSnapshotFileName(t *testing.T) { 12 + tests := []struct { 13 + input string 14 + expected string 15 + }{ 16 + {"TestMyFunction", "test_my_function"}, 17 + {"test_another_one", "test_another_one"}, 18 + {"TestCamelCase", "test_camel_case"}, 19 + {"TestWithNumbers123", "test_with_numbers123"}, 20 + {"TestABC", "test_a_b_c"}, 21 + {"test", "test"}, 22 + {"TEST", "t_e_s_t"}, 23 + } 24 + 25 + for _, tt := range tests { 26 + t.Run(tt.input, func(t *testing.T) { 27 + result := files.SnapshotFileName(tt.input) 28 + if result != tt.expected { 29 + t.Errorf("SnapshotFileName(%s) = %s, want %s", tt.input, result, tt.expected) 30 + } 31 + }) 32 + } 33 + } 34 + 35 + func TestSerializeDeserialize(t *testing.T) { 36 + snap := &files.Snapshot{ 37 + Version: "1.0.0", 38 + Name: "TestExample", 39 + Content: "test content\nmultiline", 40 + } 41 + 42 + serialized := snap.Serialize() 43 + expected := "---\nversion: 1.0.0\ntest_name: TestExample\n---\ntest content\nmultiline" 44 + if serialized != expected { 45 + t.Errorf("Serialize():\nexpected:\n%s\n\ngot:\n%s", expected, serialized) 46 + } 47 + 48 + deserialized, err := files.Deserialize(serialized) 49 + if err != nil { 50 + t.Fatalf("Deserialize failed: %v", err) 51 + } 52 + 53 + if deserialized.Version != snap.Version { 54 + t.Errorf("Version mismatch: %s != %s", deserialized.Version, snap.Version) 55 + } 56 + if deserialized.Name != snap.Name { 57 + t.Errorf("Name mismatch: %s != %s", deserialized.Name, snap.Name) 58 + } 59 + if deserialized.Content != snap.Content { 60 + t.Errorf("Content mismatch: %s != %s", deserialized.Content, snap.Content) 61 + } 62 + } 63 + 64 + func TestDeserializeInvalidFormat(t *testing.T) { 65 + tests := []struct { 66 + name string 67 + input string 68 + }{ 69 + {"missing separators", "no separators here"}, 70 + {"only one separator", "---\nno closing separator"}, 71 + {"empty string", ""}, 72 + } 73 + 74 + for _, tt := range tests { 75 + t.Run(tt.name, func(t *testing.T) { 76 + _, err := files.Deserialize(tt.input) 77 + if err == nil { 78 + t.Error("expected error for invalid format") 79 + } 80 + }) 81 + } 82 + } 83 + 84 + func TestDeserializeValidFormats(t *testing.T) { 85 + tests := []struct { 86 + name string 87 + input string 88 + wantVer string 89 + wantTest string 90 + wantContent string 91 + }{ 92 + { 93 + "simple", 94 + "---\nversion: 1.0\ntest_name: Test\n---\ncontent", 95 + "1.0", 96 + "Test", 97 + "content", 98 + }, 99 + { 100 + "multiline content", 101 + "---\nversion: 0.1\ntest_name: MyTest\n---\nline1\nline2\nline3", 102 + "0.1", 103 + "MyTest", 104 + "line1\nline2\nline3", 105 + }, 106 + { 107 + "with extra fields", 108 + "---\nversion: 1.0\ntest_name: Test\nextra: ignored\n---\ncontent", 109 + "1.0", 110 + "Test", 111 + "content", 112 + }, 113 + } 114 + 115 + for _, tt := range tests { 116 + t.Run(tt.name, func(t *testing.T) { 117 + snap, err := files.Deserialize(tt.input) 118 + if err != nil { 119 + t.Fatalf("Deserialize failed: %v", err) 120 + } 121 + if snap.Version != tt.wantVer { 122 + t.Errorf("Version = %s, want %s", snap.Version, tt.wantVer) 123 + } 124 + if snap.Name != tt.wantTest { 125 + t.Errorf("Name = %s, want %s", snap.Name, tt.wantTest) 126 + } 127 + if snap.Content != tt.wantContent { 128 + t.Errorf("Content = %s, want %s", snap.Content, tt.wantContent) 129 + } 130 + }) 131 + } 132 + } 133 + 134 + func TestSaveAndReadSnapshot(t *testing.T) { 135 + snap := &files.Snapshot{ 136 + Version: "0.1.0", 137 + Name: "TestSaveRead", 138 + Content: "saved content", 139 + } 140 + 141 + if err := files.SaveSnapshot(snap, "test"); err != nil { 142 + t.Fatalf("SaveSnapshot failed: %v", err) 143 + } 144 + 145 + read, err := files.ReadSnapshot("TestSaveRead", "test") 146 + if err != nil { 147 + t.Fatalf("ReadSnapshot failed: %v", err) 148 + } 149 + 150 + if read.Content != snap.Content { 151 + t.Errorf("Content mismatch: %s != %s", read.Content, snap.Content) 152 + } 153 + if read.Version != snap.Version { 154 + t.Errorf("Version mismatch: %s != %s", read.Version, snap.Version) 155 + } 156 + 157 + cleanupSnapshot(t, "TestSaveRead", "test") 158 + } 159 + 160 + func TestReadSnapshotNotFound(t *testing.T) { 161 + _, err := files.ReadSnapshot("NonExistentTest", "nonexistent") 162 + if err == nil { 163 + t.Error("expected error for non-existent snapshot") 164 + } 165 + } 166 + 167 + func TestAcceptSnapshot(t *testing.T) { 168 + newSnap := &files.Snapshot{ 169 + Version: "0.1.0", 170 + Name: "TestAccept", 171 + Content: "new content to accept", 172 + } 173 + 174 + if err := files.SaveSnapshot(newSnap, "new"); err != nil { 175 + t.Fatalf("SaveSnapshot failed: %v", err) 176 + } 177 + 178 + if err := files.AcceptSnapshot("TestAccept"); err != nil { 179 + t.Fatalf("AcceptSnapshot failed: %v", err) 180 + } 181 + 182 + accepted, err := files.ReadSnapshot("TestAccept", "accepted") 183 + if err != nil { 184 + t.Fatalf("ReadSnapshot failed: %v", err) 185 + } 186 + 187 + if accepted.Content != newSnap.Content { 188 + t.Errorf("Content mismatch: %s != %s", accepted.Content, newSnap.Content) 189 + } 190 + 191 + _, err = files.ReadSnapshot("TestAccept", "new") 192 + if err == nil { 193 + t.Error("expected error: .new file should be deleted after accept") 194 + } 195 + 196 + cleanupSnapshot(t, "TestAccept", "accepted") 197 + } 198 + 199 + func TestRejectSnapshot(t *testing.T) { 200 + snap := &files.Snapshot{ 201 + Version: "0.1.0", 202 + Name: "TestReject", 203 + Content: "content to reject", 204 + } 205 + 206 + if err := files.SaveSnapshot(snap, "new"); err != nil { 207 + t.Fatalf("SaveSnapshot failed: %v", err) 208 + } 209 + 210 + if err := files.RejectSnapshot("TestReject"); err != nil { 211 + t.Fatalf("RejectSnapshot failed: %v", err) 212 + } 213 + 214 + _, err := files.ReadSnapshot("TestReject", "new") 215 + if err == nil { 216 + t.Error("expected error: .new file should be deleted after reject") 217 + } 218 + } 219 + 220 + func cleanupSnapshot(t *testing.T, testName, state string) { 221 + t.Helper() 222 + 223 + root, err := os.Getwd() 224 + if err != nil { 225 + t.Logf("cleanup: failed to get cwd: %v", err) 226 + return 227 + } 228 + 229 + for root != "/" && root != "" { 230 + if _, err := os.Stat(filepath.Join(root, "go.mod")); err == nil { 231 + break 232 + } 233 + root = filepath.Dir(root) 234 + } 235 + 236 + fileName := files.SnapshotFileName(testName) + "." + state 237 + filePath := filepath.Join(root, "__snapshots__", fileName) 238 + _ = os.Remove(filePath) 239 + }
+166
internal/pretty/pretty_test.go
··· 1 + package pretty_test 2 + 3 + import ( 4 + "os" 5 + "testing" 6 + 7 + "github.com/ptdewey/freeze/internal/pretty" 8 + ) 9 + 10 + func TestColorFunctionsWithColor(t *testing.T) { 11 + os.Unsetenv("NO_COLOR") 12 + 13 + tests := []struct { 14 + name string 15 + fn func(string) string 16 + text string 17 + }{ 18 + {"Red", pretty.Red, "error"}, 19 + {"Green", pretty.Green, "success"}, 20 + {"Yellow", pretty.Yellow, "warning"}, 21 + {"Blue", pretty.Blue, "info"}, 22 + {"Gray", pretty.Gray, "gray"}, 23 + {"Bold", pretty.Bold, "bold"}, 24 + } 25 + 26 + for _, tt := range tests { 27 + t.Run(tt.name, func(t *testing.T) { 28 + result := tt.fn(tt.text) 29 + if result == "" { 30 + t.Errorf("%s returned empty string", tt.name) 31 + } 32 + if result == tt.text { 33 + t.Errorf("%s did not add color codes", tt.name) 34 + } 35 + if !contains(result, tt.text) { 36 + t.Errorf("%s does not contain original text", tt.name) 37 + } 38 + }) 39 + } 40 + } 41 + 42 + func TestColorFunctionsNoColor(t *testing.T) { 43 + os.Setenv("NO_COLOR", "1") 44 + defer os.Unsetenv("NO_COLOR") 45 + 46 + tests := []struct { 47 + name string 48 + fn func(string) string 49 + text string 50 + }{ 51 + {"Red", pretty.Red, "error"}, 52 + {"Green", pretty.Green, "success"}, 53 + {"Yellow", pretty.Yellow, "warning"}, 54 + {"Blue", pretty.Blue, "info"}, 55 + {"Gray", pretty.Gray, "gray"}, 56 + {"Bold", pretty.Bold, "bold"}, 57 + } 58 + 59 + for _, tt := range tests { 60 + t.Run(tt.name, func(t *testing.T) { 61 + result := tt.fn(tt.text) 62 + if result != tt.text { 63 + t.Errorf("%s should return plain text when NO_COLOR is set", tt.name) 64 + } 65 + }) 66 + } 67 + } 68 + 69 + func TestHeader(t *testing.T) { 70 + os.Unsetenv("NO_COLOR") 71 + 72 + result := pretty.Header("test header") 73 + if result == "" { 74 + t.Error("Header returned empty string") 75 + } 76 + if result == "test header" { 77 + t.Error("Header should apply formatting") 78 + } 79 + if !contains(result, "test header") { 80 + t.Error("Header should contain original text") 81 + } 82 + } 83 + 84 + func TestSuccess(t *testing.T) { 85 + os.Unsetenv("NO_COLOR") 86 + 87 + result := pretty.Success("success message") 88 + if result == "" { 89 + t.Error("Success returned empty string") 90 + } 91 + if !contains(result, "success message") { 92 + t.Error("Success should contain original text") 93 + } 94 + } 95 + 96 + func TestError(t *testing.T) { 97 + os.Unsetenv("NO_COLOR") 98 + 99 + result := pretty.Error("error message") 100 + if result == "" { 101 + t.Error("Error returned empty string") 102 + } 103 + if !contains(result, "error message") { 104 + t.Error("Error should contain original text") 105 + } 106 + } 107 + 108 + func TestWarning(t *testing.T) { 109 + os.Unsetenv("NO_COLOR") 110 + 111 + result := pretty.Warning("warning message") 112 + if result == "" { 113 + t.Error("Warning returned empty string") 114 + } 115 + if !contains(result, "warning message") { 116 + t.Error("Warning should contain original text") 117 + } 118 + } 119 + 120 + func TestTerminalWidth(t *testing.T) { 121 + tests := []struct { 122 + name string 123 + envValue string 124 + expected int 125 + }{ 126 + {"default", "", 80}, 127 + {"valid width", "120", 120}, 128 + {"invalid width", "invalid", 80}, 129 + {"zero width", "0", 80}, 130 + {"negative width", "-10", 80}, 131 + {"large width", "1000", 1000}, 132 + } 133 + 134 + for _, tt := range tests { 135 + t.Run(tt.name, func(t *testing.T) { 136 + if tt.envValue == "" { 137 + os.Unsetenv("COLUMNS") 138 + } else { 139 + os.Setenv("COLUMNS", tt.envValue) 140 + } 141 + defer os.Unsetenv("COLUMNS") 142 + 143 + result := pretty.TerminalWidth() 144 + if result != tt.expected { 145 + t.Errorf("TerminalWidth() = %d, want %d", result, tt.expected) 146 + } 147 + }) 148 + } 149 + } 150 + 151 + func TestClearScreen(t *testing.T) { 152 + pretty.ClearScreen() 153 + } 154 + 155 + func TestClearLine(t *testing.T) { 156 + pretty.ClearLine() 157 + } 158 + 159 + func contains(s, substr string) bool { 160 + for i := 0; i <= len(s)-len(substr); i++ { 161 + if s[i:i+len(substr)] == substr { 162 + return true 163 + } 164 + } 165 + return false 166 + }
+1 -1
review.go internal/review/review.go
··· 1 - package freeze 1 + package review 2 2 3 3 import ( 4 4 "bufio"