Files
summercms/modules/cabana/phase181_records_test.go
Jakub Zych bc5685eb93 feat(quick-261016-nqk): ListExtendRecords and FormRecordTitle admin hooks (G-18.1-4)
pact.ListExtendRecords fills unstored fields of one list page before its
columns are read (WinterCMS listExtendRecords); such columns compile
unsortable and refuse sortable/searchable. pact.FormRecordTitle names a
record; the show, create and update responses send it as meta.title.
2026-10-16 17:19:54 +02:00

64 lines
2.7 KiB
Go

package cabana_test
import (
"encoding/json"
"fmt"
"net/http"
"strings"
"testing"
)
// TestListExtendRecords: the list hook completes each page once, before the
// columns are read, so an unstored field's column shows the value it set; a
// hook error fails the list with the generic 500 and no error text.
func TestListExtendRecords(t *testing.T) {
env, gdb := newRosterEnv(t)
ada := rosterInsert(t, gdb, rosterPerson{Tenant: "acme", Name: "Ada"})
gone := rosterInsert(t, gdb, rosterPerson{Tenant: "acme", Name: "Bob", DeletedAt: rosterDeleted()})
body, raw := rosterList(t, env, "")
badges := map[string]any{}
for _, row := range body.Data {
badges[fmt.Sprint(row["id"])] = row["badge"]
}
if badges[fmt.Sprint(ada)] != fmt.Sprintf("A#%d", ada) || badges[fmt.Sprint(gone)] != fmt.Sprintf("B#%d deleted", gone) {
t.Fatalf("badges = %v\n%s", badges, raw)
}
if got := env.spy.takeExtended(); len(got) != 1 || got[0] != 2 {
t.Fatalf("ListExtendRecords pages = %v, want one page of 2", got)
}
// The unstored column is not sortable.
env.expect(t, http.StatusUnprocessableEntity, http.MethodGet, rosterPeople+"?sort=badge", "", "bearer")
logs := captureLog(t)
rosterInsert(t, gdb, rosterPerson{Tenant: "acme", Name: rosterExtendErr})
rec := env.expect(t, http.StatusInternalServerError, http.MethodGet, rosterPeople, "", "bearer")
if got := rosterError(t, rec); got.Code != "error" || strings.Contains(rec.Body.String(), "hunter2") {
t.Fatalf("500 body = %s", rec.Body.String())
}
if failed := logs.matching("cabana: list extend records failed"); len(failed) != 1 || !strings.Contains(failed[0], "controller=acme.roster.people") {
t.Fatalf("server log = %q", failed)
}
}
// TestFormRecordTitle: the show response carries the controller's record
// title as meta.title; a hook error fails the request without its text.
func TestFormRecordTitle(t *testing.T) {
env, gdb := newRosterEnv(t)
ada := rosterInsert(t, gdb, rosterPerson{Tenant: "acme", Name: "Ada"})
rec := env.expect(t, http.StatusOK, http.MethodGet, fmt.Sprintf("%s/%d", rosterPeople, ada), "", "bearer")
var body struct {
Meta map[string]json.RawMessage `json:"meta"`
}
if err := json.Unmarshal(rec.Body.Bytes(), &body); err != nil {
t.Fatal(err)
}
if got := string(body.Meta["title"]); got != fmt.Sprintf("%q", fmt.Sprintf("Person #%d (Ada)", ada)) {
t.Fatalf("meta.title = %s\n%s", got, rec.Body.String())
}
bad := rosterInsert(t, gdb, rosterPerson{Tenant: "acme", Name: rosterTitleErr})
rec = env.expect(t, http.StatusInternalServerError, http.MethodGet, fmt.Sprintf("%s/%d", rosterPeople, bad), "", "bearer")
if strings.Contains(rec.Body.String(), "hunter2") {
t.Fatalf("500 body = %s", rec.Body.String())
}
}