package routes import ( "os" "regexp" "sort" "strings" "testing" "github.com/gofiber/fiber/v2" ) // Un submódulo del menú es una URL escrita a mano en el seed. Si no coincide // con ninguna ruta registrada, el ítem aparece en el menú, se le puede asignar // permiso a un rol, y al hacer clic da 404 — que es exactamente el síntoma // reportado en /app/doc/paginas. El compilador no ve nada de esto porque son // cadenas sueltas en dos archivos distintos. func TestUrlsDeSubmodulosTienenRuta(t *testing.T) { rutas := rutasGETRegistradas(t) urls, err := urlsSembradas() if err != nil { t.Skipf("no se pudo leer el seed: %v", err) } if len(urls) == 0 { t.Fatal("no se encontró ninguna URL de submódulo en el seed") } var rotas []string for _, u := range urls { if !rutas[u] { rotas = append(rotas, u) } } sort.Strings(rotas) for _, u := range rotas { t.Errorf("el submódulo %q no tiene ruta registrada: el menú lo muestra y da 404 al entrar", u) } } // rutasGETRegistradas devuelve el conjunto de rutas GET que sirven una página. func rutasGETRegistradas(t *testing.T) map[string]bool { t.Helper() // LoadRoutes monta todos los grupos: un submódulo puede apuntar a una ruta // pública (ej. /status) y no solo a /app/*. app := fiber.New() LoadRoutes(app) out := map[string]bool{} for _, capa := range app.Stack() { for _, r := range capa { if r.Method == "GET" { out[r.Path] = true } } } return out } // Solo las tuplas {título, descripción, url} de los slices de entries y las // asignaciones Url:. Buscar cualquier "/app/..." suelto daba falsos positivos // con las líneas que corrigen una URL vieja en la base. var reURLSubmodulo = regexp.MustCompile(`\{"[^"]*",\s*"[^"]*",\s*"(/[a-z0-9/_-]+)"\}|Url:\s*"(/[a-z0-9/_-]+)"`) // urlsSembradas saca las URLs de submódulo del seed. Se lee el fuente porque // los seeds son literales en el código, no datos que se puedan consultar. func urlsSembradas() ([]string, error) { b, err := os.ReadFile("../../migrations/migrate.go") if err != nil { return nil, err } texto := string(b) vistas := map[string]bool{} var out []string for _, m := range reURLSubmodulo.FindAllStringSubmatch(texto, -1) { u := m[1] if u == "" { u = m[2] } // Solo interesan las que se usan como Url de un submódulo; las de // redirección o comparación quedan cubiertas igual y no molestan. if vistas[u] || strings.Contains(u, "//") { continue } vistas[u] = true out = append(out, u) } return out, nil }