b17e9e993c
gates / gates (push) Successful in 6m44s
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0159rPz1ZhFKsS53msqPYxtS
131 lines
5.1 KiB
Go
131 lines
5.1 KiB
Go
package web
|
|
|
|
import (
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
|
|
"gitea.dooplex.hu/admin/felhom-hub/internal/store"
|
|
)
|
|
|
|
// R-1085, the input half — Scenario D: both floor forms refuse a withdrawn build (with or without the "v"), store
|
|
// NOTHING, and still save an ordinary version. Red-proof: remove a refuseSpoiledFloor call → its form saves the tag.
|
|
func TestGlobalFloorForm_RefusesAWithdrawnBuild(t *testing.T) {
|
|
s, st := newTestServer(t)
|
|
if err := st.SetGlobalMinControllerVersion("0.314.0"); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, v := range []string{"0.315.0", "v0.316.3"} {
|
|
loc := postGlobalFloor(t, s, url.Values{"min_controller_version": {v}})
|
|
if !strings.Contains(loc, "flash=floor_spoiled") {
|
|
t.Fatalf("%s: want flash=floor_spoiled, got %q", v, loc)
|
|
}
|
|
if got := st.GetGlobalMinControllerVersion(); got != "0.314.0" {
|
|
t.Fatalf("%s: a refused form must store nothing — the floor is now %q", v, got)
|
|
}
|
|
}
|
|
if loc := postGlobalFloor(t, s, url.Values{"min_controller_version": {"0.317.0"}}); !strings.Contains(loc, "flash=floor_set") ||
|
|
st.GetGlobalMinControllerVersion() != "0.317.0" {
|
|
t.Fatalf("an ordinary version must still save, got %q / %q", loc, st.GetGlobalMinControllerVersion())
|
|
}
|
|
}
|
|
|
|
func TestCustomerFloorForm_RefusesAWithdrawnBuild(t *testing.T) {
|
|
s, st := newTestServer(t)
|
|
if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "c1", RetrievalPassword: "x", APIKey: "y"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
post := func(v string) string {
|
|
form := url.Values{"min_controller_version": {v}}
|
|
r := httptest.NewRequest(http.MethodPost, "/customers/c1/floor", strings.NewReader(form.Encode()))
|
|
r.Header.Set("Content-Type", "application/x-www-form-urlencoded")
|
|
w := httptest.NewRecorder()
|
|
s.handleSetCustomerFloor(w, r, "c1")
|
|
return w.Header().Get("Location")
|
|
}
|
|
for _, v := range []string{"v0.315.0", "0.316.3"} {
|
|
if loc := post(v); !strings.Contains(loc, "flash=floor_spoiled") {
|
|
t.Fatalf("%s: want flash=floor_spoiled, got %q", v, loc)
|
|
}
|
|
if cfg, _ := st.GetCustomerConfig("c1"); cfg.MinControllerVersion != "" {
|
|
t.Fatalf("%s: a refused override must store nothing, got %q", v, cfg.MinControllerVersion)
|
|
}
|
|
}
|
|
if loc := post("0.317.0"); !strings.Contains(loc, "flash=floor_set") {
|
|
t.Fatalf("an ordinary version must still save, got %q", loc)
|
|
}
|
|
}
|
|
|
|
// The confirm dialog's probe answers valid:false, spoiled:true — and counts nothing — for a withdrawn build.
|
|
func TestGlobalFloorImpact_WithdrawnBuildIsNotValid(t *testing.T) {
|
|
s, st := newTestServer(t)
|
|
seedReport(t, st, "old", "0.300.0")
|
|
probe := func(v string) (valid, spoiled bool, below int) {
|
|
w := httptest.NewRecorder()
|
|
s.handleGlobalFloorImpact(w, httptest.NewRequest(http.MethodGet, "/configuration/global-floor/impact?v="+v, nil))
|
|
var resp struct {
|
|
Valid bool `json:"valid"`
|
|
Spoiled bool `json:"spoiled"`
|
|
Below int `json:"below"`
|
|
}
|
|
if err := json.Unmarshal(w.Body.Bytes(), &resp); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
return resp.Valid, resp.Spoiled, resp.Below
|
|
}
|
|
if valid, spoiled, below := probe("0.316.3"); valid || !spoiled || below != 0 {
|
|
t.Errorf("withdrawn: want valid=false spoiled=true below=0, got %v %v %d", valid, spoiled, below)
|
|
}
|
|
if valid, spoiled, below := probe("0.317.0"); !valid || spoiled || below != 1 {
|
|
t.Errorf("ordinary: want valid=true spoiled=false below=1, got %v %v %d", valid, spoiled, below)
|
|
}
|
|
}
|
|
|
|
// Template gates, one render per branch (hub rules: seam-wiring covers template gates): the Configuration page and
|
|
// the customer page say „withdrawn" when the effective floor is a withdrawn build, and say nothing otherwise.
|
|
func TestConfigurationPage_SaysWithdrawnOnlyForAWithdrawnFloor(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
floor string
|
|
want bool
|
|
}{{"0.316.3", true}, {"0.317.0", false}} {
|
|
s, st := newTestServer(t)
|
|
if err := st.SetGlobalMinControllerVersion(tc.floor); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
rr := httptest.NewRecorder()
|
|
s.handleConfiguration(rr, httptest.NewRequest(http.MethodGet, "/configuration", nil))
|
|
if rr.Code != http.StatusOK {
|
|
t.Fatalf("status = %d", rr.Code)
|
|
}
|
|
if got := strings.Contains(rr.Body.String(), `data-floor-withdrawn="1"`); got != tc.want {
|
|
t.Errorf("global floor %s: withdrawn notice shown = %v, want %v", tc.floor, got, tc.want)
|
|
}
|
|
}
|
|
}
|
|
|
|
func TestCustomerPage_SaysWithdrawnOnlyForAWithdrawnFloor(t *testing.T) {
|
|
for _, tc := range []struct {
|
|
floor string
|
|
want bool
|
|
}{{"0.315.0", true}, {"0.317.0", false}} {
|
|
s, st := newTestServer(t)
|
|
if err := st.SaveCustomerConfig(&store.CustomerConfig{CustomerID: "acme", CustomerName: "Acme", Domain: "acme.hu",
|
|
RetrievalPassword: "pw", APIKey: "k", Status: "active"}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.SaveReport("acme", []byte(tabsTestReportJSON)); err != nil { // the floor card renders only for a reporting box
|
|
t.Fatal(err)
|
|
}
|
|
if err := st.SetMinControllerVersion("acme", tc.floor); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
html := renderCustomerPage(t, s, "acme")
|
|
if got := strings.Contains(html, `data-floor-withdrawn="1"`); got != tc.want {
|
|
t.Errorf("customer floor %s: withdrawn notice shown = %v, want %v", tc.floor, got, tc.want)
|
|
}
|
|
}
|
|
}
|