path-sync 0.2.1__py3-none-any.whl → 0.3.1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
path_sync/models_test.py DELETED
@@ -1,69 +0,0 @@
1
- from path_sync.models import (
2
- Destination,
3
- PathMapping,
4
- PRDefaults,
5
- SrcConfig,
6
- find_repo_root,
7
- resolve_config_path,
8
- )
9
-
10
-
11
- def test_path_mapping_resolved():
12
- m1 = PathMapping(src_path="src/file.py")
13
- assert m1.resolved_dest_path() == "src/file.py"
14
-
15
- m2 = PathMapping(src_path="src/file.py", dest_path="dest/file.py")
16
- assert m2.resolved_dest_path() == "dest/file.py"
17
-
18
-
19
- def test_resolve_config_path(tmp_path):
20
- src_path = resolve_config_path(tmp_path, "sdlc")
21
- assert src_path == tmp_path / ".github" / "sdlc.src.yaml"
22
-
23
-
24
- def test_find_repo_root(tmp_repo):
25
- subdir = tmp_repo / "a" / "b"
26
- subdir.mkdir(parents=True)
27
- found = find_repo_root(subdir)
28
- assert found == tmp_repo
29
-
30
-
31
- def test_src_config_find_destination():
32
- config = SrcConfig(
33
- name="test",
34
- destinations=[
35
- Destination(name="repo1", dest_path_relative="../repo1"),
36
- Destination(name="repo2", dest_path_relative="../repo2"),
37
- ],
38
- )
39
- dest = config.find_destination("repo1")
40
- assert dest.name == "repo1"
41
-
42
-
43
- def test_destination_skip_sections():
44
- dest = Destination(
45
- name="test",
46
- dest_path_relative="../test",
47
- skip_sections={"justfile": ["pkg-ext"], "pyproject.toml": ["coverage"]},
48
- )
49
- assert dest.skip_sections["justfile"] == ["pkg-ext"]
50
- assert dest.skip_sections.get("unknown", []) == []
51
-
52
-
53
- def test_pr_defaults_format_body():
54
- pr = PRDefaults()
55
- body = pr.format_body(
56
- src_repo_url="https://github.com/user/my-repo",
57
- src_sha="abc12345def67890",
58
- sync_log="INFO Wrote: file.py",
59
- dest_name="dest1",
60
- )
61
- assert "[my-repo](https://github.com/user/my-repo)" in body
62
- assert "`abc12345`" in body
63
- assert "INFO Wrote: file.py" in body
64
-
65
-
66
- def test_pr_defaults_format_body_extracts_repo_name():
67
- pr = PRDefaults(body_template="{src_repo_name}")
68
- assert pr.format_body("https://github.com/u/repo", "sha", "", "") == "repo"
69
- assert pr.format_body("https://github.com/u/repo.git", "sha", "", "") == "repo"
@@ -1,128 +0,0 @@
1
- import pytest
2
-
3
- from path_sync import sections
4
-
5
- JUSTFILE_CONTENT = """\
6
- # path-sync copy -n python-template
7
-
8
- # === OK_EDIT ===
9
- # Custom variables
10
-
11
- # === DO_NOT_EDIT: path-sync standard ===
12
- pre-push: lint test
13
- # === OK_EDIT ===
14
-
15
- # === DO_NOT_EDIT: path-sync coverage ===
16
- cov:
17
- uv run pytest --cov
18
- # === OK_EDIT ===
19
- """
20
-
21
-
22
- def test_parse_sections():
23
- result = sections.parse_sections(JUSTFILE_CONTENT)
24
- assert len(result) == 2
25
- assert result[0].id == "standard"
26
- assert result[0].content == "pre-push: lint test"
27
- assert result[1].id == "coverage"
28
- assert "uv run pytest --cov" in result[1].content
29
-
30
-
31
- def test_parse_sections_no_markers():
32
- assert sections.parse_sections("just plain content\nno markers") == []
33
-
34
-
35
- def test_parse_sections_nested_error():
36
- content = """\
37
- # === DO_NOT_EDIT: path-sync outer ===
38
- # === DO_NOT_EDIT: path-sync inner ===
39
- # === OK_EDIT ===
40
- # === OK_EDIT ===
41
- """
42
- with pytest.raises(ValueError, match="Nested section"):
43
- sections.parse_sections(content)
44
-
45
-
46
- def test_parse_sections_unclosed_error():
47
- content = "# === DO_NOT_EDIT: path-sync test ===\nsome content"
48
- with pytest.raises(ValueError, match="Unclosed section"):
49
- sections.parse_sections(content)
50
-
51
-
52
- def test_parse_sections_standalone_ok_edit():
53
- content = "# === OK_EDIT ===\nsome content\n# === OK_EDIT ==="
54
- assert (
55
- sections.parse_sections(content) == []
56
- ) # standalone OK_EDIT is valid, ignored
57
-
58
-
59
- def test_has_sections():
60
- assert sections.has_sections(JUSTFILE_CONTENT)
61
- assert not sections.has_sections("plain content")
62
-
63
-
64
- def test_wrap_in_default_section():
65
- result = sections.wrap_in_default_section("content here")
66
- assert "DO_NOT_EDIT: path-sync default" in result
67
- assert "content here" in result
68
- assert result.endswith("# === OK_EDIT ===")
69
-
70
-
71
- def test_extract_sections():
72
- result = sections.extract_sections(JUSTFILE_CONTENT)
73
- assert result == {
74
- "standard": "pre-push: lint test",
75
- "coverage": "cov:\n uv run pytest --cov",
76
- }
77
-
78
-
79
- def test_replace_sections_updates_content():
80
- dest = """\
81
- # === DO_NOT_EDIT: path-sync standard ===
82
- old content
83
- # === OK_EDIT ==="""
84
- src_sections = {"standard": "new content"}
85
- result = sections.replace_sections(dest, src_sections)
86
- assert "new content" in result
87
- assert "old content" not in result
88
-
89
-
90
- def test_replace_sections_preserves_ok_edit():
91
- dest = """\
92
- # custom header
93
- # === DO_NOT_EDIT: path-sync standard ===
94
- old
95
- # === OK_EDIT ===
96
- # my custom stuff"""
97
- result = sections.replace_sections(dest, {"standard": "new"})
98
- assert "# custom header" in result
99
- assert "# my custom stuff" in result
100
-
101
-
102
- def test_replace_sections_skip():
103
- dest = """\
104
- # === DO_NOT_EDIT: path-sync standard ===
105
- keep this
106
- # === OK_EDIT ==="""
107
- result = sections.replace_sections(
108
- dest, {"standard": "replaced"}, skip_sections=["standard"]
109
- )
110
- assert "keep this" in result # skipped sections preserve dest content
111
- assert "replaced" not in result
112
-
113
-
114
- def test_replace_sections_adds_new():
115
- dest = "# plain file"
116
- result = sections.replace_sections(dest, {"newid": "new content"})
117
- assert "DO_NOT_EDIT: path-sync newid" in result
118
- assert "new content" in result
119
-
120
-
121
- def test_replace_sections_keeps_dest_only():
122
- dest = """\
123
- # === DO_NOT_EDIT: path-sync custom ===
124
- my custom section
125
- # === OK_EDIT ==="""
126
- result = sections.replace_sections(dest, {})
127
- assert "my custom section" in result # dest-only sections preserved
128
- assert "DO_NOT_EDIT: path-sync custom" in result
@@ -1,114 +0,0 @@
1
- from git import Repo
2
-
3
- from path_sync import validation
4
- from path_sync.header import get_header_line
5
-
6
- HEADER = get_header_line(".py", "test-config")
7
-
8
-
9
- def _setup_baseline(repo_path, filename: str, content: str) -> None:
10
- """Commit content as baseline on main and create origin/main ref."""
11
- repo = Repo(repo_path)
12
- file_path = repo_path / filename
13
- file_path.write_text(content)
14
- repo.index.add([filename])
15
- repo.index.commit("baseline")
16
- repo.create_head("origin/main", repo.head.commit)
17
-
18
-
19
- def test_modify_ok_edit_passes(tmp_repo):
20
- baseline = f"""{HEADER}
21
- # === OK_EDIT ===
22
- user content
23
- # === DO_NOT_EDIT: path-sync standard ===
24
- protected
25
- # === OK_EDIT ===
26
- """
27
- _setup_baseline(tmp_repo, "test.py", baseline)
28
-
29
- current = f"""{HEADER}
30
- # === OK_EDIT ===
31
- modified user content
32
- # === DO_NOT_EDIT: path-sync standard ===
33
- protected
34
- # === OK_EDIT ===
35
- """
36
- (tmp_repo / "test.py").write_text(current)
37
-
38
- result = validation.validate_no_unauthorized_changes(tmp_repo, "main")
39
- assert result == []
40
-
41
-
42
- def test_modify_do_not_edit_fails(tmp_repo):
43
- baseline = f"""{HEADER}
44
- # === DO_NOT_EDIT: path-sync standard ===
45
- protected content
46
- # === OK_EDIT ===
47
- """
48
- _setup_baseline(tmp_repo, "test.py", baseline)
49
-
50
- current = f"""{HEADER}
51
- # === DO_NOT_EDIT: path-sync standard ===
52
- MODIFIED protected content
53
- # === OK_EDIT ===
54
- """
55
- (tmp_repo / "test.py").write_text(current)
56
-
57
- result = validation.validate_no_unauthorized_changes(tmp_repo, "main")
58
- assert result == ["test.py:standard"]
59
-
60
-
61
- def test_skip_section_passes(tmp_repo):
62
- baseline = f"""{HEADER}
63
- # === DO_NOT_EDIT: path-sync coverage ===
64
- protected
65
- # === OK_EDIT ===
66
- """
67
- _setup_baseline(tmp_repo, "test.py", baseline)
68
-
69
- current = f"""{HEADER}
70
- # === DO_NOT_EDIT: path-sync coverage ===
71
- MODIFIED
72
- # === OK_EDIT ===
73
- """
74
- (tmp_repo / "test.py").write_text(current)
75
-
76
- result = validation.validate_no_unauthorized_changes(
77
- tmp_repo, "main", skip_sections={"test.py": {"coverage"}}
78
- )
79
- assert result == []
80
-
81
-
82
- def test_section_removed_fails(tmp_repo):
83
- baseline = f"""{HEADER}
84
- # === DO_NOT_EDIT: path-sync standard ===
85
- protected
86
- # === OK_EDIT ===
87
- """
88
- _setup_baseline(tmp_repo, "test.py", baseline)
89
-
90
- current = f"""{HEADER}
91
- # no sections anymore
92
- """
93
- (tmp_repo / "test.py").write_text(current)
94
-
95
- result = validation.validate_no_unauthorized_changes(tmp_repo, "main")
96
- assert result == ["test.py:standard"]
97
-
98
-
99
- def test_no_sections_full_file_comparison(tmp_repo):
100
- baseline = f"{HEADER}\noriginal content\n"
101
- _setup_baseline(tmp_repo, "test.py", baseline)
102
-
103
- (tmp_repo / "test.py").write_text(f"{HEADER}\nmodified content\n")
104
-
105
- result = validation.validate_no_unauthorized_changes(tmp_repo, "main")
106
- assert result == ["test.py"]
107
-
108
-
109
- def test_parse_skip_sections():
110
- result = validation.parse_skip_sections("justfile:coverage,pyproject.toml:default")
111
- assert result == {"justfile": {"coverage"}, "pyproject.toml": {"default"}}
112
-
113
- result = validation.parse_skip_sections("path:a,path:b")
114
- assert result == {"path": {"a", "b"}}
@@ -1,23 +0,0 @@
1
- path_sync/__init__.py,sha256=fdUhqFKjflvMkVbUuA_RMMv4am652KWK8N3IVQzj6ok,63
2
- path_sync/__main__.py,sha256=Hh0Na0BlS0EwBAerhWD4mnaB-oMo6jufFVQBuzNZk3g,296
3
- path_sync/cmd_boot.py,sha256=hYSrMF9QHVXX5feO2UE3lFSJvV38tEsJaNl0sjU2gbw,2896
4
- path_sync/cmd_copy.py,sha256=RcIv3s6HDiOHU3Z1Q5HYzVIOGDc_4cYqYGyj9Gpnjlk,15701
5
- path_sync/cmd_copy_test.py,sha256=dcpRaFAmXuUavIvtA59_In4Fa58FKRklNcYmeGsEuVA,5049
6
- path_sync/cmd_validate.py,sha256=wcMo_JR2jxFtqaNQt1mk_Mnwy_jPSpFHCXFuzNOphEU,1624
7
- path_sync/conftest.py,sha256=-iu7W2Bh2aRzTR1x3aMnkWkrVnDMp6ezdh1AFgNcYUE,343
8
- path_sync/file_utils.py,sha256=5C33qzKFQdwChi5YwUWBujj126t0P6dbGSU_5hWExpE,194
9
- path_sync/git_ops.py,sha256=1ixXKtseZAWAJP9uJcAb78IGQOmRekriv-n_T1nx0mI,5945
10
- path_sync/header.py,sha256=2YSCj7ainj5TPFINBHn8Uc2ECu591pZfd7NSOZMX5XA,2293
11
- path_sync/header_test.py,sha256=R9jwOulSKR70HrFoxYBXUx3DGjdzfB2tZNu9sjL_3rA,1401
12
- path_sync/models.py,sha256=GR2J8PRtAORltvnL73In6zjdNbkELtUomHdwPHfsWwU,3832
13
- path_sync/models_test.py,sha256=m9kZbl3CGABrg58owNLx4Aiv5LPvKz0t61o0336J5x8,2052
14
- path_sync/sections.py,sha256=jzzzt2e-umjY0Ab-d7W-29y7aVVBtf_vEX_abFEdZdo,3681
15
- path_sync/sections_test.py,sha256=lsApYMe1BqGL7T0sYhbs28VCrDm9dmkq0G3JAvGOlPI,3546
16
- path_sync/typer_app.py,sha256=lEGMRXql3Se3VbmwAohvpUaL2cbY-RwhPUq8kL7bPbc,177
17
- path_sync/validation.py,sha256=RP9SWd69mGvB4MxGx4RFJiyIQ-X482Y5h7G7VYAOxiE,2766
18
- path_sync/validation_test.py,sha256=NdK1JHAtjGIm7m7uVm0fM751ttJhKXrS21gt1eEDhP0,3071
19
- path_sync/yaml_utils.py,sha256=yj6Bl54EltjLEcVKaiA5Ahb9byT6OUMh0xIEzTsrvnQ,498
20
- path_sync-0.2.1.dist-info/METADATA,sha256=4-DnsGErtoA24qkvnBMXK4HuKm7YR-ZhX4FZ5h2zXXQ,7785
21
- path_sync-0.2.1.dist-info/WHEEL,sha256=WLgqFyCfm_KASv4WHyYy0P3pM_m7J5L9k2skdKLirC8,87
22
- path_sync-0.2.1.dist-info/entry_points.txt,sha256=jTsL0c-9gP-4_Jt3EPgihtpLcwQR0AFAf1AUpD50AlI,54
23
- path_sync-0.2.1.dist-info/RECORD,,
File without changes
File without changes
File without changes