- 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
101 lines
3.5 KiB
Go
101 lines
3.5 KiB
Go
package surf
|
|
|
|
import (
|
|
"net/http"
|
|
"reflect"
|
|
"slices"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/party"
|
|
)
|
|
|
|
// TestPierMount (SC-1, D-30, D-33) runs in both builds. Without the
|
|
// summercms_mcp tag: mcp off leaves the route table exactly as without the
|
|
// key, and mcp.enabled true fails boot naming the tag. With the tag (the
|
|
// gate's tagged run): mcp.enabled true mounts POST, GET and DELETE at the
|
|
// endpoint as stream routes and the two protected resource metadata
|
|
// documents, and a plugin route already at the endpoint path fails boot
|
|
// naming its plugin.
|
|
func TestPierMount(t *testing.T) {
|
|
plugins := []party.Plugin{dispatchPlugin{}}
|
|
files := func(mcp string) map[string]string {
|
|
f := map[string]string{"http.yaml": transportBodyLimits, "app.yaml": "name: mount-test\nurl: http://acme.test\n"}
|
|
if mcp != "" {
|
|
f["mcp.yaml"] = mcp
|
|
}
|
|
return f
|
|
}
|
|
|
|
t.Run("off leaves the route table unchanged", func(t *testing.T) {
|
|
base, err := BuildRouter(transportApp(t, "development", files("")), plugins)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, mcp := range []string{"enabled: false\n", "path: /agents\nallow_personal_tokens: true\n"} {
|
|
router, err := BuildRouter(transportApp(t, "development", files(mcp)), plugins)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if !reflect.DeepEqual(router.Routes(), base.Routes()) {
|
|
t.Fatalf("%q changes the route table:\n%+v\n%+v", mcp, router.Routes(), base.Routes())
|
|
}
|
|
}
|
|
})
|
|
|
|
if mountPier == nil {
|
|
t.Run("enabled without the tag fails boot", func(t *testing.T) {
|
|
_, err := BuildRouter(transportApp(t, "development", files("enabled: true\n")), plugins)
|
|
if err == nil || !strings.Contains(err.Error(), MCPBuildTag) {
|
|
t.Fatalf("err = %v, want one naming %s", err, MCPBuildTag)
|
|
}
|
|
})
|
|
return
|
|
}
|
|
|
|
t.Run("enabled mounts the endpoint as stream routes", func(t *testing.T) {
|
|
router, err := BuildRouter(transportApp(t, "development", files("enabled: true\n")), plugins)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
var endpoint []string
|
|
for _, rt := range router.routes {
|
|
if rt.path == "/mcp" {
|
|
if !rt.stream || rt.pluginID != "summercms.pier" || !slices.Contains(rt.middleware, "throttle:120,1") {
|
|
t.Fatalf("%s /mcp = stream %v plugin %q middleware %v", rt.method, rt.stream, rt.pluginID, rt.middleware)
|
|
}
|
|
endpoint = append(endpoint, rt.method)
|
|
}
|
|
}
|
|
slices.Sort(endpoint)
|
|
if !slices.Equal(endpoint, []string{http.MethodDelete, http.MethodGet, http.MethodPost}) {
|
|
t.Fatalf("endpoint methods = %v", endpoint)
|
|
}
|
|
for _, path := range []string{"/.well-known/oauth-protected-resource", "/.well-known/oauth-protected-resource/mcp"} {
|
|
found := false
|
|
for _, ri := range router.Routes() {
|
|
if ri.Method == http.MethodGet && ri.Pattern == path && ri.PluginID == "summercms.pier" {
|
|
found = true
|
|
}
|
|
}
|
|
if !found {
|
|
t.Fatalf("GET %s is not mounted", path)
|
|
}
|
|
}
|
|
if _, ok := router.Handler(http.MethodPost, "/mcp"); !ok {
|
|
t.Fatal("no compiled handler for POST /mcp")
|
|
}
|
|
})
|
|
|
|
t.Run("a plugin route at the endpoint fails boot", func(t *testing.T) {
|
|
squatter := routesPluginFunc{id: "acme.squatter", routes: func(r pact.Router) {
|
|
r.Get("/mcp", func(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) })
|
|
}}
|
|
_, err := BuildRouter(transportApp(t, "development", files("enabled: true\n")), append(plugins, squatter))
|
|
if err == nil || !strings.Contains(err.Error(), "acme.squatter") {
|
|
t.Fatalf("err = %v, want one naming acme.squatter", err)
|
|
}
|
|
})
|
|
}
|