package processor import ( "testing" "apskel-pos-be/internal/constants" "apskel-pos-be/internal/entities" ) func fPtr(v float64) *float64 { return &v } func TestResolveLineQuantity(t *testing.T) { weightProduct := &entities.Product{Name: "Ikan Tude", SellBy: constants.SellByWeight} unitProduct := &entities.Product{Name: "Nasi Goreng", SellBy: constants.SellByUnit} tests := []struct { name string product *entities.Product quantity int weight *float64 wantQuantity int wantWeight *float64 wantErr bool }{ { name: "weighed line is pinned to quantity 1", // Mirrors chk_order_items_weight_single_line: even if a client sends 3, // one weighing is one line. product: weightProduct, quantity: 3, weight: fPtr(4.2), wantQuantity: 1, wantWeight: fPtr(4.2), }, { name: "weight is rounded to stored precision", product: weightProduct, quantity: 1, weight: fPtr(5.6004), wantQuantity: 1, wantWeight: fPtr(5.6), }, { name: "weight product without a weight is rejected", product: weightProduct, quantity: 1, weight: nil, wantErr: true, }, { name: "weight that rounds away to zero is rejected", product: weightProduct, quantity: 1, weight: fPtr(0.0004), wantErr: true, }, { name: "unit line keeps its quantity and carries no weight", product: unitProduct, quantity: 3, weight: nil, wantQuantity: 3, wantWeight: nil, }, { name: "weight on a unit product is rejected", // Otherwise the line would silently be priced by weight. product: unitProduct, quantity: 1, weight: fPtr(4.2), wantErr: true, }, { name: "non-positive quantity on a unit product is rejected", product: unitProduct, quantity: 0, weight: nil, wantErr: true, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { gotQty, gotWeight, err := resolveLineQuantity(tt.product, tt.quantity, tt.weight) if tt.wantErr { if err == nil { t.Fatalf("expected an error, got quantity=%v weight=%v", gotQty, gotWeight) } return } if err != nil { t.Fatalf("unexpected error: %v", err) } if gotQty != tt.wantQuantity { t.Errorf("quantity = %v, want %v", gotQty, tt.wantQuantity) } switch { case tt.wantWeight == nil && gotWeight != nil: t.Errorf("weight = %v, want nil", *gotWeight) case tt.wantWeight != nil && gotWeight == nil: t.Errorf("weight = nil, want %v", *tt.wantWeight) case tt.wantWeight != nil && *gotWeight != *tt.wantWeight: t.Errorf("weight = %v, want %v", *gotWeight, *tt.wantWeight) } }) } } // A product created before this feature has sell_by defaulted to 'unit' by the // migration, but a zero-valued struct must behave the same way. func TestResolveLineQuantityTreatsEmptySellByAsUnit(t *testing.T) { legacy := &entities.Product{Name: "Legacy"} quantity, weight, err := resolveLineQuantity(legacy, 2, nil) if err != nil { t.Fatalf("unexpected error: %v", err) } if quantity != 2 || weight != nil { t.Errorf("got quantity=%v weight=%v, want quantity=2 weight=nil", quantity, weight) } }