package gateway import ( "strings" "testing" ) // Per-key AUTO chain UI contract. The feature spans three layers (API handler, // config field, WebUI) and the WebUI part is plain inline JS, so a rename or a // dropped button fails silently at runtime: the admin simply finds no way to // configure a key's chain and has no error to chase. // // These checks pin the wiring that is easy to break by refactor. func TestUIPerKeyAutoWiring(t *testing.T) { src := uiSource(t) // The admin key list must offer the editor, otherwise the whole feature is // unreachable from the UI. Assert the *call site* inside keyCanvasHtml, // not just that the functions exist: removing the button leaves every // definition intact, and that is exactly the regression to catch. kcStart := strings.Index(src, "function keyCanvasHtml") if kcStart < 0 { t.Fatal("keyCanvasHtml is gone — cannot check the AUTO chain button") } kcEnd := strings.Index(src[kcStart:], "\n }") if kcEnd < 0 { t.Fatal("could not delimit keyCanvasHtml") } kcBody := src[kcStart : kcStart+kcEnd] if !strings.Contains(kcBody, "openKeyAutoModal(") { t.Fatal("per-key AUTO chain button is missing from the key card header") } for _, fn := range []string{ "function openKeyAutoModal", "function keyAutoRow", "function keyAutoAddRow", "function keyAutoClear", "function keyAutoSave", } { if !strings.Contains(src, fn) { t.Fatalf("UI is missing %s — the editor cannot function", fn) } } // The editor must talk to the endpoint the gateway actually serves. if !strings.Contains(src, "/auto\"") { t.Fatal("UI never calls the /api/keys/{key}/auto endpoint") } } // The editor's empty-row path sends {"auto":[]} and relies on the server // treating that as "clear the override". If the UI instead sent a sentinel, // a future server change would silently persist a broken empty chain. func TestUIPerKeyAutoSendsEmptyListToClear(t *testing.T) { src := uiSource(t) if !strings.Contains(src, "JSON.stringify({ auto: rules })") { t.Fatal("keyAutoSave must PUT the collected rule list verbatim") } if !strings.Contains(src, "keyAutoClear") { t.Fatal("no way to clear a key's own chain from the UI") } } // Both languages need the strings, otherwise a non-Chinese admin sees raw // i18n keys in the modal. func TestUIPerKeyAutoHasBothLanguages(t *testing.T) { src := uiSource(t) for _, k := range []string{ "kAutoChainTitle", "kAutoChainHint", "kAutoChainInherit", "kAutoChainAdd", "kAutoChainSave", "kAutoChainEmpty", } { n := strings.Count(src, k+`: "`) if n < 2 { t.Fatalf("i18n key %s defined %d time(s), want both zh and en", k, n) } } }