Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions gateway/httpapi/ui_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package httpapi

import (
"bytes"
"encoding/base64"
"image/png"
"net/http"
"strings"
"testing"
Expand Down Expand Up @@ -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())
}
})
}
1 change: 1 addition & 0 deletions gateway/httpapi/web/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="color-scheme" content="dark">
<meta name="theme-color" content="#0b0e14">
<link rel="icon" type="image/png" sizes="32x32" href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAEAklEQVR42sRXTWxUVRT+7r3vvem8MqXt8CdoS4QiiX+JpklrYtywIUajTSgrV7IwEk1M2LjRREKiS3XhioUmxAR0UZu44CcRMf5AQE0ERgtOpZVKaSudKTP0vbn3mntuW8r8vDeVdnoWb/Lu3HfPOd/9zvnudXDXvOZU+k0NvAqgizEmsIymtZYAhhhw+HZ+8iMAgRln5uH7/iaI5FHG2LNogGmtz0AW+wuFwj/cZG6d84Y4p6yNL5E8anyL5lT6LcbYPjTYGGOdnpfMMT+VvswY24lVMK11xgRQWm7CLYWYTjXnWutasEUtVg/s5e/CqbaQkkHVBbVW4IyDO4l7FqNvSgE0dBTxIBx3vvAWzCl3zoWD3pcPYU3bg1BKLvpPonBrDCOZU/g78w2UluDcgVYKblMKPXsPIuG3UZDlxrlAbnIYZwffg1bynuDLENAUaefju5He3AEDAl80w6z95K7XceX8V/j28zcQ3MnTuHAT6Orei+ZWj+bIEuhbxgEh7Bo3h0dxbvDgHEo1A7Amw1lICcxMjWP099M0nTseNm7tRsu6zdjZ8yKCOzmcPrKfsjPwZ38ZIASMbdr+DLwmH8Vb/+JG9iy4cJGb+LPCec0ACDYBTF2/jBOHX7FQQyPV3oHdrx3Dhq2PoevpPfj15IeYHh8iJE59uo/gd9wk9rz9PfyO7Zi6/hu+/qQP3PCccwjhVhCRx3QsOG4ThNsE1/ORm8gi88NnMGt4yQRaN+4gnphFTZZCePRLE7Tt9DTmeJRE1USX2L0gS8Hdd84x58lmxioQji1hJ7pRSJSCIpjZD2ja467ufiJYGEjkbmZxvz2sZgBGPFvWb0Nv3yFKy8C4Zcdz2ND5BPFj5OJxTI1dspCvRABKAWvXP4Selw4sQgQwrWE0cw7fHTtAdWnRWYEAzJYVZ/IYu5oBA6MmVcjdwMilE7hy4QuUZm/fd/bRZegAE9d+xuDHL1B3BGUfUqkJJ7EszmNJSJSeZy+zPKjFZtIOHal8Vb91Yktv7hmnhCRgStnSpD5sSSNLs8STusTof2m6UvCSKfT2vY+E30pj/toHIAOgfcuj1DltK87ix4F34sQIC+JigjWQxwYATfK87am+CjFa09aOR3qeXxCjnwbejRMjRiS7dvE4pscfxvhf562kRW4RgyrN4uqFL6PleGK4qhix5pZ1unI/LdtNl7P7Fnu0Ig5EkdCQuC4OzAvLUjWCC6+emZVVYA6G5efCKMbXe96r91BqNngIq2dD3NzVVsu78W1w8/xU+qS9mumGudZanSnkJ3dxuqXKYr8ZaNyFRNHl1Pgm8oVhOBMGxSPmrqaBDgBtjDG+AtfzPxjwQSE/uT8Mw2kz/l8AAAD//8ElplyNdfmiAAAAAElFTkSuQmCC">
<title>pi5 · status</title>
<style>
/* design tokens — sampled from seilbekskindirov.dev */
Expand Down
Loading