Files
Felis/internal/api/handlers_auth_owner_test.go
T

90 lines
3.4 KiB
Go

package api
import (
"encoding/json"
"errors"
"net/http"
"net/http/httptest"
"testing"
)
const ownerStatusPath = "/api/v1/auth/owner-status"
func ownerBoundOf(t *testing.T, w *httptest.ResponseRecorder) bool {
t.Helper()
if w.Code != http.StatusOK {
t.Fatalf("code = %d, want 200 (%s)", w.Code, w.Body.String())
}
var v struct {
OwnerBound *bool `json:"owner_bound"`
}
if err := json.Unmarshal(w.Body.Bytes(), &v); err != nil || v.OwnerBound == nil {
t.Fatalf("body = %s, want {\"owner_bound\": bool} (err %v)", w.Body.String(), err)
}
return *v.OwnerBound
}
// TestOwnerStatusReportsAnUnclaimedInstall pins what the sign-in page reads: false before
// any staff account exists, true once one does, and a player account alone is not an
// Owner. It answers with local auth still off, which is the state it exists to explain.
func TestOwnerStatusReportsAnUnclaimedInstall(t *testing.T) {
repo := newFakeRepo()
api := newTestAPI(repo, newFakeCluster())
if localAuthEnabled(t.Context(), repo) {
t.Fatal("precondition: a fresh fake must have local auth off")
}
if ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Error("an install with no accounts reports an Owner")
}
repo.staff["player"] = &StaffUser{ID: "u1", Username: "player", Role: "user"}
if ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Error("a player account alone reports an Owner")
}
repo.staff["boss"] = &StaffUser{ID: "o1", Username: "boss", Role: "owner"}
if !ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Error("an install with an Owner reports none")
}
}
// TestOwnerStatusCachesTheBoundAnswer pins that once an Owner is seen the probe stops
// querying: a store that then fails still gets the cached true.
func TestOwnerStatusCachesTheBoundAnswer(t *testing.T) {
repo := newFakeRepo()
repo.staff["boss"] = &StaffUser{ID: "o1", Username: "boss", Role: "owner"}
api := newTestAPI(repo, newFakeCluster())
if !ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Fatal("an install with an Owner reports none")
}
repo.failAdminExists = errors.New("store down")
if !ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Error("the bound answer was not cached")
}
}
// TestOwnerStatusStoreFailure pins that an outage is a 503, never a false "no Owner":
// the page must fall back to its doors rather than tell a claimed install to run setup.
// An unbound answer is not cached either, so the next call reads the store again.
func TestOwnerStatusStoreFailure(t *testing.T) {
repo := newFakeRepo()
api := newTestAPI(repo, newFakeCluster())
repo.failAdminExists = errors.New("store down")
w := do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)
if w.Code != http.StatusServiceUnavailable {
t.Fatalf("code = %d, want 503 (%s)", w.Code, w.Body.String())
}
if got := decodeErr(t, w); got != "auth_unavailable" {
t.Errorf("error = %q, want auth_unavailable", got)
}
repo.failAdminExists = nil
if ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Error("an install with no accounts reports an Owner")
}
repo.staff["boss"] = &StaffUser{ID: "o1", Username: "boss", Role: "owner"}
if !ownerBoundOf(t, do(api.ExternalHandler(), "GET", ownerStatusPath, "", nil)) {
t.Error("an unbound answer was cached past the Owner's arrival")
}
}