- lighthouse: TestSuppression (Widget silenced, Gadget not, nesting, stale outer ctx), TestBulkEmitsOnce, TestBroadcastEdges (zero-key batch, update actor, id-only delete, method contract, multi-channel, savepoint), TestBroadcastPublishFailure, TestFromSelectsDriver, TestMountSurfaces, TestRegistry under -race, drivers, args JSON, Bind (coverage 91.7%) - centrifugo: TestTokenClaims, TestTokenHandler, TestClientRequests, TestClientLoadConfig and a TestProxy table porting the WinterCMS WS-005, WS-007 and WS-013 cases (coverage 92.4%)
143 lines
6.1 KiB
Go
143 lines
6.1 KiB
Go
package lighthouse
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// recRouter records the groups and routes Mount registers.
|
|
type recRouter struct {
|
|
groups []recGroup
|
|
routes []string
|
|
}
|
|
|
|
type recGroup struct {
|
|
raw bool
|
|
prefix string
|
|
middleware []string
|
|
routes []string
|
|
}
|
|
|
|
func (r *recRouter) group(raw bool, prefix string, mw []string, fn func(pact.Router)) {
|
|
inner := &recRouter{}
|
|
fn(inner)
|
|
r.groups = append(r.groups, recGroup{raw: raw, prefix: prefix, middleware: append([]string(nil), mw...), routes: inner.routes})
|
|
}
|
|
|
|
func (r *recRouter) Group(prefix string, mw []string, fn func(pact.Router)) {
|
|
r.group(false, prefix, mw, fn)
|
|
}
|
|
func (r *recRouter) GroupRaw(prefix string, mw []string, fn func(pact.Router)) {
|
|
r.group(true, prefix, mw, fn)
|
|
}
|
|
func (r *recRouter) add(m, p string) { r.routes = append(r.routes, m+" "+p) }
|
|
func (r *recRouter) Get(p string, _ http.HandlerFunc, _ ...string) { r.add("GET", p) }
|
|
func (r *recRouter) Post(p string, _ http.HandlerFunc, _ ...string) { r.add("POST", p) }
|
|
func (r *recRouter) Put(p string, _ http.HandlerFunc, _ ...string) { r.add("PUT", p) }
|
|
func (r *recRouter) Patch(p string, _ http.HandlerFunc, _ ...string) { r.add("PATCH", p) }
|
|
func (r *recRouter) Delete(p string, _ http.HandlerFunc, _ ...string) { r.add("DELETE", p) }
|
|
func (r *recRouter) Where(string, string) {}
|
|
func (r *recRouter) WhereIn(string, ...string) {}
|
|
|
|
// routeDriver is a driver that declares the given routes.
|
|
type routeDriver struct{ routes []Route }
|
|
|
|
func (d routeDriver) Name() string { return "acme" }
|
|
func (d routeDriver) Routes() []Route { return d.routes }
|
|
func (routeDriver) Publish(context.Context, string, string, json.RawMessage) error {
|
|
return nil
|
|
}
|
|
func (routeDriver) Broadcast(context.Context, []string, string, json.RawMessage) error {
|
|
return nil
|
|
}
|
|
|
|
func okHandler(http.ResponseWriter, *http.Request) {}
|
|
|
|
// TestMountSurfaces covers D-13 and T-11-19: UserAuth and Public routes
|
|
// mount in a group with the surface middleware then the shared middleware,
|
|
// ServerToServer routes in a raw group, every method is registered, a
|
|
// UserAuth route without a guard is refused before anything is mounted,
|
|
// and a nil driver or the null driver mounts nothing.
|
|
func TestMountSurfaces(t *testing.T) {
|
|
s := Surfaces{UserAuth: []string{"jwt.auth"}, ServerToServer: []string{"acme.s2s"}, Public: []string{"acme.public"}, Middleware: []string{"throttle:ws-api"}}
|
|
d := routeDriver{routes: []Route{
|
|
{Name: "token", Method: "get", Path: "/api/rt/token", Surface: UserAuth, Handler: okHandler},
|
|
{Name: "subscribe", Method: http.MethodPost, Path: "/api/rt/subscribe", Surface: ServerToServer, Handler: okHandler},
|
|
{Name: "put", Method: http.MethodPut, Path: "/api/rt/p", Surface: Public, Handler: okHandler},
|
|
{Name: "patch", Method: http.MethodPatch, Path: "/api/rt/p", Surface: Public, Handler: okHandler},
|
|
{Name: "delete", Method: http.MethodDelete, Path: "/api/rt/p", Surface: Public, Handler: okHandler},
|
|
}}
|
|
r := &recRouter{}
|
|
if err := Mount(r, d, s); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
want := []recGroup{
|
|
{false, "/", []string{"jwt.auth", "throttle:ws-api"}, []string{"GET /api/rt/token"}},
|
|
{true, "/", []string{"acme.s2s", "throttle:ws-api"}, []string{"POST /api/rt/subscribe"}},
|
|
{false, "/", []string{"acme.public", "throttle:ws-api"}, []string{"PUT /api/rt/p"}},
|
|
{false, "/", []string{"acme.public", "throttle:ws-api"}, []string{"PATCH /api/rt/p"}},
|
|
{false, "/", []string{"acme.public", "throttle:ws-api"}, []string{"DELETE /api/rt/p"}},
|
|
}
|
|
if len(r.groups) != len(want) {
|
|
t.Fatalf("groups = %+v", r.groups)
|
|
}
|
|
for i, w := range want {
|
|
g := r.groups[i]
|
|
if g.raw != w.raw || g.prefix != w.prefix || strings.Join(g.middleware, ",") != strings.Join(w.middleware, ",") || strings.Join(g.routes, ",") != strings.Join(w.routes, ",") {
|
|
t.Errorf("group %d = %+v, want %+v", i, g, w)
|
|
}
|
|
}
|
|
if len(s.UserAuth) != 1 || s.UserAuth[0] != "jwt.auth" {
|
|
t.Fatal("Mount mutated the caller's surface middleware")
|
|
}
|
|
|
|
refusals := []struct {
|
|
name string
|
|
route Route
|
|
s Surfaces
|
|
want string
|
|
}{
|
|
{"user_route_without_guard", Route{Name: "token", Method: "GET", Path: "/t", Surface: UserAuth, Handler: okHandler}, Surfaces{Middleware: []string{"throttle"}}, "Surfaces.UserAuth is empty"},
|
|
{"no_handler", Route{Name: "x", Method: "GET", Path: "/t", Surface: Public}, s, "has no handler"},
|
|
{"relative_path", Route{Name: "x", Method: "GET", Path: "t", Surface: Public, Handler: okHandler}, s, "must be absolute"},
|
|
{"bad_method", Route{Name: "x", Method: "TRACE", Path: "/t", Surface: Public, Handler: okHandler}, s, "unsupported method"},
|
|
{"unknown_surface", Route{Name: "x", Method: "GET", Path: "/t", Surface: Surface(9), Handler: okHandler}, s, "unknown surface Surface(9)"},
|
|
}
|
|
for _, c := range refusals {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
r := &recRouter{}
|
|
// A valid route first: nothing may be mounted when a later one
|
|
// is invalid.
|
|
valid := Route{Name: "ok", Method: "POST", Path: "/ok", Surface: ServerToServer, Handler: okHandler}
|
|
err := Mount(r, routeDriver{routes: []Route{valid, c.route}}, c.s)
|
|
if err == nil || !strings.Contains(err.Error(), c.want) || !strings.Contains(err.Error(), "driver acme route") {
|
|
t.Fatalf("err = %v, want %q", err, c.want)
|
|
}
|
|
if len(r.groups) != 0 {
|
|
t.Fatalf("mounted %d group(s) before refusing", len(r.groups))
|
|
}
|
|
})
|
|
}
|
|
|
|
if err := Mount(nil, d, s); err == nil {
|
|
t.Fatal("nil router accepted")
|
|
}
|
|
r = &recRouter{}
|
|
if err := Mount(r, nil, s); err != nil || len(r.groups) != 0 {
|
|
t.Fatalf("nil driver: err %v, groups %d", err, len(r.groups))
|
|
}
|
|
if err := Mount(r, nullDriver{}, Surfaces{}); err != nil || len(r.groups) != 0 {
|
|
t.Fatalf("null driver: err %v, groups %d", err, len(r.groups))
|
|
}
|
|
for surface, name := range map[Surface]string{UserAuth: "UserAuth", ServerToServer: "ServerToServer", Public: "Public", Surface(0): "Surface(0)"} {
|
|
if surface.String() != name {
|
|
t.Errorf("Surface(%d).String() = %q", int(surface), surface.String())
|
|
}
|
|
}
|
|
}
|