package memoriatest import ( "amadan.net/oficina/estilo.git/base" "amadan.net/oficina/estilo.git/busy" "amadan.net/oficina/estilo.git/chrome" estilomenu "amadan.net/oficina/estilo.git/menu" "amadan.net/oficina/estilo.git/sky" "bytes" "os" "path/filepath" "testing" "amadan.net/oficina/estilo.git/theme" "amadan.net/rastrillo/rastrillo/ui" ) // The scaffold delivered these files once; they are app-owned from // then on. This test pins each vendored copy byte-identical to the // library it came from: a reviewer runs the suite instead of reading // ~56KB of assets as app diff, and a framework upgrade that forgets // to re-copy is caught instead of drifting silently. If you edit one // DELIBERATELY, delete its line below — the file is yours. // // tokens.css is the only one. select.js, datetime.js and calendar.js // are rastrillo's too, but no page here links them, so they are not on // disk (TestNoUnusedRastrilloScriptsOnDisk); rastrillo.js is estilo's. func TestVendoredAssetsMatchTheLibrary(t *testing.T) { for name, lib := range map[string][]byte{ "tokens.css": ui.TokensCSS(), } { vendored, err := os.ReadFile(filepath.Join("..", "memoria", "static", name)) if err != nil { t.Errorf("read vendored %s: %v", name, err) continue } if !bytes.Equal(vendored, lib) { t.Errorf("static/%s differs from the library copy; re-copy it (or delete its pin if the edit was deliberate)", name) } } } // static/theme.css is Calma with Memoria's accent, from estilo (estilo // AGENTS.md, decisions table, row 8). This pins it to the estilo this app // is built against. After an estilo upgrade, re-write it with // `go run ./scripts/writeestilo` — never by hand. func TestThemeIsEstilosForMemoria(t *testing.T) { want, ok := theme.ForApp("memoria") if !ok { t.Fatal("estilo's theme ring has no memoria") } got, err := os.ReadFile(filepath.Join("..", "memoria", "static", "theme.css")) if err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Error("static/theme.css differs from estilo's theme for memoria; run go run ./scripts/writeestilo") } } // app.go mounts Onest at a literal pattern (the route gate reads // literals); this keeps that literal equal to estilo's versioned prefix. func TestFontRouteIsEstilosPrefix(t *testing.T) { src, err := os.ReadFile(filepath.Join("..", "memoria", "app.go")) if err != nil { t.Fatal(err) } if !bytes.Contains(src, []byte(`"GET `+theme.FontPrefix+`"`)) { t.Errorf("app.go does not mount Onest at %s", theme.FontPrefix) } } // rastrillo's busy rule (Paul, 2026-09-29): a submit button's label gives // way to a spinner for at least 650ms, the button disables, and Back // resets it. static/ carries it as estilo does — the shim without the old // rule, busy.js and busy.css — pinned here to the estilo this app builds // against. After an estilo upgrade: go run ./scripts/writeestilo. func TestBusyFilesAreEstilos(t *testing.T) { for name, want := range map[string][]byte{ "rastrillo.js": busy.ShimJS(), "busy.js": busy.JS(), "busy.css": busy.CSS(), } { got, err := os.ReadFile(filepath.Join("..", "memoria", "static", name)) if err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Errorf("static/%s differs from estilo's; run go run ./scripts/writeestilo", name) } } } // app.go mounts the sign-in door's sky at a literal pattern, for the same // reason as the font: the route gate reads literals. This keeps it equal // to estilo's versioned prefix, so a new sky version cannot leave the // door linking layers nothing serves. func TestSkyRouteIsEstilosPrefix(t *testing.T) { src, err := os.ReadFile(filepath.Join("..", "memoria", "app.go")) if err != nil { t.Fatal(err) } if !bytes.Contains(src, []byte(`"GET `+sky.Prefix+`"`)) { t.Errorf("app.go does not mount the sky at %s", sky.Prefix) } } // The sign-in door (estilo chrome's estilo-signin and friends) is styled // by chrome.css and sky.css, vendored into static/ so they are served // fingerprinted like every other stylesheet. Pinned to the estilo this // app builds against; after an upgrade: go run ./scripts/writeestilo. func TestDoorStylesAreEstilos(t *testing.T) { // And the rest of the shared chrome (round 2): links (base.css), the // floating panels (menu.css) and chrome.js's Escape and outside-click // close. for name, want := range map[string][]byte{ "chrome.css": chrome.CSS(), "sky.css": sky.CSS(), "base.css": base.CSS(), "menu.css": estilomenu.CSS(), "chrome.js": chrome.JS(), } { got, err := os.ReadFile(filepath.Join("..", "memoria", "static", name)) if err != nil { t.Fatal(err) } if !bytes.Equal(got, want) { t.Errorf("static/%s differs from estilo's; run go run ./scripts/writeestilo", name) } } } // rastrillo doctor --fix re-copies what the library ships and the app // lacks, and at 29e4491 that includes the old combined shim (with the // old busy rule inside) as rastrillo.js, plus select.js, datetime.js and // calendar.js. Memoria links none of the three, and its rastrillo.js is // estilo's split shim (TestBusyFilesAreEstilos): the old shim loaded // beside busy.js makes the two busy rules fight. After a --fix, run // go run ./scripts/writeestilo and delete what it added; this fails // until both are done. func TestNoUnusedRastrilloScriptsOnDisk(t *testing.T) { for _, name := range []string{"select.js", "datetime.js", "calendar.js"} { if _, err := os.Stat(filepath.Join("..", "memoria", "static", name)); err == nil { t.Errorf("static/%s is on disk; no page links it — delete it (rastrillo doctor --fix adds it)", name) } } }