Files
member-console/internal/server/domains_test.go
T
cgalo5758 8e3c68c6be Make UI surfaces honestly reflect system state
- Add deployment-name branding to titles, mastheads, and OG tags
- Share one grant delivery-state query with lineage across grants
  surfaces
- Show pool status/usage, org owners, and config readiness
- Make billing views projection-aware with recency and sync vocabulary
- Guard FedWiki creation without domains and render route-aware 404s
2026-08-23 01:45:52 -05:00

133 lines
5.4 KiB
Go

package server
import (
"context"
"errors"
"log/slog"
"net/http/httptest"
"strings"
"testing"
)
type fakeAuthorizer struct {
served map[string]bool
err error
lastQuery string
calls int
}
func (f *fakeAuthorizer) AuthorizeDomain(_ context.Context, fqdn string) (bool, error) {
f.calls++
f.lastQuery = fqdn
if f.err != nil {
return false, f.err
}
return f.served[fqdn], nil
}
// TestDomainAskHandler covers the domain-authorization spec scenarios that
// live at the HTTP layer: 200 iff the authorizer says servable, exact-match
// only (no implicit child authorization), normalization before lookup, and
// non-200 for missing parameters and authorizer failures. The
// servable-status semantics (readonly served, archived refused) and the
// legacy-answerer fallback live behind the seam, in the domains registry's
// RegistryAuthorizer (internal/domains/authorizer.go).
//
// wantQuery == "" doubles as "the authorizer must not be reached at all",
// which is what the malformed-name rows assert: the route is
// unauthenticated, so a name that cannot be an FQDN is refused on shape
// before it can cost a lookup.
func TestDomainAskHandler(t *testing.T) {
served := map[string]bool{"alice.example.test": true}
tests := []struct {
name string
target string
served map[string]bool
err error
wantStatus int
wantQuery string
}{
{"servable domain authorized", "/domains/ask?domain=alice.example.test", served, nil, 200, "alice.example.test"},
{"unknown domain refused", "/domains/ask?domain=nope.example.test", served, nil, 404, "nope.example.test"},
{"child of allocated name not implicitly authorized", "/domains/ask?domain=blog.alice.example.test", served, nil, 404, "blog.alice.example.test"},
{"query normalized before lookup", "/domains/ask?domain=Alice.Example.Test.", served, nil, 200, "alice.example.test"},
{"missing domain parameter", "/domains/ask", served, nil, 400, ""},
{"empty domain parameter", "/domains/ask?domain=", served, nil, 400, ""},
{"whitespace-only domain parameter", "/domains/ask?domain=%20%20", served, nil, 400, ""},
{"single label refused without a lookup", "/domains/ask?domain=localhost", served, nil, 400, ""},
{"illegal characters refused without a lookup", "/domains/ask?domain=not_a~domain.example.test", served, nil, 400, ""},
{"empty label refused without a lookup", "/domains/ask?domain=alice..example.test", served, nil, 400, ""},
{"leading-hyphen label refused without a lookup", "/domains/ask?domain=-alice.example.test", served, nil, 400, ""},
{"over-length name refused without a lookup", "/domains/ask?domain=" + strings.Repeat("a.", 130) + "test", served, nil, 400, ""},
{"authorizer failure is non-200", "/domains/ask?domain=alice.example.test", served, errors.New("db down"), 500, "alice.example.test"},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
fake := &fakeAuthorizer{served: tt.served, err: tt.err}
rec := httptest.NewRecorder()
// No Accept header, matching a TLS proxy's on-demand-TLS ask: the
// machine-facing contract (status + plain-text body) applies.
DomainAskHandler(fake, slog.Default(), nil)(rec, httptest.NewRequest("GET", tt.target, nil))
if rec.Code != tt.wantStatus {
t.Errorf("status = %d, want %d", rec.Code, tt.wantStatus)
}
if tt.wantQuery == "" {
if fake.calls != 0 {
t.Errorf("authorizer called %d times for missing/empty param, want 0", fake.calls)
}
} else if fake.lastQuery != tt.wantQuery {
t.Errorf("authorizer queried %q, want %q", fake.lastQuery, tt.wantQuery)
}
})
}
}
// TestDomainAskHandlerBrowserRendering covers the domain-authorization
// delta's added requirement: a malformed request from a browser (Accept:
// text/html) gets the styled, human-readable error page, never a bare
// internal parameter name on an unstyled page; the status and the
// machine-facing plain-text contract are unchanged for a caller that does
// not ask for HTML (spec: domain-authorization).
func TestDomainAskHandlerBrowserRendering(t *testing.T) {
fake := &fakeAuthorizer{served: map[string]bool{}}
tmpl := errorPageTemplates(t)
t.Run("browser sees the styled page", func(t *testing.T) {
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/domains/ask", nil)
req.Header.Set("Accept", "text/html,application/xhtml+xml")
DomainAskHandler(fake, slog.Default(), tmpl)(rec, req)
if rec.Code != 400 {
t.Fatalf("status = %d, want 400", rec.Code)
}
body := rec.Body.String()
if !strings.Contains(body, "<html") {
t.Errorf("browser request got unstyled body: %q", body)
}
if strings.Contains(body, "domain parameter required") {
t.Errorf("styled page still carries the bare internal parameter name: %q", body)
}
if !strings.Contains(body, "missing its domain") {
t.Errorf("styled page missing plain-language explanation: %q", body)
}
})
t.Run("non-browser caller keeps the plain-text contract", func(t *testing.T) {
rec := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/domains/ask", nil) // no Accept header
DomainAskHandler(fake, slog.Default(), tmpl)(rec, req)
if rec.Code != 400 {
t.Fatalf("status = %d, want 400", rec.Code)
}
body := rec.Body.String()
if strings.Contains(body, "<html") {
t.Errorf("non-browser caller got a styled page: %q", body)
}
if strings.TrimSpace(body) != "domain parameter required" {
t.Errorf("body = %q, want the unchanged machine-facing message", body)
}
})
}