test(10-05): cover every Phase 10 Go change with branch-level tests

- bouncer TestPhase10CookieGuard: cookie read without Bearer, Bearer wins,
  empty cookie, frontend audience and blacklisted jti rejected
- boardwalk TestPhase10BoardwalkServing: HEAD, query strings, encoded
  traversal, index by name, nested prefix, MIME fallback, constructor errors
- cabana TestPhase10Coverage: mounted unsafe routes vs the CSRF walk, option
  and filter edges, read-only labels, relation message defaults, bundle
  fallback locale, cookie refresh of an expired token in the refresh window
- phrasebook override precedence, new locale, Bundle merge order, Forms shapes
- surf prefix collision for deeper paths and the default /backend prefix
- swagger2openapi TestUnionRewrite and converter branch tests
- framework tests no longer name the application (acme fixtures instead)
This commit is contained in:
Jakub Zych
2026-09-27 18:05:28 +02:00
parent 1c2a66df45
commit ef448da1cc
14 changed files with 1055 additions and 35 deletions

View File

@@ -197,3 +197,137 @@ func TestPhase10SPAKeysResolve(t *testing.T) {
}
}
}
// TestPhase10LangPrecedence pins the D-20 layering: an override beats the
// plugin's own strings and the framework's backend strings, a later plugin's
// override beats an earlier one, and an override can add a whole locale.
func TestPhase10LangPrecedence(t *testing.T) {
demoLang := fstest.MapFS{
"lang/en/lang.yaml": {Data: []byte("title: Demo\nsubtitle: Widgets\n")},
"lang/pl/lang.yaml": {Data: []byte("title: Demo PL\n")},
}
first := overridePlugin{id: "acme.demo", lang: demoLang, overrides: fstest.MapFS{
"lang/pl/backend/lang.yaml": {Data: []byte("form:\n save: Pierwszy\n cancel: Pierwsze anuluj\n")},
"lang/pl/acme.demo/lang.yaml": {Data: []byte("title: Demo nadpisane\n")},
}}
second := overridePlugin{id: "acme.site", overrides: fstest.MapFS{
"lang/pl/backend/lang.yaml": {Data: []byte("form:\n save: Drugi\n")},
"lang/fr/backend/lang.yaml": {Data: []byte("form:\n save: Enregistrer\n")},
"lang/fr/acme.demo/lang.yaml": {Data: []byte("title: Démo\n")},
}}
tr, err := activateWith(t, first, second)
if err != nil {
t.Fatalf("activate: %v", err)
}
for _, tc := range []struct{ locale, key, want string }{
// Framework string untouched by any override.
{"pl", "backend::lang.form.delete", "Usuń"},
// Override beats framework; the later plugin's override wins.
{"pl", "backend::lang.form.save", "Drugi"},
{"pl", "backend::lang.form.cancel", "Pierwsze anuluj"},
// Override beats the plugin's own string; the plugin beats its fallback.
{"pl", "acme.demo::lang.title", "Demo nadpisane"},
{"en", "acme.demo::lang.title", "Demo"},
{"pl", "acme.demo::lang.subtitle", "Widgets"},
// A locale added only through overrides.
{"fr", "backend::lang.form.save", "Enregistrer"},
{"fr", "acme.demo::lang.title", "Démo"},
{"fr", "backend::lang.form.delete", "Delete"},
} {
if got := tr.GetIn(tc.locale, tc.key, nil); got != tc.want {
t.Fatalf("%s %s = %q want %q", tc.locale, tc.key, got, tc.want)
}
}
if got := tr.Resolved("fr", "backend::lang."); got != "fr" {
t.Fatalf("Resolved(fr) = %q", got)
}
if got := tr.Resolved("de", "backend::lang."); got != "en" {
t.Fatalf("Resolved(de) = %q, want the en fallback", got)
}
}
// TestPhase10BundleMerge proves Bundle layers the requested locale over its
// region parent and the fallback, filters by prefix and skips keys that do
// not convert to CLDR forms.
func TestPhase10BundleMerge(t *testing.T) {
cat := NewCatalog()
if err := cat.Load("acme.demo", fstest.MapFS{
"lang/en/lang.yaml": {Data: []byte("a: A en\nb: B en\nc: C en\nexact: \"{0} none|[1,*] :count\"\n")},
"lang/pt/lang.yaml": {Data: []byte("a: A pt\nb: B pt\n")},
"lang/pt-BR/lang.yaml": {Data: []byte("a: A pt-BR\n")},
}); err != nil {
t.Fatal(err)
}
if err := cat.Load("acme.other", fstest.MapFS{"lang/en/lang.yaml": {Data: []byte("a: other\n")}}); err != nil {
t.Fatal(err)
}
tr := NewTranslator(cat, Options{Locale: "en", Fallback: "en"})
bundle := tr.Bundle("pt-BR", "acme.demo::")
want := map[string]string{
"acme.demo::lang.a": "A pt-BR",
"acme.demo::lang.b": "B pt",
"acme.demo::lang.c": "C en",
}
if len(bundle) != len(want) {
t.Fatalf("bundle = %v", bundle)
}
for key, text := range want {
if bundle[key]["other"] != text {
t.Fatalf("%s = %v want %q", key, bundle[key], text)
}
}
if _, ok := bundle["acme.demo::lang.exact"]; ok {
t.Fatal("an exact/range pipe entered the bundle")
}
if got := tr.Resolved("pt-BR", "acme.demo::"); got != "pt-BR" {
t.Fatalf("Resolved = %q", got)
}
var nilTr *Translator
if b := nilTr.Bundle("en", ""); len(b) != 0 {
t.Fatalf("nil translator bundle = %v", b)
}
if _, ok := nilTr.Forms("en", "x"); ok {
t.Fatal("nil translator produced forms")
}
}
// TestPhase10FormsShapes converts every entry shape the catalog stores: plain
// text, a YAML plural map, a category pipe, and pipes that cannot convert
// (exact, range, or without an other form).
func TestPhase10FormsShapes(t *testing.T) {
for _, tc := range []struct {
name string
e entry
want map[string]string
ok bool
}{
{"plain", entry{text: "Hi"}, map[string]string{"other": "Hi"}, true},
{"empty plain", entry{}, map[string]string{"other": ""}, true},
{"plural map", entry{plurals: map[string]string{"one": "1", "other": "n"}}, map[string]string{"one": "1", "other": "n"}, true},
{"category pipe", entry{pipes: []pipePart{{cat: "one", text: "1"}, {cat: "other", text: "n"}}}, map[string]string{"one": "1", "other": "n"}, true},
{"pipe without other", entry{pipes: []pipePart{{cat: "one", text: "1"}}}, nil, false},
{"pipe with an uncategorised part", entry{pipes: []pipePart{{text: "x"}, {cat: "other", text: "n"}}}, nil, false},
} {
t.Run(tc.name, func(t *testing.T) {
got, ok := entryForms(tc.e)
if ok != tc.ok {
t.Fatalf("ok=%v want %v (%v)", ok, tc.ok, got)
}
if len(got) != len(tc.want) {
t.Fatalf("forms=%v want %v", got, tc.want)
}
for cat, text := range tc.want {
if got[cat] != text {
t.Fatalf("forms=%v want %v", got, tc.want)
}
}
})
}
one, two := 0.0, 1.0
if _, ok := entryForms(entry{pipes: []pipePart{{exact: &one, text: "none"}, {cat: "other", text: "n"}}}); ok {
t.Fatal("exact pipe converted")
}
if _, ok := entryForms(entry{pipes: []pipePart{{lo: &two, text: "some"}, {cat: "other", text: "n"}}}); ok {
t.Fatal("range pipe converted")
}
}

View File

@@ -113,7 +113,7 @@ func TestPluralSmoke(t *testing.T) {
"helloName: \"hi :name :Name :NAME\"\n" +
"raw:\n other: \"{{.Count}} bottles\"\n")},
"lang/pl/lang.yaml": {Data: []byte("" +
"albums:\n one: \":count album\"\n few: \":count albumy\"\n many: \":count albumów\"\n other: \":count albumu\"\n" +
"albums:\n one: \":count plik\"\n few: \":count pliki\"\n many: \":count plików\"\n other: \":count pliku\"\n" +
"posts: \":count wpis|:count wpisy|:count wpisów|:count wpisu\"\n")},
}
cat := NewCatalog()
@@ -122,7 +122,7 @@ func TestPluralSmoke(t *testing.T) {
}
tr := NewTranslator(cat, Options{})
pl := map[any]string{1: "1 album", 2: "2 albumy", 5: "5 albumów", 22: "22 albumy", 0: "0 albumów", 1.5: "1.5 albumu"}
pl := map[any]string{1: "1 plik", 2: "2 pliki", 5: "5 plików", 22: "22 pliki", 0: "0 plików", 1.5: "1.5 pliku"}
for n, want := range pl {
if got := tr.ChoiceIn("pl", "golem15.hello::lang.albums", n, nil); got != want {
t.Fatalf("pl albums %v = %q, want %q", n, got, want)