Skip to content

Commit 685a2eb

Browse files
committed
tests/cli(test[add]): xfail regression for --no-merge data loss
why: Document the duplicate-root data loss bug surfaced by vcspull add --no-merge. what: - add parametrized fixture mirroring the reported config layout to reproduce data loss - mark the regression test xfail until the loader/writer preserve duplicate sections
1 parent 34ed07b commit 685a2eb

File tree

1 file changed

+113
-0
lines changed

1 file changed

+113
-0
lines changed

tests/cli/test_add.py

Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
import pytest
1414

15+
from vcspull._internal.config_reader import DuplicateAwareConfigReader
1516
from vcspull.cli.add import add_repo, create_add_subparser, handle_add_command
1617
from vcspull.util import contract_user_home
1718

@@ -772,3 +773,115 @@ def test_add_parser_rejects_extra_positional() -> None:
772773

773774
with pytest.raises(SystemExit):
774775
parser.parse_args(["add", "name", "https://example.com/repo.git"])
776+
777+
778+
class NoMergePreservationFixture(t.NamedTuple):
779+
"""Fixture for asserting --no-merge keeps duplicate sections intact."""
780+
781+
test_id: str
782+
initial_yaml: str
783+
expected_original_repos: tuple[str, ...]
784+
new_repo_name: str
785+
new_repo_url: str
786+
workspace_label: str
787+
788+
789+
NO_MERGE_PRESERVATION_FIXTURES: list[NoMergePreservationFixture] = [
790+
NoMergePreservationFixture(
791+
test_id="duplicate-root-yaml",
792+
initial_yaml=textwrap.dedent(
793+
"""\
794+
~/study/python/:
795+
Flexget:
796+
repo: git+https://github.com/Flexget/Flexget.git
797+
MyST-Parser:
798+
repo: git@github.com:executablebooks/MyST-Parser.git
799+
RootTheBox:
800+
repo: git+https://github.com/moloch--/RootTheBox.git
801+
~/study/python/:
802+
bubbles:
803+
repo: git+https://github.com/Stiivi/bubbles.git
804+
cubes:
805+
repo: git+https://github.com/Stiivi/cubes.git
806+
"""
807+
),
808+
expected_original_repos=(
809+
"Flexget",
810+
"MyST-Parser",
811+
"RootTheBox",
812+
"bubbles",
813+
"cubes",
814+
),
815+
new_repo_name="pytest-docker",
816+
new_repo_url="git+https://github.com/avast/pytest-docker",
817+
workspace_label="~/study/python/",
818+
),
819+
]
820+
821+
822+
@pytest.mark.xfail(
823+
reason="vcspull add --no-merge overwrites earlier duplicate workspace sections (data loss bug)",
824+
)
825+
@pytest.mark.parametrize(
826+
list(NoMergePreservationFixture._fields),
827+
NO_MERGE_PRESERVATION_FIXTURES,
828+
ids=[fixture.test_id for fixture in NO_MERGE_PRESERVATION_FIXTURES],
829+
)
830+
def test_add_repo_no_merge_preserves_duplicate_sections(
831+
test_id: str,
832+
initial_yaml: str,
833+
expected_original_repos: tuple[str, ...],
834+
new_repo_name: str,
835+
new_repo_url: str,
836+
workspace_label: str,
837+
tmp_path: pathlib.Path,
838+
monkeypatch: MonkeyPatch,
839+
) -> None:
840+
"""vcspull add should not drop duplicate workspace sections when --no-merge."""
841+
monkeypatch.setenv("HOME", str(tmp_path))
842+
monkeypatch.chdir(tmp_path)
843+
844+
config_file = tmp_path / ".vcspull.yaml"
845+
config_file.write_text(initial_yaml, encoding="utf-8")
846+
847+
repo_path = tmp_path / "study/python" / new_repo_name
848+
repo_path.mkdir(parents=True, exist_ok=True)
849+
850+
(
851+
_initial_config,
852+
initial_duplicates,
853+
) = DuplicateAwareConfigReader.load_with_duplicates(config_file)
854+
assert workspace_label in initial_duplicates
855+
assert len(initial_duplicates[workspace_label]) == 2
856+
857+
add_repo(
858+
name=new_repo_name,
859+
url=new_repo_url,
860+
config_file_path_str=str(config_file),
861+
path=str(repo_path),
862+
workspace_root_path=workspace_label,
863+
dry_run=False,
864+
merge_duplicates=False,
865+
)
866+
867+
(
868+
_final_config,
869+
duplicate_sections,
870+
) = DuplicateAwareConfigReader.load_with_duplicates(config_file)
871+
872+
assert workspace_label in duplicate_sections, f"{test_id}: workspace missing"
873+
workspace_entries = duplicate_sections[workspace_label]
874+
assert len(workspace_entries) == 2, f"{test_id}: duplicate sections collapsed"
875+
876+
combined_repos: set[str] = set()
877+
contains_new_repo = False
878+
879+
for entry in workspace_entries:
880+
assert isinstance(entry, dict), f"{test_id}: workspace entry not dict"
881+
combined_repos.update(entry.keys())
882+
if new_repo_name in entry:
883+
contains_new_repo = True
884+
885+
expected_repos = set(expected_original_repos) | {new_repo_name}
886+
assert combined_repos == expected_repos, f"{test_id}: repositories mismatch"
887+
assert contains_new_repo, f"{test_id}: new repo missing from duplicate sections"

0 commit comments

Comments
 (0)