diff --git a/gateway/httpapi/ui_test.go b/gateway/httpapi/ui_test.go index ee38c3b..52aae26 100644 --- a/gateway/httpapi/ui_test.go +++ b/gateway/httpapi/ui_test.go @@ -1,6 +1,9 @@ package httpapi import ( + "bytes" + "encoding/base64" + "image/png" "net/http" "strings" "testing" @@ -73,4 +76,50 @@ func TestHandleDashboard(t *testing.T) { t.Fatal("dashboardHTML is empty: the //go:embed path is broken or the asset is empty") } }) + + // The favicon rides inline in the page rather than as a second embedded + // asset, so nothing but this test stands between a stray edit and a tab + // that silently falls back to the browser placeholder. Asserted by marker, + // never by the exact blob: the mark is regenerated from time to time. + t.Run("the page carries the favicon as an inline PNG data URI", func(t *testing.T) { + t.Parallel() + + srv := newTestServer(&fakeRepo{}, "tok") + body := doRequest(t, srv, http.MethodGet, "/", "tok").Body.String() + + const iconAttr = `rel="icon"` + if n := strings.Count(body, iconAttr); n != 1 { + t.Fatalf("the page has %d %s links, want exactly 1", n, iconAttr) + } + + link := body[strings.Index(body, iconAttr):] + if end := strings.IndexByte(link, '>'); end >= 0 { + link = link[:end] + } + + const marker = "data:image/png;base64," + i := strings.Index(link, marker) + if i < 0 { + t.Fatalf("the icon href is not a %q URI: the favicon must stay inline so the dashboard remains a single self-contained asset", marker) + } + encoded := link[i+len(marker):] + end := strings.IndexByte(encoded, '"') + if end < 0 { + t.Fatal("the icon data URI has no closing quote") + } + + raw, err := base64.StdEncoding.DecodeString(encoded[:end]) + if err != nil { + t.Fatalf("decode the icon payload: %v", err) + } + // Decode in full, not just the header: a truncated blob still carries + // an intact IHDR, so DecodeConfig alone would wave a broken icon through. + img, err := png.Decode(bytes.NewReader(raw)) + if err != nil { + t.Fatalf("the icon payload is not a valid PNG: %v", err) + } + if b := img.Bounds(); b.Dx() != 32 || b.Dy() != 32 { + t.Fatalf("icon is %dx%d, want 32x32", b.Dx(), b.Dy()) + } + }) } diff --git a/gateway/httpapi/web/index.html b/gateway/httpapi/web/index.html index 5547096..cb201f6 100644 --- a/gateway/httpapi/web/index.html +++ b/gateway/httpapi/web/index.html @@ -5,6 +5,7 @@ +