diff --git a/src/core/schema_adapters.py b/src/core/schema_adapters.py index f02a87e3a..8707a6d32 100644 --- a/src/core/schema_adapters.py +++ b/src/core/schema_adapters.py @@ -425,6 +425,7 @@ class ListAuthorizedPropertiesResponse(AdCPBaseModel): model_config = {"arbitrary_types_allowed": True} # Fields from AdCP spec v2.4 + # Per /schemas/v1/media-buy/list-authorized-properties-response.json publisher_domains: list[str] = Field( ..., description="Publisher domains this agent is authorized to represent", min_length=1 ) diff --git a/src/core/schemas.py b/src/core/schemas.py index 458d05fa0..196a4330f 100644 --- a/src/core/schemas.py +++ b/src/core/schemas.py @@ -3412,9 +3412,6 @@ class ListAuthorizedPropertiesResponse(AdCPBaseModel): """ publisher_domains: list[str] = Field(..., description="Publisher domains this agent is authorized to represent") - tags: dict[str, PropertyTagMetadata] = Field( - default_factory=dict, description="Metadata for each tag referenced by properties" - ) primary_channels: list[str] | None = Field( None, description="Primary advertising channels in this portfolio (helps buyers filter relevance)" ) diff --git a/tests/unit/test_a2a_response_attribute_access.py b/tests/unit/test_a2a_response_attribute_access.py index 28567cea8..3de4e9aa9 100644 --- a/tests/unit/test_a2a_response_attribute_access.py +++ b/tests/unit/test_a2a_response_attribute_access.py @@ -77,14 +77,18 @@ def test_list_creative_formats_response_attribute_access(self): assert isinstance(response.formats, list) def test_list_authorized_properties_response_attribute_access(self): - """Verify ListAuthorizedPropertiesResponse has expected flat structure.""" - response = ListAuthorizedPropertiesResponse(publisher_domains=["example.com"], tags={}) + """Verify ListAuthorizedPropertiesResponse has expected flat structure per AdCP spec.""" + # Per /schemas/v1/media-buy/list-authorized-properties-response.json + response = ListAuthorizedPropertiesResponse( + publisher_domains=["example.com"], + primary_channels=["display"], + ) - # Verify expected attributes exist + # Verify expected attributes exist (per AdCP v2.4 spec) assert hasattr(response, "publisher_domains") - assert hasattr(response, "tags") + assert hasattr(response, "primary_channels") assert isinstance(response.publisher_domains, list) - assert isinstance(response.tags, dict) + assert isinstance(response.primary_channels, list) def test_a2a_list_creatives_handler_attribute_extraction(self): """Verify A2A handler can extract attributes correctly from response. diff --git a/tests/unit/test_adcp_contract.py b/tests/unit/test_adcp_contract.py index 4dfe12341..5670af863 100644 --- a/tests/unit/test_adcp_contract.py +++ b/tests/unit/test_adcp_contract.py @@ -1929,11 +1929,12 @@ def test_list_authorized_properties_request_adcp_compliance(self): def test_list_authorized_properties_response_adcp_compliance(self): """Test that ListAuthorizedPropertiesResponse complies with AdCP v2.4 list-authorized-properties-response schema.""" # Create response with required fields only (per AdCP spec, optional fields should be omitted if not set) - tag_metadata = PropertyTagMetadata(name="Premium Content", description="Premium content tag") + # Per /schemas/v1/media-buy/list-authorized-properties-response.json, only these fields are spec-compliant: + # - publisher_domains (required) + # - primary_channels, primary_countries, portfolio_description, advertising_policies, last_updated, errors (optional) response = ListAuthorizedPropertiesResponse( publisher_domains=["example.com"], - tags={"premium_content": tag_metadata}, - # errors omitted - per AdCP spec, optional fields with None/empty values should be omitted + # All optional fields omitted - per AdCP spec, optional fields with None/empty values should be omitted ) # Test AdCP-compliant response @@ -1948,10 +1949,6 @@ def test_list_authorized_properties_response_adcp_compliance(self): # Verify publisher_domains is array assert isinstance(adcp_response["publisher_domains"], list) - # Verify tags is object when present - assert "tags" in adcp_response, "tags was set, should be present" - assert isinstance(adcp_response["tags"], dict) - # Verify optional fields with None values are omitted per AdCP spec assert "errors" not in adcp_response, "errors with None/empty value should be omitted" assert "primary_channels" not in adcp_response, "primary_channels with None value should be omitted" diff --git a/tests/unit/test_authorized_properties.py b/tests/unit/test_authorized_properties.py index af279b036..880acc744 100644 --- a/tests/unit/test_authorized_properties.py +++ b/tests/unit/test_authorized_properties.py @@ -172,20 +172,22 @@ def test_response_with_minimal_fields(self): response = ListAuthorizedPropertiesResponse(publisher_domains=["example.com"]) assert response.publisher_domains == ["example.com"] - assert response.tags == {} assert response.errors is None def test_response_with_all_fields(self): - """Test response with all fields (per AdCP v2.4 spec).""" - tag_metadata = PropertyTagMetadata(name="Premium Content", description="Premium content tag") + """Test response with all optional fields (per AdCP v2.4 spec).""" response = ListAuthorizedPropertiesResponse( publisher_domains=["example.com"], - tags={"premium_content": tag_metadata}, + primary_channels=["display", "video"], + primary_countries=["US", "GB"], + portfolio_description="Premium content portfolio", + advertising_policies="No tobacco or alcohol ads", + last_updated="2025-10-27T12:00:00Z", errors=[{"code": "WARNING", "message": "Test warning"}], ) assert len(response.publisher_domains) == 1 - assert "premium_content" in response.tags + assert response.primary_channels == ["display", "video"] assert len(response.errors) == 1 def test_response_model_dump_omits_none_values(self): @@ -201,10 +203,10 @@ def test_response_model_dump_omits_none_values(self): def test_response_adcp_compliance(self): """Test that ListAuthorizedPropertiesResponse complies with AdCP v2.4 schema.""" # Create response with required fields only (no optional fields set) + # Per /schemas/v1/media-buy/list-authorized-properties-response.json response = ListAuthorizedPropertiesResponse( publisher_domains=["example.com"], - tags={"test": PropertyTagMetadata(name="Test", description="Test tag")}, - # errors not set - should be omitted per AdCP spec + # All optional fields omitted - should be excluded from model_dump per AdCP spec ) # Test AdCP-compliant response @@ -224,8 +226,8 @@ def test_response_adcp_compliance(self): assert "advertising_policies" not in adcp_response, "advertising_policies with None value should be omitted" assert "last_updated" not in adcp_response, "last_updated with None value should be omitted" - # Verify field count (only publisher_domains and tags should be present) - assert len(adcp_response) == 2, f"Expected 2 fields, got {len(adcp_response)}: {list(adcp_response.keys())}" + # Verify field count (only publisher_domains should be present) + assert len(adcp_response) == 1, f"Expected 1 field, got {len(adcp_response)}: {list(adcp_response.keys())}" # Test with optional fields explicitly set to non-None values response_with_optionals = ListAuthorizedPropertiesResponse(