package server import ( "encoding/json" "net/http" "os" "path/filepath" "strings" "testing" "time" "housecarl/internal/api" ) func TestHealthz(t *testing.T) { e := newEnv(t) link := filepath.Join(t.TempDir(), "nftables.service") e.srv.unitLinks = []string{link} get := func() (int, health) { t.Helper() e.srv.health.at = time.Time{} // no caching between the steps resp, err := http.Get(e.ts.URL + "/healthz") if err != nil { t.Fatal(err) } defer resp.Body.Close() var h health json.NewDecoder(resp.Body).Decode(&h) return resp.StatusCode, h } // Nothing configured yet: nothing to be in force. if code, h := get(); code != 200 || !h.OK || h.Firewall != "unconfigured" || h.Version == "" { t.Errorf("unconfigured: %d %+v", code, h) } if code := e.call(e.token, "POST", "/zones", map[string]any{"name": "wan", "interfaces": []string{"wan"}, "external": true}, nil); code != 201 { t.Fatalf("creating a zone: %d", code) } if code, h := get(); code != 200 || h.Firewall != "applied" || h.Probation != 1 || len(h.Warnings) != 0 { t.Errorf("applied, on probation: %d %+v", code, h) } os.WriteFile(link, nil, 0o644) if _, h := get(); strings.Join(h.Warnings, ",") != "nftables_service" { t.Errorf("warnings %v", h.Warnings) } e.fw.mu.Lock() e.fw.fallback = true e.fw.mu.Unlock() if code, h := get(); code != 503 || h.OK || h.Firewall != "fallback" { t.Errorf("fallback: %d %+v", code, h) } var st api.Status e.call(e.token, "GET", "/status", nil, &st) if st.Applied || !strings.Contains(st.ApplyError, "boot fallback") { t.Errorf("status with the fallback: applied %v, %q", st.Applied, st.ApplyError) } // Asked often, the ruleset is read once per few seconds. e.fw.mu.Lock() e.fw.fallback = false e.fw.mu.Unlock() resp, _ := http.Get(e.ts.URL + "/healthz") resp.Body.Close() if resp.StatusCode != 503 { t.Errorf("not cached: %d", resp.StatusCode) } }