From cd15a16e30ded744b6e1f942e5cab31fd6f20767 Mon Sep 17 00:00:00 2001 From: FanouZeng-TT <18280587072@163.com> Date: Fri, 14 Aug 2026 15:17:34 +0800 Subject: [PATCH] fix: fixture dynamic fulfillment option ids --- README.md | 25 ++++++- fulfillment_test.py | 87 +++++++++++++----------- integration_test_utils.py | 43 ++++++++++++ test_data/flower_shop/test_fixtures.json | 24 ++++++- 4 files changed, 137 insertions(+), 42 deletions(-) diff --git a/README.md b/README.md index 033dcc8..f868c6e 100644 --- a/README.md +++ b/README.md @@ -104,7 +104,29 @@ Example `test_fixtures.json`: "email": "jane.doe@example.com" }, "free_shipping_min_subtotal": 100.0, - "free_shipping_item_sku": "item_1" + "free_shipping_item_sku": "item_1", + "dynamic_fulfillment": { + "domestic": { + "destination": { + "street_address": "123 Main St", + "address_locality": "Springfield", + "address_region": "IL", + "postal_code": "62704", + "address_country": "US" + }, + "expected_option_id": "exp-ship-us" + }, + "international": { + "destination": { + "street_address": "25 King St W", + "address_locality": "Toronto", + "address_region": "ON", + "postal_code": "M5V 2H1", + "address_country": "CA" + }, + "expected_option_id": "exp-ship-intl" + } + } }, "shipping_locations": { "domestic_destination": { @@ -128,6 +150,7 @@ Example `test_fixtures.json`: - `known_customer_without_address`: **Optional**. A buyer your server recognizes but has no stored addresses for. The corresponding test skips if not configured. - `free_shipping_min_subtotal`: **Optional**. Order subtotal (major units) at which your server offers a zero-cost fulfillment option. The threshold test skips if not configured. - `free_shipping_item_sku`: **Optional**. An item SKU eligible for free shipping regardless of order value. The item test skips if not configured. + - `dynamic_fulfillment`: **Optional**. Destination/option pairs for the dynamic fulfillment test: each case declares a destination address (response-schema field names) and the option id your server returns for that destination. The test verifies that updating the fulfillment address to the domestic destination makes that destination's option available, and likewise for the international destination; it skips if not configured. - `shipping_locations`: Addresses used for fulfillment tests. ### 4. Run the Tests diff --git a/fulfillment_test.py b/fulfillment_test.py index eb684ac..3a2e8fa 100644 --- a/fulfillment_test.py +++ b/fulfillment_test.py @@ -195,80 +195,87 @@ def test_dynamic_fulfillment(self) -> None: """Test that fulfillment options are dynamically generated based on address. Given a checkout session, - When the fulfillment address is updated to a US address, then a US-specific - option is available. - When the fulfillment address is updated to a CA address, then a CA-specific - option is available. + When the fulfillment address is updated to the domestic destination, then + the option id declared for it in the fixtures is available. + When the fulfillment address is updated to the international destination, + then the option id declared for it in the fixtures is available. """ + dynamic = self.fixture_ctx.get_dynamic_fulfillment() + if dynamic is None: + self.skipTest( + "No dynamic fulfillment cases configured in fixtures " + "(test_fixtures.dynamic_fulfillment)." + ) + response_json = self.create_checkout_session(select_fulfillment=False) checkout_obj = checkout.Checkout(**response_json) - # 1. Update with US Address - # addr_1 is US in CSV - addr_data = integration_test_utils.test_data.addresses[0] - us_address = { - "id": "dest_us", - "address_country": addr_data["country"], - "postal_code": addr_data["postal_code"], + # 1. Update with the domestic destination + domestic = dynamic["domestic"] + domestic_dest = { + "id": "dest_domestic", + **domestic["destination"], } - - fulfillment_us = { + fulfillment_domestic = { "methods": [ { "type": "shipping", "id": "method_1", "line_item_ids": [checkout_obj.line_items[0].id], - "destinations": [us_address], - "selected_destination_id": "dest_us", + "destinations": [domestic_dest], + "selected_destination_id": "dest_domestic", } ] } response_json = self.update_checkout_session( - checkout_obj, fulfillment=fulfillment_us + checkout_obj, fulfillment=fulfillment_domestic ) - us_checkout = checkout.Checkout(**response_json) + domestic_checkout = checkout.Checkout(**response_json) - # Check for US options - options = us_checkout.model_extra["fulfillment"]["methods"][0]["groups"][0][ - "options" - ] + # Check that the declared domestic option is available + options = domestic_checkout.model_extra["fulfillment"]["methods"][0][ + "groups" + ][0]["options"] self.assertTrue( - options and any(o["id"] == "exp-ship-us" for o in options), - f"Expected US express option, got {options}", + options + and any(o["id"] == domestic["expected_option_id"] for o in options), + "Expected domestic option " + f"{domestic['expected_option_id']}, got {options}", ) - # 2. Update with CA Address - ca_address = { - "id": "dest_ca", - "address_country": "CA", - "postal_code": "M5V 2H1", + # 2. Update with the international destination + international = dynamic["international"] + international_dest = { + "id": "dest_international", + **international["destination"], } - - fulfillment_ca = { + fulfillment_international = { "methods": [ { "type": "shipping", "id": "method_1", "line_item_ids": [checkout_obj.line_items[0].id], - "destinations": [ca_address], - "selected_destination_id": "dest_ca", + "destinations": [international_dest], + "selected_destination_id": "dest_international", } ] } response_json = self.update_checkout_session( - us_checkout, fulfillment=fulfillment_ca + domestic_checkout, fulfillment=fulfillment_international ) - ca_checkout = checkout.Checkout(**response_json) + international_checkout = checkout.Checkout(**response_json) - # Check for International options - options = ca_checkout.model_extra["fulfillment"]["methods"][0]["groups"][0][ - "options" - ] + # Check that the declared international option is available + options = international_checkout.model_extra["fulfillment"]["methods"][0][ + "groups" + ][0]["options"] self.assertTrue( - options and any(o["id"] == "exp-ship-intl" for o in options), - f"Expected Intl express option, got {options}", + options + and any(o["id"] == international["expected_option_id"] for o in options), + "Expected international option " + f"{international['expected_option_id']}, got {options}", ) def test_unknown_customer_no_address(self) -> None: diff --git a/integration_test_utils.py b/integration_test_utils.py index 10d7926..18dc88e 100644 --- a/integration_test_utils.py +++ b/integration_test_utils.py @@ -403,6 +403,20 @@ class CustomerFixture(TypedDict, total=False): addresses: list[AddressFixture] +class DynamicFulfillmentCaseFixture(TypedDict): + """A destination and fulfillment option expected for dynamic options.""" + + destination: AddressFixture + expected_option_id: str + + +class DynamicFulfillmentFixture(TypedDict): + """Dynamic fulfillment cases declared by the server under test.""" + + domestic: DynamicFulfillmentCaseFixture + international: DynamicFulfillmentCaseFixture + + class DynamicFixtureContext: """Context for loading dynamic test fixtures from configuration.""" @@ -648,6 +662,35 @@ def get_free_shipping_item_sku(self) -> str | None: return None return str(val) + def get_dynamic_fulfillment(self) -> DynamicFulfillmentFixture | None: + """Get destination/option pairs for dynamic fulfillment tests. + + Returns None when the server under test declares no dynamic fulfillment + cases, in which case the dynamic-options test skips. Each case declares a + destination address (response-schema field names) and the fulfillment + option id the server returns for that destination. + """ + val = self._get_test_fixture("dynamic_fulfillment") + if not isinstance(val, dict): + return None + if not isinstance(val.get("domestic"), dict) or not isinstance( + val.get("international"), dict + ): + return None + domestic = val["domestic"] + international = val["international"] + if not isinstance(domestic.get("destination"), dict) or not isinstance( + domestic.get("expected_option_id"), str + ): + return None + if not isinstance(international.get("destination"), dict) or not isinstance( + international.get("expected_option_id"), str + ): + return None + return DynamicFulfillmentFixture( + domestic=domestic, international=international + ) + ConfiguredFixtureContext = DynamicFixtureContext diff --git a/test_data/flower_shop/test_fixtures.json b/test_data/flower_shop/test_fixtures.json index e79f829..92c23dc 100644 --- a/test_data/flower_shop/test_fixtures.json +++ b/test_data/flower_shop/test_fixtures.json @@ -36,7 +36,29 @@ "email": "jane.doe@example.com" }, "free_shipping_min_subtotal": 100.0, - "free_shipping_item_sku": "bouquet_roses" + "free_shipping_item_sku": "bouquet_roses", + "dynamic_fulfillment": { + "domestic": { + "destination": { + "street_address": "123 Main St", + "address_locality": "Springfield", + "address_region": "IL", + "postal_code": "62704", + "address_country": "US" + }, + "expected_option_id": "exp-ship-us" + }, + "international": { + "destination": { + "street_address": "25 King St W", + "address_locality": "Toronto", + "address_region": "ON", + "postal_code": "M5V 2H1", + "address_country": "CA" + }, + "expected_option_id": "exp-ship-intl" + } + } }, "shipping_locations": { "domestic_destination": {