- money_field_test.go: compile gates (moved from the smoke file and extended), model column gate, save-time value check, list projection - settings secret projection and put without a database; tabs, settings richeditor sanitize and the danger flag as internal unit tests - Postgres: TestMoneyFieldSave, TestSettingsSecretPostgres and the Phase 18 payload cases of TestPhase10OpenAPIConformance - vitest: money.ts helpers spec, MoneyField blur-only and server-error cases - gate arrays and the cabana README Testing section name every test
76 lines
2.9 KiB
Go
76 lines
2.9 KiB
Go
package cabana
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.golem15.com/golem15/summercms/modules/bouncer"
|
|
"git.golem15.com/golem15/summercms/modules/pact"
|
|
)
|
|
|
|
// TestRecordActionDanger checks UI-SPEC F5 without a database: the record
|
|
// actions offered on a record carry pact.AdminRecordAction.Danger through
|
|
// to cabana's RecordAction, in the form's declared order; an ordinary
|
|
// action defaults to false and omits the key on the wire, so existing
|
|
// payloads stay byte-identical; an action the principal may not run or
|
|
// that does not apply is not offered at all.
|
|
func TestRecordActionDanger(t *testing.T) {
|
|
run := func(context.Context, pact.AdminRecordActionInput) (pact.AdminRecordActionResult, error) {
|
|
return pact.AdminRecordActionResult{}, nil
|
|
}
|
|
cc := &CompiledController{
|
|
Form: &FormSchema{recordActions: []string{"resend", "void", "archive", "locked"}},
|
|
RecordActions: map[string]pact.AdminRecordAction{
|
|
"void": {Name: "void", Label: "Void invoice", Confirm: "Void this invoice?", Danger: true, Run: run},
|
|
"resend": {Name: "resend", Label: "Send again", Run: run},
|
|
"archive": {Name: "archive", Label: "Archive", Danger: true, Run: run, Applies: func(context.Context, any) (bool, error) { return false, nil }},
|
|
"locked": {Name: "locked", Label: "Locked", Permissions: []string{"acme.payments.void"}, Run: run},
|
|
},
|
|
}
|
|
editor := &bouncer.Principal{ID: 2, Backend: true, PermissionGrants: map[string]bool{"acme.payments.access": true}}
|
|
actions, err := (CRUDService{}).offeredRecordActions(bouncer.WithUser(context.Background(), editor), cc, struct{}{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(actions) != 2 {
|
|
t.Fatalf("actions = %+v, want resend and void (archive does not apply, locked needs a permission)", actions)
|
|
}
|
|
if actions[0].Name != "resend" || actions[0].Danger || actions[1].Name != "void" || !actions[1].Danger {
|
|
t.Fatalf("actions = %+v", actions)
|
|
}
|
|
if actions[1].Label != "Void invoice" || actions[1].Confirm != "Void this invoice?" {
|
|
t.Fatalf("void = %+v", actions[1])
|
|
}
|
|
resend, err := json.Marshal(actions[0])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
void, err := json.Marshal(actions[1])
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if strings.Contains(string(resend), "danger") {
|
|
t.Fatalf("an ordinary action carries the danger key: %s", resend)
|
|
}
|
|
if !strings.Contains(string(void), `"danger":true`) {
|
|
t.Fatalf("the danger action lacks danger:true: %s", void)
|
|
}
|
|
|
|
super := &bouncer.Principal{ID: 1, Backend: true, IsSuperuser: true}
|
|
actions, err = (CRUDService{}).offeredRecordActions(bouncer.WithUser(context.Background(), super), cc, struct{}{})
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if len(actions) != 3 || actions[2].Name != "locked" || actions[2].Danger {
|
|
t.Fatalf("superuser actions = %+v", actions)
|
|
}
|
|
|
|
// The zero value of the pact field is false: actions written before
|
|
// Phase 18 keep the default confirm variant.
|
|
if (pact.AdminRecordAction{Name: "legacy"}).Danger {
|
|
t.Fatal("Danger defaults to true")
|
|
}
|
|
}
|