···11package git
2233import (
44+ "os"
45 "path/filepath"
66+ "strings"
57 "testing"
68 "time"
79···384386 require.NoError(s.T(), err)
385387 assert.Len(s.T(), v1tag, 1, "expected 1 tag")
386388}
389389+390390+func (s *TagSuite) TestTags_PatternGlob() {
391391+ s.setupRepoWithTags()
392392+393393+ tags, err := s.repo.Tags(&TagsOptions{
394394+ Pattern: "refs/tags/v1.*",
395395+ })
396396+ require.NoError(s.T(), err)
397397+ require.Len(s.T(), tags, 2, "glob pattern refs/tags/v1.* should match 2 tags")
398398+399399+ names := map[string]bool{}
400400+ for _, t := range tags {
401401+ names[t.Name] = true
402402+ }
403403+ assert.True(s.T(), names["v1.0.0"], "v1.0.0 should match pattern")
404404+ assert.True(s.T(), names["v1.1.0"], "v1.1.0 should match pattern")
405405+}
406406+407407+func (s *TagSuite) TestTags_PatternNoMatch() {
408408+ s.setupRepoWithTags()
409409+410410+ tags, err := s.repo.Tags(&TagsOptions{
411411+ Pattern: "refs/tags/v9.*",
412412+ })
413413+ require.NoError(s.T(), err)
414414+ assert.Empty(s.T(), tags, "non-matching pattern should return no tags")
415415+}
416416+417417+func (s *TagSuite) TestTags_VerifyLightweightTagFields() {
418418+ s.setupRepoWithTags()
419419+420420+ tags, err := s.repo.Tags(nil)
421421+ require.NoError(s.T(), err)
422422+423423+ var v2Tag *object.Tag
424424+ for i := range tags {
425425+ if tags[i].Name == "v2.0.0" {
426426+ v2Tag = &tags[i]
427427+ break
428428+ }
429429+ }
430430+ require.NotNil(s.T(), v2Tag, "v2.0.0 tag not found")
431431+432432+ assert.Empty(s.T(), v2Tag.Tagger.Name, "lightweight tag should have no tagger name")
433433+ assert.Empty(s.T(), v2Tag.Tagger.Email, "lightweight tag should have no tagger email")
434434+ assert.True(s.T(), v2Tag.Tagger.When.IsZero(), "lightweight tag should have zero tagger date")
435435+ // For a lightweight tag %(contents:subject) returns the commit subject, not a tag annotation
436436+ assert.Equal(s.T(), "Add file3", v2Tag.Message, "lightweight tag message should be the commit subject")
437437+ assert.False(s.T(), v2Tag.Hash.IsZero(), "lightweight tag hash should be the commit hash")
438438+ assert.Equal(s.T(), plumbing.CommitObject, v2Tag.TargetType, "lightweight tag should resolve to a commit")
439439+}
440440+441441+func (s *TagSuite) TestTags_SubjectOnlyMessage() {
442442+ s.setupRepoWithTags()
443443+444444+ tags, err := s.repo.Tags(nil)
445445+ require.NoError(s.T(), err)
446446+447447+ var v11Tag *object.Tag
448448+ for i := range tags {
449449+ if tags[i].Name == "v1.1.0" {
450450+ v11Tag = &tags[i]
451451+ break
452452+ }
453453+ }
454454+ require.NotNil(s.T(), v11Tag, "v1.1.0 tag not found")
455455+456456+ // v1.1.0 was created with a subject-only message (no body paragraph)
457457+ assert.Equal(s.T(), "Release version 1.1.0", v11Tag.Message,
458458+ "subject-only tag message should equal the subject line")
459459+}
460460+461461+func (s *TagSuite) TestTags_FullOrdering() {
462462+ s.setupRepoWithTags()
463463+464464+ tags, err := s.repo.Tags(nil)
465465+ require.NoError(s.T(), err)
466466+ require.Len(s.T(), tags, 5)
467467+468468+ // Annotated tags carry explicit tagger dates (baseTime+3h, +2h, +1h) so they
469469+ // sort ahead of lightweight tags whose commits have no explicit author date.
470470+ assert.Equal(s.T(), "v3.0.0", tags[0].Name, "v3.0.0 should be newest (baseTime+3h)")
471471+ assert.Equal(s.T(), "v1.1.0", tags[1].Name, "v1.1.0 should be second (baseTime+2h)")
472472+ assert.Equal(s.T(), "v1.0.0", tags[2].Name, "v1.0.0 should be third (baseTime+1h)")
473473+474474+ // Lightweight tags v2.0.0 and v2.1.0 have zero-time commits and sort to the
475475+ // end; their relative order is not guaranteed.
476476+ lastName := map[string]bool{tags[3].Name: true, tags[4].Name: true}
477477+ assert.True(s.T(), lastName["v2.0.0"], "v2.0.0 should be in the last two positions")
478478+ assert.True(s.T(), lastName["v2.1.0"], "v2.1.0 should be in the last two positions")
479479+}
480480+481481+func (s *TagSuite) TestTags_ForEachRefError() {
482482+ s.setupRepoWithTags()
483483+484484+ // Remove .git so the underlying git command fails.
485485+ err := os.RemoveAll(filepath.Join(s.repo.path, ".git"))
486486+ require.NoError(s.T(), err)
487487+488488+ _, err = s.repo.Tags(nil)
489489+ assert.Error(s.T(), err, "Tags should return an error when the git command fails")
490490+}
491491+492492+func (s *TagSuite) TestParseTagRecord_BodyOnly() {
493493+ // When subject is empty and body is non-empty the else branch sets message = body.
494494+ fields := []string{
495495+ "v1.0.0", // tagName
496496+ "abc123", // objectHash
497497+ "tag", // objectType
498498+ "def456", // targetHash
499499+ "commit", // targetType
500500+ "Tagger", // taggerName
501501+ "<t@t.com>", // taggerEmail
502502+ "0", // taggerDate
503503+ "", // subject — empty
504504+ "body text", // body — non-empty
505505+ "", // signature
506506+ }
507507+ line := strings.Join(fields, fieldSeparator)
508508+ tag, ok, err := parseTagRecord(line)
509509+ require.NoError(s.T(), err)
510510+ require.True(s.T(), ok)
511511+ assert.Equal(s.T(), "body text", tag.Message, "body-only message should equal the body field")
512512+}
513513+514514+func (s *TagSuite) TestParseTagRecord_ShortRecord() {
515515+ // A record with fewer than 6 fields must be skipped without error.
516516+ short := strings.Join([]string{"v1.0.0", "abc123", "tag", "def456"}, fieldSeparator)
517517+ tag, ok, err := parseTagRecord(short)
518518+ require.NoError(s.T(), err)
519519+ assert.False(s.T(), ok, "short record should be skipped")
520520+ assert.Equal(s.T(), object.Tag{}, tag)
521521+}
522522+523523+func (s *TagSuite) TestParseTagRecord_InvalidObjectType() {
524524+ // A record whose objecttype field is unrecognised must surface an error.
525525+ fields := []string{
526526+ "v1.0.0", // tagName
527527+ "abc123", // objectHash
528528+ "invalid_type", // objectType — not a valid git object type
529529+ "def456", // targetHash
530530+ "commit", // targetType
531531+ "Tagger", // taggerName
532532+ "<t@t.com>", // taggerEmail
533533+ "0", // taggerDate
534534+ "subject", // subject
535535+ "body", // body
536536+ "", // signature
537537+ }
538538+ line := strings.Join(fields, fieldSeparator)
539539+ _, ok, err := parseTagRecord(line)
540540+ assert.Error(s.T(), err, "invalid object type should return an error")
541541+ assert.False(s.T(), ok)
542542+}