test(12.2-05): cover every Go behaviour of phase 12.2

- lagoon: Date and TimeOfDay through JSON, text and real DATE/TIME
  columns; Fill text fallback without changing earlier conversions;
  required on zero dates; deferred_bindings shape, store isolation and
  envelope; PurgeDeferred cut-off, after-commit blobs, SKIP LOCKED,
  skipped types and the deferred:purge command
- attach: Store limits, extensions, MIME patterns, default lists, key
  shape and blob cleanup; IsAllowedImage formats, polyglots, ceiling
- conga and pact: framework purge schedule entry and forged jobs; the
  six relation child hook interfaces
- cabana: fileupload and datepicker compile, upload, remove, caption,
  reorder and bounds; deferred commit order, rollback, applied-only and
  concurrent saves; relation contracts, forms, CRUD, deferral, schema
This commit is contained in:
Jakub Zych
2026-10-02 20:34:23 +02:00
parent 9c87a59532
commit c8d41a68c6
16 changed files with 3509 additions and 10 deletions

View File

@@ -1,7 +1,10 @@
package pact
import (
"context"
"errors"
"io/fs"
"strings"
"testing"
"testing/fstest"
@@ -146,3 +149,101 @@ func TestHTTPCapabilitiesDiscoveredByTypeAssertion(t *testing.T) {
t.Fatalf("Models = %+v", models.Models())
}
}
// relationHooks implements every relation child hook with the documented
// signature and records the calls.
type relationHooks struct{ calls *[]string }
func (h relationHooks) record(name, relation string, parent, child any) error {
*h.calls = append(*h.calls, name+":"+relation+":"+parent.(string)+":"+child.(string))
if relation == "fail" {
return errors.New(name + " refused")
}
return nil
}
func (h relationHooks) RelationBeforeCreate(_ context.Context, relation string, parent, child any) error {
return h.record("BeforeCreate", relation, parent, child)
}
func (h relationHooks) RelationAfterCreate(_ context.Context, relation string, parent, child any) error {
return h.record("AfterCreate", relation, parent, child)
}
func (h relationHooks) RelationBeforeUpdate(_ context.Context, relation string, parent, child any) error {
return h.record("BeforeUpdate", relation, parent, child)
}
func (h relationHooks) RelationAfterUpdate(_ context.Context, relation string, parent, child any) error {
return h.record("AfterUpdate", relation, parent, child)
}
func (h relationHooks) RelationBeforeDelete(_ context.Context, relation string, parent, child any) error {
return h.record("BeforeDelete", relation, parent, child)
}
func (h relationHooks) RelationAfterDelete(_ context.Context, relation string, parent, child any) error {
return h.record("AfterDelete", relation, parent, child)
}
var (
_ RelationBeforeCreate = relationHooks{}
_ RelationAfterCreate = relationHooks{}
_ RelationBeforeUpdate = relationHooks{}
_ RelationAfterUpdate = relationHooks{}
_ RelationBeforeDelete = relationHooks{}
_ RelationAfterDelete = relationHooks{}
)
// TestRelationHookInterfaces: each of the six optional relation child hooks
// is discovered by a type assertion on the controller value, receives the
// relation name, the parent and the child, and passes its error back; a
// controller without them asserts false.
func TestRelationHookInterfaces(t *testing.T) {
var calls []string
var ctl any = relationHooks{calls: &calls}
hooks := []func(context.Context, string, any, any) error{}
if h, ok := ctl.(RelationBeforeCreate); ok {
hooks = append(hooks, h.RelationBeforeCreate)
}
if h, ok := ctl.(RelationAfterCreate); ok {
hooks = append(hooks, h.RelationAfterCreate)
}
if h, ok := ctl.(RelationBeforeUpdate); ok {
hooks = append(hooks, h.RelationBeforeUpdate)
}
if h, ok := ctl.(RelationAfterUpdate); ok {
hooks = append(hooks, h.RelationAfterUpdate)
}
if h, ok := ctl.(RelationBeforeDelete); ok {
hooks = append(hooks, h.RelationBeforeDelete)
}
if h, ok := ctl.(RelationAfterDelete); ok {
hooks = append(hooks, h.RelationAfterDelete)
}
if len(hooks) != 6 {
t.Fatalf("discovered %d hooks, want 6", len(hooks))
}
for _, hook := range hooks {
if err := hook(t.Context(), "parts", "gadget", "part"); err != nil {
t.Fatal(err)
}
if err := hook(t.Context(), "fail", "gadget", "part"); err == nil {
t.Fatal("hook error was not passed back")
}
}
want := "BeforeCreate:parts:gadget:part,BeforeCreate:fail:gadget:part,AfterCreate:parts:gadget:part,AfterCreate:fail:gadget:part," +
"BeforeUpdate:parts:gadget:part,BeforeUpdate:fail:gadget:part,AfterUpdate:parts:gadget:part,AfterUpdate:fail:gadget:part," +
"BeforeDelete:parts:gadget:part,BeforeDelete:fail:gadget:part,AfterDelete:parts:gadget:part,AfterDelete:fail:gadget:part"
if got := strings.Join(calls, ","); got != want {
t.Fatalf("calls = %s", got)
}
var plain any = struct{}{}
for name, ok := range map[string]bool{
"BeforeCreate": func() bool { _, ok := plain.(RelationBeforeCreate); return ok }(),
"AfterCreate": func() bool { _, ok := plain.(RelationAfterCreate); return ok }(),
"BeforeUpdate": func() bool { _, ok := plain.(RelationBeforeUpdate); return ok }(),
"AfterUpdate": func() bool { _, ok := plain.(RelationAfterUpdate); return ok }(),
"BeforeDelete": func() bool { _, ok := plain.(RelationBeforeDelete); return ok }(),
"AfterDelete": func() bool { _, ok := plain.(RelationAfterDelete); return ok }(),
} {
if ok {
t.Fatalf("a plain value implements Relation%s", name)
}
}
}