Stage 10: admin console & dictionary ops (complaint review, hot-reload, broadcasts) (#11)
Tests · Go / test (push) Successful in 7s
Tests · Integration / integration (push) Successful in 13s

This commit was merged in pull request #11.
This commit is contained in:
2026-06-04 07:27:49 +00:00
parent 4c4beace85
commit 3a640a17a4
49 changed files with 2548 additions and 200 deletions
+23 -16
View File
@@ -9,27 +9,28 @@ import (
"scrabble/gateway/internal/admin"
)
func newAdmin(t *testing.T) (*httptest.Server, func()) {
// newAdmin fronts a fake backend with the admin proxy. The fake backend records the
// path it receives so a test can assert the proxy forwards /_gm verbatim.
func newAdmin(t *testing.T) (front *httptest.Server, gotPath *string, cleanup func()) {
t.Helper()
var path string
backend := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if r.URL.Path != "/api/v1/admin/ping" {
t.Errorf("backend path = %q, want /api/v1/admin/ping", r.URL.Path)
}
_, _ = w.Write([]byte("pong"))
path = r.URL.Path
_, _ = w.Write([]byte("console"))
}))
proxy, err := admin.NewProxy(backend.URL, "ops", "secret", nil)
if err != nil {
t.Fatalf("new proxy: %v", err)
}
front := httptest.NewServer(proxy)
return front, func() { front.Close(); backend.Close() }
front = httptest.NewServer(proxy)
return front, &path, func() { front.Close(); backend.Close() }
}
func TestAdminRejectsMissingCredentials(t *testing.T) {
front, cleanup := newAdmin(t)
front, _, cleanup := newAdmin(t)
defer cleanup()
resp, err := http.Get(front.URL + "/admin/ping")
resp, err := http.Get(front.URL + "/_gm/")
if err != nil {
t.Fatal(err)
}
@@ -37,13 +38,16 @@ func TestAdminRejectsMissingCredentials(t *testing.T) {
if resp.StatusCode != http.StatusUnauthorized {
t.Fatalf("status = %d, want 401", resp.StatusCode)
}
if resp.Header.Get("WWW-Authenticate") == "" {
t.Error("missing WWW-Authenticate challenge")
}
}
func TestAdminProxiesWithCredentials(t *testing.T) {
front, cleanup := newAdmin(t)
func TestAdminProxiesVerbatimWithCredentials(t *testing.T) {
front, gotPath, cleanup := newAdmin(t)
defer cleanup()
req, _ := http.NewRequest(http.MethodGet, front.URL+"/admin/ping", nil)
req, _ := http.NewRequest(http.MethodGet, front.URL+"/_gm/complaints", nil)
req.SetBasicAuth("ops", "secret")
resp, err := http.DefaultClient.Do(req)
if err != nil {
@@ -51,16 +55,19 @@ func TestAdminProxiesWithCredentials(t *testing.T) {
}
defer func() { _ = resp.Body.Close() }()
body, _ := io.ReadAll(resp.Body)
if resp.StatusCode != http.StatusOK || string(body) != "pong" {
t.Fatalf("status = %d body = %q, want 200 pong", resp.StatusCode, body)
if resp.StatusCode != http.StatusOK || string(body) != "console" {
t.Fatalf("status = %d body = %q, want 200 console", resp.StatusCode, body)
}
if *gotPath != "/_gm/complaints" {
t.Errorf("backend path = %q, want /_gm/complaints (verbatim)", *gotPath)
}
}
func TestAdminRejectsWrongPassword(t *testing.T) {
front, cleanup := newAdmin(t)
front, _, cleanup := newAdmin(t)
defer cleanup()
req, _ := http.NewRequest(http.MethodGet, front.URL+"/admin/ping", nil)
req, _ := http.NewRequest(http.MethodGet, front.URL+"/_gm/", nil)
req.SetBasicAuth("ops", "wrong")
resp, err := http.DefaultClient.Do(req)
if err != nil {