- pier: TestCatalog, TestDispatch, TestGuardRules, TestSessions (Redis
across two instances and the memory store), TestFanOut; the store clock
and stateless-server seams in export_test.go
- surf: TestStreamingRoute (throttle, CORS profile and access-log pattern on
a stream route) and TestPierMount in both builds; the streaming smoke
restores the mount hook it replaces
- wristband TestMCPScopes; cabana TestAccountContributions (Postgres part:
transaction and unchanged My account payloads), TestRecordActionPrompt,
TestScopeLimitForMethods; TestRecordActionPromptRun pins the empty Value
of an action without a prompt
- vitest: 403 and failed-load cases for My account contributions, the
cancelled prompt
- gate: every name required (tagged runs included), account.go unchanged
since 51d088a, admin stage live
125 lines
5.1 KiB
Go
125 lines
5.1 KiB
Go
package cabana
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"strings"
|
|
"testing"
|
|
"testing/fstest"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/backpack"
|
|
"git.golem15.com/golem15/summercms/modules/bouncer"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
"git.golem15.com/golem15/summercms/modules/party"
|
|
"git.golem15.com/golem15/summercms/modules/phrasebook"
|
|
"git.golem15.com/golem15/summercms/modules/towel"
|
|
)
|
|
|
|
// TestRecordActionPrompt (D-34): a prompted record action's prompt is
|
|
// localized in the show payload (label, placeholder and comment in the
|
|
// request's language, the default cap of 255 when MaxLength is 0); its
|
|
// value is trimmed and capped in runes; a non-string or empty payload is
|
|
// 422 on payload; an action without a Prompt refuses any payload; and a
|
|
// prompt-free value is the empty string. The Postgres run of the value
|
|
// into Run, and the empty Value a plain action receives, are pinned by
|
|
// TestRecordActionPromptRun.
|
|
func TestRecordActionPrompt(t *testing.T) {
|
|
cat := phrasebook.NewCatalog()
|
|
if err := cat.Load("acme", fstest.MapFS{
|
|
"lang/en/lang.yaml": {Data: []byte("site_login: Site login\nsite_login_hint: The login on the public site.\nlogin_placeholder: login\n")},
|
|
"lang/pl/lang.yaml": {Data: []byte("site_login: Login na stronie\nsite_login_hint: Login w serwisie publicznym.\nlogin_placeholder: login\n")},
|
|
}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
tr := phrasebook.NewTranslator(cat, phrasebook.Options{Locale: "en", Fallback: "en"})
|
|
|
|
var got []string
|
|
extender := screensExtender{extensions: []pact.AdminActionExtension{{
|
|
Controller: AdministratorsControllerID,
|
|
RecordActions: []pact.AdminRecordAction{
|
|
promptedAction("acme_link", &pact.AdminActionPrompt{Label: "acme::lang.site_login", Placeholder: "acme::lang.login_placeholder", Comment: "acme::lang.site_login_hint", MaxLength: 5}, &got),
|
|
promptedAction("acme_note", &pact.AdminActionPrompt{Label: "acme::lang.site_login"}, &got),
|
|
pingAction(),
|
|
},
|
|
}}}
|
|
reg, err := compileWithScreens(t, []party.Plugin{extender})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
cc, _ := reg.Get(AdministratorsControllerID)
|
|
super := &bouncer.Principal{ID: 1, Backend: true, IsSuperuser: true}
|
|
|
|
t.Run("prompt localized in the show payload", func(t *testing.T) {
|
|
for locale, want := range map[string][2]string{
|
|
"en": {"Site login", "The login on the public site."},
|
|
"pl": {"Login na stronie", "Login w serwisie publicznym."},
|
|
} {
|
|
ctx := towel.WithLocale(bouncer.WithUser(context.Background(), super), locale)
|
|
offered, err := (CRUDService{tr: tr}).offeredRecordActions(ctx, cc, &BackendUser{ID: 5, IsActivated: true})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, a := range offered {
|
|
switch a.Name {
|
|
case "acme_link":
|
|
if a.Prompt == nil || a.Prompt.Label != want[0] || a.Prompt.Comment != want[1] || a.Prompt.Placeholder != "login" || a.Prompt.MaxLength != 5 {
|
|
t.Fatalf("%s acme_link prompt = %+v", locale, a.Prompt)
|
|
}
|
|
case "acme_note":
|
|
if a.Prompt == nil || a.Prompt.Label != want[0] || a.Prompt.MaxLength != 255 {
|
|
t.Fatalf("%s acme_note prompt = %+v", locale, a.Prompt)
|
|
}
|
|
case "acme_ping":
|
|
if a.Prompt != nil {
|
|
t.Fatalf("acme_ping carries a prompt: %+v", a.Prompt)
|
|
}
|
|
}
|
|
}
|
|
}
|
|
})
|
|
|
|
t.Run("value trimmed and capped in runes", func(t *testing.T) {
|
|
prompt := &pact.AdminActionPrompt{MaxLength: 5}
|
|
for raw, want := range map[string]string{`" ąęś "`: "ąęś", `"abcde"`: "abcde", "\"\\tabc\\n\"": "abc"} {
|
|
if v, err := promptValue(context.Background(), nil, prompt, json.RawMessage(raw)); err != nil || v != want {
|
|
t.Fatalf("%s = %q, %v", raw, v, err)
|
|
}
|
|
}
|
|
for _, raw := range []string{`"abcdef"`, `"ąęśćżź"`, `""`, `" "`, `7`, `null`, `["a"]`, `{"v":"a"}`} {
|
|
if v, err := promptValue(context.Background(), nil, prompt, json.RawMessage(raw)); err == nil {
|
|
t.Fatalf("%s accepted as %q", raw, v)
|
|
}
|
|
}
|
|
if v, err := promptValue(context.Background(), nil, nil, json.RawMessage(`"x"`)); err != nil || v != "" {
|
|
t.Fatalf("no prompt = %q, %v", v, err)
|
|
}
|
|
})
|
|
|
|
t.Run("route refusals", func(t *testing.T) {
|
|
svc := &service{app: backpack.New(nil), reg: reg}
|
|
post := func(action, body string) *httptest.ResponseRecorder {
|
|
req := httptest.NewRequest(http.MethodPost, "/", strings.NewReader(body))
|
|
for k, v := range map[string]string{"vendor": "summercms", "plugin": "cabana", "controller": "users", "id": "5", "action": action} {
|
|
req.SetPathValue(k, v)
|
|
}
|
|
req = req.WithContext(bouncer.WithUser(req.Context(), super))
|
|
rec := httptest.NewRecorder()
|
|
svc.recordAction(rec, req)
|
|
return rec
|
|
}
|
|
for _, body := range []string{`{"payload":42}`, `{"payload":true}`, `{"payload":"abcdefgh"}`, `{"payload":""}`, `{}`} {
|
|
if rec := post("acme_link", body); rec.Code != http.StatusUnprocessableEntity || !strings.Contains(rec.Body.String(), `"payload"`) {
|
|
t.Fatalf("%s = %d %s", body, rec.Code, rec.Body.String())
|
|
}
|
|
}
|
|
if rec := post("acme_ping", `{"payload":"x"}`); rec.Code != http.StatusUnprocessableEntity {
|
|
t.Fatalf("payload on a plain action = %d %s", rec.Code, rec.Body.String())
|
|
}
|
|
if len(got) != 0 {
|
|
t.Fatalf("a refused request ran the action: %v", got)
|
|
}
|
|
})
|
|
}
|