package constants import "testing" // The nine stage keys are a wire contract. The customer client parses them // verbatim and silently falls back to `booked` on anything it does not // recognise — so a renamed or misspelt key here does not fail loudly, it makes // a moving parcel look un-started on someone's tracking screen. func TestStageKeysAreSpeltExactlyAsTheClientParsesThem(t *testing.T) { want := map[string]string{ CxStageBooked: "booked", CxStageAssigned: "assigned", CxStageOnTheWay: "on_the_way", CxStageArrived: "arrived", CxStagePickedUp: "picked_up", CxStageOrderCreated: "order_created", CxStageInTransit: "in_transit", CxStageOutForDelivery: "out_for_delivery", CxStageDelivered: "delivered", } for got, expected := range want { if got != expected { t.Errorf("stage key = %q, want %q", got, expected) } } if len(CxStageOrder) != 9 { t.Errorf("CxStageOrder has %d entries, want 9 — every stage the client "+ "knows must be rankable", len(CxStageOrder)) } } // Ranks must be strictly increasing in journey order. advanceBooking refuses to // move a booking backwards by comparing these, so a tie or an inversion would // let a stage silently fail to apply. func TestStageRanksIncreaseInJourneyOrder(t *testing.T) { ordered := []string{ CxStageBooked, CxStageAssigned, CxStageOnTheWay, CxStageArrived, CxStagePickedUp, CxStageOrderCreated, CxStageInTransit, CxStageOutForDelivery, CxStageDelivered, } for i := 1; i < len(ordered); i++ { if CxStageRank(ordered[i]) <= CxStageRank(ordered[i-1]) { t.Errorf("%q ranks %d, not after %q at %d", ordered[i], CxStageRank(ordered[i]), ordered[i-1], CxStageRank(ordered[i-1])) } } } // An unknown or empty stage must rank BEHIND booked, not tie with it. A booking // written before this surface existed has no stage at all, and a tie would stop // it ever advancing off nothing. func TestUnknownStageRanksBehindEverything(t *testing.T) { if CxStageRank("") != -1 { t.Errorf("empty stage ranks %d, want -1", CxStageRank("")) } if CxStageRank("teleported") != -1 { t.Errorf("unknown stage ranks %d, want -1", CxStageRank("teleported")) } if CxStageRank("") >= CxStageRank(CxStageBooked) { t.Error("an unknown stage does not rank behind booked") } } // Cancellation closes after `arrived` — the contract's rule, and money depends // on it: cancelling after pickup would mean a parcel already collected and paid // for is marked cancelled. func TestCancellationWindowClosesAfterArrived(t *testing.T) { open := []string{"", CxStageBooked, CxStageAssigned, CxStageOnTheWay, CxStageArrived} for _, stage := range open { if !CxCancellable(stage) { t.Errorf("stage %q should still be cancellable", stage) } } closed := []string{ CxStagePickedUp, CxStageOrderCreated, CxStageInTransit, CxStageOutForDelivery, CxStageDelivered, } for _, stage := range closed { if CxCancellable(stage) { t.Errorf("stage %q must not be cancellable — the parcel is collected", stage) } } } // The customer statuses and actor types are also on the wire. func TestCustomerStatusAndActorValues(t *testing.T) { pairs := map[string]string{ CxStatusActive: "active", CxStatusCompleted: "completed", CxStatusCancelled: "cancelled", CxActorMiler: "miler", CxActorOps: "ops", CxActorCustomer: "customer", CxActorSystem: "system", } for got, want := range pairs { if got != want { t.Errorf("constant = %q, want %q", got, want) } } } // Booking source decides whether a booking gets a customer projection at all — // cxstage returns early for anything that is not Customer_App. The stored value // predates this work and must not be "fixed". func TestBookingSourceValuesAreTheStoredOnes(t *testing.T) { if BookingSourceCustomerApp != "Customer_App" { t.Errorf("BookingSourceCustomerApp = %q, want %q", BookingSourceCustomerApp, "Customer_App") } if BookingSourceExpress != "CRM_Console" { t.Errorf("BookingSourceExpress = %q — this is an existing database value, "+ "not a label to rename", BookingSourceExpress) } }