defmodule SimpleshopTheme.ShippingTest do use SimpleshopTheme.DataCase, async: false alias SimpleshopTheme.Shipping alias SimpleshopTheme.Shipping.ShippingRate import SimpleshopTheme.ProductsFixtures describe "upsert_rates/2" do test "inserts new rates" do conn = provider_connection_fixture() rates = [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" }, %{ blueprint_id: 100, print_provider_id: 200, country_code: "US", first_item_cost: 650, additional_item_cost: 150, currency: "USD" } ] assert {:ok, 2} = Shipping.upsert_rates(conn.id, rates) assert Repo.aggregate(ShippingRate, :count) == 2 end test "replaces existing rates on conflict" do conn = provider_connection_fixture() rates = [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" } ] {:ok, 1} = Shipping.upsert_rates(conn.id, rates) updated_rates = [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 550, additional_item_cost: 200, currency: "GBP" } ] {:ok, 1} = Shipping.upsert_rates(conn.id, updated_rates) assert Repo.aggregate(ShippingRate, :count) == 1 rate = Repo.one(ShippingRate) assert rate.first_item_cost == 550 assert rate.additional_item_cost == 200 end end describe "get_rate/3" do test "returns rate when it exists" do conn = provider_connection_fixture() {:ok, _} = Shipping.upsert_rates(conn.id, [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" } ]) rate = Shipping.get_rate(100, 200, "GB") assert rate.first_item_cost == 450 assert rate.country_code == "GB" end test "returns nil when no rate exists" do assert Shipping.get_rate(999, 999, "XX") == nil end end describe "calculate_for_cart/2" do test "returns {:ok, 0} for empty cart" do assert {:ok, 0} = Shipping.calculate_for_cart([], "GB") end test "calculates shipping for single item" do conn = provider_connection_fixture() product = product_fixture(%{ provider_connection: conn, provider_data: %{"blueprint_id" => 100, "print_provider_id" => 200} }) variant = product_variant_fixture(%{product: product}) {:ok, _} = Shipping.upsert_rates(conn.id, [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" } ]) cart_items = [%{variant_id: variant.id, quantity: 1}] assert {:ok, 450} = Shipping.calculate_for_cart(cart_items, "GB") end test "calculates shipping for multiple quantities" do conn = provider_connection_fixture() product = product_fixture(%{ provider_connection: conn, provider_data: %{"blueprint_id" => 100, "print_provider_id" => 200} }) variant = product_variant_fixture(%{product: product}) {:ok, _} = Shipping.upsert_rates(conn.id, [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" } ]) cart_items = [%{variant_id: variant.id, quantity: 3}] # first_item_cost + 2 * additional_item_cost = 450 + 200 = 650 assert {:ok, 650} = Shipping.calculate_for_cart(cart_items, "GB") end test "returns error when no rates found" do conn = provider_connection_fixture() product = product_fixture(%{ provider_connection: conn, provider_data: %{"blueprint_id" => 100, "print_provider_id" => 200} }) variant = product_variant_fixture(%{product: product}) cart_items = [%{variant_id: variant.id, quantity: 1}] assert {:error, :rates_not_found} = Shipping.calculate_for_cart(cart_items, "XX") end test "groups items by print provider" do conn = provider_connection_fixture() product1 = product_fixture(%{ provider_connection: conn, provider_data: %{"blueprint_id" => 100, "print_provider_id" => 200} }) product2 = product_fixture(%{ provider_connection: conn, provider_data: %{"blueprint_id" => 101, "print_provider_id" => 200} }) variant1 = product_variant_fixture(%{product: product1}) variant2 = product_variant_fixture(%{product: product2}) {:ok, _} = Shipping.upsert_rates(conn.id, [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" }, %{ blueprint_id: 101, print_provider_id: 200, country_code: "GB", first_item_cost: 500, additional_item_cost: 150, currency: "GBP" } ]) cart_items = [ %{variant_id: variant1.id, quantity: 1}, %{variant_id: variant2.id, quantity: 1} ] # Same provider: highest first_item_cost (500) + 1 additional item cost assert {:ok, cost} = Shipping.calculate_for_cart(cart_items, "GB") # 500 (highest first item) + one additional item cost (100 or 150) assert cost in [600, 650] end end describe "upsert_rates/3 with exchange rates" do test "converts rates to GBP at sync time with buffer" do conn = provider_connection_fixture() exchange_rates = %{"USD" => 0.80} rates = [ %{ blueprint_id: 100, print_provider_id: 1, country_code: "US", first_item_cost: 1000, additional_item_cost: 500, currency: "USD" } ] {:ok, 1} = Shipping.upsert_rates(conn.id, rates, exchange_rates) rate = Shipping.get_rate(100, 1, "US") assert rate.currency == "GBP" # 1000 USD cents * 0.80 rate * 1.05 buffer, ceil'd assert rate.first_item_cost == 841 assert rate.additional_item_cost == 421 end test "leaves GBP rates unconverted" do conn = provider_connection_fixture() exchange_rates = %{"USD" => 0.80} rates = [ %{ blueprint_id: 100, print_provider_id: 1, country_code: "GB", first_item_cost: 500, additional_item_cost: 200, currency: "GBP" } ] {:ok, 1} = Shipping.upsert_rates(conn.id, rates, exchange_rates) rate = Shipping.get_rate(100, 1, "GB") assert rate.currency == "GBP" assert rate.first_item_cost == 500 assert rate.additional_item_cost == 200 end end describe "list_available_countries/0" do test "returns distinct country codes" do conn = provider_connection_fixture() {:ok, _} = Shipping.upsert_rates(conn.id, [ %{ blueprint_id: 100, print_provider_id: 200, country_code: "GB", first_item_cost: 450, additional_item_cost: 100, currency: "GBP" }, %{ blueprint_id: 100, print_provider_id: 200, country_code: "US", first_item_cost: 650, additional_item_cost: 150, currency: "USD" }, %{ blueprint_id: 101, print_provider_id: 200, country_code: "GB", first_item_cost: 500, additional_item_cost: 100, currency: "GBP" } ]) countries = Shipping.list_available_countries() assert "GB" in countries assert "US" in countries assert length(countries) == 2 end end end