// ui_host_mode_test.go — covers handleUIHostModeSave: toggling a // host's always-on flag via POST /hosts/{id}/mode. package http import ( "context" stdhttp "net/http" "net/url" "strings" "testing" ) // TestHostModeSaveToggle verifies the checkbox-absent ⇒ intermittent // and checkbox-present ⇒ always-on semantics, and that the audit row // lands for each request. func TestHostModeSaveToggle(t *testing.T) { t.Parallel() _, ts, st := rawTestServerWithUI(t) hostID, _ := enrolHostForUI(t, nil, st, "mode-toggle-host") cookie := loginAsAdmin(t, st) cli := &stdhttp.Client{ CheckRedirect: func(*stdhttp.Request, []*stdhttp.Request) error { return stdhttp.ErrUseLastResponse }, } // --- POST with no always_on field => intermittent --- form := url.Values{} req, _ := stdhttp.NewRequest("POST", ts.URL+"/hosts/"+hostID+"/mode", strings.NewReader(form.Encode())) req.Header.Set("Content-Type", "application/x-www-form-urlencoded") req.AddCookie(cookie) res, err := cli.Do(req) if err != nil { t.Fatalf("do: %v", err) } _ = res.Body.Close() if res.StatusCode != stdhttp.StatusSeeOther { t.Fatalf("status: got %d, want 303", res.StatusCode) } if loc := res.Header.Get("Location"); loc != "/hosts/"+hostID { t.Errorf("Location: got %q, want /hosts/%s", loc, hostID) } got, err := st.GetHost(context.Background(), hostID) if err != nil { t.Fatalf("GetHost: %v", err) } if got.AlwaysOn { t.Errorf("AlwaysOn after empty form: got true, want false") } // --- POST with always_on=on => always-on --- form2 := url.Values{"always_on": {"on"}} req2, _ := stdhttp.NewRequest("POST", ts.URL+"/hosts/"+hostID+"/mode", strings.NewReader(form2.Encode())) req2.Header.Set("Content-Type", "application/x-www-form-urlencoded") req2.AddCookie(cookie) res2, err := cli.Do(req2) if err != nil { t.Fatalf("do: %v", err) } _ = res2.Body.Close() if res2.StatusCode != stdhttp.StatusSeeOther { t.Fatalf("status: got %d, want 303", res2.StatusCode) } got2, err := st.GetHost(context.Background(), hostID) if err != nil { t.Fatalf("GetHost: %v", err) } if !got2.AlwaysOn { t.Errorf("AlwaysOn after always_on=on: got false, want true") } // Audit rows must exist (one per request). var n int if err := st.DB().QueryRow( `SELECT COUNT(*) FROM audit_log WHERE action = 'host.mode_updated' AND target_id = ?`, hostID).Scan(&n); err != nil { t.Fatalf("count audit: %v", err) } if n != 2 { t.Errorf("audit rows: got %d, want 2", n) } }