changes with app product stock
This commit is contained in:
@@ -234,3 +234,122 @@ func TestReimportWithoutACategoryLeavesTheExistingOneAlone(t *testing.T) {
|
||||
t.Errorf("a category-less import overwrote an existing category")
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
"If we have the stock, we can order it" — the customer app's browse rule.
|
||||
|
||||
The products below are Suriya Store's Peelamedu shelf as it actually stood:
|
||||
three products listed, one with stock. Before this filter the app offered all
|
||||
three, so a shopper could order two chocolate bars the shop did not have.
|
||||
*/
|
||||
|
||||
func names(products []models.Products) []string {
|
||||
out := make([]string, 0, len(products))
|
||||
for _, p := range products {
|
||||
out = append(out, p.Productname)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
var peelamedu = []models.Products{
|
||||
{Productid: 7084, Productname: "Cadbury 5 Star 9.8 g", Productstock: 0},
|
||||
{Productid: 7085, Productname: "Cadbury 5 Star 18g", Productstock: 0},
|
||||
{Productid: 7086, Productname: "Cadbury Dairy Milk 100g", Productstock: 25},
|
||||
}
|
||||
|
||||
func TestOnlyProductsWithStockAreOffered(t *testing.T) {
|
||||
got := InStockOnly(peelamedu, 1170)
|
||||
|
||||
if len(got) != 1 {
|
||||
t.Fatalf("offered %d products, want 1: %v", len(got), names(got))
|
||||
}
|
||||
if got[0].Productid != 7086 {
|
||||
t.Errorf("offered %d, want 7086 (the only one in stock)", got[0].Productid)
|
||||
}
|
||||
}
|
||||
|
||||
// An outlet with nothing in stock offers nothing — it does not fall back to
|
||||
// showing the shelf. An empty shop is the honest answer.
|
||||
func TestAnOutletWithNoStockOffersNothing(t *testing.T) {
|
||||
empty := []models.Products{
|
||||
{Productid: 7084, Productstock: 0},
|
||||
{Productid: 7085, Productstock: 0},
|
||||
}
|
||||
if got := InStockOnly(empty, 1172); len(got) != 0 {
|
||||
t.Errorf("offered %d products from an empty shelf", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
// Without an outlet, GetProducts scopes its balance subquery to locationid 0,
|
||||
// which matches nothing and returns every product with Productstock 0. Filtering
|
||||
// there would empty the catalogue instead of leaving it unscoped.
|
||||
func TestAnUnscopedBrowseIsNotFiltered(t *testing.T) {
|
||||
if got := InStockOnly(peelamedu, 0); len(got) != 3 {
|
||||
t.Errorf("unscoped browse returned %d, want all 3 unfiltered", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
// A negative balance is not stock. The ledger can go below zero when an "out"
|
||||
// exceeds what was received, and `> 0` must not read that as sellable.
|
||||
func TestANegativeBalanceIsNotStock(t *testing.T) {
|
||||
oversold := []models.Products{{Productid: 7084, Productstock: -3}}
|
||||
if got := InStockOnly(oversold, 1170); len(got) != 0 {
|
||||
t.Errorf("an oversold product was offered for sale")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFilteringDoesNotDisturbTheCallersSlice(t *testing.T) {
|
||||
source := []models.Products{
|
||||
{Productid: 1, Productstock: 0},
|
||||
{Productid: 2, Productstock: 5},
|
||||
}
|
||||
_ = InStockOnly(source, 1170)
|
||||
if len(source) != 2 || source[0].Productid != 1 || source[1].Productid != 2 {
|
||||
t.Errorf("the input slice was modified: %+v", source)
|
||||
}
|
||||
}
|
||||
|
||||
func (f *fakeProductRepo) GetSubcategories(categoryID int) ([]models.Subcategory, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Returns (nil, nil) so GetProductsBySubcategory falls through to its plain
|
||||
// {"details": ...} shape. The tenant header block is not what this test is about.
|
||||
func (f *fakeProductRepo) GetTenantInfo(tenantID, applocationID int) (map[string]interface{}, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (f *fakeProductRepo) GetProducts(params models.ProductFilter) ([]models.Products, error) {
|
||||
f.calls = append(f.calls, "GetProducts")
|
||||
return peelamedu, nil
|
||||
}
|
||||
|
||||
// The filter has to be WIRED, not merely written.
|
||||
//
|
||||
// The tests above exercise InStockOnly directly, which would keep passing if
|
||||
// nobody called it. This goes through the endpoint the customer app actually
|
||||
// hits and asserts on what a shopper would be shown.
|
||||
func TestTheBrowseEndpointOffersOnlyStockedProducts(t *testing.T) {
|
||||
repo := newFakeRepo()
|
||||
svc := NewProductService(repo, &fakeCatalogueService{})
|
||||
|
||||
result, err := svc.GetProductsBySubcategory(models.ProductFilter{
|
||||
CategoryID: 2, TenantID: 1135, LocationID: 1170,
|
||||
})
|
||||
if err != nil {
|
||||
t.Fatalf("GetProductsBySubcategory: %v", err)
|
||||
}
|
||||
|
||||
groups, _ := result["details"].([]models.SubcategoryProductResponse)
|
||||
var offered []models.Products
|
||||
for _, g := range groups {
|
||||
offered = append(offered, g.Products...)
|
||||
}
|
||||
|
||||
if len(offered) != 1 {
|
||||
t.Fatalf("the app was offered %d products, want 1: %v", len(offered), names(offered))
|
||||
}
|
||||
if offered[0].Productid != 7086 {
|
||||
t.Errorf("offered product %d, want 7086 — the only one Peelamedu has", offered[0].Productid)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user