pydantic model generator for atproto lexicons
1"""integration tests for git provider support."""
2
3import subprocess
4import tempfile
5from pathlib import Path
6
7import pytest
8
9pytestmark = pytest.mark.integration
10
11# repos known to exist on each provider with lexicons
12GIT_PROVIDER_REPOS = [
13 pytest.param(
14 "https://github.com/bluesky-social/atproto.git",
15 id="github-full-url",
16 ),
17 pytest.param(
18 "bluesky-social/atproto",
19 id="github-shorthand",
20 ),
21 pytest.param(
22 "https://tangled.org/zzstoatzz.io/plyr.fm.git",
23 id="tangled-full-url",
24 ),
25 pytest.param(
26 "zzstoatzz.io/plyr.fm",
27 id="tangled-shorthand",
28 ),
29]
30
31
32@pytest.mark.parametrize("source", GIT_PROVIDER_REPOS)
33def test_clone_and_generate(source: str) -> None:
34 """test cloning and generating from various git sources."""
35 with tempfile.TemporaryDirectory() as tmp:
36 output_dir = Path(tmp) / "output"
37 result = subprocess.run(
38 ["uv", "run", "pmgfal", source, "-o", str(output_dir), "--no-cache"],
39 capture_output=True,
40 text=True,
41 timeout=120,
42 )
43 assert result.returncode == 0, f"failed: {result.stderr}"
44 assert output_dir.exists()
45 py_files = list(output_dir.glob("*.py"))
46 assert len(py_files) > 0, "no python files generated"
47
48
49def test_shorthand_fallback_on_nonexistent() -> None:
50 """test that shorthand tries providers in order and fails gracefully."""
51 result = subprocess.run(
52 [
53 "uv",
54 "run",
55 "pmgfal",
56 "nonexistent-owner-12345/nonexistent-repo-67890",
57 "-o",
58 "/tmp/test",
59 ],
60 capture_output=True,
61 text=True,
62 timeout=120,
63 )
64 assert result.returncode == 1
65 output = result.stdout + result.stderr
66 assert "trying github" in output
67 assert "trying tangled" in output
68 assert "could not find" in output
69
70
71def test_invalid_source_not_directory() -> None:
72 """test error on invalid local path."""
73 result = subprocess.run(
74 ["uv", "run", "pmgfal", "/nonexistent/path/to/lexicons", "-o", "/tmp/test"],
75 capture_output=True,
76 text=True,
77 )
78 assert result.returncode == 1
79 output = result.stdout + result.stderr
80 assert "not a directory" in output
81
82
83def test_plyr_fm_generates_valid_models() -> None:
84 """test that plyr.fm lexicons generate importable pydantic models."""
85 with tempfile.TemporaryDirectory() as tmp:
86 output_dir = Path(tmp) / "output"
87
88 # generate models from plyr.fm
89 result = subprocess.run(
90 [
91 "uv",
92 "run",
93 "pmgfal",
94 "zzstoatzz.io/plyr.fm",
95 "-o",
96 str(output_dir),
97 "-p",
98 "fm.plyr",
99 "--no-cache",
100 ],
101 capture_output=True,
102 text=True,
103 timeout=120,
104 )
105 assert result.returncode == 0, (
106 f"generation failed: {result.stdout}{result.stderr}"
107 )
108
109 # find the generated file
110 py_files = list(output_dir.glob("*.py"))
111 assert len(py_files) == 1, f"expected 1 file, got {py_files}"
112
113 # read and validate the generated code
114 content = py_files[0].read_text()
115
116 # should have pydantic imports
117 assert "from pydantic import BaseModel" in content
118
119 # should have fm.plyr models (check for common ones)
120 assert "class FmPlyr" in content
121
122 # compile the generated code to check for syntax errors
123 compile(content, py_files[0].name, "exec")