from decimal import Decimal import json from django.contrib.auth import get_user_model from django.test import Client, TestCase from django.urls import reverse from shipping.models import Shipment from shipping.services import buy_label, create_shipment_for_order, pirate_ship_csv, quote_rates from shop.models import Order class ShippingServiceTests(TestCase): def setUp(self): self.order = Order.objects.create( number="ORD-20260101-001", email="buyer@example.com", customer_name="Pat Lee", status=Order.Status.PAID, amount=Decimal("18.00"), shipping_address={ "line1": "123 Main", "city": "Aurora", "state": "IL", "zip": "60506", "country": "US", }, ) def test_quote_and_buy_stub_label(self): shipment = create_shipment_for_order(self.order) rates = quote_rates(shipment) self.assertGreaterEqual(len(rates), 1) shipment.refresh_from_db() self.assertEqual(shipment.status, Shipment.Status.RATED) buy_label(shipment, rate_id=rates[0]["id"]) shipment.refresh_from_db() self.assertEqual(shipment.status, Shipment.Status.LABELED) self.assertTrue(shipment.tracking_number) def test_pirate_ship_csv_includes_unshipped(self): csv_body = pirate_ship_csv() self.assertIn("ORD-20260101-001", csv_body) self.assertIn("123 Main", csv_body) self.assertIn("buyer@example.com", csv_body) def test_csv_omits_labeled_orders(self): shipment = create_shipment_for_order(self.order) quote_rates(shipment) buy_label(shipment) self.assertNotIn(self.order.number, pirate_ship_csv()) def test_stub_buy_sets_pre_transit_tracking(self): shipment = create_shipment_for_order(self.order) quote_rates(shipment) buy_label(shipment) shipment.refresh_from_db() self.assertTrue(shipment.tracking_number) self.assertEqual(shipment.tracking_status, Shipment.TrackingStatus.PRE_TRANSIT) self.assertTrue(shipment.tracking_url) def test_attach_tracking_from_pirate_ship(self): from shipping.services import attach_tracking shipment = create_shipment_for_order(self.order) attach_tracking(shipment, tracking_number="9400111899223197428490", carrier="USPS") shipment.refresh_from_db() self.assertEqual(shipment.status, Shipment.Status.LABELED) self.assertEqual(shipment.tracking_status, Shipment.TrackingStatus.PRE_TRANSIT) self.assertIn("usps.com", shipment.tracking_url.lower()) def test_easypost_webhook_updates_status(self): shipment = create_shipment_for_order(self.order) shipment.tracking_number = "EZ1000000001" shipment.status = Shipment.Status.LABELED shipment.save() payload = { "description": "tracker.updated", "result": { "id": "trk_test", "tracking_code": "EZ1000000001", "status": "in_transit", "public_url": "https://track.easypost.com/djE0", "tracking_details": [ { "status": "in_transit", "message": "Departed facility", "datetime": "2026-09-07T12:00:00Z", "tracking_location": {"city": "Chicago", "state": "IL"}, } ], }, } response = Client().post( reverse("shipping:easypost_webhook"), data=json.dumps(payload), content_type="application/json", ) self.assertEqual(response.status_code, 200) shipment.refresh_from_db() self.assertEqual(shipment.tracking_status, Shipment.TrackingStatus.IN_TRANSIT) self.assertEqual(shipment.tracker_id, "trk_test") self.assertEqual(shipment.tracking_events[0]["location"], "Chicago IL") class ShippingPortalTests(TestCase): def setUp(self): User = get_user_model() self.user = User.objects.create_user("shipper", password="test-pass-123", is_staff=True) self.client = Client() self.client.login(username="shipper", password="test-pass-123") self.order = Order.objects.create( number="ORD-20260101-002", email="a@example.com", status=Order.Status.PAID, amount=Decimal("9.00"), shipping_address={"line1": "9 Oak", "city": "Town", "state": "IL", "zip": "60189"}, ) def test_list_requires_login(self): self.assertEqual(Client().get(reverse("shipping:shipment_list")).status_code, 302) def test_create_shipment_from_portal(self): response = self.client.post( reverse("shipping:shipment_create"), {"order": str(self.order.pk), "weight_oz": "8"}, ) self.assertEqual(response.status_code, 302) shipment = Shipment.objects.get() self.assertEqual(shipment.weight_oz, 8) self.assertEqual(shipment.status, Shipment.Status.RATED) def test_csv_export(self): response = self.client.get(reverse("shipping:pirate_ship_export")) self.assertEqual(response.status_code, 200) self.assertEqual(response["Content-Type"], "text/csv") self.assertIn(b"ORD-20260101-002", response.content)