github.com/purpleclay/gitz@v0.8.2-0.20240515052600-43f80eea2fe1/log_test.go (about) 1 package git_test 2 3 import ( 4 "bufio" 5 "os" 6 "strings" 7 "testing" 8 9 git "github.com/purpleclay/gitz" 10 "github.com/purpleclay/gitz/gittest" 11 "github.com/stretchr/testify/assert" 12 "github.com/stretchr/testify/require" 13 ) 14 15 func TestLog(t *testing.T) { 16 log := `fix: parsing error when input string is too long 17 ci: extend the existing build workflow to include integration tests 18 docs: create initial mkdocs material documentation 19 feat: add second operation to library 20 feat: add first operation to library` 21 22 gittest.InitRepository(t, gittest.WithLog(log)) 23 24 client, _ := git.NewClient() 25 out, err := client.Log() 26 require.NoError(t, err) 27 28 lines := countLogLines(t, out.Raw) 29 require.Equal(t, 6, lines) 30 require.Equal(t, 6, len(out.Commits)) 31 32 assert.Contains(t, out.Raw, "fix: parsing error when input string is too long") 33 assert.Equal(t, "fix: parsing error when input string is too long", out.Commits[0].Message) 34 35 assert.Contains(t, out.Raw, "ci: extend the existing build workflow to include integration tests") 36 assert.Equal(t, "ci: extend the existing build workflow to include integration tests", out.Commits[1].Message) 37 38 assert.Contains(t, out.Raw, "docs: create initial mkdocs material documentation") 39 assert.Equal(t, "docs: create initial mkdocs material documentation", out.Commits[2].Message) 40 41 assert.Contains(t, out.Raw, "feat: add second operation to library") 42 assert.Equal(t, "feat: add second operation to library", out.Commits[3].Message) 43 44 assert.Contains(t, out.Raw, "feat: add first operation to library") 45 assert.Equal(t, "feat: add first operation to library", out.Commits[4].Message) 46 47 assert.Contains(t, out.Raw, gittest.InitialCommit) 48 assert.Equal(t, gittest.InitialCommit, out.Commits[5].Message) 49 } 50 51 func TestLogMultiLineCommit(t *testing.T) { 52 log := `> feat: this is a commit that will 53 be spread 54 across multiple lines` 55 gittest.InitRepository(t, gittest.WithLog(log)) 56 57 client, _ := git.NewClient() 58 out, err := client.Log() 59 60 require.NoError(t, err) 61 require.Len(t, out.Commits, 2) 62 assert.Equal(t, `feat: this is a commit that will 63 be spread 64 across multiple lines`, out.Commits[0].Message) 65 assert.Equal(t, gittest.InitialCommit, out.Commits[1].Message) 66 } 67 68 // A utility function that will scan the raw output from a git log and 69 // count all of the returned log lines. It is important to note, that 70 // in some scenarios the log will contain the [gittest.InitialCommit] 71 // used to initialize the repository 72 func countLogLines(t *testing.T, log string) int { 73 t.Helper() 74 scanner := bufio.NewScanner(strings.NewReader(log)) 75 scanner.Split(bufio.ScanLines) 76 77 count := 0 78 for scanner.Scan() { 79 count++ 80 } 81 82 return count 83 } 84 85 func TestLogValidateParsing(t *testing.T) { 86 gittest.InitRepository(t) 87 88 client, _ := git.NewClient() 89 out, err := client.Log() 90 91 require.NoError(t, err) 92 require.Len(t, out.Commits, 1) 93 94 assert.Equal(t, gittest.InitialCommit, out.Commits[0].Message) 95 96 lastCommit := gittest.LastCommit(t) 97 assert.Equal(t, lastCommit.Hash, out.Commits[0].Hash) 98 assert.Equal(t, lastCommit.AbbrevHash, out.Commits[0].AbbrevHash) 99 } 100 101 func TestLogError(t *testing.T) { 102 nonWorkingDirectory(t) 103 104 client, _ := git.NewClient() 105 _, err := client.Log() 106 107 require.Error(t, err) 108 } 109 110 func nonWorkingDirectory(t *testing.T) { 111 t.Helper() 112 113 current, err := os.Getwd() 114 require.NoError(t, err) 115 116 tmpDir := t.TempDir() 117 require.NoError(t, os.Chdir(tmpDir)) 118 119 t.Cleanup(func() { 120 require.NoError(t, os.Chdir(current)) 121 }) 122 } 123 124 func TestLogWithRawOnly(t *testing.T) { 125 gittest.InitRepository(t) 126 127 client, _ := git.NewClient() 128 out, err := client.Log(git.WithRawOnly()) 129 130 require.NoError(t, err) 131 assert.Empty(t, out.Commits) 132 } 133 134 func TestLogWithRef(t *testing.T) { 135 log := `(tag: 0.1.1) fix: unexpected bytes in message while parsing 136 (tag: 0.1.0) docs: create initial mkdocs material documentation 137 feat: build exciting new library` 138 139 gittest.InitRepository(t, gittest.WithLog(log)) 140 141 client, _ := git.NewClient() 142 out, err := client.Log(git.WithRef("0.1.0")) 143 require.NoError(t, err) 144 145 lines := countLogLines(t, out.Raw) 146 require.Equal(t, 3, lines) 147 148 assert.Contains(t, out.Raw, "docs: create initial mkdocs material documentation") 149 assert.Contains(t, out.Raw, "feat: build exciting new library") 150 assert.Contains(t, out.Raw, gittest.InitialCommit) 151 } 152 153 func TestLogWithRefRange(t *testing.T) { 154 log := `(tag: 0.2.0) feat: add ability to filter on results 155 (tag: 0.1.1) fix: unexpected bytes in message while parsing 156 docs: update documentation to include fix 157 (tag: 0.1.0) docs: create initial mkdocs material documentation 158 feat: build exciting new library` 159 160 tests := []struct { 161 name string 162 fromRef string 163 toRef string 164 expectedLines int 165 expectedCommits []string 166 }{ 167 { 168 name: "FromAndToRefsProvided", 169 fromRef: "0.1.1", 170 toRef: "0.1.0", 171 expectedLines: 2, 172 expectedCommits: []string{ 173 "fix: unexpected bytes in message while parsing", 174 "docs: update documentation to include fix", 175 }, 176 }, 177 { 178 name: "FromRefOnly", 179 fromRef: "0.1.0", 180 expectedLines: 3, 181 expectedCommits: []string{ 182 "docs: create initial mkdocs material documentation", 183 "feat: build exciting new library", 184 gittest.InitialCommit, 185 }, 186 }, 187 { 188 name: "ToRefOnly", 189 toRef: "0.1.1", 190 expectedLines: 1, 191 expectedCommits: []string{ 192 "feat: add ability to filter on results", 193 }, 194 }, 195 { 196 name: "TrimsWhitespaceAroundRefs", 197 fromRef: " 0.2.0 ", 198 toRef: " 0.1.1 ", 199 expectedLines: 1, 200 expectedCommits: []string{ 201 "feat: add ability to filter on results", 202 }, 203 }, 204 } 205 for _, tt := range tests { 206 t.Run(tt.name, func(t *testing.T) { 207 gittest.InitRepository(t, gittest.WithLog(log)) 208 209 client, _ := git.NewClient() 210 out, err := client.Log(git.WithRefRange(tt.fromRef, tt.toRef)) 211 require.NoError(t, err) 212 213 lines := countLogLines(t, out.Raw) 214 require.Equal(t, tt.expectedLines, lines) 215 216 for _, commit := range tt.expectedCommits { 217 require.Contains(t, out.Raw, commit) 218 } 219 }) 220 } 221 } 222 223 func TestLogWithPaths(t *testing.T) { 224 gittest.InitRepository(t, 225 gittest.WithLocalCommits("this should not appear in the log"), 226 gittest.WithStagedFiles("dir1/a.txt", "dir2/b.txt")) 227 228 gittest.Commit(t, "feat: include both dir1/a.txt and dir2/b.txt") 229 overwriteFile(t, "dir1/a.txt", "Help, I have been overwritten!") 230 gittest.StageFile(t, "dir1/a.txt") 231 gittest.Commit(t, "fix: changed file dir1/a.txt") 232 233 client, _ := git.NewClient() 234 out, err := client.Log(git.WithPaths("dir1")) 235 require.NoError(t, err) 236 237 lines := countLogLines(t, out.Raw) 238 require.Equal(t, 2, lines) 239 assert.Contains(t, out.Raw, "fix: changed file dir1/a.txt") 240 assert.Contains(t, out.Raw, "feat: include both dir1/a.txt and dir2/b.txt") 241 } 242 243 func overwriteFile(t *testing.T, path, content string) { 244 t.Helper() 245 246 fi, err := os.Create(path) 247 require.NoError(t, err) 248 defer fi.Close() 249 250 fi.WriteString(content) 251 require.NoError(t, fi.Sync()) 252 } 253 254 func TestLogWithSkip(t *testing.T) { 255 log := `feat: add options to support skipping of log entries 256 ci: improve github workflow 257 docs: update documentation to include new option` 258 259 tests := []struct { 260 name string 261 skipCount int 262 expectedLines int 263 expectedCommits []string 264 }{ 265 { 266 name: "IsIgnored", 267 skipCount: 0, 268 expectedLines: 4, 269 expectedCommits: []string{ 270 "feat: add options to support skipping of log entries", 271 "ci: improve github workflow", 272 "docs: update documentation to include new option", 273 gittest.InitialCommit, 274 }, 275 }, 276 { 277 name: "SkipFirstEntry", 278 skipCount: 1, 279 expectedLines: 3, 280 expectedCommits: []string{ 281 "ci: improve github workflow", 282 "docs: update documentation to include new option", 283 gittest.InitialCommit, 284 }, 285 }, 286 { 287 name: "SkipExceedsLogLength", 288 skipCount: 10, 289 expectedLines: 0, 290 }, 291 } 292 for _, tt := range tests { 293 t.Run(tt.name, func(t *testing.T) { 294 gittest.InitRepository(t, gittest.WithLog(log)) 295 296 client, _ := git.NewClient() 297 out, err := client.Log(git.WithSkip(tt.skipCount)) 298 require.NoError(t, err) 299 300 lines := countLogLines(t, out.Raw) 301 require.Equal(t, tt.expectedLines, lines) 302 303 for _, commit := range tt.expectedCommits { 304 require.Contains(t, out.Raw, commit) 305 } 306 }) 307 } 308 } 309 310 func TestLogWithTake(t *testing.T) { 311 log := `feat: add options to support taking n number of log entries 312 docs: update documentation to include new option` 313 314 tests := []struct { 315 name string 316 takeCount int 317 expectedLines int 318 expectedCommits []string 319 }{ 320 { 321 name: "TakeZero", 322 takeCount: 0, 323 expectedLines: 0, 324 }, 325 { 326 name: "TakeLatestEntry", 327 takeCount: 1, 328 expectedLines: 1, 329 expectedCommits: []string{ 330 "feat: add options to support taking n number of log entries", 331 }, 332 }, 333 { 334 name: "TakeExceedsLogLength", 335 takeCount: 10, 336 expectedLines: 3, 337 expectedCommits: []string{ 338 "feat: add options to support taking n number of log entries", 339 "docs: update documentation to include new option", 340 gittest.InitialCommit, 341 }, 342 }, 343 } 344 for _, tt := range tests { 345 t.Run(tt.name, func(t *testing.T) { 346 gittest.InitRepository(t, gittest.WithLog(log)) 347 348 client, _ := git.NewClient() 349 out, err := client.Log(git.WithTake(tt.takeCount)) 350 require.NoError(t, err) 351 352 lines := countLogLines(t, out.Raw) 353 require.Equal(t, tt.expectedLines, lines) 354 355 for _, commit := range tt.expectedCommits { 356 require.Contains(t, out.Raw, commit) 357 } 358 }) 359 } 360 } 361 362 func TestLogWithSkipAndTake(t *testing.T) { 363 log := `feat: include options to filter logs between points in time and from a specific directory 364 feat: include option for generating an annotated tag 365 feat: add basic git push support 366 feat: detect if git is available when creating a new client 367 chore: simplify feature request issue 368 feat: add basic git log operation support 369 feat: add support for a basic file staging operation 370 feat: add basic support for git commit operations 371 chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6 372 feat: add basic support for git tag operations 373 chore: configure basic structure of project` 374 375 gittest.InitRepository(t, gittest.WithLog(log)) 376 377 client, _ := git.NewClient() 378 out, err := client.Log(git.WithSkip(3), git.WithTake(2)) 379 require.NoError(t, err) 380 381 lines := countLogLines(t, out.Raw) 382 require.Equal(t, 2, lines) 383 assert.Contains(t, out.Raw, "feat: detect if git is available when creating a new client") 384 assert.Contains(t, out.Raw, "chore: simplify feature request issue") 385 } 386 387 func TestWithGrep(t *testing.T) { 388 log := `feat: add option to match commits by regex 389 docs: document how to use new option for commit matching 390 chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6` 391 392 gittest.InitRepository(t, gittest.WithLog(log)) 393 394 client, _ := git.NewClient() 395 out, err := client.Log(git.WithGrep("regex$", "option")) 396 require.NoError(t, err) 397 398 lines := countLogLines(t, out.Raw) 399 require.Equal(t, 2, lines) 400 assert.Contains(t, out.Raw, "feat: add option to match commits by regex") 401 assert.Contains(t, out.Raw, "docs: document how to use new option for commit matching") 402 } 403 404 func TestWithGrepAndMatchAll(t *testing.T) { 405 log := `feat: add option to match commits by regex 406 docs: document how to use new option for commit matching 407 chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6` 408 409 gittest.InitRepository(t, gittest.WithLog(log)) 410 411 client, _ := git.NewClient() 412 out, err := client.Log(git.WithGrep("regex$", "option"), git.WithMatchAll()) 413 require.NoError(t, err) 414 415 lines := countLogLines(t, out.Raw) 416 require.Equal(t, 1, lines) 417 assert.Contains(t, out.Raw, "feat: add option to match commits by regex") 418 } 419 420 func TestWithInvertGrep(t *testing.T) { 421 log := `feat: add option to match commits by regex 422 docs: document how to use new option for commit matching 423 chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6` 424 425 gittest.InitRepository(t, gittest.WithLog(log)) 426 427 client, _ := git.NewClient() 428 out, err := client.Log(git.WithInvertGrep("regex$", "option")) 429 require.NoError(t, err) 430 431 lines := countLogLines(t, out.Raw) 432 require.Equal(t, 2, lines) 433 assert.Contains(t, out.Raw, "chore(deps): bump dependabot/fetch-metadata from 1.3.5 to 1.3.6") 434 assert.Contains(t, out.Raw, gittest.InitialCommit) 435 }