feat(registry): random_hex_id_go_core + spa_handler_go_infra
Dos primitivas reutilizables para apps web del registry: - random_hex_id_go_core: IDs hex aleatorios (apps con SQLite + IDs string) - spa_handler_go_infra: http.Handler que sirve embed.FS con fallback a index.html (patron SPA para React Router/dnd-kit) Ambas creadas via fn-constructor durante apps/kanban (issue 0053). Tests pasan, fn index OK. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,77 @@
|
||||
package infra
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
"testing/fstest"
|
||||
)
|
||||
|
||||
func TestSPAHandler(t *testing.T) {
|
||||
mapFS := fstest.MapFS{
|
||||
"index.html": {
|
||||
Data: []byte(`<!doctype html><html><body>SPA</body></html>`),
|
||||
},
|
||||
"assets/app.js": {
|
||||
Data: []byte(`console.log("app")`),
|
||||
},
|
||||
}
|
||||
|
||||
handler := SPAHandler(mapFS, "index.html")
|
||||
|
||||
t.Run("sirve archivo estatico existente con status 200", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/assets/app.js", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Errorf("got status %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
body := rec.Body.String()
|
||||
if body != `console.log("app")` {
|
||||
t.Errorf("unexpected body: %q", body)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("ruta inexistente hace fallback a index.html con Content-Type text/html", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/some/route", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Errorf("got status %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
ct := rec.Header().Get("Content-Type")
|
||||
if ct != "text/html; charset=utf-8" {
|
||||
t.Errorf("got Content-Type %q, want %q", ct, "text/html; charset=utf-8")
|
||||
}
|
||||
body := rec.Body.String()
|
||||
if body == "" {
|
||||
t.Error("expected non-empty body (index.html)")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("raiz retorna index.html", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusOK {
|
||||
t.Errorf("got status %d, want %d", rec.Code, http.StatusOK)
|
||||
}
|
||||
ct := rec.Header().Get("Content-Type")
|
||||
if ct != "text/html; charset=utf-8" {
|
||||
t.Errorf("got Content-Type %q, want %q", ct, "text/html; charset=utf-8")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("path con .. retorna 400", func(t *testing.T) {
|
||||
req := httptest.NewRequest(http.MethodGet, "/foo/../bar", nil)
|
||||
rec := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rec, req)
|
||||
|
||||
if rec.Code != http.StatusBadRequest {
|
||||
t.Errorf("got status %d, want %d", rec.Code, http.StatusBadRequest)
|
||||
}
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user