changes with app product stock

This commit is contained in:
2026-08-28 12:43:07 +05:30
parent 46da26c452
commit aef105979a
3 changed files with 175 additions and 0 deletions

View File

@@ -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)
}
}