Files
Felis/internal/api/handlers_mcuuid_test.go

71 lines
3.2 KiB
Go

package api
import (
"encoding/json"
"net/http"
"testing"
)
// Every door that takes an mc_uuid refuses text that is not a UUID with 400
// bad_mc_uuid. The columns are Postgres uuids, and such text used to reach the
// database, fail there with 22P02 and come back as a 500. A UUID in another
// spelling is stored and echoed in the one Postgres gives back.
func TestMCUUIDMustBeAUUID(t *testing.T) {
repo := newFakeRepo()
repo.byName["survival"] = &ServerRecord{Name: "survival", OwnerID: "u1"}
repo.claimOK["survival"] = true
repo.seedUser(UserView{ID: "u2", Username: "alice", Role: "user"})
cl := newFakeCluster()
cl.byName["survival"] = &ServerInfo{Name: "survival", AutostartPolicy: "public"}
api := newTestAPI(repo, cl)
api.External = staticExternal{p: &Principal{UserID: "owner1", Role: "owner", ViaAdminAccess: true}}
ih, eh := api.InternalHandler(), api.ExternalHandler()
const bad = "not-a-uuid"
body := `{"mc_uuid":"` + bad + `"}`
for _, c := range []struct {
h http.Handler
method, path, body string
}{
{ih, "POST", "/api/v1/internal/servers/survival/join-event", body},
{ih, "POST", "/api/v1/internal/servers/survival/wake", body},
{ih, "POST", "/api/v1/internal/servers/survival/claim", body},
{ih, "GET", "/api/v1/internal/player/menu-access/" + bad, ""},
{ih, "POST", "/api/v1/internal/account/link/code", body},
{ih, "GET", "/api/v1/internal/account/link/status/" + bad, ""},
{ih, "POST", "/api/v1/internal/account/migrate/start", body},
{ih, "GET", "/api/v1/internal/player/blacklist/" + bad, ""},
{eh, "PUT", "/api/v1/servers/survival/allowlist/" + bad, `{"can_wake":true}`},
{eh, "DELETE", "/api/v1/users/u2/links/" + bad, ""},
{eh, "POST", "/api/v1/users/u2/links", body},
} {
w := do(c.h, c.method, c.path, c.body, jsonHeader)
if w.Code != http.StatusBadRequest || decodeErr(t, w) != "bad_mc_uuid" {
t.Errorf("%s %s with %q: code = %d body %s, want 400 bad_mc_uuid", c.method, c.path, bad, w.Code, w.Body.String())
}
}
if len(repo.joins) != 0 || len(repo.linkCodes) != 0 || len(repo.links) != 0 || len(repo.audits) != 0 {
t.Fatalf("a refused mc_uuid wrote joins=%v codes=%v links=%v audits=%v", repo.joins, repo.linkCodes, repo.links, repo.audits)
}
const spelled, canonical = " 069A79F444E94726A5BEFCA90E38AAF5 ", "069a79f4-44e9-4726-a5be-fca90e38aaf5"
w := do(eh, "POST", "/api/v1/users/u2/links", `{"mc_uuid":"`+spelled+`"}`, jsonHeader)
var linked struct {
MCUUID string `json:"mc_uuid"`
}
if err := json.Unmarshal(w.Body.Bytes(), &linked); err != nil || w.Code != http.StatusOK || linked.MCUUID != canonical {
t.Fatalf("link of %q: code = %d body %s, want 200 echoing %s", spelled, w.Code, w.Body.String(), canonical)
}
if repo.links[canonical] != "u2" || len(repo.links) != 1 {
t.Fatalf("links = %v, want only %s → u2", repo.links, canonical)
}
if w := do(ih, "POST", "/api/v1/internal/account/link/code", `{"mc_uuid":"`+spelled+`"}`, jsonHeader); w.Code != http.StatusCreated {
t.Fatalf("link code for %q: code = %d body %s, want 201", spelled, w.Code, w.Body.String())
}
for _, lc := range repo.linkCodes {
if lc.mcUUID != canonical {
t.Fatalf("link code minted for %q, want %s", lc.mcUUID, canonical)
}
}
}