- pier: TestCatalog, TestDispatch, TestGuardRules, TestSessions (Redis
across two instances and the memory store), TestFanOut; the store clock
and stateless-server seams in export_test.go
- surf: TestStreamingRoute (throttle, CORS profile and access-log pattern on
a stream route) and TestPierMount in both builds; the streaming smoke
restores the mount hook it replaces
- wristband TestMCPScopes; cabana TestAccountContributions (Postgres part:
transaction and unchanged My account payloads), TestRecordActionPrompt,
TestScopeLimitForMethods; TestRecordActionPromptRun pins the empty Value
of an action without a prompt
- vitest: 403 and failed-load cases for My account contributions, the
cancelled prompt
- gate: every name required (tagged runs included), account.go unchanged
since 51d088a, admin stage live
50 lines
2.1 KiB
Go
50 lines
2.1 KiB
Go
package cabana
|
|
|
|
import (
|
|
"net/http"
|
|
"slices"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/party"
|
|
)
|
|
|
|
// TestScopeLimitForMethods (phase 19.7 D-22, the scope ceiling of an admin
|
|
// personal token on the MCP endpoint): ScopeLimitFor filters a token's
|
|
// stored scopes by the dispatched route's method exactly as the backend
|
|
// guard does for a request. A GET-only scope limits GET and leaves no scope
|
|
// for POST (known 1, method-allowed 0), "*" is full for every method, and
|
|
// scopes the registry no longer knows count as none (unauthenticated).
|
|
func TestScopeLimitForMethods(t *testing.T) {
|
|
reg, err := compileTokenCatalog([]party.Plugin{scopePlugin{
|
|
id: "acme.demo",
|
|
perms: []pact.Permission{{Code: "acme.demo.access", Roles: []string{"developer"}}},
|
|
scopes: []pact.TokenScope{{
|
|
Name: "demo-read", Label: "Demo", Description: "Reads the demo.",
|
|
Permissions: []string{"acme.demo.access"}, Methods: []string{"GET", "HEAD"},
|
|
}},
|
|
}})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
app := backpack.New(nil)
|
|
if err := app.Publish(&backendResolver{guard: &compositeGuard{reg: reg}}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
|
|
limit, known, ok := ScopeLimitFor(app, []string{"demo-read"}, http.MethodGet)
|
|
if limit == nil || limit.Full || known != 1 || ok != 1 || len(limit.Scopes) != 1 || !slices.Equal(limit.Scopes[0], []string{"acme.demo.access"}) {
|
|
t.Fatalf("GET = %+v, %d, %d", limit, known, ok)
|
|
}
|
|
if limit, known, ok := ScopeLimitFor(app, []string{"demo-read"}, http.MethodPost); limit != nil || known != 1 || ok != 0 {
|
|
t.Fatalf("POST = %+v, %d, %d, want no scope for the method", limit, known, ok)
|
|
}
|
|
if limit, known, ok := ScopeLimitFor(app, []string{"*"}, http.MethodDelete); limit == nil || !limit.Full || known != 1 || ok != 1 {
|
|
t.Fatalf("* DELETE = %+v, %d, %d, want full", limit, known, ok)
|
|
}
|
|
if limit, known, ok := ScopeLimitFor(app, []string{"gone"}, http.MethodGet); limit != nil || known != 0 || ok != 0 {
|
|
t.Fatalf("unknown scope = %+v, %d, %d", limit, known, ok)
|
|
}
|
|
}
|