107 lines
3.0 KiB
Go
107 lines
3.0 KiB
Go
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)
|
||
|
|
}
|
||
|
|
}
|