feat: add transactional emails for order confirmation and shipping

Plain text emails via Swoosh OrderNotifier module. Order confirmation
triggered from Stripe webhook after payment, shipping notification
from Printify shipment webhook with polling fallback.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
jamey
2026-02-08 10:17:19 +00:00
parent 3e19887499
commit 0af8997623
6 changed files with 328 additions and 15 deletions

View File

@@ -0,0 +1,161 @@
defmodule SimpleshopTheme.Orders.OrderNotifierTest do
use SimpleshopTheme.DataCase, async: true
import Swoosh.TestAssertions
import SimpleshopTheme.OrdersFixtures
alias SimpleshopTheme.Orders
alias SimpleshopTheme.Orders.OrderNotifier
describe "deliver_order_confirmation/1" do
test "sends confirmation with order details" do
order =
order_fixture(%{
customer_email: "buyer@example.com",
payment_status: "paid",
shipping_address: %{
"name" => "Jane Doe",
"line1" => "42 Test Street",
"city" => "London",
"postal_code" => "SW1A 1AA",
"country" => "GB"
}
})
assert {:ok, _email} = OrderNotifier.deliver_order_confirmation(order)
assert_email_sent(fn email ->
assert email.to == [{"", "buyer@example.com"}]
assert email.subject =~ "Order confirmed"
assert email.subject =~ order.order_number
assert email.text_body =~ order.order_number
assert email.text_body =~ "Test product"
assert email.text_body =~ "Jane Doe"
assert email.text_body =~ "42 Test Street"
assert email.text_body =~ "London"
end)
end
test "includes item quantities and prices" do
order =
order_fixture(%{
customer_email: "buyer@example.com",
payment_status: "paid",
quantity: 2,
unit_price: 1500
})
assert {:ok, _email} = OrderNotifier.deliver_order_confirmation(order)
assert_email_sent(fn email ->
assert email.text_body =~ "2x Test product"
end)
end
test "includes order total" do
order =
order_fixture(%{
customer_email: "buyer@example.com",
payment_status: "paid",
unit_price: 2500
})
assert {:ok, _email} = OrderNotifier.deliver_order_confirmation(order)
assert_email_sent(fn email ->
assert email.text_body =~ "Total:"
end)
end
test "skips when customer_email is nil" do
order = order_fixture(%{customer_email: nil})
# Override to nil since fixture sets a default
{:ok, order} = Orders.update_order(order, %{customer_email: nil})
assert {:ok, :no_email} = OrderNotifier.deliver_order_confirmation(order)
assert_no_email_sent()
end
test "skips when customer_email is empty string" do
order = order_fixture()
{:ok, order} = Orders.update_order(order, %{customer_email: ""})
assert {:ok, :no_email} = OrderNotifier.deliver_order_confirmation(order)
assert_no_email_sent()
end
test "handles missing shipping address" do
order = order_fixture(%{customer_email: "buyer@example.com", payment_status: "paid"})
assert {:ok, _email} = OrderNotifier.deliver_order_confirmation(order)
assert_email_sent(subject: "Order confirmed - #{order.order_number}")
end
end
describe "deliver_shipping_notification/1" do
test "sends notification with tracking info" do
{order, _v, _p, _c} = submitted_order_fixture(%{customer_email: "buyer@example.com"})
{:ok, order} =
Orders.update_fulfilment(order, %{
fulfilment_status: "shipped",
tracking_number: "1Z999AA1",
tracking_url: "https://ups.com/track/1Z999AA1",
shipped_at: DateTime.utc_now() |> DateTime.truncate(:second)
})
assert {:ok, _email} = OrderNotifier.deliver_shipping_notification(order)
assert_email_sent(fn email ->
assert email.to == [{"", "buyer@example.com"}]
assert email.subject =~ "Your order has shipped"
assert email.subject =~ order.order_number
assert email.text_body =~ "1Z999AA1"
assert email.text_body =~ "https://ups.com/track/1Z999AA1"
end)
end
test "handles missing tracking info gracefully" do
{order, _v, _p, _c} = submitted_order_fixture(%{customer_email: "buyer@example.com"})
{:ok, order} =
Orders.update_fulfilment(order, %{
fulfilment_status: "shipped",
shipped_at: DateTime.utc_now() |> DateTime.truncate(:second)
})
assert {:ok, _email} = OrderNotifier.deliver_shipping_notification(order)
assert_email_sent(fn email ->
assert email.text_body =~ "Tracking details will follow"
end)
end
test "includes tracking number without URL" do
{order, _v, _p, _c} = submitted_order_fixture(%{customer_email: "buyer@example.com"})
{:ok, order} =
Orders.update_fulfilment(order, %{
fulfilment_status: "shipped",
tracking_number: "RM123456789GB",
shipped_at: DateTime.utc_now() |> DateTime.truncate(:second)
})
assert {:ok, _email} = OrderNotifier.deliver_shipping_notification(order)
assert_email_sent(fn email ->
assert email.text_body =~ "RM123456789GB"
assert not (email.text_body =~ "Tracking details will follow")
end)
end
test "skips when customer_email is nil" do
{order, _v, _p, _c} = submitted_order_fixture()
{:ok, order} = Orders.update_order(order, %{customer_email: nil})
assert {:ok, :no_email} = OrderNotifier.deliver_shipping_notification(order)
assert_no_email_sent()
end
end
end