Files
Warp/server/internal/federation/gate_test.go
T
tajniak81andClaude Opus 5 b4fb60a8ab
CI / Test (ubuntu-latest) (push) Canceled after 0s
CI / Test (windows-latest) (push) Canceled after 0s
CI / Storage tests on PostgreSQL (push) Canceled after 0s
CI / golangci-lint (push) Canceled after 0s
CI / Smoke test the binary (push) Canceled after 0s
Phase 0: bootstrap the Warp homeserver
Scaffolds the server module and everything Phase 0 of PLAN.md calls for:
a binary that boots, serves its discovery endpoints and nothing else yet.

- config: one YAML file plus per-field WARP_* environment overrides,
  unknown fields rejected, validation that reports every problem at once
- logging and metrics: slog, a Prometheus registry, per-route HTTP
  instrumentation labelled by pattern rather than path
- httputil: the Matrix error envelope, error-returning handlers, CORS,
  request logging, panic recovery, and a router over http.ServeMux
- endpoints: /_matrix/client/versions, /.well-known/matrix/client,
  /.well-known/matrix/server (gated), /healthz, /metrics, and
  M_UNRECOGNIZED for everything else
- storage: driver registry, shared database/sql base, checksummed
  migration runner, sqlite and postgres drivers, pocketbase stub
- settings: server_settings table behind a cached service, so the
  federation and registration switches change without a restart
- federation: the gate that makes federation a runtime mode
- cmd/warp: serve, migrate, generate-keys, version
- Makefile, golangci-lint v2, GitHub Actions CI (Linux and Windows tests,
  race on Linux, storage tests against PostgreSQL, lint, boot smoke test),
  Dockerfile, example config and docs

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-10-15 17:01:13 +02:00

98 lines
2.9 KiB
Go

package federation_test
import (
"net/http"
"net/http/httptest"
"strings"
"sync/atomic"
"testing"
"github.com/dariuszjaniak/warp/server/internal/federation"
)
func TestGateFollowsTheSwitch(t *testing.T) {
var enabled atomic.Bool
gate := federation.NewGate(enabled.Load, nil)
handler := gate.Protect(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {
w.WriteHeader(http.StatusOK)
}))
rec := httptest.NewRecorder()
handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/_matrix/federation/v1/version", nil))
if rec.Code != http.StatusNotFound {
t.Fatalf("status = %d, want 404 while federation is off", rec.Code)
}
enabled.Store(true)
rec = httptest.NewRecorder()
handler.ServeHTTP(rec, httptest.NewRequest(http.MethodGet, "/_matrix/federation/v1/version", nil))
if rec.Code != http.StatusOK {
t.Fatalf("status = %d, want 200 once federation is on", rec.Code)
}
}
func TestNilCallbackMeansDisabled(t *testing.T) {
gate := federation.NewGate(nil, nil)
if gate.Enabled() {
t.Error("a gate without a switch must be closed")
}
}
func TestPolicy(t *testing.T) {
cases := []struct {
name string
allowlist []string
denylist []string
server string
want bool
}{
{"empty lists allow anything", nil, nil, "matrix.org", true},
{"denylist blocks", nil, []string{"evil.example"}, "evil.example", false},
{"denylist is case insensitive", nil, []string{"evil.example"}, "Evil.Example", false},
{"allowlist admits members", []string{"a.example"}, nil, "a.example", true},
{"allowlist excludes everyone else", []string{"a.example"}, nil, "b.example", false},
{"denylist wins over allowlist", []string{"a.example"}, []string{"a.example"}, "a.example", false},
{"empty server name is refused", nil, nil, "", false},
{"whitespace is trimmed", []string{"a.example"}, nil, " a.example ", true},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
p := federation.NewPolicy(tc.allowlist, tc.denylist)
if got := p.Allowed(tc.server); got != tc.want {
t.Errorf("Allowed(%q) = %v, want %v", tc.server, got, tc.want)
}
})
}
}
func TestPolicyReplace(t *testing.T) {
p := federation.NewPolicy([]string{"a.example"}, nil)
if !p.Allowed("a.example") {
t.Fatal("a.example should be allowed")
}
p.Replace(nil, []string{"a.example"})
if p.Allowed("a.example") {
t.Error("after Replace, a.example should be denied")
}
allow, deny := p.Lists()
if len(allow) != 0 || len(deny) != 1 || deny[0] != "a.example" {
t.Errorf("Lists() = %v, %v", allow, deny)
}
}
func TestRejectClientRequestExplainsItself(t *testing.T) {
err := federation.RejectClientRequest("joining a room on another server")
if err == nil {
t.Fatal("expected an error")
}
msg := err.Error()
for _, want := range []string{"M_FORBIDDEN", "Federation is disabled", "joining a room"} {
if !strings.Contains(msg, want) {
t.Errorf("error %q should mention %q", msg, want)
}
}
}