api for health score toggle test reverse

This commit is contained in:
2026-10-06 14:58:17 +05:30
parent fc2caffcd1
commit f67cbad79a
2 changed files with 89 additions and 1 deletions

View File

@@ -214,7 +214,18 @@ type Products struct {
// The PLATFORM console ignores this: Nearle staff see every score on every // The PLATFORM console ignores this: Nearle staff see every score on every
// product, because the decision being made there is whether the data is good // product, because the decision being made there is whether the data is good
// enough to publish at all. // enough to publish at all.
Showhealthscore bool `json:"showhealthscore" gorm:"column:showhealthscore;default:true"` //
// NO `default` IN THE GORM TAG, and that is not an oversight. GORM skips a
// zero-value field whose tag names a default, so `false` was never written:
// the INSERT omitted the column, the database default of true applied, and a
// product imported with the score switched off came back switched on. It
// shipped that way and was found by importing one and reading it back.
//
// The DEFAULT lives on the column instead, set by the migration in main.go.
// That still covers what it is for — rows that predate the column, and any
// INSERT that genuinely omits it — without teaching GORM to drop a
// deliberate false on the way past.
Showhealthscore bool `json:"showhealthscore" gorm:"column:showhealthscore"`
Productdesc string `json:"productdesc,omitempty"` Productdesc string `json:"productdesc,omitempty"`
Productsku string `json:"productsku,omitempty"` Productsku string `json:"productsku,omitempty"`
@@ -331,6 +342,19 @@ type Locationproducts struct {
Diffpercent float64 `json:"diffpercent,omitempty"` Diffpercent float64 `json:"diffpercent,omitempty"`
Othercost float64 `json:"othercost,omitempty"` Othercost float64 `json:"othercost,omitempty"`
Approve int `json:"approve" gorm:"default:0"` Approve int `json:"approve" gorm:"default:0"`
// Whether this product's health score is shown to shoppers.
//
// It has to be HERE and not only on Products, because this is the struct the
// admin catalogue reads. Without it the console received no value at all,
// the drawer's switch rendered "on" for every product including the ones
// that were off, and a product already hidden showed no panel and so no way
// to turn it back on. The column was being written correctly the whole time
// and simply never read back — which also made every "it did not save"
// reading taken from this endpoint meaningless.
//
// No `omitempty`: a false has to survive the trip, and omitempty would drop
// exactly the value this field exists to carry.
Showhealthscore bool `json:"showhealthscore"`
// Productstatus string `json:"productstatus" gorm:"default:available"` // Productstatus string `json:"productstatus" gorm:"default:available"`
Status string `json:"status" gorm:"default:outofstock"` Status string `json:"status" gorm:"default:outofstock"`

View File

@@ -0,0 +1,64 @@
package models
import (
"reflect"
"strings"
"testing"
)
/*
`showhealthscore` must not carry a GORM default.
GORM skips a zero-value field whose tag names a default — the documented
behaviour is that a `false`, `0` or `""` is left out of the INSERT so the
database default applies. For a boolean whose whole purpose is being set to
false, that means the one value anybody would set it to is the one that cannot
be written.
It shipped that way. A product imported with the health score switched off came
back switched on, and it took importing one and reading it back to find out,
because every layer above reported success: the console sent `false`, the
request carried `false`, the handler read `false`, GORM dropped it, and the
column default wrote `true`.
The DEFAULT belongs on the column, set by the migration in main.go. That covers
rows predating the column and any INSERT that genuinely omits it, without
teaching the ORM to discard a deliberate false on the way past.
*/
func TestShowHealthScoreCarriesNoGormDefault(t *testing.T) {
field, ok := reflect.TypeFor[Products]().FieldByName("Showhealthscore")
if !ok {
t.Fatal("Products.Showhealthscore has moved or been renamed")
}
tag := field.Tag.Get("gorm")
if strings.Contains(strings.ToLower(tag), "default") {
t.Fatalf(
"gorm tag %q names a default. GORM then skips this field when it is false, "+
"so a product whose health score is switched off is written as switched on. "+
"The column default is set by the migration in main.go instead.",
tag,
)
}
// The column still has to be named, since the Go field is one word and the
// column is too but GORM's default naming would make it `show_health_score`.
if !strings.Contains(tag, "column:showhealthscore") {
t.Errorf("gorm tag %q no longer names the column", tag)
}
}
func TestShowHealthScoreIsAPlainBoolOnTheWire(t *testing.T) {
// Not a pointer, and not `omitempty`. Every product says what it is: the
// console reads it to set the switch, and an absent key would be
// indistinguishable from false on a screen that has to show one or the
// other.
field, _ := reflect.TypeFor[Products]().FieldByName("Showhealthscore")
if field.Type.Kind() != reflect.Bool {
t.Errorf("Showhealthscore is %s, want bool", field.Type.Kind())
}
if tag := field.Tag.Get("json"); tag != "showhealthscore" {
t.Errorf("json tag is %q — an omitempty here would hide every `false`", tag)
}
}