adcp 2.12.2__tar.gz → 2.13.0__tar.gz
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.
- {adcp-2.12.2/src/adcp.egg-info → adcp-2.13.0}/PKG-INFO +1 -1
- {adcp-2.12.2 → adcp-2.13.0}/pyproject.toml +1 -1
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/__init__.py +1 -1
- adcp-2.13.0/src/adcp/types/base.py +219 -0
- {adcp-2.12.2 → adcp-2.13.0/src/adcp.egg-info}/PKG-INFO +1 -1
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp.egg-info/SOURCES.txt +1 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_public_api.py +50 -0
- adcp-2.13.0/tests/test_response_str.py +350 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_simple_api.py +38 -0
- adcp-2.12.2/src/adcp/types/base.py +0 -26
- {adcp-2.12.2 → adcp-2.13.0}/LICENSE +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/MANIFEST.in +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/README.md +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/setup.cfg +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/ADCP_VERSION +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/__main__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/adagents.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/client.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/config.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/exceptions.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/protocols/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/protocols/a2a.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/protocols/base.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/protocols/mcp.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/py.typed +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/simple.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/testing/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/testing/test_helpers.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/_generated.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/aliases.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/core.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/adagents.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/activation_key.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/audio_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/css_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/daast_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/html_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/image_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/javascript_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/text_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/url_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/vast_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/video_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/assets/webhook_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/brand_manifest.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/context.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/creative_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/creative_assignment.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/creative_filters.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/creative_manifest.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/creative_policy.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/delivery_metrics.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/deployment.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/destination.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/dimensions.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/error.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/ext.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/format.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/format_id.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/frequency_cap.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/measurement.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/media_buy.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/package.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/performance_feedback.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/placement.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/product.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/product_filters.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/promoted_offerings.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/promoted_products.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/property.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/property_id.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/property_tag.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/protocol_envelope.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/publisher_property_selector.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/push_notification_config.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/reporting_capabilities.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/signal_filters.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/sub_asset.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/targeting.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/core/webhook_payload.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/creative/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/creative/list_creative_formats_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/creative/list_creative_formats_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/creative/preview_creative_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/creative/preview_creative_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/creative/preview_render.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/adcp_domain.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/asset_content_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/auth_scheme.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/available_metric.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/channels.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/co_branding_requirement.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/creative_action.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/creative_agent_capability.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/creative_sort_field.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/creative_status.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/daast_tracking_event.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/daast_version.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/delivery_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/dimension_unit.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/feed_format.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/feedback_source.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/format_category.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/format_id_parameter.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/frequency_cap_scope.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/history_entry_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/http_method.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/identifier_types.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/javascript_module_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/landing_page_requirement.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/markdown_flavor.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/media_buy_status.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/metric_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/notification_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/pacing.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/preview_output_format.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/pricing_model.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/property_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/publisher_identifier_types.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/reporting_frequency.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/signal_catalog_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/sort_direction.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/standard_format_ids.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/task_status.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/task_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/update_frequency.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/url_asset_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/validation_mode.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/vast_tracking_event.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/vast_version.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/webhook_response_type.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/enums/webhook_security_method.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/build_creative_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/build_creative_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/create_media_buy_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/create_media_buy_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/get_media_buy_delivery_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/get_products_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/get_products_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/list_authorized_properties_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/list_authorized_properties_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/list_creative_formats_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/list_creative_formats_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/list_creatives_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/list_creatives_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/package_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/provide_performance_feedback_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/provide_performance_feedback_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/sync_creatives_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/sync_creatives_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/update_media_buy_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/media_buy/update_media_buy_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/cpc_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/cpcv_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/cpm_auction_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/cpm_fixed_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/cpp_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/cpv_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/flat_rate_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/vcpm_auction_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/pricing_options/vcpm_fixed_option.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/protocols/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/protocols/adcp_extension.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/signals/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/signals/activate_signal_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/signals/activate_signal_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/signals/get_signals_request.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/types/generated_poc/signals/get_signals_response.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/utils/__init__.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/utils/operation_id.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/utils/preview_cache.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/utils/response_parser.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp/validation.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp.egg-info/dependency_links.txt +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp.egg-info/entry_points.txt +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp.egg-info/requires.txt +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/src/adcp.egg-info/top_level.txt +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_adagents.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_cli.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_client.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_code_generation.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_discriminated_unions.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_format_id_validation.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_helpers.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_preview_html.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_protocols.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_response_parser.py +0 -0
- {adcp-2.12.2 → adcp-2.13.0}/tests/test_type_aliases.py +0 -0
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "adcp"
|
|
7
|
-
version = "2.
|
|
7
|
+
version = "2.13.0"
|
|
8
8
|
description = "Official Python client for the Ad Context Protocol (AdCP)"
|
|
9
9
|
authors = [
|
|
10
10
|
{name = "AdCP Community", email = "maintainers@adcontextprotocol.org"}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
"""Base model for AdCP types with spec-compliant serialization."""
|
|
4
|
+
|
|
5
|
+
from collections.abc import Callable
|
|
6
|
+
from typing import Any
|
|
7
|
+
|
|
8
|
+
from pydantic import BaseModel
|
|
9
|
+
|
|
10
|
+
# Type alias to shorten long type annotations
|
|
11
|
+
MessageFormatter = Callable[[Any], str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _pluralize(count: int, singular: str, plural: str | None = None) -> str:
|
|
15
|
+
"""Return singular or plural form based on count."""
|
|
16
|
+
if count == 1:
|
|
17
|
+
return singular
|
|
18
|
+
return plural if plural else f"{singular}s"
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
# Registry of human-readable message formatters for response types.
|
|
22
|
+
# Key is the class name, value is a callable that takes the instance and returns a message.
|
|
23
|
+
_RESPONSE_MESSAGE_REGISTRY: dict[str, MessageFormatter] = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _register_response_message(cls_name: str) -> Callable[[MessageFormatter], MessageFormatter]:
|
|
27
|
+
"""Decorator to register a message formatter for a response type."""
|
|
28
|
+
|
|
29
|
+
def decorator(func: MessageFormatter) -> MessageFormatter:
|
|
30
|
+
_RESPONSE_MESSAGE_REGISTRY[cls_name] = func
|
|
31
|
+
return func
|
|
32
|
+
|
|
33
|
+
return decorator
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# Response message formatters
|
|
37
|
+
@_register_response_message("GetProductsResponse")
|
|
38
|
+
def _get_products_message(self: Any) -> str:
|
|
39
|
+
products = getattr(self, "products", None)
|
|
40
|
+
if products is None or len(products) == 0:
|
|
41
|
+
return "No products matched your requirements."
|
|
42
|
+
count = len(products)
|
|
43
|
+
return f"Found {count} {_pluralize(count, 'product')} matching your requirements."
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
@_register_response_message("ListCreativeFormatsResponse")
|
|
47
|
+
def _list_creative_formats_message(self: Any) -> str:
|
|
48
|
+
formats = getattr(self, "formats", None)
|
|
49
|
+
if formats is None:
|
|
50
|
+
return "No creative formats found."
|
|
51
|
+
count = len(formats)
|
|
52
|
+
return f"Found {count} supported creative {_pluralize(count, 'format')}."
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
@_register_response_message("GetSignalsResponse")
|
|
56
|
+
def _get_signals_message(self: Any) -> str:
|
|
57
|
+
signals = getattr(self, "signals", None)
|
|
58
|
+
if signals is None:
|
|
59
|
+
return "No signals found."
|
|
60
|
+
count = len(signals)
|
|
61
|
+
return f"Found {count} {_pluralize(count, 'signal')} available for targeting."
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@_register_response_message("ListAuthorizedPropertiesResponse")
|
|
65
|
+
def _list_authorized_properties_message(self: Any) -> str:
|
|
66
|
+
domains = getattr(self, "publisher_domains", None)
|
|
67
|
+
if domains is None:
|
|
68
|
+
return "No authorized properties found."
|
|
69
|
+
count = len(domains)
|
|
70
|
+
return f"Authorized to represent {count} publisher {_pluralize(count, 'domain')}."
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
@_register_response_message("ListCreativesResponse")
|
|
74
|
+
def _list_creatives_message(self: Any) -> str:
|
|
75
|
+
creatives = getattr(self, "creatives", None)
|
|
76
|
+
if creatives is None:
|
|
77
|
+
return "No creatives found."
|
|
78
|
+
count = len(creatives)
|
|
79
|
+
return f"Found {count} {_pluralize(count, 'creative')} in the system."
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
@_register_response_message("CreateMediaBuyResponse1")
|
|
83
|
+
def _create_media_buy_success_message(self: Any) -> str:
|
|
84
|
+
media_buy_id = getattr(self, "media_buy_id", None)
|
|
85
|
+
packages = getattr(self, "packages", None)
|
|
86
|
+
package_count = len(packages) if packages else 0
|
|
87
|
+
return (
|
|
88
|
+
f"Media buy {media_buy_id} created with "
|
|
89
|
+
f"{package_count} {_pluralize(package_count, 'package')}."
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
@_register_response_message("CreateMediaBuyResponse2")
|
|
94
|
+
def _create_media_buy_error_message(self: Any) -> str:
|
|
95
|
+
errors = getattr(self, "errors", None)
|
|
96
|
+
error_count = len(errors) if errors else 0
|
|
97
|
+
return f"Media buy creation failed with {error_count} {_pluralize(error_count, 'error')}."
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
@_register_response_message("UpdateMediaBuyResponse1")
|
|
101
|
+
def _update_media_buy_success_message(self: Any) -> str:
|
|
102
|
+
media_buy_id = getattr(self, "media_buy_id", None)
|
|
103
|
+
return f"Media buy {media_buy_id} updated successfully."
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
@_register_response_message("UpdateMediaBuyResponse2")
|
|
107
|
+
def _update_media_buy_error_message(self: Any) -> str:
|
|
108
|
+
errors = getattr(self, "errors", None)
|
|
109
|
+
error_count = len(errors) if errors else 0
|
|
110
|
+
return f"Media buy update failed with {error_count} {_pluralize(error_count, 'error')}."
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
@_register_response_message("SyncCreativesResponse1")
|
|
114
|
+
def _sync_creatives_success_message(self: Any) -> str:
|
|
115
|
+
creatives = getattr(self, "creatives", None)
|
|
116
|
+
creative_count = len(creatives) if creatives else 0
|
|
117
|
+
return f"Synced {creative_count} {_pluralize(creative_count, 'creative')} successfully."
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@_register_response_message("SyncCreativesResponse2")
|
|
121
|
+
def _sync_creatives_error_message(self: Any) -> str:
|
|
122
|
+
errors = getattr(self, "errors", None)
|
|
123
|
+
error_count = len(errors) if errors else 0
|
|
124
|
+
return f"Creative sync failed with {error_count} {_pluralize(error_count, 'error')}."
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
@_register_response_message("ActivateSignalResponse1")
|
|
128
|
+
def _activate_signal_success_message(self: Any) -> str:
|
|
129
|
+
return "Signal activated successfully."
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
@_register_response_message("ActivateSignalResponse2")
|
|
133
|
+
def _activate_signal_error_message(self: Any) -> str:
|
|
134
|
+
errors = getattr(self, "errors", None)
|
|
135
|
+
error_count = len(errors) if errors else 0
|
|
136
|
+
return f"Signal activation failed with {error_count} {_pluralize(error_count, 'error')}."
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
@_register_response_message("PreviewCreativeResponse1")
|
|
140
|
+
def _preview_creative_single_message(self: Any) -> str:
|
|
141
|
+
previews = getattr(self, "previews", None)
|
|
142
|
+
preview_count = len(previews) if previews else 0
|
|
143
|
+
return f"Generated {preview_count} {_pluralize(preview_count, 'preview')}."
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
@_register_response_message("PreviewCreativeResponse2")
|
|
147
|
+
def _preview_creative_batch_message(self: Any) -> str:
|
|
148
|
+
results = getattr(self, "results", None)
|
|
149
|
+
result_count = len(results) if results else 0
|
|
150
|
+
return f"Generated previews for {result_count} {_pluralize(result_count, 'manifest')}."
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@_register_response_message("BuildCreativeResponse1")
|
|
154
|
+
def _build_creative_success_message(self: Any) -> str:
|
|
155
|
+
return "Creative built successfully."
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
@_register_response_message("BuildCreativeResponse2")
|
|
159
|
+
def _build_creative_error_message(self: Any) -> str:
|
|
160
|
+
errors = getattr(self, "errors", None)
|
|
161
|
+
error_count = len(errors) if errors else 0
|
|
162
|
+
return f"Creative build failed with {error_count} {_pluralize(error_count, 'error')}."
|
|
163
|
+
|
|
164
|
+
|
|
165
|
+
@_register_response_message("GetMediaBuyDeliveryResponse")
|
|
166
|
+
def _get_media_buy_delivery_message(self: Any) -> str:
|
|
167
|
+
deliveries = getattr(self, "media_buy_deliveries", None)
|
|
168
|
+
if deliveries is None:
|
|
169
|
+
return "No delivery data available."
|
|
170
|
+
count = len(deliveries)
|
|
171
|
+
return f"Retrieved delivery data for {count} media {_pluralize(count, 'buy', 'buys')}."
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
@_register_response_message("ProvidePerformanceFeedbackResponse1")
|
|
175
|
+
def _provide_performance_feedback_success_message(self: Any) -> str:
|
|
176
|
+
return "Performance feedback recorded successfully."
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
@_register_response_message("ProvidePerformanceFeedbackResponse2")
|
|
180
|
+
def _provide_performance_feedback_error_message(self: Any) -> str:
|
|
181
|
+
errors = getattr(self, "errors", None)
|
|
182
|
+
error_count = len(errors) if errors else 0
|
|
183
|
+
return (
|
|
184
|
+
f"Performance feedback recording failed with "
|
|
185
|
+
f"{error_count} {_pluralize(error_count, 'error')}."
|
|
186
|
+
)
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
class AdCPBaseModel(BaseModel):
|
|
190
|
+
"""Base model for AdCP types with spec-compliant serialization.
|
|
191
|
+
|
|
192
|
+
AdCP JSON schemas use additionalProperties: false and do not allow null
|
|
193
|
+
for optional fields. Therefore, optional fields must be omitted entirely
|
|
194
|
+
when not present (not sent as null).
|
|
195
|
+
"""
|
|
196
|
+
|
|
197
|
+
def model_dump(self, **kwargs: Any) -> dict[str, Any]:
|
|
198
|
+
if "exclude_none" not in kwargs:
|
|
199
|
+
kwargs["exclude_none"] = True
|
|
200
|
+
return super().model_dump(**kwargs)
|
|
201
|
+
|
|
202
|
+
def model_dump_json(self, **kwargs: Any) -> str:
|
|
203
|
+
if "exclude_none" not in kwargs:
|
|
204
|
+
kwargs["exclude_none"] = True
|
|
205
|
+
return super().model_dump_json(**kwargs)
|
|
206
|
+
|
|
207
|
+
def summary(self) -> str:
|
|
208
|
+
"""Human-readable summary for protocol responses.
|
|
209
|
+
|
|
210
|
+
Returns a standardized human-readable message suitable for MCP tool
|
|
211
|
+
results, A2A task communications, and REST API responses.
|
|
212
|
+
|
|
213
|
+
For types without a registered formatter, returns a generic message
|
|
214
|
+
with the class name.
|
|
215
|
+
"""
|
|
216
|
+
formatter = _RESPONSE_MESSAGE_REGISTRY.get(self.__class__.__name__)
|
|
217
|
+
if formatter:
|
|
218
|
+
return formatter(self)
|
|
219
|
+
return f"{self.__class__.__name__} response"
|
|
@@ -282,3 +282,53 @@ def test_public_api_has_version():
|
|
|
282
282
|
assert hasattr(adcp, "__version__"), "adcp package should export __version__"
|
|
283
283
|
assert isinstance(adcp.__version__, str), "__version__ should be a string"
|
|
284
284
|
assert len(adcp.__version__) > 0, "__version__ should not be empty"
|
|
285
|
+
|
|
286
|
+
|
|
287
|
+
def test_list_creative_formats_request_has_filter_params():
|
|
288
|
+
"""ListCreativeFormatsRequest type has filter parameters per AdCP spec.
|
|
289
|
+
|
|
290
|
+
The SDK supports is_responsive and name_search parameters for filtering
|
|
291
|
+
creative formats. These parameters are part of the AdCP specification.
|
|
292
|
+
"""
|
|
293
|
+
from adcp import ListCreativeFormatsRequest
|
|
294
|
+
|
|
295
|
+
model_fields = ListCreativeFormatsRequest.model_fields
|
|
296
|
+
|
|
297
|
+
# Core filter parameters from AdCP spec
|
|
298
|
+
expected_fields = [
|
|
299
|
+
"is_responsive", # Filter for responsive formats
|
|
300
|
+
"name_search", # Search formats by name (case-insensitive partial match)
|
|
301
|
+
"asset_types", # Filter by asset types (image, video, etc.)
|
|
302
|
+
"type", # Filter by format category (display, video, etc.)
|
|
303
|
+
"format_ids", # Return only specific format IDs
|
|
304
|
+
"min_width", # Minimum width filter
|
|
305
|
+
"max_width", # Maximum width filter
|
|
306
|
+
"min_height", # Minimum height filter
|
|
307
|
+
"max_height", # Maximum height filter
|
|
308
|
+
"context", # Context object for request
|
|
309
|
+
"ext", # Extension object
|
|
310
|
+
]
|
|
311
|
+
|
|
312
|
+
for field_name in expected_fields:
|
|
313
|
+
assert field_name in model_fields, (
|
|
314
|
+
f"ListCreativeFormatsRequest missing field: {field_name}"
|
|
315
|
+
)
|
|
316
|
+
|
|
317
|
+
|
|
318
|
+
def test_list_creative_formats_request_filter_params_types():
|
|
319
|
+
"""ListCreativeFormatsRequest filter parameters have correct types."""
|
|
320
|
+
from adcp import ListCreativeFormatsRequest
|
|
321
|
+
|
|
322
|
+
# Create request with filter parameters - should not raise
|
|
323
|
+
request = ListCreativeFormatsRequest(
|
|
324
|
+
is_responsive=True,
|
|
325
|
+
name_search="mobile",
|
|
326
|
+
)
|
|
327
|
+
|
|
328
|
+
assert request.is_responsive is True
|
|
329
|
+
assert request.name_search == "mobile"
|
|
330
|
+
|
|
331
|
+
# Verify serialization includes the filter parameters
|
|
332
|
+
data = request.model_dump(exclude_none=True)
|
|
333
|
+
assert data["is_responsive"] is True
|
|
334
|
+
assert data["name_search"] == "mobile"
|
|
@@ -0,0 +1,350 @@
|
|
|
1
|
+
"""Tests for .summary() method on response types.
|
|
2
|
+
|
|
3
|
+
These tests verify that response types return human-readable messages
|
|
4
|
+
suitable for MCP tool results, A2A task communications, and REST API responses.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from adcp.types._generated import (
|
|
10
|
+
ActivateSignalResponse1,
|
|
11
|
+
ActivateSignalResponse2,
|
|
12
|
+
BuildCreativeResponse1,
|
|
13
|
+
BuildCreativeResponse2,
|
|
14
|
+
CreateMediaBuyResponse1,
|
|
15
|
+
CreateMediaBuyResponse2,
|
|
16
|
+
GetMediaBuyDeliveryResponse,
|
|
17
|
+
GetProductsResponse,
|
|
18
|
+
GetSignalsResponse,
|
|
19
|
+
ListAuthorizedPropertiesResponse,
|
|
20
|
+
ListCreativeFormatsResponse,
|
|
21
|
+
ListCreativesResponse,
|
|
22
|
+
PreviewCreativeResponse1,
|
|
23
|
+
PreviewCreativeResponse2,
|
|
24
|
+
ProvidePerformanceFeedbackResponse1,
|
|
25
|
+
ProvidePerformanceFeedbackResponse2,
|
|
26
|
+
SyncCreativesResponse1,
|
|
27
|
+
SyncCreativesResponse2,
|
|
28
|
+
UpdateMediaBuyResponse1,
|
|
29
|
+
UpdateMediaBuyResponse2,
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
class TestGetProductsResponseMessage:
|
|
34
|
+
"""Tests for GetProductsResponse.summary()."""
|
|
35
|
+
|
|
36
|
+
def test_singular_product(self):
|
|
37
|
+
"""Single product uses singular form."""
|
|
38
|
+
response = GetProductsResponse.model_construct(
|
|
39
|
+
products=[{"product_id": "p1", "name": "Test"}]
|
|
40
|
+
)
|
|
41
|
+
assert response.summary() == "Found 1 product matching your requirements."
|
|
42
|
+
|
|
43
|
+
def test_multiple_products(self):
|
|
44
|
+
"""Multiple products uses plural form."""
|
|
45
|
+
response = GetProductsResponse.model_construct(
|
|
46
|
+
products=[
|
|
47
|
+
{"product_id": "p1", "name": "Test 1"},
|
|
48
|
+
{"product_id": "p2", "name": "Test 2"},
|
|
49
|
+
{"product_id": "p3", "name": "Test 3"},
|
|
50
|
+
]
|
|
51
|
+
)
|
|
52
|
+
assert response.summary() == "Found 3 products matching your requirements."
|
|
53
|
+
|
|
54
|
+
def test_zero_products(self):
|
|
55
|
+
"""Zero products uses conversational message."""
|
|
56
|
+
response = GetProductsResponse.model_construct(products=[])
|
|
57
|
+
assert response.summary() == "No products matched your requirements."
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
class TestListCreativeFormatsResponseMessage:
|
|
61
|
+
"""Tests for ListCreativeFormatsResponse.summary()."""
|
|
62
|
+
|
|
63
|
+
def test_singular_format(self):
|
|
64
|
+
"""Single format uses singular form."""
|
|
65
|
+
response = ListCreativeFormatsResponse.model_construct(
|
|
66
|
+
formats=[{"format_id": "f1", "name": "Banner"}]
|
|
67
|
+
)
|
|
68
|
+
assert response.summary() == "Found 1 supported creative format."
|
|
69
|
+
|
|
70
|
+
def test_multiple_formats(self):
|
|
71
|
+
"""Multiple formats uses plural form."""
|
|
72
|
+
response = ListCreativeFormatsResponse.model_construct(
|
|
73
|
+
formats=[
|
|
74
|
+
{"format_id": "f1", "name": "Banner 1"},
|
|
75
|
+
{"format_id": "f2", "name": "Banner 2"},
|
|
76
|
+
]
|
|
77
|
+
)
|
|
78
|
+
assert response.summary() == "Found 2 supported creative formats."
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class TestGetSignalsResponseMessage:
|
|
82
|
+
"""Tests for GetSignalsResponse.summary()."""
|
|
83
|
+
|
|
84
|
+
def test_singular_signal(self):
|
|
85
|
+
"""Single signal uses singular form."""
|
|
86
|
+
response = GetSignalsResponse.model_construct(signals=[{"signal_id": "s1"}])
|
|
87
|
+
assert response.summary() == "Found 1 signal available for targeting."
|
|
88
|
+
|
|
89
|
+
def test_multiple_signals(self):
|
|
90
|
+
"""Multiple signals uses plural form."""
|
|
91
|
+
response = GetSignalsResponse.model_construct(
|
|
92
|
+
signals=[{"signal_id": "s1"}, {"signal_id": "s2"}]
|
|
93
|
+
)
|
|
94
|
+
assert response.summary() == "Found 2 signals available for targeting."
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class TestListAuthorizedPropertiesResponseMessage:
|
|
98
|
+
"""Tests for ListAuthorizedPropertiesResponse.summary()."""
|
|
99
|
+
|
|
100
|
+
def test_singular_domain(self):
|
|
101
|
+
"""Single domain uses singular form."""
|
|
102
|
+
response = ListAuthorizedPropertiesResponse.model_construct(
|
|
103
|
+
publisher_domains=["example.com"]
|
|
104
|
+
)
|
|
105
|
+
assert response.summary() == "Authorized to represent 1 publisher domain."
|
|
106
|
+
|
|
107
|
+
def test_multiple_domains(self):
|
|
108
|
+
"""Multiple domains uses plural form."""
|
|
109
|
+
response = ListAuthorizedPropertiesResponse.model_construct(
|
|
110
|
+
publisher_domains=["example.com", "test.com", "demo.com"]
|
|
111
|
+
)
|
|
112
|
+
assert response.summary() == "Authorized to represent 3 publisher domains."
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
class TestListCreativesResponseMessage:
|
|
116
|
+
"""Tests for ListCreativesResponse.summary()."""
|
|
117
|
+
|
|
118
|
+
def test_singular_creative(self):
|
|
119
|
+
"""Single creative uses singular form."""
|
|
120
|
+
response = ListCreativesResponse.model_construct(
|
|
121
|
+
creatives=[{"creative_id": "c1"}]
|
|
122
|
+
)
|
|
123
|
+
assert response.summary() == "Found 1 creative in the system."
|
|
124
|
+
|
|
125
|
+
def test_multiple_creatives(self):
|
|
126
|
+
"""Multiple creatives uses plural form."""
|
|
127
|
+
response = ListCreativesResponse.model_construct(
|
|
128
|
+
creatives=[{"creative_id": "c1"}, {"creative_id": "c2"}]
|
|
129
|
+
)
|
|
130
|
+
assert response.summary() == "Found 2 creatives in the system."
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class TestCreateMediaBuyResponseMessage:
|
|
134
|
+
"""Tests for CreateMediaBuyResponse success/error variants."""
|
|
135
|
+
|
|
136
|
+
def test_success_singular_package(self):
|
|
137
|
+
"""Success with single package."""
|
|
138
|
+
response = CreateMediaBuyResponse1.model_construct(
|
|
139
|
+
media_buy_id="mb_123",
|
|
140
|
+
buyer_ref="ref_456",
|
|
141
|
+
packages=[{"package_id": "pkg_1"}],
|
|
142
|
+
)
|
|
143
|
+
assert response.summary() == "Media buy mb_123 created with 1 package."
|
|
144
|
+
|
|
145
|
+
def test_success_multiple_packages(self):
|
|
146
|
+
"""Success with multiple packages."""
|
|
147
|
+
response = CreateMediaBuyResponse1.model_construct(
|
|
148
|
+
media_buy_id="mb_456",
|
|
149
|
+
buyer_ref="ref_789",
|
|
150
|
+
packages=[{"package_id": "pkg_1"}, {"package_id": "pkg_2"}],
|
|
151
|
+
)
|
|
152
|
+
assert response.summary() == "Media buy mb_456 created with 2 packages."
|
|
153
|
+
|
|
154
|
+
def test_error_singular(self):
|
|
155
|
+
"""Error with single error."""
|
|
156
|
+
response = CreateMediaBuyResponse2.model_construct(
|
|
157
|
+
errors=[{"code": "invalid", "message": "Failed"}]
|
|
158
|
+
)
|
|
159
|
+
assert response.summary() == "Media buy creation failed with 1 error."
|
|
160
|
+
|
|
161
|
+
def test_error_multiple(self):
|
|
162
|
+
"""Error with multiple errors."""
|
|
163
|
+
response = CreateMediaBuyResponse2.model_construct(
|
|
164
|
+
errors=[
|
|
165
|
+
{"code": "invalid", "message": "Error 1"},
|
|
166
|
+
{"code": "invalid", "message": "Error 2"},
|
|
167
|
+
]
|
|
168
|
+
)
|
|
169
|
+
assert response.summary() == "Media buy creation failed with 2 errors."
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
class TestUpdateMediaBuyResponseMessage:
|
|
173
|
+
"""Tests for UpdateMediaBuyResponse success/error variants."""
|
|
174
|
+
|
|
175
|
+
def test_success(self):
|
|
176
|
+
"""Success message includes media buy ID."""
|
|
177
|
+
response = UpdateMediaBuyResponse1.model_construct(
|
|
178
|
+
media_buy_id="mb_789",
|
|
179
|
+
packages=[],
|
|
180
|
+
)
|
|
181
|
+
assert response.summary() == "Media buy mb_789 updated successfully."
|
|
182
|
+
|
|
183
|
+
def test_error(self):
|
|
184
|
+
"""Error message includes error count."""
|
|
185
|
+
response = UpdateMediaBuyResponse2.model_construct(
|
|
186
|
+
errors=[{"code": "not_found", "message": "Not found"}]
|
|
187
|
+
)
|
|
188
|
+
assert response.summary() == "Media buy update failed with 1 error."
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
class TestSyncCreativesResponseMessage:
|
|
192
|
+
"""Tests for SyncCreativesResponse success/error variants."""
|
|
193
|
+
|
|
194
|
+
def test_success_singular(self):
|
|
195
|
+
"""Success with single creative synced."""
|
|
196
|
+
response = SyncCreativesResponse1.model_construct(
|
|
197
|
+
creatives=[{"creative_id": "c1", "action": "created"}]
|
|
198
|
+
)
|
|
199
|
+
assert response.summary() == "Synced 1 creative successfully."
|
|
200
|
+
|
|
201
|
+
def test_success_multiple(self):
|
|
202
|
+
"""Success with multiple creatives synced."""
|
|
203
|
+
response = SyncCreativesResponse1.model_construct(
|
|
204
|
+
creatives=[
|
|
205
|
+
{"creative_id": "c1", "action": "created"},
|
|
206
|
+
{"creative_id": "c2", "action": "updated"},
|
|
207
|
+
{"creative_id": "c3", "action": "created"},
|
|
208
|
+
]
|
|
209
|
+
)
|
|
210
|
+
assert response.summary() == "Synced 3 creatives successfully."
|
|
211
|
+
|
|
212
|
+
def test_error(self):
|
|
213
|
+
"""Error message includes error count."""
|
|
214
|
+
response = SyncCreativesResponse2.model_construct(
|
|
215
|
+
errors=[{"code": "sync_failed", "message": "Failed"}]
|
|
216
|
+
)
|
|
217
|
+
assert response.summary() == "Creative sync failed with 1 error."
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
class TestActivateSignalResponseMessage:
|
|
221
|
+
"""Tests for ActivateSignalResponse success/error variants."""
|
|
222
|
+
|
|
223
|
+
def test_success(self):
|
|
224
|
+
"""Success message is simple confirmation."""
|
|
225
|
+
response = ActivateSignalResponse1.model_construct(
|
|
226
|
+
activation_status="active"
|
|
227
|
+
)
|
|
228
|
+
assert response.summary() == "Signal activated successfully."
|
|
229
|
+
|
|
230
|
+
def test_error(self):
|
|
231
|
+
"""Error message includes error count."""
|
|
232
|
+
response = ActivateSignalResponse2.model_construct(
|
|
233
|
+
errors=[{"code": "activation_failed", "message": "Failed"}]
|
|
234
|
+
)
|
|
235
|
+
assert response.summary() == "Signal activation failed with 1 error."
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
class TestPreviewCreativeResponseMessage:
|
|
239
|
+
"""Tests for PreviewCreativeResponse single/batch variants."""
|
|
240
|
+
|
|
241
|
+
def test_single_singular(self):
|
|
242
|
+
"""Single request with one preview."""
|
|
243
|
+
response = PreviewCreativeResponse1.model_construct(
|
|
244
|
+
response_type="single",
|
|
245
|
+
expires_at="2025-12-01T00:00:00Z",
|
|
246
|
+
previews=[{"preview_id": "p1"}],
|
|
247
|
+
)
|
|
248
|
+
assert response.summary() == "Generated 1 preview."
|
|
249
|
+
|
|
250
|
+
def test_single_multiple(self):
|
|
251
|
+
"""Single request with multiple previews."""
|
|
252
|
+
response = PreviewCreativeResponse1.model_construct(
|
|
253
|
+
response_type="single",
|
|
254
|
+
expires_at="2025-12-01T00:00:00Z",
|
|
255
|
+
previews=[{"preview_id": "p1"}, {"preview_id": "p2"}],
|
|
256
|
+
)
|
|
257
|
+
assert response.summary() == "Generated 2 previews."
|
|
258
|
+
|
|
259
|
+
def test_batch_singular(self):
|
|
260
|
+
"""Batch request with one manifest."""
|
|
261
|
+
response = PreviewCreativeResponse2.model_construct(
|
|
262
|
+
response_type="batch",
|
|
263
|
+
results=[{"manifest_id": "m1"}],
|
|
264
|
+
)
|
|
265
|
+
assert response.summary() == "Generated previews for 1 manifest."
|
|
266
|
+
|
|
267
|
+
def test_batch_multiple(self):
|
|
268
|
+
"""Batch request with multiple manifests."""
|
|
269
|
+
response = PreviewCreativeResponse2.model_construct(
|
|
270
|
+
response_type="batch",
|
|
271
|
+
results=[{"manifest_id": "m1"}, {"manifest_id": "m2"}],
|
|
272
|
+
)
|
|
273
|
+
assert response.summary() == "Generated previews for 2 manifests."
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
class TestBuildCreativeResponseMessage:
|
|
277
|
+
"""Tests for BuildCreativeResponse success/error variants."""
|
|
278
|
+
|
|
279
|
+
def test_success(self):
|
|
280
|
+
"""Success message is simple confirmation."""
|
|
281
|
+
response = BuildCreativeResponse1.model_construct(
|
|
282
|
+
assets=[{"url": "https://example.com/asset"}]
|
|
283
|
+
)
|
|
284
|
+
assert response.summary() == "Creative built successfully."
|
|
285
|
+
|
|
286
|
+
def test_error(self):
|
|
287
|
+
"""Error message includes error count."""
|
|
288
|
+
response = BuildCreativeResponse2.model_construct(
|
|
289
|
+
errors=[{"code": "build_failed", "message": "Failed"}]
|
|
290
|
+
)
|
|
291
|
+
assert response.summary() == "Creative build failed with 1 error."
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
class TestGetMediaBuyDeliveryResponseMessage:
|
|
295
|
+
"""Tests for GetMediaBuyDeliveryResponse.summary()."""
|
|
296
|
+
|
|
297
|
+
def test_with_single_media_buy(self):
|
|
298
|
+
"""Response with single media buy delivery data."""
|
|
299
|
+
response = GetMediaBuyDeliveryResponse.model_construct(
|
|
300
|
+
media_buy_deliveries=[{"media_buy_id": "mb_123"}]
|
|
301
|
+
)
|
|
302
|
+
assert response.summary() == "Retrieved delivery data for 1 media buy."
|
|
303
|
+
|
|
304
|
+
def test_with_multiple_media_buys(self):
|
|
305
|
+
"""Response with multiple media buy delivery data."""
|
|
306
|
+
response = GetMediaBuyDeliveryResponse.model_construct(
|
|
307
|
+
media_buy_deliveries=[
|
|
308
|
+
{"media_buy_id": "mb_123"},
|
|
309
|
+
{"media_buy_id": "mb_456"},
|
|
310
|
+
]
|
|
311
|
+
)
|
|
312
|
+
assert response.summary() == "Retrieved delivery data for 2 media buys."
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
class TestProvidePerformanceFeedbackResponseMessage:
|
|
316
|
+
"""Tests for ProvidePerformanceFeedbackResponse success/error variants."""
|
|
317
|
+
|
|
318
|
+
def test_success(self):
|
|
319
|
+
"""Success message is simple confirmation."""
|
|
320
|
+
response = ProvidePerformanceFeedbackResponse1.model_construct(
|
|
321
|
+
acknowledged=True
|
|
322
|
+
)
|
|
323
|
+
assert response.summary() == "Performance feedback recorded successfully."
|
|
324
|
+
|
|
325
|
+
def test_error(self):
|
|
326
|
+
"""Error message includes error count."""
|
|
327
|
+
response = ProvidePerformanceFeedbackResponse2.model_construct(
|
|
328
|
+
errors=[{"code": "feedback_failed", "message": "Failed"}]
|
|
329
|
+
)
|
|
330
|
+
assert response.summary() == "Performance feedback recording failed with 1 error."
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
class TestNonResponseTypeMessage:
|
|
334
|
+
"""Tests for .summary() on non-response types."""
|
|
335
|
+
|
|
336
|
+
def test_request_type_returns_generic_message(self):
|
|
337
|
+
"""Request types return generic message with class name."""
|
|
338
|
+
from adcp.types import GetProductsRequest
|
|
339
|
+
|
|
340
|
+
request = GetProductsRequest(brief="Test brief")
|
|
341
|
+
assert request.summary() == "GetProductsRequest response"
|
|
342
|
+
|
|
343
|
+
def test_str_returns_pydantic_default(self):
|
|
344
|
+
"""str() returns Pydantic's default representation for inspection."""
|
|
345
|
+
from adcp.types import GetProductsRequest
|
|
346
|
+
|
|
347
|
+
request = GetProductsRequest(brief="Test brief")
|
|
348
|
+
result = str(request)
|
|
349
|
+
# Should be Pydantic's default format, not a custom message
|
|
350
|
+
assert "GetProductsRequest" in result or "brief=" in result
|