package main import ( "encoding/json" "net/http" "net/http/httptest" "strings" "testing" ) // mockBroker stands in for the enrol-broker admin plane. It asserts the manager // asks for JSON on approve, and returns a decided Enrolment. func mockBroker(t *testing.T) *httptest.Server { t.Helper() mux := http.NewServeMux() mux.HandleFunc("GET /pending", func(w http.ResponseWriter, r *http.Request) { writeJSON(w, http.StatusOK, []brokerEnrolment{{ ID: "enr-1", State: "pending", Fingerprint: "amber-castle-river-delta-orbit-nova", CN: "fleet-host", PubKeyHash: "pk123", Context: brokerCtxContext{MAC: "de:ad:be:ef:00:09", EKHash: "ek-abc"}, }}) }) mux.HandleFunc("POST /approve/{id}", func(w http.ResponseWriter, r *http.Request) { if !strings.Contains(r.Header.Get("Accept"), "application/json") { t.Errorf("approve did not send Accept: application/json (got %q)", r.Header.Get("Accept")) } writeJSON(w, http.StatusOK, brokerEnrolment{ ID: r.PathValue("id"), State: "approved", Fingerprint: "amber-castle-river-delta-orbit-nova", CN: "fleet-host", CertSerial: "beef0009", Hostnqn: "nqn.2026-06.org.example.fleet:host-beef0009", Context: brokerCtxContext{MAC: "de:ad:be:ef:00:09", EKHash: "ek-abc"}, }) }) mux.HandleFunc("POST /deny/{id}", func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(http.StatusNoContent) }) return httptest.NewServer(mux) } func TestAuthoriseApproveBirthsInventory(t *testing.T) { bs := mockBroker(t) defer bs.Close() store, _ := newJSONStore("") s := &server{store: store, broker: &brokerClient{base: bs.URL, hc: bs.Client()}} mux := http.NewServeMux() s.routes(mux) // pending proxies through pw := httptest.NewRecorder() mux.ServeHTTP(pw, httptest.NewRequest(http.MethodGet, "/api/enrol/pending", nil)) if pw.Code != http.StatusOK { t.Fatalf("pending -> %d", pw.Code) } var plist struct { Pending []brokerEnrolment `json:"pending"` } json.Unmarshal(pw.Body.Bytes(), &plist) if len(plist.Pending) != 1 || plist.Pending[0].ID != "enr-1" { t.Fatalf("pending list = %+v", plist.Pending) } // approve with a binding -> inventory record born aw := httptest.NewRecorder() req := httptest.NewRequest(http.MethodPost, "/api/enrol/enr-1/approve", strings.NewReader(`{"asset_tag":"KIOSK-9","switch_port":"sw1/9","location":"Bay 3"}`)) mux.ServeHTTP(aw, req) if aw.Code != http.StatusOK { t.Fatalf("approve -> %d: %s", aw.Code, aw.Body.String()) } h, ok := store.Get("beef0009") if !ok { t.Fatal("inventory record was not born on approve") } if h.Identity.Hostnqn != "nqn.2026-06.org.example.fleet:host-beef0009" { t.Errorf("hostnqn = %q", h.Identity.Hostnqn) } if h.Identity.MAC != "de:ad:be:ef:00:09" || h.Identity.EKHash != "ek-abc" { t.Errorf("identity context not carried: %+v", h.Identity) } if h.Binding.AssetTag != "KIOSK-9" || h.Binding.SwitchPort != "sw1/9" || h.Binding.Location != "Bay 3" { t.Errorf("binding not recorded: %+v", h.Binding) } if h.Enrolled.Fingerprint != "amber-castle-river-delta-orbit-nova" { t.Errorf("fingerprint = %q", h.Enrolled.Fingerprint) } if h.Enrolled.Approver != "dev-insecure" { // no client cert in the test request t.Errorf("approver = %q, want the manager admin CN", h.Enrolled.Approver) } } func TestAuthoriseDisabledWhenNoBroker(t *testing.T) { store, _ := newJSONStore("") s := &server{store: store, broker: nil} // authorise not configured mux := http.NewServeMux() s.routes(mux) rw := httptest.NewRecorder() mux.ServeHTTP(rw, httptest.NewRequest(http.MethodGet, "/api/enrol/pending", nil)) if rw.Code != http.StatusServiceUnavailable { t.Fatalf("expected 503 when no broker, got %d", rw.Code) } }