55import contextlib
66import logging
77import pathlib
8- import textwrap
98import typing as t
109
1110import pytest
1211import yaml
12+ from syrupy .assertion import SnapshotAssertion
1313
1414from vcspull .cli import cli
1515from vcspull .cli .fmt import format_config , format_config_file , normalize_repo_config
2323 from _pytest .logging import LogCaptureFixture
2424
2525
26- def _assert_yaml_snapshot (config_path : pathlib .Path , expected : str ) -> None :
27- """Compare saved YAML config against an expected snapshot."""
28- snapshot = textwrap .dedent (expected ).strip ("\n " ) + "\n "
29- content = config_path .read_text (encoding = "utf-8" )
30- assert content == snapshot
31-
32-
3326class WorkspaceRootFixture (t .NamedTuple ):
3427 """Fixture for workspace root normalization cases."""
3528
@@ -88,6 +81,7 @@ class WorkspaceRootFixture(t.NamedTuple):
8881def test_workspace_root_normalization (
8982 test_id : str ,
9083 config_factory : t .Callable [[pathlib .Path ], dict [str , t .Any ]],
84+ snapshot_json : SnapshotAssertion ,
9185) -> None :
9286 """Ensure format_config merges duplicate workspace roots."""
9387 home_dir = pathlib .Path .home ()
@@ -110,6 +104,7 @@ def test_workspace_root_normalization(
110104 assert sorted (normalized_config .keys ()) == expected_labels
111105 formatted , _changes = format_config (normalized_config )
112106 assert sorted (formatted .keys ()) == expected_labels
107+ assert formatted == snapshot_json
113108 assert merge_changes >= len (config ) - len (canonical_paths )
114109
115110
@@ -159,19 +154,19 @@ def test_normalize_repo_config_both_url_and_repo() -> None:
159154 assert normalized == config
160155
161156
162- def test_format_config_sorts_directories () -> None :
157+ def test_format_config_sorts_directories (snapshot_json : SnapshotAssertion ) -> None :
163158 """Workspace roots should be sorted alphabetically."""
164159 config = {
165160 "~/zzz/" : {"repo1" : "url1" },
166161 "~/aaa/" : {"repo2" : "url2" },
167162 "~/mmm/" : {"repo3" : "url3" },
168163 }
169164 formatted , changes = format_config (config )
170- assert list ( formatted . keys ()) == [ "~/aaa/" , "~/mmm/" , "~/zzz/" ]
165+ assert formatted == snapshot_json
171166 assert changes > 0
172167
173168
174- def test_format_config_sorts_repositories () -> None :
169+ def test_format_config_sorts_repositories (snapshot_json : SnapshotAssertion ) -> None :
175170 """Repositories within a workspace root should be sorted."""
176171 config = {
177172 "~/projects/" : {
@@ -181,11 +176,11 @@ def test_format_config_sorts_repositories() -> None:
181176 },
182177 }
183178 formatted , changes = format_config (config )
184- assert list ( formatted [ "~/projects/" ]. keys ()) == [ "alpha" , "beta" , "zebra" ]
179+ assert formatted == snapshot_json
185180 assert changes > 0
186181
187182
188- def test_format_config_converts_compact_entries () -> None :
183+ def test_format_config_converts_compact_entries (snapshot_json : SnapshotAssertion ) -> None :
189184 """Compact repository entries should convert to verbose form."""
190185 config = {
191186 "~/projects/" : {
@@ -195,19 +190,13 @@ def test_format_config_converts_compact_entries() -> None:
195190 },
196191 }
197192 formatted , changes = format_config (config )
198- assert formatted ["~/projects/" ]["repo1" ] == {
199- "repo" : "git+https://github.com/user/repo1.git" ,
200- }
201- assert formatted ["~/projects/" ]["repo2" ] == {
202- "repo" : "git+https://github.com/user/repo2.git" ,
203- }
204- assert formatted ["~/projects/" ]["repo3" ] == {
205- "repo" : "git+https://github.com/user/repo3.git" ,
206- }
193+ assert formatted == snapshot_json
207194 assert changes == 2
208195
209196
210- def test_format_config_no_changes_when_already_normalized () -> None :
197+ def test_format_config_no_changes_when_already_normalized (
198+ snapshot_json : SnapshotAssertion ,
199+ ) -> None :
211200 """Formatter should report zero changes for already-normalized configs."""
212201 config = {
213202 "~/aaa/" : {
@@ -219,11 +208,11 @@ def test_format_config_no_changes_when_already_normalized() -> None:
219208 },
220209 }
221210 formatted , changes = format_config (config )
222- assert formatted == config
211+ assert formatted == snapshot_json
223212 assert changes == 0
224213
225214
226- def test_format_config_complex_changes () -> None :
215+ def test_format_config_complex_changes (snapshot_json : SnapshotAssertion ) -> None :
227216 """Formatter should handle sorting and conversions together."""
228217 config = {
229218 "~/zzz/" : {
@@ -239,13 +228,7 @@ def test_format_config_complex_changes() -> None:
239228 },
240229 }
241230 formatted , changes = format_config (config )
242- assert list (formatted .keys ()) == ["~/aaa/" , "~/zzz/" ]
243- assert list (formatted ["~/zzz/" ].keys ()) == ["alpha" , "beta" , "zebra" ]
244- assert formatted ["~/aaa/" ]["repo1" ] == {"repo" : "another-compact" }
245- assert formatted ["~/zzz/" ]["zebra" ] == {"repo" : "compact-url" }
246- assert formatted ["~/zzz/" ]["alpha" ] == {"repo" : "verbose-url" }
247- assert formatted ["~/zzz/" ]["beta" ]["repo" ] == "already-good"
248- assert formatted ["~/zzz/" ]["beta" ]["remotes" ] == {"upstream" : "upstream-url" }
231+ assert formatted == snapshot_json
249232 assert changes > 0
250233
251234
@@ -276,7 +259,10 @@ def test_format_config_file_without_write(
276259 assert "Run with --write to apply" in caplog .text
277260
278261
279- def test_format_config_file_with_write (tmp_path : pathlib .Path ) -> None :
262+ def test_format_config_file_with_write (
263+ tmp_path : pathlib .Path ,
264+ snapshot_yaml : SnapshotAssertion ,
265+ ) -> None :
280266 """format_config_file should rewrite file when write=True."""
281267 config_file = tmp_path / ".vcspull.yaml"
282268 original_config = {
@@ -289,8 +275,8 @@ def test_format_config_file_with_write(tmp_path: pathlib.Path) -> None:
289275
290276 format_config_file (str (config_file ), write = True , format_all = False )
291277
292- saved_config = yaml . safe_load ( config_file .read_text (encoding = "utf-8" ) )
293- assert saved_config [ "~/zzz/" ][ "repo1" ] == { "repo" : "url1" }
278+ yaml_text = config_file .read_text (encoding = "utf-8" )
279+ assert yaml_text == snapshot_yaml ( name = "format-config-file-with-write" )
294280
295281
296282def test_format_config_file_invalid_yaml (
@@ -442,6 +428,7 @@ def test_format_config_detects_and_merges_duplicate_roots(
442428def test_format_config_no_merge_flag_skips_duplicate_merge (
443429 tmp_path : pathlib .Path ,
444430 caplog : LogCaptureFixture ,
431+ snapshot_yaml : SnapshotAssertion ,
445432) -> None :
446433 """--no-merge should prevent duplicate workspace roots from being combined."""
447434 config_file = tmp_path / ".vcspull.yaml"
@@ -464,17 +451,13 @@ def test_format_config_no_merge_flag_skips_duplicate_merge(
464451
465452 text = caplog .text
466453 assert "skipping merge" in text
467- _assert_yaml_snapshot (
468- config_file ,
469- """~/study/c/:
470- repo2:
471- repo: url2
472- """ ,
473- )
454+ yaml_text = config_file .read_text (encoding = "utf-8" )
455+ assert yaml_text == snapshot_yaml (name = "format-config-file-no-merge" )
474456
475457
476458def test_format_config_merges_duplicate_roots_when_writing (
477459 tmp_path : pathlib .Path ,
460+ snapshot_yaml : SnapshotAssertion ,
478461) -> None :
479462 """When merging, formatted file should contain combined repositories."""
480463 config_file = tmp_path / ".vcspull.yaml"
@@ -489,15 +472,8 @@ def test_format_config_merges_duplicate_roots_when_writing(
489472
490473 format_config_file (str (config_file ), write = True , format_all = False )
491474
492- _assert_yaml_snapshot (
493- config_file ,
494- """~/study/c/:
495- repo1:
496- repo: url1
497- repo2:
498- repo: url2
499- """ ,
500- )
475+ yaml_text = config_file .read_text (encoding = "utf-8" )
476+ assert yaml_text == snapshot_yaml (name = "format-config-file-merge" )
501477
502478
503479class FmtIntegrationFixture (t .NamedTuple ):
@@ -506,29 +482,21 @@ class FmtIntegrationFixture(t.NamedTuple):
506482 test_id : str
507483 cli_args : list [str ]
508484 expected_log_fragment : str
509- expected_yaml : str
485+ snapshot_name : str
510486
511487
512488FMT_CLI_FIXTURES : list [FmtIntegrationFixture ] = [
513489 FmtIntegrationFixture (
514490 test_id = "merge-default" ,
515491 cli_args = ["fmt" , "-f" , "{config}" , "--write" ],
516492 expected_log_fragment = "Merged" ,
517- expected_yaml = """~/study/c/:
518- repo1:
519- repo: url1
520- repo2:
521- repo: url2
522- """ ,
493+ snapshot_name = "fmt-cli-merge" ,
523494 ),
524495 FmtIntegrationFixture (
525496 test_id = "no-merge" ,
526497 cli_args = ["fmt" , "-f" , "{config}" , "--write" , "--no-merge" ],
527498 expected_log_fragment = "skipping merge" ,
528- expected_yaml = """~/study/c/:
529- repo2:
530- repo: url2
531- """ ,
499+ snapshot_name = "fmt-cli-no-merge" ,
532500 ),
533501]
534502
@@ -542,10 +510,11 @@ def test_fmt_cli_integration(
542510 tmp_path : pathlib .Path ,
543511 caplog : LogCaptureFixture ,
544512 monkeypatch : pytest .MonkeyPatch ,
513+ snapshot_yaml : SnapshotAssertion ,
545514 test_id : str ,
546515 cli_args : list [str ],
547516 expected_log_fragment : str ,
548- expected_yaml : str ,
517+ snapshot_name : str ,
549518) -> None :
550519 """Run vcspull fmt via CLI to ensure duplicate handling respects flags."""
551520 config_file = tmp_path / ".vcspull.yaml"
@@ -568,5 +537,6 @@ def test_fmt_cli_integration(
568537 with caplog .at_level (logging .INFO ), contextlib .suppress (SystemExit ):
569538 cli (formatted_args )
570539
571- _assert_yaml_snapshot (config_file , expected_yaml )
540+ yaml_text = config_file .read_text (encoding = "utf-8" )
541+ assert yaml_text == snapshot_yaml (name = snapshot_name )
572542 assert expected_log_fragment in caplog .text
0 commit comments