Skip to content

Commit 0bf07c3

Browse files
committed
tests/cli(add): Cover path-only workflow edge cases
why: New CLI behavior needs regression coverage for relative paths, workspace overrides, tilde log output, and legacy positional rejection. what: - extend path fixtures to toggle relative inputs, workspace overrides, and dry-run tilde assertions - normalize logs for snapshots while preserving contracted config paths when required - add unit checks for dry-run logging and parser rejection of extra positional args
1 parent 4d2a548 commit 0bf07c3

File tree

2 files changed

+196
-17
lines changed

2 files changed

+196
-17
lines changed

tests/cli/__snapshots__/test_add.ambr

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
dict({
44
'log': '''
55
WARNING vcspull.cli.add:add.py:<line> • Duplicate workspace root ~/study/python/ appears 2 times; skipping merge because --no-merge was provided.
6-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker.git) to <config> under '~/study/python/'.
6+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker.git) to ~/.vcspull.yaml under '~/study/python/'.
77

88
''',
99
'test_id': 'merge-off',
@@ -13,7 +13,7 @@
1313
dict({
1414
'log': '''
1515
INFO vcspull.cli.add:add.py:<line> • Merged 2 duplicate entries for workspace root ~/study/python/
16-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker.git) to <config> under '~/study/python/'.
16+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker.git) to ~/.vcspull.yaml under '~/study/python/'.
1717

1818
''',
1919
'test_id': 'merge-on',
@@ -27,13 +27,28 @@
2727
INFO vcspull.cli.add:add.py:<line> • workspace: ~/study/python/
2828
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
2929
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: y (auto-confirm)
30-
INFO vcspull.cli.add:add.py:<line> Config file <config> not found. A new one will be created.
31-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker) to <config> under '~/study/python/'.
30+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
31+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/avast/pytest-docker) to ~/.vcspull.yaml under '~/study/python/'.
3232

3333
''',
3434
'test_id': 'path-auto-confirm',
3535
})
3636
# ---
37+
# name: test_handle_add_command_path_mode[path-dry-run-shows-tilde-config]
38+
dict({
39+
'log': '''
40+
INFO vcspull.cli.add:add.py:<line> Found new repository to import:
41+
INFO vcspull.cli.add:add.py:<line> + pytest-docker (https://github.com/example/tilde)
42+
INFO vcspull.cli.add:add.py:<line> • workspace: ~/study/python/
43+
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
44+
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: skipped (dry-run)
45+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
46+
INFO vcspull.cli.add:add.py:<line> → Would add 'pytest-docker' (git+https://github.com/example/tilde) to ~/.vcspull.yaml under '~/study/python/'.
47+
48+
''',
49+
'test_id': 'path-dry-run-shows-tilde-config',
50+
})
51+
# ---
3752
# name: test_handle_add_command_path_mode[path-explicit-url]
3853
dict({
3954
'log': '''
@@ -42,8 +57,8 @@
4257
INFO vcspull.cli.add:add.py:<line> • workspace: ~/study/python/
4358
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
4459
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: y (auto-confirm)
45-
INFO vcspull.cli.add:add.py:<line> Config file <config> not found. A new one will be created.
46-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/manual/source) to <config> under '~/study/python/'.
60+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
61+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/manual/source) to ~/.vcspull.yaml under '~/study/python/'.
4762

4863
''',
4964
'test_id': 'path-explicit-url',
@@ -56,8 +71,8 @@
5671
INFO vcspull.cli.add:add.py:<line> + project-alias (https://github.com/example/project)
5772
INFO vcspull.cli.add:add.py:<line> • workspace: ~/study/python/
5873
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
59-
INFO vcspull.cli.add:add.py:<line> Config file <config> not found. A new one will be created.
60-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'project-alias' (git+https://github.com/example/project) to <config> under '~/study/python/'.
74+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
75+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'project-alias' (git+https://github.com/example/project) to ~/.vcspull.yaml under '~/study/python/'.
6176

6277
''',
6378
'test_id': 'path-interactive-accept',
@@ -85,7 +100,7 @@
85100
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
86101
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: y (auto-confirm)
87102
WARNING vcspull.cli.add:add.py:<line> • Duplicate workspace root ~/study/python/ appears 2 times; skipping merge because --no-merge was provided.
88-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/example/no-merge) to <config> under '~/study/python/'.
103+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/example/no-merge) to ~/.vcspull.yaml under '~/study/python/'.
89104

90105
''',
91106
'test_id': 'path-no-merge',
@@ -100,10 +115,40 @@
100115
INFO vcspull.cli.add:add.py:<line> • workspace: ~/study/python/
101116
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
102117
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: y (auto-confirm)
103-
INFO vcspull.cli.add:add.py:<line> Config file <config> not found. A new one will be created.
104-
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (<repo_path>) to <config> under '~/study/python/'.
118+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
119+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (<repo_path>) to ~/.vcspull.yaml under '~/study/python/'.
105120

106121
''',
107122
'test_id': 'path-no-remote',
108123
})
109124
# ---
125+
# name: test_handle_add_command_path_mode[path-relative-derives-workspace]
126+
dict({
127+
'log': '''
128+
INFO vcspull.cli.add:add.py:<line> Found new repository to import:
129+
INFO vcspull.cli.add:add.py:<line> + pytest-docker (https://github.com/example/rel)
130+
INFO vcspull.cli.add:add.py:<line> • workspace: ~/study/python/
131+
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
132+
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: y (auto-confirm)
133+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
134+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/example/rel) to ~/.vcspull.yaml under '~/study/python/'.
135+
136+
''',
137+
'test_id': 'path-relative-derives-workspace',
138+
})
139+
# ---
140+
# name: test_handle_add_command_path_mode[path-workspace-override]
141+
dict({
142+
'log': '''
143+
INFO vcspull.cli.add:add.py:<line> Found new repository to import:
144+
INFO vcspull.cli.add:add.py:<line> + pytest-docker (https://github.com/example/workspace)
145+
INFO vcspull.cli.add:add.py:<line> • workspace: ~/custom/
146+
INFO vcspull.cli.add:add.py:<line> ↳ path: ~/study/python/pytest-docker
147+
INFO vcspull.cli.add:add.py:<line> ? Import this repository? [y/N]: y (auto-confirm)
148+
INFO vcspull.cli.add:add.py:<line> Config file ~/.vcspull.yaml not found. A new one will be created.
149+
INFO vcspull.cli.add:add.py:<line> ✓ Successfully added 'pytest-docker' (git+https://github.com/example/workspace) to ~/.vcspull.yaml under '~/custom/'.
150+
151+
''',
152+
'test_id': 'path-workspace-override',
153+
})
154+
# ---

tests/cli/test_add.py

Lines changed: 140 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from __future__ import annotations
44

55
import argparse
6+
import os
67
import logging
78
import re
89
import subprocess
@@ -12,7 +13,7 @@
1213
import pytest
1314
from syrupy.assertion import SnapshotAssertion
1415

15-
from vcspull.cli.add import add_repo, handle_add_command
16+
from vcspull.cli.add import add_repo, create_add_subparser, handle_add_command
1617
from vcspull.util import contract_user_home
1718

1819
if t.TYPE_CHECKING:
@@ -420,6 +421,10 @@ class PathAddFixture(t.NamedTuple):
420421
expected_warning: str | None
421422
merge_duplicates: bool
422423
preexisting_yaml: str | None
424+
use_relative_repo_path: bool
425+
workspace_override: str | None
426+
expected_workspace_label: str
427+
preserve_config_path_in_log: bool
423428

424429

425430
PATH_ADD_FIXTURES: list[PathAddFixture] = [
@@ -436,6 +441,10 @@ class PathAddFixture(t.NamedTuple):
436441
expected_warning=None,
437442
merge_duplicates=True,
438443
preexisting_yaml=None,
444+
use_relative_repo_path=False,
445+
workspace_override=None,
446+
expected_workspace_label="~/study/python/",
447+
preserve_config_path_in_log=False,
439448
),
440449
PathAddFixture(
441450
test_id="path-interactive-accept",
@@ -450,6 +459,10 @@ class PathAddFixture(t.NamedTuple):
450459
expected_warning=None,
451460
merge_duplicates=True,
452461
preexisting_yaml=None,
462+
use_relative_repo_path=False,
463+
workspace_override=None,
464+
expected_workspace_label="~/study/python/",
465+
preserve_config_path_in_log=False,
453466
),
454467
PathAddFixture(
455468
test_id="path-interactive-decline",
@@ -464,6 +477,10 @@ class PathAddFixture(t.NamedTuple):
464477
expected_warning=None,
465478
merge_duplicates=True,
466479
preexisting_yaml=None,
480+
use_relative_repo_path=False,
481+
workspace_override=None,
482+
expected_workspace_label="~/study/python/",
483+
preserve_config_path_in_log=False,
467484
),
468485
PathAddFixture(
469486
test_id="path-no-remote",
@@ -478,6 +495,10 @@ class PathAddFixture(t.NamedTuple):
478495
expected_warning="Unable to determine git remote",
479496
merge_duplicates=True,
480497
preexisting_yaml=None,
498+
use_relative_repo_path=False,
499+
workspace_override=None,
500+
expected_workspace_label="~/study/python/",
501+
preserve_config_path_in_log=False,
481502
),
482503
PathAddFixture(
483504
test_id="path-no-merge",
@@ -499,6 +520,10 @@ class PathAddFixture(t.NamedTuple):
499520
repo2:
500521
repo: git+https://example.com/repo2.git
501522
""",
523+
use_relative_repo_path=False,
524+
workspace_override=None,
525+
expected_workspace_label="~/study/python/",
526+
preserve_config_path_in_log=False,
502527
),
503528
PathAddFixture(
504529
test_id="path-explicit-url",
@@ -513,6 +538,64 @@ class PathAddFixture(t.NamedTuple):
513538
expected_warning=None,
514539
merge_duplicates=True,
515540
preexisting_yaml=None,
541+
use_relative_repo_path=False,
542+
workspace_override=None,
543+
expected_workspace_label="~/study/python/",
544+
preserve_config_path_in_log=False,
545+
),
546+
PathAddFixture(
547+
test_id="path-relative-derives-workspace",
548+
remote_url="https://github.com/example/rel",
549+
explicit_url=None,
550+
assume_yes=True,
551+
prompt_response=None,
552+
dry_run=False,
553+
expected_written=True,
554+
expected_url_kind="remote",
555+
override_name=None,
556+
expected_warning=None,
557+
merge_duplicates=True,
558+
preexisting_yaml=None,
559+
use_relative_repo_path=True,
560+
workspace_override=None,
561+
expected_workspace_label="~/study/python/",
562+
preserve_config_path_in_log=False,
563+
),
564+
PathAddFixture(
565+
test_id="path-workspace-override",
566+
remote_url="https://github.com/example/workspace",
567+
explicit_url=None,
568+
assume_yes=True,
569+
prompt_response=None,
570+
dry_run=False,
571+
expected_written=True,
572+
expected_url_kind="remote",
573+
override_name=None,
574+
expected_warning=None,
575+
merge_duplicates=True,
576+
preexisting_yaml=None,
577+
use_relative_repo_path=False,
578+
workspace_override="~/custom/",
579+
expected_workspace_label="~/custom/",
580+
preserve_config_path_in_log=False,
581+
),
582+
PathAddFixture(
583+
test_id="path-dry-run-shows-tilde-config",
584+
remote_url="https://github.com/example/tilde",
585+
explicit_url=None,
586+
assume_yes=False,
587+
prompt_response=None,
588+
dry_run=True,
589+
expected_written=False,
590+
expected_url_kind="remote",
591+
override_name=None,
592+
expected_warning=None,
593+
merge_duplicates=True,
594+
preexisting_yaml=None,
595+
use_relative_repo_path=False,
596+
workspace_override=None,
597+
expected_workspace_label="~/study/python/",
598+
preserve_config_path_in_log=True,
516599
),
517600
]
518601

@@ -535,6 +618,10 @@ def test_handle_add_command_path_mode(
535618
expected_warning: str | None,
536619
merge_duplicates: bool,
537620
preexisting_yaml: str | None,
621+
use_relative_repo_path: bool,
622+
workspace_override: str | None,
623+
expected_workspace_label: str,
624+
preserve_config_path_in_log: bool,
538625
tmp_path: pathlib.Path,
539626
monkeypatch: MonkeyPatch,
540627
caplog: t.Any,
@@ -557,12 +644,18 @@ def test_handle_add_command_path_mode(
557644
expected_input = prompt_response if prompt_response is not None else "y"
558645
monkeypatch.setattr("builtins.input", lambda _: expected_input)
559646

647+
repo_arg: str
648+
if use_relative_repo_path:
649+
repo_arg = os.fspath(repo_path.relative_to(tmp_path))
650+
else:
651+
repo_arg = str(repo_path)
652+
560653
args = argparse.Namespace(
561-
repo_path=str(repo_path),
654+
repo_path=repo_arg,
562655
url=explicit_url,
563656
override_name=override_name,
564657
config=str(config_file),
565-
workspace_root_path=None,
658+
workspace_root_path=workspace_override,
566659
dry_run=dry_run,
567660
assume_yes=assume_yes,
568661
merge_duplicates=merge_duplicates,
@@ -579,7 +672,11 @@ def test_handle_add_command_path_mode(
579672
normalized_log = log_output.replace(str(config_file), "<config>")
580673
normalized_log = normalized_log.replace(str(repo_path), "<repo_path>")
581674
normalized_log = re.sub(r"add\.py:\d+", "add.py:<line>", normalized_log)
582-
snapshot.assert_match({"test_id": test_id, "log": normalized_log})
675+
if preserve_config_path_in_log:
676+
assert contract_user_home(config_file) in log_output
677+
snapshot.assert_match({"test_id": test_id, "log": normalized_log.replace("<config>", "~/.vcspull.yaml")})
678+
else:
679+
snapshot.assert_match({"test_id": test_id, "log": normalized_log})
583680

584681
if dry_run:
585682
assert "skipped (dry-run)" in log_output
@@ -599,7 +696,7 @@ def test_handle_add_command_path_mode(
599696
with config_file.open(encoding="utf-8") as fh:
600697
config_data = yaml.safe_load(fh)
601698

602-
workspace = "~/study/python/"
699+
workspace = expected_workspace_label
603700
assert workspace in config_data
604701
assert repo_name in config_data[workspace]
605702

@@ -632,4 +729,41 @@ def test_handle_add_command_path_mode(
632729
workspace = config_data.get("~/study/python/")
633730
if workspace is not None:
634731
assert repo_name not in workspace
635-
assert "Aborted import" in log_output
732+
if not dry_run:
733+
assert "Aborted import" in log_output
734+
735+
736+
def test_add_repo_dry_run_contracts_config_path(
737+
tmp_path: pathlib.Path,
738+
monkeypatch: MonkeyPatch,
739+
caplog: t.Any,
740+
) -> None:
741+
"""Dry-run logging shows tilde-shortened config file paths."""
742+
caplog.set_level(logging.INFO)
743+
744+
monkeypatch.setenv("HOME", str(tmp_path))
745+
monkeypatch.chdir(tmp_path)
746+
747+
config_path = tmp_path / ".vcspull.yaml"
748+
749+
add_repo(
750+
name="tilde-repo",
751+
url="git+https://example.com/tilde-repo.git",
752+
config_file_path_str=str(config_path),
753+
path=str(tmp_path / "projects/tilde-repo"),
754+
workspace_root_path=None,
755+
dry_run=True,
756+
)
757+
758+
assert "~/.vcspull.yaml" in caplog.text
759+
760+
761+
def test_add_parser_rejects_extra_positional() -> None:
762+
"""Passing both name and URL should raise a parse error in the new parser."""
763+
parser = argparse.ArgumentParser(prog="vcspull")
764+
subparsers = parser.add_subparsers(dest="command", required=True)
765+
add_parser = subparsers.add_parser("add")
766+
create_add_subparser(add_parser)
767+
768+
with pytest.raises(SystemExit):
769+
parser.parse_args(["add", "name", "https://example.com/repo.git"])

0 commit comments

Comments
 (0)