Skip to content

Commit 2f89c43

Browse files
committed
tests/cmd(test[GitTag]): add tests for tag operations
why: Verify GitTagManager and GitTagCmd methods work correctly what: - Add TagCreateFixture with NamedTuple + test_id pattern - Add TagDeleteFixture for lightweight/annotated tag deletion - Add TagListFixture for ls() with pattern filtering - Add tests for filter(), show(), verify() methods - All tests use pytest fixtures (no mocks)
1 parent 629c7f1 commit 2f89c43

1 file changed

Lines changed: 256 additions & 0 deletions

File tree

tests/cmd/test_git.py

Lines changed: 256 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -623,3 +623,259 @@ def test_remote_update(
623623

624624
# update typically returns "Fetching <remote>" message
625625
assert "fetching" in result.lower() or result == ""
626+
627+
628+
# =============================================================================
629+
# GitTagCmd / GitTagManager Tests
630+
# =============================================================================
631+
632+
633+
class TagCreateFixture(t.NamedTuple):
634+
"""Test fixture for GitTagManager.create() operations."""
635+
636+
test_id: str
637+
tag_name: str
638+
message: str | None
639+
annotate: bool | None
640+
ref: str | None
641+
force: bool | None
642+
643+
644+
TAG_CREATE_FIXTURES: list[TagCreateFixture] = [
645+
TagCreateFixture(
646+
test_id="create-lightweight-tag",
647+
tag_name="lightweight-v1.0",
648+
message=None,
649+
annotate=None,
650+
ref=None,
651+
force=None,
652+
),
653+
TagCreateFixture(
654+
test_id="create-annotated-tag",
655+
tag_name="annotated-v1.0",
656+
message="Release version 1.0",
657+
annotate=None, # message implies annotated
658+
ref=None,
659+
force=None,
660+
),
661+
TagCreateFixture(
662+
test_id="create-tag-explicit-annotate",
663+
tag_name="explicit-annotated-v1.0",
664+
message="Explicit annotated tag",
665+
annotate=True,
666+
ref=None,
667+
force=None,
668+
),
669+
TagCreateFixture(
670+
test_id="create-tag-at-ref",
671+
tag_name="ref-v1.0",
672+
message="Tag at HEAD",
673+
annotate=None,
674+
ref="HEAD",
675+
force=None,
676+
),
677+
]
678+
679+
680+
@pytest.mark.parametrize(
681+
list(TagCreateFixture._fields),
682+
TAG_CREATE_FIXTURES,
683+
ids=[test.test_id for test in TAG_CREATE_FIXTURES],
684+
)
685+
def test_tag_create(
686+
git_repo: GitSync,
687+
test_id: str,
688+
tag_name: str,
689+
message: str | None,
690+
annotate: bool | None,
691+
ref: str | None,
692+
force: bool | None,
693+
) -> None:
694+
"""Test GitTagManager.create() with various scenarios."""
695+
result = git_repo.cmd.tags.create(
696+
name=tag_name,
697+
message=message,
698+
annotate=annotate,
699+
ref=ref,
700+
force=force,
701+
)
702+
703+
# create returns empty string on success
704+
assert result == ""
705+
706+
# Verify tag exists
707+
tag = git_repo.cmd.tags.get(tag_name=tag_name)
708+
assert tag is not None
709+
assert tag.tag_name == tag_name
710+
711+
712+
def test_tag_create_force(git_repo: GitSync) -> None:
713+
"""Test GitTagManager.create() with force flag to replace existing tag."""
714+
tag_name = "force-replace-tag"
715+
716+
# Create initial tag
717+
git_repo.cmd.tags.create(name=tag_name, message="Initial tag")
718+
719+
# Creating same tag without force returns error message
720+
result_without_force = git_repo.cmd.tags.create(
721+
name=tag_name, message="Replacement tag"
722+
)
723+
assert "already exists" in result_without_force.lower()
724+
725+
# Creating same tag with force should succeed
726+
result = git_repo.cmd.tags.create(
727+
name=tag_name, message="Replacement tag", force=True
728+
)
729+
# Force update returns "Updated tag" message
730+
assert result == "" or "updated tag" in result.lower()
731+
732+
733+
class TagDeleteFixture(t.NamedTuple):
734+
"""Test fixture for GitTagCmd.delete() operations."""
735+
736+
test_id: str
737+
tag_name: str
738+
message: str
739+
740+
741+
TAG_DELETE_FIXTURES: list[TagDeleteFixture] = [
742+
TagDeleteFixture(
743+
test_id="delete-lightweight-tag",
744+
tag_name="delete-lightweight",
745+
message="", # empty message = lightweight tag
746+
),
747+
TagDeleteFixture(
748+
test_id="delete-annotated-tag",
749+
tag_name="delete-annotated",
750+
message="Annotated tag to delete",
751+
),
752+
]
753+
754+
755+
@pytest.mark.parametrize(
756+
list(TagDeleteFixture._fields),
757+
TAG_DELETE_FIXTURES,
758+
ids=[test.test_id for test in TAG_DELETE_FIXTURES],
759+
)
760+
def test_tag_delete(
761+
git_repo: GitSync,
762+
test_id: str,
763+
tag_name: str,
764+
message: str,
765+
) -> None:
766+
"""Test GitTagCmd.delete() with various scenarios."""
767+
# Create tag first
768+
if message:
769+
git_repo.cmd.tags.create(name=tag_name, message=message)
770+
else:
771+
git_repo.cmd.tags.create(name=tag_name)
772+
773+
# Get and delete the tag
774+
tag = git_repo.cmd.tags.get(tag_name=tag_name)
775+
assert tag is not None
776+
777+
result = tag.delete()
778+
assert "deleted tag" in result.lower()
779+
780+
# Verify tag is gone
781+
with pytest.raises(ObjectDoesNotExist):
782+
git_repo.cmd.tags.get(tag_name=tag_name)
783+
784+
785+
class TagListFixture(t.NamedTuple):
786+
"""Test fixture for GitTagManager.ls() operations."""
787+
788+
test_id: str
789+
setup_tags: list[str]
790+
pattern: str | None
791+
expected_min_count: int
792+
793+
794+
TAG_LIST_FIXTURES: list[TagListFixture] = [
795+
TagListFixture(
796+
test_id="list-all-tags",
797+
setup_tags=["list-v1.0", "list-v2.0", "list-v3.0"],
798+
pattern=None,
799+
expected_min_count=3,
800+
),
801+
TagListFixture(
802+
test_id="list-tags-with-pattern",
803+
setup_tags=["pattern-alpha", "pattern-beta", "other-tag"],
804+
pattern="pattern-*",
805+
expected_min_count=2,
806+
),
807+
]
808+
809+
810+
@pytest.mark.parametrize(
811+
list(TagListFixture._fields),
812+
TAG_LIST_FIXTURES,
813+
ids=[test.test_id for test in TAG_LIST_FIXTURES],
814+
)
815+
def test_tag_list(
816+
git_repo: GitSync,
817+
test_id: str,
818+
setup_tags: list[str],
819+
pattern: str | None,
820+
expected_min_count: int,
821+
) -> None:
822+
"""Test GitTagManager.ls() with various scenarios."""
823+
# Create setup tags
824+
for tag_name in setup_tags:
825+
git_repo.cmd.tags.create(name=tag_name, message=f"Tag {tag_name}")
826+
827+
# List tags
828+
tags = git_repo.cmd.tags.ls(pattern=pattern)
829+
830+
# Verify minimum count
831+
assert len(tags) >= expected_min_count
832+
833+
834+
def test_tag_filter(git_repo: GitSync) -> None:
835+
"""Test GitTagManager.filter() with QueryList filtering."""
836+
# Create tags with different prefixes
837+
git_repo.cmd.tags.create(name="release-1.0", message="Release 1.0")
838+
git_repo.cmd.tags.create(name="release-2.0", message="Release 2.0")
839+
git_repo.cmd.tags.create(name="beta-1.0", message="Beta 1.0")
840+
841+
# Filter by prefix
842+
release_tags = git_repo.cmd.tags.filter(tag_name__startswith="release-")
843+
assert len(release_tags) >= 2
844+
845+
beta_tags = git_repo.cmd.tags.filter(tag_name__contains="beta")
846+
assert len(beta_tags) >= 1
847+
848+
849+
def test_tag_show(git_repo: GitSync) -> None:
850+
"""Test GitTagCmd.show() for annotated tags."""
851+
tag_name = "show-test-tag"
852+
message = "This is a test tag for show"
853+
854+
# Create annotated tag
855+
git_repo.cmd.tags.create(name=tag_name, message=message)
856+
857+
# Get tag and show
858+
tag = git_repo.cmd.tags.get(tag_name=tag_name)
859+
assert tag is not None
860+
861+
result = tag.show()
862+
863+
# show output should contain tag name and message
864+
assert tag_name in result
865+
assert message in result
866+
867+
868+
def test_tag_verify_unsigned(git_repo: GitSync) -> None:
869+
"""Test GitTagCmd.verify() for unsigned/lightweight tags."""
870+
tag_name = "verify-unsigned-tag"
871+
872+
# Create lightweight tag (can't be verified)
873+
git_repo.cmd.tags.create(name=tag_name)
874+
875+
tag = git_repo.cmd.tags.get(tag_name=tag_name)
876+
assert tag is not None
877+
878+
result = tag.verify()
879+
880+
# verify on unsigned tag should return error message
881+
assert "error" in result.lower() or "cannot verify" in result.lower()

0 commit comments

Comments
 (0)