/
/
/
1"""Tests for the manifest back-sync helper used on Dependabot PRs."""
2
3import json
4from pathlib import Path
5
6from scripts.sync_manifest_from_requirements import (
7 _package_name,
8 _parse_requirements_all,
9 _sync_manifest,
10)
11
12
13def _write_manifest(tmp_path: Path, requirements: list[str]) -> Path:
14 """Write a minimal manifest.json with the given requirements and return its path."""
15 manifest = {
16 "type": "player",
17 "domain": "demo",
18 "name": "Demo",
19 "description": "A demo provider.",
20 "codeowners": ["@octocat"],
21 "requirements": requirements,
22 }
23 path = tmp_path / "manifest.json"
24 path.write_text(json.dumps(manifest, indent=2) + "\n", encoding="utf-8")
25 return path
26
27
28def test_package_name_handles_casing_extras_and_separators() -> None:
29 """Distribution names are normalized; git/URL specs return None."""
30 assert _package_name("PyChromecast==14.0.10") == "pychromecast"
31 assert _package_name("gql[all]==4.0.0") == "gql"
32 assert _package_name("aiohttp_fast_zlib==0.3.0") == "aiohttp-fast-zlib"
33 # PEP 503: dot/underscore/dash all collapse to a single dash.
34 assert _package_name("niconico.py-ma==1.0.0") == "niconico-py-ma"
35 assert _package_name("git+https://example.com/foo.git") is None
36
37
38def test_parse_requirements_all_skips_directives_and_synthesized_lines() -> None:
39 """Synthesized platform_machine entries are ignored, but other PEP 508 markers are kept."""
40 content = (
41 "# WARNING: this file is autogenerated!\n\n"
42 "--extra-index-url https://download.pytorch.org/whl/cpu\n\n"
43 "srptools>=1.0.0\n"
44 "torch==2.3.0+cpu; sys_platform == 'linux' and platform_machine == 'x86_64'\n"
45 "torch==2.3.0; sys_platform != 'linux' or platform_machine != 'x86_64'\n"
46 "foo==1.0; python_version < '3.13'\n"
47 )
48 wanted = _parse_requirements_all(content)
49 assert wanted == {
50 "srptools": "srptools>=1.0.0",
51 "foo": "foo==1.0; python_version < '3.13'",
52 }
53
54
55def test_sync_updates_matching_requirement(tmp_path: Path) -> None:
56 """A bumped pin is written into the manifest, preserving the other requirements verbatim."""
57 manifest = _write_manifest(tmp_path, ["srptools>=1.0.0", "PyChromecast==14.0.10"])
58 changes = _sync_manifest(manifest, {"srptools": "srptools>=1.0.1"})
59 assert any("srptools>=1.0.0 -> srptools>=1.0.1" in change for change in changes)
60 requirements = json.loads(manifest.read_text())["requirements"]
61 assert requirements == ["srptools>=1.0.1", "PyChromecast==14.0.10"]
62
63
64def test_sync_is_noop_when_already_in_sync(tmp_path: Path) -> None:
65 """A manifest already matching requirements_all.txt is left untouched."""
66 manifest = _write_manifest(tmp_path, ["srptools>=1.0.1"])
67 before = manifest.read_text()
68 assert _sync_manifest(manifest, {"srptools": "srptools>=1.0.1"}) == []
69 assert manifest.read_text() == before
70