changelog generator & diff tool stormlightlabs.github.io/git-storm/
changelog changeset markdown golang git
at main 8.5 kB view raw
1package main 2 3import ( 4 "encoding/json" 5 "strings" 6 "testing" 7 8 "github.com/stormlightlabs/git-storm/internal/changelog" 9 "github.com/stormlightlabs/git-storm/internal/testutils" 10) 11 12func TestCreateReleaseTag(t *testing.T) { 13 repo := testutils.SetupTestRepo(t) 14 worktree, err := repo.Worktree() 15 if err != nil { 16 t.Fatalf("Failed to get worktree: %v", err) 17 } 18 19 repoPath := worktree.Filesystem.Root() 20 version := &changelog.Version{ 21 Number: "1.0.0", 22 Date: "2024-01-15", 23 Sections: []changelog.Section{ 24 { 25 Type: "added", 26 Entries: []string{ 27 "New authentication system", 28 "User profile management", 29 }, 30 }, 31 { 32 Type: "fixed", 33 Entries: []string{ 34 "Memory leak in database connection pool", 35 }, 36 }, 37 }, 38 } 39 40 err = createReleaseTag(repoPath, "1.0.0", version) 41 if err != nil { 42 t.Fatalf("createReleaseTag() error = %v", err) 43 } 44 45 tagRef, err := repo.Tag("v1.0.0") 46 if err != nil { 47 t.Fatalf("Tag v1.0.0 should exist, got error: %v", err) 48 } 49 50 tagObj, err := repo.TagObject(tagRef.Hash()) 51 if err != nil { 52 t.Fatalf("Tag should be annotated, got error: %v", err) 53 } 54 55 head, err := repo.Head() 56 if err != nil { 57 t.Fatalf("Failed to get HEAD: %v", err) 58 } 59 60 testutils.Expect.Equal(t, tagObj.Target, head.Hash(), "Tag should point to HEAD") 61 62 message := tagObj.Message 63 testutils.Expect.True(t, strings.Contains(message, "Release 1.0.0"), "Tag message should contain version") 64 testutils.Expect.True(t, strings.Contains(message, "Added:"), "Tag message should contain Added section") 65 testutils.Expect.True(t, strings.Contains(message, "Fixed:"), "Tag message should contain Fixed section") 66 testutils.Expect.True(t, strings.Contains(message, "New authentication system"), "Tag message should contain entry") 67 testutils.Expect.True(t, strings.Contains(message, "Memory leak"), "Tag message should contain entry") 68} 69 70func TestCreateReleaseTag_DuplicateTag(t *testing.T) { 71 repo := testutils.SetupTestRepo(t) 72 worktree, err := repo.Worktree() 73 if err != nil { 74 t.Fatalf("Failed to get worktree: %v", err) 75 } 76 77 repoPath := worktree.Filesystem.Root() 78 version := &changelog.Version{ 79 Number: "1.0.0", 80 Date: "2024-01-15", 81 Sections: []changelog.Section{ 82 { 83 Type: "added", 84 Entries: []string{"Feature 1"}, 85 }, 86 }, 87 } 88 89 err = createReleaseTag(repoPath, "1.0.0", version) 90 if err != nil { 91 t.Fatalf("First createReleaseTag() error = %v", err) 92 } 93 94 err = createReleaseTag(repoPath, "1.0.0", version) 95 if err == nil { 96 t.Error("Expected error when creating duplicate tag, got nil") 97 } 98 99 testutils.Expect.True(t, strings.Contains(err.Error(), "already exists"), "Error should indicate tag already exists") 100} 101 102func TestCreateReleaseTag_TagNameFormat(t *testing.T) { 103 tests := []struct { 104 version string 105 expectedTag string 106 }{ 107 {"1.0.0", "v1.0.0"}, 108 {"2.5.3", "v2.5.3"}, 109 {"0.1.0", "v0.1.0"}, 110 } 111 112 for _, tt := range tests { 113 t.Run(tt.version, func(t *testing.T) { 114 repo := testutils.SetupTestRepo(t) 115 worktree, err := repo.Worktree() 116 if err != nil { 117 t.Fatalf("Failed to get worktree: %v", err) 118 } 119 120 repoPath := worktree.Filesystem.Root() 121 version := &changelog.Version{ 122 Number: tt.version, 123 Date: "2024-01-15", 124 Sections: []changelog.Section{ 125 { 126 Type: "added", 127 Entries: []string{"Feature"}, 128 }, 129 }, 130 } 131 132 err = createReleaseTag(repoPath, tt.version, version) 133 if err != nil { 134 t.Fatalf("createReleaseTag() error = %v", err) 135 } 136 137 _, err = repo.Tag(tt.expectedTag) 138 if err != nil { 139 t.Errorf("Tag %s should exist, got error: %v", tt.expectedTag, err) 140 } 141 }) 142 } 143} 144 145func TestBuildTagMessage(t *testing.T) { 146 version := &changelog.Version{ 147 Number: "1.2.3", 148 Date: "2024-01-15", 149 Sections: []changelog.Section{ 150 { 151 Type: "added", 152 Entries: []string{ 153 "Feature A", 154 "Feature B", 155 }, 156 }, 157 { 158 Type: "changed", 159 Entries: []string{"Updated API"}, 160 }, 161 { 162 Type: "fixed", 163 Entries: []string{ 164 "Bug 1", 165 "Bug 2", 166 }, 167 }, 168 }, 169 } 170 message := buildTagMessage("1.2.3", version) 171 172 testutils.Expect.True(t, strings.HasPrefix(message, "Release 1.2.3\n\n"), "Message should start with release header") 173 174 testutils.Expect.True(t, strings.Contains(message, "Added:\n"), "Should contain Added section") 175 testutils.Expect.True(t, strings.Contains(message, "Changed:\n"), "Should contain Changed section") 176 testutils.Expect.True(t, strings.Contains(message, "Fixed:\n"), "Should contain Fixed section") 177 178 testutils.Expect.True(t, strings.Contains(message, "- Feature A\n"), "Should contain entry") 179 testutils.Expect.True(t, strings.Contains(message, "- Feature B\n"), "Should contain entry") 180 testutils.Expect.True(t, strings.Contains(message, "- Updated API\n"), "Should contain entry") 181 testutils.Expect.True(t, strings.Contains(message, "- Bug 1\n"), "Should contain entry") 182 testutils.Expect.True(t, strings.Contains(message, "- Bug 2\n"), "Should contain entry") 183 184 sections := strings.Split(message, "\n\n") 185 testutils.Expect.True(t, len(sections) >= 3, "Sections should be separated by blank lines") 186} 187 188func TestBuildTagMessage_EmptyVersion(t *testing.T) { 189 version := &changelog.Version{ 190 Number: "1.0.0", 191 Date: "2024-01-15", 192 Sections: []changelog.Section{}, 193 } 194 message := buildTagMessage("1.0.0", version) 195 196 testutils.Expect.True(t, strings.HasPrefix(message, "Release 1.0.0\n\n"), "Should still have release header even with no sections") 197} 198 199func TestResolveReleaseVersion(t *testing.T) { 200 existing := &changelog.Changelog{Versions: []changelog.Version{{Number: "Unreleased"}, {Number: "1.2.3"}}} 201 202 version, err := resolveReleaseVersion("", "minor", existing) 203 if err != nil { 204 t.Fatalf("resolveReleaseVersion returned error: %v", err) 205 } 206 if version != "1.3.0" { 207 t.Fatalf("expected 1.3.0, got %s", version) 208 } 209 210 version, err = resolveReleaseVersion("2.0.0", "", existing) 211 if err != nil { 212 t.Fatalf("resolveReleaseVersion returned error: %v", err) 213 } 214 if version != "2.0.0" { 215 t.Fatalf("expected 2.0.0, got %s", version) 216 } 217 218 if _, err := resolveReleaseVersion("2.0.0", "patch", existing); err == nil { 219 t.Fatal("expected error when both --version and --bump are set") 220 } 221 222 blankChangelog := &changelog.Changelog{} 223 version, err = resolveReleaseVersion("", "patch", blankChangelog) 224 if err != nil { 225 t.Fatalf("resolveReleaseVersion returned error: %v", err) 226 } 227 if version != "0.0.1" { 228 t.Fatalf("expected 0.0.1 for empty changelog, got %s", version) 229 } 230} 231 232func TestReleaseOutput_JSONStructure(t *testing.T) { 233 output := ReleaseOutput{ 234 Version: "1.0.0", 235 Date: "2024-01-15", 236 EntriesCount: 3, 237 ChangelogPath: "CHANGELOG.md", 238 TagCreated: true, 239 TagName: "v1.0.0", 240 ChangesCleared: true, 241 DeletedCount: 3, 242 DryRun: false, 243 VersionData: &changelog.Version{ 244 Number: "1.0.0", 245 Date: "2024-01-15", 246 Sections: []changelog.Section{ 247 { 248 Type: "added", 249 Entries: []string{"Feature 1"}, 250 }, 251 }, 252 }, 253 } 254 255 jsonBytes, err := json.MarshalIndent(output, "", " ") 256 if err != nil { 257 t.Fatalf("Failed to marshal JSON: %v", err) 258 } 259 260 var unmarshaled ReleaseOutput 261 err = json.Unmarshal(jsonBytes, &unmarshaled) 262 if err != nil { 263 t.Fatalf("Failed to unmarshal JSON: %v", err) 264 } 265 266 testutils.Expect.Equal(t, unmarshaled.Version, "1.0.0") 267 testutils.Expect.Equal(t, unmarshaled.Date, "2024-01-15") 268 testutils.Expect.Equal(t, unmarshaled.EntriesCount, 3) 269 testutils.Expect.Equal(t, unmarshaled.TagCreated, true) 270 testutils.Expect.Equal(t, unmarshaled.TagName, "v1.0.0") 271 testutils.Expect.Equal(t, unmarshaled.ChangesCleared, true) 272 testutils.Expect.Equal(t, unmarshaled.DeletedCount, 3) 273} 274 275func TestReleaseOutput_DryRunJSON(t *testing.T) { 276 output := ReleaseOutput{ 277 Version: "1.0.0", 278 Date: "2024-01-15", 279 EntriesCount: 2, 280 ChangelogPath: "CHANGELOG.md", 281 DryRun: true, 282 VersionData: &changelog.Version{ 283 Number: "1.0.0", 284 Date: "2024-01-15", 285 Sections: []changelog.Section{ 286 { 287 Type: "fixed", 288 Entries: []string{"Bug fix"}, 289 }, 290 }, 291 }, 292 } 293 294 jsonBytes, err := json.MarshalIndent(output, "", " ") 295 if err != nil { 296 t.Fatalf("Failed to marshal JSON: %v", err) 297 } 298 299 testutils.Expect.True(t, strings.Contains(string(jsonBytes), `"dry_run": true`)) 300 testutils.Expect.True(t, strings.Contains(string(jsonBytes), `"tag_created": false`)) 301 testutils.Expect.True(t, strings.Contains(string(jsonBytes), `"changes_cleared": false`)) 302}