package setup_test import ( "bytes" "encoding/json" "net/http" "net/http/httptest" "strings" "testing" "github.com/dariuszjaniak/warp/server/internal/config" "github.com/dariuszjaniak/warp/server/internal/setup" "github.com/dariuszjaniak/warp/server/internal/userapi" ) // post sends a JSON body through the fully wrapped handler. func post(t *testing.T, srv *setup.Server, path string, body any, token string) *httptest.ResponseRecorder { t.Helper() raw, err := json.Marshal(body) if err != nil { t.Fatal(err) } req := httptest.NewRequest(http.MethodPost, path, bytes.NewReader(raw)) req.Header.Set("Content-Type", "application/json") if token != "" { req.Header.Set("Authorization", "Bearer "+token) } rec := httptest.NewRecorder() srv.Handler().ServeHTTP(rec, req) return rec } // getWithToken is `get` plus an access token. func getWithToken(t *testing.T, srv *setup.Server, path, token string) *httptest.ResponseRecorder { t.Helper() req := httptest.NewRequest(http.MethodGet, path, nil) req.Header.Set("Authorization", "Bearer "+token) rec := httptest.NewRecorder() srv.Handler().ServeHTTP(rec, req) return rec } // TestClientAPIIsWiredUp walks a client's first minutes through the whole // stack — middleware, router, metrics, components — rather than through a // hand-built router, which is what the per-package tests use. It is here to // catch a route that was written but never registered. func TestClientAPIIsWiredUp(t *testing.T) { srv := newTestServer(t, func(c *config.Config) { c.Registration.Enabled = true // The limiter keys on the client address, and every httptest // request comes from the same one. c.ClientAPI.RateLimit.Enabled = false }) rec := post(t, srv, "/_matrix/client/v3/register", map[string]any{"username": "alice", "password": "sUp3rs3kr1t"}, "") if rec.Code != http.StatusUnauthorized { t.Fatalf("register status = %d, want 401 (body: %s)", rec.Code, rec.Body.String()) } session := decode[map[string]any](t, rec)["session"] rec = post(t, srv, "/_matrix/client/v3/register", map[string]any{ "username": "alice", "password": "sUp3rs3kr1t", "auth": map[string]any{"type": "m.login.dummy", "session": session}, }, "") if rec.Code != http.StatusOK { t.Fatalf("register status = %d, want 200 (body: %s)", rec.Code, rec.Body.String()) } registered := decode[map[string]any](t, rec) token, _ := registered["access_token"].(string) if token == "" { t.Fatalf("registration returned no access token: %s", rec.Body.String()) } // /sync is not in this list: it belongs to the syncapi, which needs // the roomserver, which needs a signing key this server was started // without. TestSyncIsWiredUp covers it. for _, path := range []string{ "/_matrix/client/v3/account/whoami", "/_matrix/client/v3/capabilities", "/_matrix/client/v3/devices", } { if rec := getWithToken(t, srv, path, token); rec.Code != http.StatusOK { t.Errorf("GET %s = %d, want 200 (body: %s)", path, rec.Code, rec.Body.String()) } } // Profiles are reachable without a token. if rec := get(t, srv, http.MethodGet, "/_matrix/client/v3/profile/@alice:warp.example"); rec.Code != http.StatusOK { t.Errorf("GET /profile = %d, want 200 (body: %s)", rec.Code, rec.Body.String()) } rec = post(t, srv, "/_matrix/client/v3/login", map[string]any{ "type": "m.login.password", "identifier": map[string]any{"type": "m.id.user", "user": "alice"}, "password": "sUp3rs3kr1t", }, "") if rec.Code != http.StatusOK { t.Fatalf("login status = %d, want 200 (body: %s)", rec.Code, rec.Body.String()) } } // TestAdminAPIIsWiredUp does the same for /_warp/admin/v1, and checks the // one thing only the full stack can show: that the admin routes are labelled // for the metrics like any other. func TestAdminAPIIsWiredUp(t *testing.T) { srv := newTestServer(t, func(c *config.Config) { c.Metrics.Enabled = true }) account, err := srv.Users().CreateAccount(t.Context(), userapi.NewAccountRequest{ Localpart: "root", Password: "sUp3rs3kr1t", Admin: true, }) if err != nil { t.Fatalf("CreateAccount: %v", err) } session, err := srv.Users().CreateSession(t.Context(), userapi.SessionRequest{ Localpart: account.Localpart, }) if err != nil { t.Fatalf("CreateSession: %v", err) } rec := getWithToken(t, srv, "/_warp/admin/v1/stats", session.AccessToken) if rec.Code != http.StatusOK { t.Fatalf("GET /stats = %d, want 200 (body: %s)", rec.Code, rec.Body.String()) } if got := decode[map[string]any](t, rec)["server_name"]; got != "warp.example" { t.Errorf("server_name = %v", got) } // Flipping federation through the admin API must take effect on the // gate, with no restart: PLAN.md section 3. if rec := get(t, srv, http.MethodGet, "/.well-known/matrix/server"); rec.Code != http.StatusNotFound { t.Fatalf("with federation off, /.well-known/matrix/server = %d, want 404", rec.Code) } req := httptest.NewRequest(http.MethodPut, "/_warp/admin/v1/federation", bytes.NewReader([]byte(`{"enabled":true}`))) req.Header.Set("Authorization", "Bearer "+session.AccessToken) rec = httptest.NewRecorder() srv.Handler().ServeHTTP(rec, req) if rec.Code != http.StatusOK { t.Fatalf("PUT /federation = %d, want 200 (body: %s)", rec.Code, rec.Body.String()) } if !srv.Settings().FederationEnabled() { t.Error("the federation switch did not take effect") } metrics := get(t, srv, http.MethodGet, "/metrics").Body.String() if !strings.Contains(metrics, `route="GET /_warp/admin/v1/stats"`) { t.Error("admin routes are not labelled in the metrics") } } // TestRegistrationIsOffByDefault is the posture a new install starts in: // private, with no way in until the operator opens one. func TestRegistrationIsOffByDefault(t *testing.T) { srv := newTestServer(t, nil) rec := post(t, srv, "/_matrix/client/v3/register", map[string]any{"username": "alice", "password": "sUp3rs3kr1t"}, "") if rec.Code != http.StatusForbidden { t.Fatalf("status = %d, want 403 with registration off (body: %s)", rec.Code, rec.Body.String()) } if code := decode[map[string]string](t, rec)["errcode"]; code != "M_FORBIDDEN" { t.Errorf("errcode = %q, want M_FORBIDDEN", code) } }