Files
summercms/modules/cabana/record_action_danger_test.go
Jakub Zych 0f35c9a0ca test(18-07): framework unit coverage of money, settings secrets, tabs, richeditor and danger actions (task 2)
- 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
2026-10-13 16:58:03 +02:00

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")
}
}