package lua
import (
"encoding/json"
"os"
"os/exec"
"regexp"
"strings"
"testing"
)
// The billing page rendered EMPTY in production while its data endpoint returned
// 200 with real numbers. The cause was one line: render(st) referenced an
// undefined `s` for two KPI cells, so the ReferenceError aborted the whole
// render and every table stayed at its initial empty innerHTML.
//
// Nothing in the build, the tests or the API surfaced it. This file is the guard
// for the whole class: a plugin's injected UI that references an undefined name,
// or that depends on a container the page does not provide, fails silently.
// billingUI returns the injected markup for the billing plugin: the full page
// mount and the status-page element mount.
func billingUI(t *testing.T) (page string, statusElement string) {
t.Helper()
src, err := os.ReadFile("plugins/billing.lua")
if err != nil {
t.Fatalf("read billing.lua: %v", err)
}
// Located BY CONTENT, not by index. The plugin also uses a long string for
// its inline SVG icon, so "the first long string" is the icon and "the
// second" is the page — which is exactly the kind of positional assumption
// that breaks the next time an icon or a description is added.
page = longStringContaining(t, string(src), "billing-root")
statusElement = longStringContaining(t, string(src), "billing-status-tile")
return page, statusElement
}
// longStringContaining returns the [==[ ... ]==] body that contains marker.
func longStringContaining(t *testing.T, src, marker string) string {
t.Helper()
re := regexp.MustCompile(`(?s)\[==\[(.*?)\]==\]`)
for _, m := range re.FindAllStringSubmatch(src, -1) {
if strings.Contains(m[1], marker) {
return m[1]
}
}
t.Fatalf("no long string contains %q", marker)
return ""
}
// TestBillingMountScriptExecutes is the guard for the production bug.
//
// The Billing page rendered empty while its data endpoint returned 200 with real
// numbers. Cause: render(st) referenced an undefined `s` for two KPI cells, the
// ReferenceError aborted the render, and every table kept its initial empty
// innerHTML. Nothing in the build or the API surfaced it.
//
// Two earlier attempts at a static check were both wrong: a "declared names"
// scan flagged every CSS class inside the inline HTML strings (class, div, td),
// and a CSS-selector parse of the stylesheet reported the stylesheet itself as
// broken. Static analysis of JS embedded in HTML strings is the wrong tool.
//
// So this actually RUNS the script, in node, against a minimal DOM stub, and
// fails on any thrown error. Skipped when node is unavailable, with the reason
// printed — never silently passing as if it had checked.
func TestBillingMountScriptExecutes(t *testing.T) {
page, el := billingUI(t)
scripts := extractScripts(page)
if len(scripts) == 0 {
t.Fatal("no `)
var out []string
for _, m := range re.FindAllStringSubmatch(html, -1) {
out = append(out, m[1])
}
return out
}
// TestBillingPageCoversItsData is the other half: the page declares tables for
// per-source / per-model / per-key / per-day and must actually render into them.
// A table id that is never written to is exactly how "the page loads and shows
// nothing" happens without an error.
func TestBillingPageCoversItsData(t *testing.T) {
page, _ := billingUI(t)
for _, id := range []string{
"billing-kpis", "billing-by-source", "billing-by-model",
"billing-by-key", "billing-by-day",
} {
if !strings.Contains(page, `id="`+id+`"`) {
t.Errorf("the page has no container #%s", id)
}
}
// Every container must be written to by the script, not just declared.
scripts := extractScripts(page)
all := strings.Join(scripts, "\n")
for _, id := range []string{"billing-kpis", "billing-by-source", "billing-by-model", "billing-by-key", "billing-by-day"} {
if !strings.Contains(all, `getElementById("`+id+`")`) {
t.Errorf("#%s is declared but never read by the script — it stays empty forever", id)
}
}
}
// TestPluginIconIsNotARawEmoji guards the sidebar icon. The billing plugin
// declared icon = "💰" and the WebUI drops that verbatim into the button, while
// every native tab uses an inline SVG styled with `stroke: currentColor`. An
// emoji there renders at the wrong size and ignores the theme, so it does not
// match its neighbours — which is what the operator reported.
func TestPluginIconIsNotARawEmoji(t *testing.T) {
icon := billingIcon(mustBillingSource(t))
if icon == "" {
t.Fatal("billing declares no icon; the sidebar entry would be blank")
}
if isEmojiIcon(icon) {
t.Errorf("page icon is the raw emoji %q — the WebUI sidebar renders "+
"native tabs as inline SVG (stroke: currentColor), so an emoji is the "+
"wrong size and ignores the theme. Use an inline SVG path instead.", icon)
}
}
// isEmojiIcon reports whether s is a pictographic emoji rather than markup or a
// text glyph. Codepoints in the pictographic blocks, plus the regional-indicator
// pair used by flags.
func isEmojiIcon(s string) bool {
r := []rune(s)
if len(r) == 0 {
return false
}
// Anything containing '<' is markup (an inline