Add Handbook.md benchmark tasks
This commit is contained in:
@@ -0,0 +1,225 @@
|
||||
"""Tests for discount code tools."""
|
||||
|
||||
import json
|
||||
|
||||
import pytest
|
||||
from pydantic import ValidationError
|
||||
|
||||
from shopify import state as shopify_state
|
||||
from shopify.models import (
|
||||
CreateDiscountCodeArgs,
|
||||
DeleteDiscountCodeArgs,
|
||||
GetDiscountCodeArgs,
|
||||
ListDiscountCodesArgs,
|
||||
UpdateDiscountCodeArgs,
|
||||
)
|
||||
from shopify.tools.discounts import (
|
||||
handle_create_discount_code,
|
||||
handle_delete_discount_code,
|
||||
handle_get_discount_code,
|
||||
handle_list_discount_codes,
|
||||
handle_update_discount_code,
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def shopify_data(tmp_path):
|
||||
data_file = tmp_path / "shopify_data.json"
|
||||
data_file.write_text(
|
||||
json.dumps(
|
||||
{
|
||||
"products": {},
|
||||
"carts": {},
|
||||
"orders": {},
|
||||
"customers": {},
|
||||
"collections": {},
|
||||
"reviews": {},
|
||||
"returns": {},
|
||||
"discount_codes": {},
|
||||
"loyalty_program": {
|
||||
"enabled": True,
|
||||
"tiers": [
|
||||
{"name": "Bronze", "min_lifetime_points": 0, "discount_percent": 5},
|
||||
{"name": "Gold", "min_lifetime_points": 5000, "discount_percent": 15},
|
||||
],
|
||||
},
|
||||
"policies": [],
|
||||
"counters": {
|
||||
"cart_id": 1000,
|
||||
"line_id": 1000,
|
||||
"order_id": 2000,
|
||||
"line_item_id": 3000,
|
||||
"customer_id": 4000,
|
||||
"collection_id": 5000,
|
||||
"review_id": 6000,
|
||||
"return_id": 7000,
|
||||
"product_id": 8000,
|
||||
"variant_id": 9000,
|
||||
"discount_id": 10000,
|
||||
},
|
||||
}
|
||||
)
|
||||
)
|
||||
return data_file
|
||||
|
||||
|
||||
@pytest.fixture(autouse=True)
|
||||
def _patch_state(shopify_data, monkeypatch):
|
||||
monkeypatch.setattr(shopify_state, "_STATE_FILE", shopify_data)
|
||||
shopify_state._current_state = None
|
||||
shopify_state._stores.clear()
|
||||
shopify_state._active_store_id = "default"
|
||||
shopify_state.load_state()
|
||||
|
||||
|
||||
class TestCreateDiscountCode:
|
||||
def test_create_percentage(self):
|
||||
result = handle_create_discount_code(
|
||||
CreateDiscountCodeArgs(code="SUMMER20", value="20", discount_type="PERCENTAGE")
|
||||
)
|
||||
assert result["userErrors"] == []
|
||||
dc = result["discountCode"]
|
||||
assert dc["code"] == "SUMMER20"
|
||||
assert dc["discountType"] == "PERCENTAGE"
|
||||
assert dc["value"] == "20"
|
||||
assert dc["active"] is True
|
||||
assert dc["usageCount"] == 0
|
||||
|
||||
def test_create_fixed_amount(self):
|
||||
result = handle_create_discount_code(
|
||||
CreateDiscountCodeArgs(code="SAVE10", value="10.00", discount_type="FIXED_AMOUNT")
|
||||
)
|
||||
assert result["discountCode"]["discountType"] == "FIXED_AMOUNT"
|
||||
|
||||
def test_create_free_shipping(self):
|
||||
result = handle_create_discount_code(
|
||||
CreateDiscountCodeArgs(code="FREESHIP", value="0", discount_type="FREE_SHIPPING")
|
||||
)
|
||||
assert result["discountCode"]["discountType"] == "FREE_SHIPPING"
|
||||
|
||||
def test_create_with_minimum_purchase(self):
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="BIG20", value="20", minimum_purchase=50.0))
|
||||
assert result["discountCode"]["minimumPurchase"]["amount"] == "50.00"
|
||||
|
||||
def test_create_with_usage_limit(self):
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="LIMITED", value="10", usage_limit=100))
|
||||
assert result["discountCode"]["usageLimit"] == 100
|
||||
|
||||
def test_create_duplicate(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="DUP", value="10"))
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="DUP", value="20"))
|
||||
assert result["discountCode"] is None
|
||||
assert "already exists" in result["userErrors"][0]["message"]
|
||||
|
||||
def test_create_invalid_type(self):
|
||||
with pytest.raises(ValidationError):
|
||||
CreateDiscountCodeArgs.model_validate({"code": "BAD", "value": "10", "discount_type": "INVALID"})
|
||||
|
||||
def test_code_uppercased(self):
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="summer20", value="20"))
|
||||
assert result["discountCode"]["code"] == "SUMMER20"
|
||||
|
||||
def test_create_rejects_missing_product_restriction(self):
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="BADPRODUCT", value="10", product_ids=["p1"]))
|
||||
assert result["discountCode"] is None
|
||||
assert "Product not found: p1" in result["userErrors"][0]["message"]
|
||||
|
||||
def test_create_rejects_missing_minimum_tier(self):
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="VIP", value="10", minimum_tier="Platinum"))
|
||||
assert result["discountCode"] is None
|
||||
assert result["userErrors"][0]["field"] == "minimum_tier"
|
||||
|
||||
def test_create_accepts_configured_minimum_tier(self):
|
||||
result = handle_create_discount_code(CreateDiscountCodeArgs(code="GOLD", value="10", minimum_tier="Gold"))
|
||||
assert result["userErrors"] == []
|
||||
assert result["discountCode"]["minimumTier"] == "Gold"
|
||||
|
||||
|
||||
class TestGetDiscountCode:
|
||||
def test_get_existing(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST10", value="10"))
|
||||
result = handle_get_discount_code(GetDiscountCodeArgs(code="TEST10"))
|
||||
assert result["discountCode"]["code"] == "TEST10"
|
||||
|
||||
def test_get_case_insensitive(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST10", value="10"))
|
||||
result = handle_get_discount_code(GetDiscountCodeArgs(code="test10"))
|
||||
assert result["discountCode"]["code"] == "TEST10"
|
||||
|
||||
def test_get_nonexistent(self):
|
||||
result = handle_get_discount_code(GetDiscountCodeArgs(code="NOPE"))
|
||||
assert result["discountCode"] is None
|
||||
|
||||
|
||||
class TestListDiscountCodes:
|
||||
def test_list_empty(self):
|
||||
result = handle_list_discount_codes(ListDiscountCodesArgs())
|
||||
assert result["totalCount"] == 0
|
||||
|
||||
def test_list_all(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="A10", value="10"))
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="B20", value="20"))
|
||||
result = handle_list_discount_codes(ListDiscountCodesArgs())
|
||||
assert result["totalCount"] == 2
|
||||
|
||||
def test_list_active_only(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="ACTIVE", value="10"))
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="INACTIVE", value="20"))
|
||||
handle_update_discount_code(UpdateDiscountCodeArgs(code="INACTIVE", active=False))
|
||||
result = handle_list_discount_codes(ListDiscountCodesArgs(active_only=True))
|
||||
assert result["totalCount"] == 1
|
||||
assert result["discountCodes"][0]["code"] == "ACTIVE"
|
||||
|
||||
|
||||
class TestUpdateDiscountCode:
|
||||
def test_deactivate(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST", value="10"))
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="TEST", active=False))
|
||||
assert result["discountCode"]["active"] is False
|
||||
|
||||
def test_update_value(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST", value="10"))
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="TEST", value="25"))
|
||||
assert result["discountCode"]["value"] == "25"
|
||||
|
||||
def test_update_nonexistent(self):
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="NOPE", active=False))
|
||||
assert result["discountCode"] is None
|
||||
|
||||
def test_update_rejects_missing_product_restriction(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST", value="10"))
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="TEST", product_ids=["p1"]))
|
||||
assert result["discountCode"] is None
|
||||
assert "Product not found: p1" in result["userErrors"][0]["message"]
|
||||
|
||||
def test_update_rejects_missing_minimum_tier(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST", value="10"))
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="TEST", minimum_tier="Platinum"))
|
||||
assert result["discountCode"] is None
|
||||
assert result["userErrors"][0]["field"] == "minimum_tier"
|
||||
|
||||
def test_update_accepts_configured_minimum_tier(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST", value="10"))
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="TEST", minimum_tier="Gold"))
|
||||
assert result["userErrors"] == []
|
||||
assert result["discountCode"]["minimumTier"] == "Gold"
|
||||
|
||||
def test_update_clears_minimum_tier_with_empty_string(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TEST", value="10", minimum_tier="Gold"))
|
||||
result = handle_update_discount_code(UpdateDiscountCodeArgs(code="TEST", minimum_tier=""))
|
||||
assert result["userErrors"] == []
|
||||
assert result["discountCode"]["minimumTier"] is None
|
||||
|
||||
|
||||
class TestDeleteDiscountCode:
|
||||
def test_delete(self):
|
||||
handle_create_discount_code(CreateDiscountCodeArgs(code="TODELETE", value="10"))
|
||||
result = handle_delete_discount_code(DeleteDiscountCodeArgs(code="TODELETE"))
|
||||
assert result["deletedCode"] == "TODELETE"
|
||||
# Verify gone
|
||||
get_result = handle_get_discount_code(GetDiscountCodeArgs(code="TODELETE"))
|
||||
assert get_result["discountCode"] is None
|
||||
|
||||
def test_delete_nonexistent(self):
|
||||
result = handle_delete_discount_code(DeleteDiscountCodeArgs(code="NOPE"))
|
||||
assert result["deletedCode"] is None
|
||||
Reference in New Issue
Block a user