package repositories import "testing" // The terminal holds a unique index on barcode, so this rule decides whether a // catalogue import succeeds at all. Measured against live data when it was // written: 6,245 products, 93 distinct SKUs, and "1" used by 5,794 of them. func TestPosBarcodeFallsBackToProductIdWhenTheSkuIsNotScannable(t *testing.T) { cases := []struct { name string productID int sku string want string }{ {"the SKU almost every product shares", 844, "1", "844"}, {"blank SKU", 845, "", "845"}, {"whitespace only", 846, " ", "846"}, {"too short to be a barcode", 847, "1234567", "847"}, {"too long to be a barcode", 848, "123456789012345", "848"}, {"not digits", 849, "SKU-ABC-123", "849"}, {"digits with a space", 850, "1234 5678", "850"}, // Real scannable codes are used as-is, so the day the catalogue carries // them scanning starts working with no code change. {"EAN-8", 851, "12345678", "12345678"}, {"UPC-A", 852, "012345678905", "012345678905"}, {"EAN-13", 853, "8901030865278", "8901030865278"}, {"padded EAN-13", 854, " 8901030865278 ", "8901030865278"}, } for _, c := range cases { t.Run(c.name, func(t *testing.T) { if got := posBarcode(c.productID, c.sku); got != c.want { t.Errorf("posBarcode(%d, %q) = %q, want %q", c.productID, c.sku, got, c.want) } }) } } func TestPosBarcodesAreUniqueAcrossACatalogueOfSharedSkus(t *testing.T) { // The failure this exists to prevent: a whole catalogue collapsing onto one // barcode and the import being rejected by the terminal's unique index. seen := make(map[string]int) for id := 844; id < 844+500; id++ { barcode := posBarcode(id, "1") if first, clash := seen[barcode]; clash { t.Fatalf("products %d and %d both produced barcode %q", first, id, barcode) } seen[barcode] = id } } func TestRoundStockQtyNeverUnderDeducts(t *testing.T) { // productstocks.quantity is an integer column and a counter sells 1.5 kg of // onions. Rounding up keeps recorded stock at or below what is on the shelf; // truncating would let the shop oversell a little more with every sale. cases := []struct { quantity float64 want int }{ {1, 1}, {1.5, 2}, {0.25, 1}, {2.0, 2}, {2.01, 3}, {0, 1}, {-1, 1}, } for _, c := range cases { if got := roundStockQty(c.quantity); got != c.want { t.Errorf("roundStockQty(%g) = %d, want %d", c.quantity, got, c.want) } } } func TestLegacyOrderQtyIsUnchanged(t *testing.T) { // App orders have always truncated, and that behaviour is deliberately // preserved rather than corrected — changing it would silently alter stock // deduction for every order already flowing through createOrderTx. cases := []struct { quantity float64 want int }{ {1, 1}, {1.5, 1}, {0.5, 1}, {3.9, 3}, {0, 1}, } for _, c := range cases { if got := legacyOrderQty(c.quantity); got != c.want { t.Errorf("legacyOrderQty(%g) = %d, want %d", c.quantity, got, c.want) } } }