changelog generator & diff tool stormlightlabs.github.io/git-storm/
changelog changeset markdown golang git
at main 8.5 kB view raw
1package gitlog 2 3import ( 4 "testing" 5 "time" 6 7 "github.com/stormlightlabs/git-storm/internal/testutils" 8) 9 10func TestConventionalParser_Parse(t *testing.T) { 11 parser := &ConventionalParser{} 12 testTime := time.Now() 13 14 tests := []struct { 15 name string 16 subject string 17 body string 18 wantType string 19 wantScope string 20 wantDesc string 21 wantBreak bool 22 }{ 23 { 24 name: "simple feat", 25 subject: "feat: add new feature", 26 body: "", 27 wantType: "feat", 28 wantScope: "", 29 wantDesc: "add new feature", 30 wantBreak: false, 31 }, 32 { 33 name: "feat with scope", 34 subject: "feat(api): add pagination endpoint", 35 body: "", 36 wantType: "feat", 37 wantScope: "api", 38 wantDesc: "add pagination endpoint", 39 wantBreak: false, 40 }, 41 { 42 name: "fix with scope", 43 subject: "fix(ui): correct button alignment issue", 44 body: "", 45 wantType: "fix", 46 wantScope: "ui", 47 wantDesc: "correct button alignment issue", 48 wantBreak: false, 49 }, 50 { 51 name: "breaking change with !", 52 subject: "feat(api)!: remove support for legacy endpoints", 53 body: "", 54 wantType: "feat", 55 wantScope: "api", 56 wantDesc: "remove support for legacy endpoints", 57 wantBreak: true, 58 }, 59 { 60 name: "breaking change without scope", 61 subject: "feat!: major API redesign", 62 body: "", 63 wantType: "feat", 64 wantScope: "", 65 wantDesc: "major API redesign", 66 wantBreak: true, 67 }, 68 { 69 name: "breaking change in footer", 70 subject: "feat(api): update authentication", 71 body: "Some details here\n\nBREAKING CHANGE: API no longer accepts XML-formatted requests.", 72 wantType: "feat", 73 wantScope: "api", 74 wantDesc: "update authentication", 75 wantBreak: true, 76 }, 77 { 78 name: "docs commit", 79 subject: "docs: update README installation instructions", 80 body: "", 81 wantType: "docs", 82 wantScope: "", 83 wantDesc: "update README installation instructions", 84 wantBreak: false, 85 }, 86 { 87 name: "chore commit", 88 subject: "chore: update .gitignore", 89 body: "", 90 wantType: "chore", 91 wantScope: "", 92 wantDesc: "update .gitignore", 93 wantBreak: false, 94 }, 95 { 96 name: "non-conventional commit", 97 subject: "some random commit message", 98 body: "", 99 wantType: "unknown", 100 wantScope: "", 101 wantDesc: "some random commit message", 102 wantBreak: false, 103 }, 104 } 105 106 for _, tt := range tests { 107 t.Run(tt.name, func(t *testing.T) { 108 meta, err := parser.Parse("abc123", tt.subject, tt.body, testTime) 109 if err != nil { 110 t.Fatalf("Parse() error = %v", err) 111 } 112 113 if meta.Type != tt.wantType { 114 t.Errorf("Type = %v, want %v", meta.Type, tt.wantType) 115 } 116 if meta.Scope != tt.wantScope { 117 t.Errorf("Scope = %v, want %v", meta.Scope, tt.wantScope) 118 } 119 if meta.Description != tt.wantDesc { 120 t.Errorf("Description = %v, want %v", meta.Description, tt.wantDesc) 121 } 122 if meta.Breaking != tt.wantBreak { 123 t.Errorf("Breaking = %v, want %v", meta.Breaking, tt.wantBreak) 124 } 125 }) 126 } 127} 128 129func TestConventionalParser_Categorize(t *testing.T) { 130 parser := &ConventionalParser{} 131 132 tests := []struct { 133 name string 134 meta CommitMeta 135 wantCat string 136 }{ 137 { 138 name: "feat -> added", 139 meta: CommitMeta{Type: "feat"}, 140 wantCat: "added", 141 }, 142 { 143 name: "fix -> fixed", 144 meta: CommitMeta{Type: "fix"}, 145 wantCat: "fixed", 146 }, 147 { 148 name: "perf -> changed", 149 meta: CommitMeta{Type: "perf"}, 150 wantCat: "changed", 151 }, 152 { 153 name: "refactor -> changed", 154 meta: CommitMeta{Type: "refactor"}, 155 wantCat: "changed", 156 }, 157 { 158 name: "docs -> changed", 159 meta: CommitMeta{Type: "docs"}, 160 wantCat: "changed", 161 }, 162 { 163 name: "test -> changed", 164 meta: CommitMeta{Type: "test"}, 165 wantCat: "changed", 166 }, 167 { 168 name: "revert -> skip", 169 meta: CommitMeta{Type: "revert"}, 170 wantCat: "", 171 }, 172 { 173 name: "unknown -> skip", 174 meta: CommitMeta{Type: "unknown"}, 175 wantCat: "", 176 }, 177 } 178 179 for _, tt := range tests { 180 t.Run(tt.name, func(t *testing.T) { 181 got := parser.Categorize(tt.meta) 182 if got != tt.wantCat { 183 t.Errorf("Categorize() = %v, want %v", got, tt.wantCat) 184 } 185 }) 186 } 187} 188 189func TestConventionalParser_IsValidType(t *testing.T) { 190 parser := &ConventionalParser{} 191 192 tests := []struct { 193 name string 194 kind CommitKind 195 want bool 196 }{ 197 { 198 name: "feat is valid", 199 kind: CommitTypeFeat, 200 want: true, 201 }, 202 { 203 name: "fix is valid", 204 kind: CommitTypeFix, 205 want: true, 206 }, 207 { 208 name: "unknown is invalid", 209 kind: CommitTypeUnknown, 210 want: false, 211 }, 212 } 213 214 for _, tt := range tests { 215 t.Run(tt.name, func(t *testing.T) { 216 got := parser.IsValidType(tt.kind) 217 if got != tt.want { 218 t.Errorf("IsValidType() = %v, want %v", got, tt.want) 219 } 220 }) 221 } 222} 223 224func TestParseRefArgs(t *testing.T) { 225 tests := []struct { 226 name string 227 args []string 228 wantFrom string 229 wantTo string 230 }{ 231 { 232 name: "range syntax", 233 args: []string{"v1.0.0..v1.1.0"}, 234 wantFrom: "v1.0.0", 235 wantTo: "v1.1.0", 236 }, 237 { 238 name: "two separate args", 239 args: []string{"v1.0.0", "v1.1.0"}, 240 wantFrom: "v1.0.0", 241 wantTo: "v1.1.0", 242 }, 243 { 244 name: "single arg defaults to HEAD", 245 args: []string{"v1.0.0"}, 246 wantFrom: "v1.0.0", 247 wantTo: "HEAD", 248 }, 249 { 250 name: "empty args", 251 args: []string{}, 252 wantFrom: "", 253 wantTo: "", 254 }, 255 } 256 257 for _, tt := range tests { 258 t.Run(tt.name, func(t *testing.T) { 259 from, to := ParseRefArgs(tt.args) 260 if from != tt.wantFrom { 261 t.Errorf("ParseRefArgs() from = %v, want %v", from, tt.wantFrom) 262 } 263 if to != tt.wantTo { 264 t.Errorf("ParseRefArgs() to = %v, want %v", to, tt.wantTo) 265 } 266 }) 267 } 268} 269 270func TestGetCommitRange(t *testing.T) { 271 repo := testutils.SetupTestRepo(t) 272 273 commits := testutils.GetCommitHistory(t, repo) 274 if len(commits) < 3 { 275 t.Fatalf("Expected at least 3 commits, got %d", len(commits)) 276 } 277 278 oldCommit := commits[len(commits)-2] 279 if err := testutils.CreateTagAtCommit(t, repo, "v1.0.0", oldCommit.Hash.String()); err != nil { 280 t.Fatalf("Failed to create tag: %v", err) 281 } 282 283 testutils.AddCommit(t, repo, "d.txt", "content d", "feat: add d feature") 284 testutils.AddCommit(t, repo, "e.txt", "content e", "fix: fix e bug") 285 286 rangeCommits, err := GetCommitRange(repo, "v1.0.0", "HEAD") 287 if err != nil { 288 t.Fatalf("GetCommitRange() error = %v", err) 289 } 290 291 if len(rangeCommits) < 2 { 292 t.Errorf("Expected at least 2 commits in range, got %d", len(rangeCommits)) 293 } 294 295 for i := 1; i < len(rangeCommits); i++ { 296 if rangeCommits[i].Author.When.Before(rangeCommits[i-1].Author.When) { 297 t.Errorf("Commits are not in chronological order") 298 } 299 } 300} 301 302func TestGetCommitRange_SameRef(t *testing.T) { 303 repo := testutils.SetupTestRepo(t) 304 305 rangeCommits, err := GetCommitRange(repo, "HEAD", "HEAD") 306 if err != nil { 307 t.Fatalf("GetCommitRange() error = %v", err) 308 } 309 310 if len(rangeCommits) != 0 { 311 t.Errorf("Expected 0 commits when from and to are the same, got %d", len(rangeCommits)) 312 } 313} 314 315func TestGetFileContent(t *testing.T) { 316 repo := testutils.SetupTestRepo(t) 317 318 content, err := GetFileContent(repo, "HEAD", "README.md") 319 if err != nil { 320 t.Fatalf("GetFileContent() error = %v", err) 321 } 322 323 if content == "" { 324 t.Errorf("Expected non-empty content for README.md") 325 } 326 327 if content != "# Project\n\nInitial version" { 328 t.Errorf("GetFileContent() content = %v, want %v", content, "# Project\\n\\nInitial version") 329 } 330} 331 332func TestGetFileContent_InvalidFile(t *testing.T) { 333 repo := testutils.SetupTestRepo(t) 334 335 _, err := GetFileContent(repo, "HEAD", "nonexistent.txt") 336 if err == nil { 337 t.Errorf("Expected error for non-existent file, got nil") 338 } 339} 340 341func TestGetChangedFiles(t *testing.T) { 342 repo := testutils.SetupTestRepo(t) 343 344 commits := testutils.GetCommitHistory(t, repo) 345 if len(commits) < 2 { 346 t.Fatalf("Expected at least 2 commits, got %d", len(commits)) 347 } 348 349 files, err := GetChangedFiles(repo, commits[1].Hash.String(), commits[0].Hash.String()) 350 if err != nil { 351 t.Fatalf("GetChangedFiles() error = %v", err) 352 } 353 354 if len(files) == 0 { 355 t.Errorf("Expected at least 1 changed file, got 0") 356 } 357} 358 359func TestGetChangedFiles_NoChanges(t *testing.T) { 360 repo := testutils.SetupTestRepo(t) 361 362 files, err := GetChangedFiles(repo, "HEAD", "HEAD") 363 if err != nil { 364 t.Fatalf("GetChangedFiles() error = %v", err) 365 } 366 367 if len(files) != 0 { 368 t.Errorf("Expected 0 changed files when refs are the same, got %d", len(files)) 369 } 370}