Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 43 additions & 2 deletions integration_test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,60 @@
from ucp_sdk.models.schemas.shopping.types import (
fulfillment_method_create_request,
)
from ucp_sdk.models.schemas.shopping.types.amount import Amount
from ucp_sdk.models.schemas.shopping.types import item_create_request
from ucp_sdk.models.schemas.shopping.types import item_update_request
from ucp_sdk.models.schemas.shopping.types import line_item_create_request
from ucp_sdk.models.schemas.shopping.types import line_item_update_request
from ucp_sdk.models.schemas.shopping.types.signed_amount import SignedAmount
from ucp_sdk.models.schemas import payment_handler
from ucp_sdk.models.schemas.shopping.types import shipping_destination
from ucp_sdk.models.schemas.shopping.types.totals import Totals

UCP_VERSION = "2026-04-08"
import uvicorn


class UnifiedUpdate(CheckoutUpdateRequest):
"""Client-side unified update model to support extensions."""


def _patch_sdk_root_models() -> None:
"""Make generated April SDK root models test-friendly.

The April 2026 Python SDK models introduce RootModel wrappers for scalar
amounts and for checkout/order totals. These wrappers are correct at the
schema layer, but direct iteration/comparison is awkward in the existing
conformance assertions.
"""

def _scalar_eq(self: Any, other: Any) -> bool:
if hasattr(other, "root"):
return self.root == other.root
return self.root == other

def _scalar_int(self: Any) -> int:
return int(self.root)

def _scalar_str(self: Any) -> str:
return str(self.root)

Amount.__eq__ = _scalar_eq
Amount.__int__ = _scalar_int
Amount.__index__ = _scalar_int
Amount.__str__ = _scalar_str

SignedAmount.__eq__ = _scalar_eq
SignedAmount.__int__ = _scalar_int
SignedAmount.__index__ = _scalar_int
SignedAmount.__str__ = _scalar_str

Totals.__iter__ = lambda self: iter(self.root)


_patch_sdk_root_models()


FLAGS = flags.FLAGS
try:
flags.DEFINE_string("server_url", None, "Base URL of the server")
Expand Down Expand Up @@ -480,7 +521,7 @@ def create_checkout_payload(
payment_handler.PaymentHandler(
id="google_pay",
name="google.pay",
version="2026-01-23",
version=UCP_VERSION,
spec="https://example.com/spec",
config_schema="https://example.com/schema",
instrument_schemas=["https://example.com/instrument_schema"],
Expand Down Expand Up @@ -540,7 +581,7 @@ def create_checkout_payload(
fulfillment=fulfillment,
)
checkout_req.status = "incomplete"
checkout_req.ucp = {"version": "2026-01-23"}
checkout_req.ucp = {"version": UCP_VERSION}
checkout_req.totals = []
checkout_req.links = []

Expand Down
13 changes: 9 additions & 4 deletions protocol_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,20 @@
import integration_test_utils
import httpx
from pydantic import ValidationError
from ucp_sdk.models.schemas.ucp import BusinessSchema, ReverseDomainName
from ucp_sdk.models.schemas.ucp import BusinessSchema
from ucp_sdk.models.schemas.shopping import checkout as checkout
from ucp_sdk.models.schemas.shopping.payment import (
Payment,
)
from ucp_sdk.models.schemas.shopping.types.reverse_domain_name import (
ReverseDomainName,
)

# Rebuild models to resolve forward references
checkout.Checkout.model_rebuild(_types_namespace={"Payment": Payment})

UCP_VERSION = "2026-04-08"


class ProtocolTest(integration_test_utils.IntegrationTestBase):
"""Tests for UCP protocol compliance.
Expand Down Expand Up @@ -162,7 +167,7 @@ def test_discovery(self):

self.assertEqual(
data.get("version"),
"2026-01-23",
UCP_VERSION,
msg="Unexpected UCP version in discovery doc",
)

Expand Down Expand Up @@ -223,7 +228,7 @@ def test_discovery(self):
if isinstance(shopping_services, list)
else shopping_services
)
self.assertEqual(shopping_service.get("version"), "2026-01-23")
self.assertEqual(shopping_service.get("version"), UCP_VERSION)
self.assertIsNotNone(shopping_service.get("transport") == "rest")
self.assertIsNotNone(shopping_service.get("endpoint"))

Expand Down Expand Up @@ -265,7 +270,7 @@ def test_version_negotiation(self):

# 1. Compatible Version
headers = integration_test_utils.get_headers()
headers["UCP-Agent"] = 'profile="..."; version="2026-01-23"'
headers["UCP-Agent"] = f'profile="..."; version="{UCP_VERSION}"'
response = self.client.post(
checkout_sessions_url,
json=create_payload.model_dump(
Expand Down
8 changes: 4 additions & 4 deletions shopping-agent-test.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"ucp": {
"version": "2026-01-23",
"version": "2026-04-08",
"capabilities": {
"dev.ucp.shopping.order": [
{
"name": "dev.ucp.shopping.order",
"version": "2026-01-23",
"spec": "https://ucp.dev/2026-01-23/specification/order/",
"schema": "https://ucp.dev/2026-01-23/schemas/shopping/order.json",
"version": "2026-04-08",
"spec": "https://ucp.dev/2026-04-08/specification/order/",
"schema": "https://ucp.dev/2026-04-08/schemas/shopping/order.json",
"config": {
"webhook_url": "http://localhost:{webhook_port}/webhooks/partners/test_partner/events/order"
}
Expand Down