Skip to content

Commit 892c2e0

Browse files
bokelleyclaude
andcommitted
fix: update all tests for AdCP v2.4.0 schema changes
Updated all failing tests to work with the new schema structure generated by datamodel-code-generator. Key changes include: - Updated discriminated union tests to use numbered type variants (ActivateSignalResponse1/2, Destination1/2, etc.) - Fixed field names: activation_keys → removed, type → asset_type, property_type - Updated asset handling to use Pydantic models (ImageAsset, UrlAsset, etc.) instead of plain strings - Fixed PreviewCreativeRequest to include request_type discriminator - Updated preview response structures with proper renders and input fields - Fixed RootModel access patterns (using .root for PropertyId, etc.) - Implemented previously skipped integration tests for fetch_adagents and verify_agent_for_property with proper httpx mocking All 199 tests now passing with 0 skipped. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 8233bec commit 892c2e0

File tree

111 files changed

+5153
-14963
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

111 files changed

+5153
-14963
lines changed

scripts/consolidate_exports.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ def extract_exports_from_module(module_path: Path) -> set[str]:
2626

2727
exports = set()
2828

29-
for node in ast.walk(tree):
29+
# Only look at module-level nodes (not inside classes)
30+
for node in tree.body:
3031
# Class definitions
3132
if isinstance(node, ast.ClassDef):
3233
if not node.name.startswith("_"):

scripts/generate_models_simple.py

Lines changed: 0 additions & 890 deletions
This file was deleted.

scripts/generate_types.py

Lines changed: 223 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,223 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Generate Python types from AdCP JSON schemas using datamodel-code-generator.
4+
5+
This script processes schemas from the organized subdirectory structure and
6+
generates Pydantic v2 models with discriminated union support.
7+
"""
8+
9+
from __future__ import annotations
10+
11+
import json
12+
import re
13+
import shutil
14+
import subprocess
15+
import sys
16+
from pathlib import Path
17+
18+
# Paths
19+
REPO_ROOT = Path(__file__).parent.parent
20+
SCHEMAS_DIR = REPO_ROOT / "schemas" / "cache" / "1.0.0"
21+
OUTPUT_DIR = REPO_ROOT / "src" / "adcp" / "types" / "generated_poc"
22+
TEMP_DIR = REPO_ROOT / ".schema_temp"
23+
24+
25+
def rewrite_refs(obj: dict | list | str, current_file: str) -> dict | list | str:
26+
"""
27+
Recursively rewrite absolute $ref paths to relative paths.
28+
29+
Converts paths like "/schemas/v1/core/error.json" to "./error.json"
30+
"""
31+
if isinstance(obj, dict):
32+
result = {}
33+
for key, value in obj.items():
34+
if key == "$ref" and isinstance(value, str):
35+
# Convert absolute path to relative
36+
if value.startswith("/schemas/v1/"):
37+
# Extract just the filename
38+
filename = value.split("/")[-1]
39+
result[key] = f"./{filename}"
40+
else:
41+
result[key] = value
42+
else:
43+
result[key] = rewrite_refs(value, current_file)
44+
return result
45+
elif isinstance(obj, list):
46+
return [rewrite_refs(item, current_file) for item in obj]
47+
else:
48+
return obj
49+
50+
51+
def flatten_schemas():
52+
"""
53+
Flatten schema directory structure and rewrite $ref paths.
54+
55+
The tool has issues with nested $ref paths, so we:
56+
1. Copy only flat schemas
57+
2. Rewrite absolute $ref paths to relative paths
58+
"""
59+
print("Preparing schemas...")
60+
61+
# Clean temp directory
62+
if TEMP_DIR.exists():
63+
shutil.rmtree(TEMP_DIR)
64+
TEMP_DIR.mkdir()
65+
66+
# Copy and rewrite flat JSON schemas (not in subdirectories)
67+
for schema_file in SCHEMAS_DIR.glob("*.json"):
68+
if schema_file.is_file() and schema_file.name != "index.json":
69+
# Load schema
70+
with open(schema_file) as f:
71+
schema = json.load(f)
72+
73+
# Rewrite $ref paths
74+
schema = rewrite_refs(schema, schema_file.name)
75+
76+
# Write to temp directory
77+
output_file = TEMP_DIR / schema_file.name
78+
with open(output_file, "w") as f:
79+
json.dump(schema, f, indent=2)
80+
81+
print(f" {schema_file.name}")
82+
83+
count = len(list(TEMP_DIR.glob("*.json")))
84+
print(f"\n Prepared {count} schema files\n")
85+
return TEMP_DIR
86+
87+
88+
def fix_forward_references():
89+
"""Fix broken forward references in generated files.
90+
91+
datamodel-code-generator sometimes generates incorrect forward references like:
92+
from . import brand_manifest as brand_manifest_1
93+
field: brand_manifest.BrandManifest # Should be brand_manifest_1.BrandManifest
94+
95+
This function fixes those references.
96+
"""
97+
print("Fixing forward references...")
98+
99+
fixes_made = 0
100+
for py_file in OUTPUT_DIR.glob("*.py"):
101+
if py_file.name == "__init__.py":
102+
continue
103+
104+
with open(py_file) as f:
105+
content = f.read()
106+
107+
# Find imports like: from . import foo as foo_1
108+
import_pattern = r"from \. import (\w+) as (\w+_\d+)"
109+
imports = re.findall(import_pattern, content)
110+
111+
# For each aliased import, fix references
112+
modified = False
113+
for original, alias in imports:
114+
# Replace module_name.ClassName with alias.ClassName
115+
pattern = rf"\b{original}\.(\w+)"
116+
replacement = rf"{alias}.\1"
117+
new_content = re.sub(pattern, replacement, content)
118+
if new_content != content:
119+
content = new_content
120+
modified = True
121+
fixes_made += 1
122+
123+
if modified:
124+
with open(py_file, "w") as f:
125+
f.write(content)
126+
print(f" Fixed: {py_file.name}")
127+
128+
if fixes_made > 0:
129+
print(f"\n Fixed {fixes_made} forward reference issue(s)\n")
130+
else:
131+
print(" No fixes needed\n")
132+
133+
134+
def generate_types(input_dir: Path):
135+
"""Generate types using datamodel-code-generator."""
136+
print(f"Generating types from {input_dir}...")
137+
138+
args = [
139+
sys.executable, # Use same Python as running this script
140+
"-m",
141+
"datamodel_code_generator",
142+
"--input",
143+
str(input_dir),
144+
"--input-file-type",
145+
"jsonschema",
146+
"--output",
147+
str(OUTPUT_DIR),
148+
"--output-model-type",
149+
"pydantic_v2.BaseModel",
150+
"--base-class",
151+
"adcp.types.base.AdCPBaseModel",
152+
"--field-constraints",
153+
"--use-standard-collections",
154+
"--use-union-operator",
155+
"--target-python-version",
156+
"3.10",
157+
"--use-annotated",
158+
"--collapse-root-models",
159+
"--reuse-model",
160+
"--set-default-enum-member",
161+
"--enum-field-as-literal",
162+
"one",
163+
]
164+
165+
result = subprocess.run(
166+
args,
167+
capture_output=True,
168+
text=True,
169+
)
170+
171+
if result.stdout:
172+
print(result.stdout)
173+
174+
if result.returncode != 0:
175+
print(f"\n✗ Generation failed:", file=sys.stderr)
176+
print(result.stderr, file=sys.stderr)
177+
return False
178+
179+
return True
180+
181+
182+
def main():
183+
"""Generate types from schemas."""
184+
print("=" * 70)
185+
print("AdCP Python Type Generation")
186+
print("=" * 70)
187+
print(f"\nInput: {SCHEMAS_DIR}")
188+
print(f"Output: {OUTPUT_DIR}\n")
189+
190+
try:
191+
# Ensure output directory exists
192+
OUTPUT_DIR.mkdir(parents=True, exist_ok=True)
193+
194+
# Flatten schemas
195+
temp_schemas = flatten_schemas()
196+
197+
# Generate types
198+
if not generate_types(temp_schemas):
199+
return 1
200+
201+
# Fix forward references
202+
fix_forward_references()
203+
204+
# Clean up temp directory
205+
shutil.rmtree(temp_schemas)
206+
207+
# Count generated files
208+
py_files = list(OUTPUT_DIR.glob("*.py"))
209+
print(f"\n✓ Successfully generated types")
210+
print(f" Output: {OUTPUT_DIR}")
211+
print(f" Files: {len(py_files)}")
212+
213+
return 0
214+
215+
except Exception as e:
216+
print(f"\n✗ Error: {e}", file=sys.stderr)
217+
import traceback
218+
traceback.print_exc()
219+
return 1
220+
221+
222+
if __name__ == "__main__":
223+
sys.exit(main())

0 commit comments

Comments
 (0)