Skip to content

Commit fec187d

Browse files
committed
tests/config(test[duplicates]): cover ordered loader items and writer
why: Drive duplicate-preserving loader and writer work via failing tests and CLI regression coverage. what: - assert DuplicateAwareConfigReader exposes ordered top-level items - add writer regression verifying duplicate sections persist on save - extend CLI no-merge test expectations and discovery mocks for new loader API
1 parent 685a2eb commit fec187d

File tree

4 files changed

+106
-3
lines changed

4 files changed

+106
-3
lines changed

tests/cli/test_add.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -820,7 +820,10 @@ class NoMergePreservationFixture(t.NamedTuple):
820820

821821

822822
@pytest.mark.xfail(
823-
reason="vcspull add --no-merge overwrites earlier duplicate workspace sections (data loss bug)",
823+
reason=(
824+
"vcspull add --no-merge overwrites earlier duplicate workspace sections "
825+
"(data loss bug)"
826+
),
824827
)
825828
@pytest.mark.parametrize(
826829
list(NoMergePreservationFixture._fields),
@@ -837,7 +840,7 @@ def test_add_repo_no_merge_preserves_duplicate_sections(
837840
tmp_path: pathlib.Path,
838841
monkeypatch: MonkeyPatch,
839842
) -> None:
840-
"""vcspull add should not drop duplicate workspace sections when --no-merge."""
843+
"""CLI add should not drop duplicate workspace sections when --no-merge."""
841844
monkeypatch.setenv("HOME", str(tmp_path))
842845
monkeypatch.chdir(tmp_path)
843846

@@ -850,9 +853,11 @@ def test_add_repo_no_merge_preserves_duplicate_sections(
850853
(
851854
_initial_config,
852855
initial_duplicates,
856+
initial_items,
853857
) = DuplicateAwareConfigReader.load_with_duplicates(config_file)
854858
assert workspace_label in initial_duplicates
855859
assert len(initial_duplicates[workspace_label]) == 2
860+
assert [key for key, _ in initial_items] == [workspace_label, workspace_label]
856861

857862
add_repo(
858863
name=new_repo_name,
@@ -867,8 +872,14 @@ def test_add_repo_no_merge_preserves_duplicate_sections(
867872
(
868873
_final_config,
869874
duplicate_sections,
875+
final_items,
870876
) = DuplicateAwareConfigReader.load_with_duplicates(config_file)
871877

878+
assert [key for key, _ in final_items] == [
879+
workspace_label,
880+
workspace_label,
881+
], f"{test_id}: final items unexpectedly merged"
882+
872883
assert workspace_label in duplicate_sections, f"{test_id}: workspace missing"
873884
workspace_entries = duplicate_sections[workspace_label]
874885
assert len(workspace_entries) == 2, f"{test_id}: duplicate sections collapsed"

tests/cli/test_discover.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -441,7 +441,7 @@ def test_discover_config_load_edges(
441441
if mode == "non_dict":
442442
monkeypatch.setattr(
443443
"vcspull.cli.discover.DuplicateAwareConfigReader.load_with_duplicates",
444-
lambda _path: (["invalid"], {}),
444+
lambda _path: (["invalid"], {}, []),
445445
)
446446
else: # mode == "exception"
447447

tests/test_config_reader.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,41 @@ def test_duplicate_aware_reader_passes_through_json(tmp_path: pathlib.Path) -> N
9393
},
9494
}
9595
assert reader.duplicate_sections == {}
96+
97+
98+
def test_duplicate_aware_reader_preserves_top_level_item_order(
99+
tmp_path: pathlib.Path,
100+
) -> None:
101+
"""Loader should expose ordered top-level items so duplicates can be replayed."""
102+
yaml_content = textwrap.dedent(
103+
"""\
104+
~/study/python/:
105+
Flexget:
106+
repo: git+https://github.com/Flexget/Flexget.git
107+
~/study/python/:
108+
bubbles:
109+
repo: git+https://github.com/Stiivi/bubbles.git
110+
~/study/python/:
111+
cubes:
112+
repo: git+https://github.com/Stiivi/cubes.git
113+
""",
114+
)
115+
config_path = _write(tmp_path, "ordered.yaml", yaml_content)
116+
117+
reader = DuplicateAwareConfigReader.from_file(config_path)
118+
119+
items = reader.top_level_items
120+
assert [key for key, _ in items] == [
121+
"~/study/python/",
122+
"~/study/python/",
123+
"~/study/python/",
124+
]
125+
assert items[0][1] == {
126+
"Flexget": {"repo": "git+https://github.com/Flexget/Flexget.git"},
127+
}
128+
assert items[1][1] == {
129+
"bubbles": {"repo": "git+https://github.com/Stiivi/bubbles.git"},
130+
}
131+
assert items[2][1] == {
132+
"cubes": {"repo": "git+https://github.com/Stiivi/cubes.git"},
133+
}

tests/test_config_writer.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
"""Tests for duplicate-preserving config writer utilities."""
2+
3+
from __future__ import annotations
4+
5+
import pathlib
6+
import textwrap
7+
import typing as t
8+
9+
import pytest
10+
11+
from vcspull.config import save_config_yaml_with_items
12+
13+
FixtureEntry = tuple[str, dict[str, t.Any]]
14+
15+
16+
@pytest.mark.parametrize(
17+
("entries", "expected_yaml"),
18+
[
19+
(
20+
(
21+
(
22+
"~/study/python/",
23+
{"Flexget": {"repo": "git+https://github.com/Flexget/Flexget.git"}},
24+
),
25+
(
26+
"~/study/python/",
27+
{"bubbles": {"repo": "git+https://github.com/Stiivi/bubbles.git"}},
28+
),
29+
),
30+
textwrap.dedent(
31+
"""\
32+
~/study/python/:
33+
Flexget:
34+
repo: git+https://github.com/Flexget/Flexget.git
35+
~/study/python/:
36+
bubbles:
37+
repo: git+https://github.com/Stiivi/bubbles.git
38+
""",
39+
),
40+
),
41+
],
42+
)
43+
def test_save_config_yaml_with_items_preserves_duplicate_sections(
44+
entries: tuple[FixtureEntry, ...],
45+
expected_yaml: str,
46+
tmp_path: pathlib.Path,
47+
) -> None:
48+
"""Writing duplicates should round-trip without collapsing sections."""
49+
config_path = tmp_path / ".vcspull.yaml"
50+
51+
save_config_yaml_with_items(config_path, list(entries))
52+
53+
yaml_text = config_path.read_text(encoding="utf-8")
54+
assert yaml_text == expected_yaml

0 commit comments

Comments
 (0)