All checks were successful
Deploy to K8s / deploy (push) Successful in 8s
Add TestEmbedCompleteness and TestStaticAssetsServed in go/internal/web/assets_test.go. The completeness guard walks the on-disk templates/ and static/ directories and asserts every file is present in the corresponding embed.FS, catching forgotten files on future additions. The static mux test hits /static/css/app.css and all JS files through the same http.FileServerFS wiring used in server.go, confirming assets are served from the embedded FS with correct Content-Type and a 404 for unknown paths. Standalone binary smoke test passed manually: binary copied to /tmp (no adjacent templates/ or static/), assets served correctly. Closes M6. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
94 lines
2.7 KiB
Go
94 lines
2.7 KiB
Go
package web
|
|
|
|
import (
|
|
"io/fs"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"os"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
// TestEmbedCompleteness guards against a new template or static file being
|
|
// added to disk but missing from the embedded FS (e.g. a new directory that
|
|
// the //go:embed glob does not match).
|
|
func TestEmbedCompleteness(t *testing.T) {
|
|
cases := []struct {
|
|
name string
|
|
diskDir string
|
|
embedFS fs.FS
|
|
embedRoot string
|
|
}{
|
|
{"templates", "templates", templateFS, "templates"},
|
|
{"static", "static", staticFS, "static"},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.name, func(t *testing.T) {
|
|
diskFS := os.DirFS(tc.diskDir)
|
|
_ = fs.WalkDir(diskFS, ".", func(path string, d fs.DirEntry, err error) error {
|
|
if err != nil || d.IsDir() {
|
|
return err
|
|
}
|
|
embPath := tc.embedRoot + "/" + path
|
|
if _, statErr := fs.Stat(tc.embedFS, embPath); statErr != nil {
|
|
t.Errorf("file %q exists on disk but is missing from embed.FS (%v)", embPath, statErr)
|
|
}
|
|
return nil
|
|
})
|
|
})
|
|
}
|
|
}
|
|
|
|
// TestStaticAssetsServed verifies that /static/* is served from the embedded
|
|
// FS through the same mux wiring used in server.go, so a standalone binary
|
|
// with no adjacent static/ directory still delivers assets.
|
|
func TestStaticAssetsServed(t *testing.T) {
|
|
subFS, err := fs.Sub(staticFS, "static")
|
|
if err != nil {
|
|
t.Fatalf("fs.Sub static: %v", err)
|
|
}
|
|
mux := http.NewServeMux()
|
|
mux.Handle("/static/", http.StripPrefix("/static/", http.FileServerFS(subFS)))
|
|
|
|
cases := []struct {
|
|
path string
|
|
wantCT string
|
|
wantSnippet string
|
|
}{
|
|
{"/static/css/app.css", "text/css", "body {"},
|
|
{"/static/js/member-detail.js", "javascript", "Member-detail modal"},
|
|
{"/static/js/filters.js", "javascript", ""},
|
|
{"/static/js/payment-qr.js", "javascript", ""},
|
|
}
|
|
|
|
for _, tc := range cases {
|
|
t.Run(tc.path, func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, tc.path, nil)
|
|
w := httptest.NewRecorder()
|
|
mux.ServeHTTP(w, req)
|
|
|
|
if w.Code != http.StatusOK {
|
|
t.Fatalf("GET %s: status %d, want 200", tc.path, w.Code)
|
|
}
|
|
ct := w.Header().Get("Content-Type")
|
|
if !strings.Contains(ct, tc.wantCT) {
|
|
t.Errorf("GET %s: Content-Type %q, want it to contain %q", tc.path, ct, tc.wantCT)
|
|
}
|
|
if tc.wantSnippet != "" && !strings.Contains(w.Body.String(), tc.wantSnippet) {
|
|
t.Errorf("GET %s: body missing expected snippet %q", tc.path, tc.wantSnippet)
|
|
}
|
|
})
|
|
}
|
|
|
|
// Sanity: unknown path → 404 (file server doesn't fall through silently)
|
|
t.Run("missing-file", func(t *testing.T) {
|
|
req := httptest.NewRequest(http.MethodGet, "/static/css/nonexistent.css", nil)
|
|
w := httptest.NewRecorder()
|
|
mux.ServeHTTP(w, req)
|
|
if w.Code != http.StatusNotFound {
|
|
t.Errorf("unknown static path: status %d, want 404", w.Code)
|
|
}
|
|
})
|
|
}
|