Files
go-admin/cmd/api/server_test.go
T
zhangwenjian 15fb128236 docs📝: describe the rules rather than who follows them
The warning on Authorizator matters to anyone keeping a copy of that file, not
to one particular consumer, and it reads better addressed to all of them: check
what reads those context keys before taking this change.
2026-09-01 19:56:27 +08:00

117 lines
4.1 KiB
Go

package api
import (
"strings"
"testing"
"github.com/go-admin-team/go-admin-core/v2/sdk"
"github.com/go-admin-team/go-admin-core/v2/sdk/runtime"
)
// freshRuntime hands the test its own Runtime and puts the old one back.
//
// Both registries close permanently the first time they are run, and
// sdk.Runtime is a process-wide singleton, so a test that runs the startup
// hooks would otherwise leave every later test in this binary registering into
// a closed registry - which is only an ERROR log, not a failure. The symptom
// is a test that passes alone and loses its routes when run with the others.
func freshRuntime(t *testing.T) {
t.Helper()
previous := sdk.Runtime
t.Cleanup(func() { sdk.Runtime = previous })
sdk.Runtime = runtime.NewConfig()
}
// Acceptance 1 and 2 together: the package-level slice a fork appends to and
// the core registry a module registers through both run, package-level first,
// registration order preserved inside each.
//
// The order matters beyond neatness. A module that appends to AppRouters has to
// import go-admin/cmd/api, which is why every module used to need a seven-line
// file in the command package; SetAppRouters is the way out of that. Running
// the old registry first is what makes the change invisible to anyone who never
// takes it.
func TestRunStartupHooksRunsBothRegistriesInOrder(t *testing.T) {
freshRuntime(t)
savedPackage := AppRouters
t.Cleanup(func() { AppRouters = savedPackage })
var order []string
AppRouters = []func(){
func() { order = append(order, "package-1") },
func() { order = append(order, "package-2") },
}
sdk.Runtime.SetAppRouters(func() { order = append(order, "runtime-1") })
sdk.Runtime.SetAppRouters(func() { order = append(order, "runtime-2") })
runStartupHooks()
const want = "package-1,package-2,runtime-1,runtime-2"
if got := strings.Join(order, ","); got != want {
t.Errorf("ran %q, want %q", got, want)
}
}
// Acceptance 17: a before callback registered through core actually runs.
//
// It did not, ever: core stored the callbacks and nothing executed them, so
// SetBefore was accepted and silently did nothing. The gap survived because
// core offered the registry without ever running it, leaving each consumer to
// write - or forget - its own loop.
func TestBeforeCallbacksRun(t *testing.T) {
freshRuntime(t)
savedPackage := AppRouters
t.Cleanup(func() { AppRouters = savedPackage })
AppRouters = nil
var order []string
sdk.Runtime.SetBefore(func() { order = append(order, "before-1") })
sdk.Runtime.SetBefore(func() { order = append(order, "before-2") })
sdk.Runtime.SetAppRouters(func() { order = append(order, "router") })
runStartupHooks()
// Routers first, then before: both happen ahead of ListenAndServe, and a
// router callback is what puts the engine in place for anything that comes
// after it.
const want = "router,before-1,before-2"
if got := strings.Join(order, ","); got != want {
t.Errorf("ran %q, want %q", got, want)
}
}
// A panicking module must not take the server down with it. The guard lives in
// core; this asserts that go-admin actually goes through it rather than around
// it with a loop of its own.
func TestAPanickingRouterDoesNotStopStartup(t *testing.T) {
freshRuntime(t)
savedPackage := AppRouters
t.Cleanup(func() { AppRouters = savedPackage })
AppRouters = nil
var order []string
sdk.Runtime.SetAppRouters(func() { order = append(order, "first") })
sdk.Runtime.SetAppRouters(func() { panic("a third-party module blew up") })
sdk.Runtime.SetAppRouters(func() { order = append(order, "third") })
sdk.Runtime.SetBefore(func() { order = append(order, "before") })
runStartupHooks()
const want = "first,third,before"
if got := strings.Join(order, ","); got != want {
t.Errorf("ran %q, want %q", got, want)
}
}
// The default AppRouters must keep the admin routes on it. Emptying the slice
// would not fail to compile anywhere - it would just serve a server with no
// admin API and no error.
func TestAdminRouterIsRegisteredOnThePackageSlice(t *testing.T) {
if len(AppRouters) == 0 {
t.Fatal("AppRouters is empty; the admin router is registered in init()")
}
}