test(router): закрепить пространства имён API тестом регистрации маршрутов

Ошибка в регистрации маршрутов проявляется паникой при старте сервиса, а не
ответом с кодом: конфликт с wildcard фронтенда или дублирующая регистрация
обнаружились бы только на живом сервере. Смена namespace относится ровно к
этому классу изменений.
This commit is contained in:
2026-08-27 20:51:04 +05:00
parent 086b5d6624
commit 5574b7c89a
2 changed files with 122 additions and 1 deletions
+107
View File
@@ -0,0 +1,107 @@
package router
import (
"strings"
"testing"
"github.com/gin-gonic/gin"
"hy2xs-admin/model/constant"
)
// Регистрация маршрутов — единственное место, где ошибка проявляется паникой
// при старте сервиса, а не ответом с кодом. Смена пространства имён API как
// раз относится к таким изменениям: конфликт с wildcard-маршрутом фронтенда
// или дублирующая регистрация обнаружились бы только на живом сервере.
func buildRoutes(t *testing.T) gin.RoutesInfo {
t.Helper()
gin.SetMode(gin.TestMode)
engine := gin.New()
defer func() {
if r := recover(); r != nil {
t.Fatalf("сборка маршрутов паникует: %v", r)
}
}()
Router(engine)
return engine.Routes()
}
func TestRouterRegistersMachineAuthUnderInternalNamespace(t *testing.T) {
routes := buildRoutes(t)
found := false
for _, route := range routes {
if route.Method == "POST" && route.Path == constant.HysteriaMachineAuthPath {
found = true
}
}
if !found {
t.Fatalf("machine-auth endpoint не зарегистрирован на %s", constant.HysteriaMachineAuthPath)
}
}
func TestRouterKeepsAdminAndAuthUnderApiNamespace(t *testing.T) {
routes := buildRoutes(t)
expected := []string{
constant.AdminAPIBase + "/auth/login",
constant.AdminAPIBase + "/peers",
constant.AdminAPIBase + "/config/listConfig",
constant.AdminAPIBase + "/peer-import",
constant.AdminAPIBase + "/peer-export",
}
present := make(map[string]bool, len(routes))
for _, route := range routes {
present[route.Path] = true
}
for _, path := range expected {
if !present[path] {
t.Errorf("маршрут %s не зарегистрирован", path)
}
}
}
// Старое пространство имён — часть runtime-контракта, а не внутреннее имя:
// его возвращение сломало бы конфиг Hysteria на уже установленных серверах.
func TestRouterHasNoLegacyNamespace(t *testing.T) {
for _, route := range buildRoutes(t) {
if strings.HasPrefix(route.Path, "/hui") {
t.Errorf("вернулось legacy-пространство имён: %s %s", route.Method, route.Path)
}
}
}
// Маршруты, операциями которых продукт не владеет, удалены, а не оставлены
// отвечающими «feature disabled».
func TestRouterHasNoRemovedRoutes(t *testing.T) {
removed := []string{
"exportConfig",
"importConfig",
"updateHysteria2Config",
"importHysteria2Config",
"hysteria2ChangeVersion",
"listRelease",
"restartServer",
"uploadCertFile",
"hysteria2AcmePath",
}
for _, route := range buildRoutes(t) {
for _, dead := range removed {
if strings.Contains(route.Path, dead) {
t.Errorf("удалённый маршрут вернулся: %s %s", route.Method, route.Path)
}
}
}
}
func TestRouterExposesHealthz(t *testing.T) {
found := false
for _, route := range buildRoutes(t) {
if route.Method == "GET" && route.Path == "/healthz" {
found = true
}
}
if !found {
t.Fatal("/healthz не зарегистрирован")
}
}