feat(12.2-01): add deferred:purge, its daily framework schedule and relation child hooks

- deferred:purge [--days] in lagoon.RuntimeCommands (purge_days, default 5)
- lagoon.FrameworkSchedule entry at purge_at (default 03:00, empty disables)
- conga prepends framework entries as summercms.lagoon[i]:<command>
- pact.Relation{Before,After}{Create,Update,Delete} optional hooks
- lagoon, conga and pact READMEs, scheduling and setup docs
This commit is contained in:
Jakub Zych
2026-10-02 17:45:41 +02:00
parent f48a886f94
commit 8818d7b023
11 changed files with 378 additions and 8 deletions

View File

@@ -790,3 +790,56 @@ func TestScheduleDueAt(t *testing.T) {
}
}
}
// TestFrameworkScheduleSmoke covers the framework's own daily purge entry:
// first in the entry list when the app has config, removed by an empty
// purge_at, absent for a config-less app, and a boot error when malformed.
func TestFrameworkScheduleSmoke(t *testing.T) {
plugins := schedulePlugins(pact.ScheduledCommand{Command: "acme:tick", Cadence: pact.Daily()})
withConfig := func(t *testing.T, extra map[string]any) *backpack.App {
t.Helper()
cfg, err := compass.Open(compass.Options{Dir: t.TempDir(), Env: "testing", Environ: []string{}})
if err != nil {
t.Fatal(err)
}
for k, v := range extra {
if err := cfg.Set(k, v); err != nil {
t.Fatal(err)
}
}
return backpack.New(cfg)
}
entries, err := scheduleEntries(withConfig(t, nil), plugins)
if err != nil {
t.Fatal(err)
}
if len(entries) != 2 || entries[0].id != "summercms.lagoon[0]:deferred:purge" || entries[0].plugin != "summercms.lagoon" {
t.Fatalf("entries = %+v", entries)
}
if entries[0].cmd.Cadence != pact.DailyAt(3, 0) || entries[1].id != "acme.test[0]:acme:tick" {
t.Fatalf("framework entry %+v, next %s", entries[0].cmd, entries[1].id)
}
_, table, err := periodicJobs(withConfig(t, map[string]any{"database.deferred_bindings.purge_at": "04:30"}), plugins)
if err != nil {
t.Fatal(err)
}
if fw, ok := table["summercms.lagoon[0]:deferred:purge"]; !ok || fw.Cadence != pact.DailyAt(4, 30) {
t.Fatalf("compiled table = %+v", table)
}
entries, err = scheduleEntries(withConfig(t, map[string]any{"database.deferred_bindings.purge_at": ""}), plugins)
if err != nil || len(entries) != 1 || entries[0].id != "acme.test[0]:acme:tick" {
t.Fatalf("disabled: %+v (%v)", entries, err)
}
entries, err = scheduleEntries(backpack.New(nil), plugins)
if err != nil || len(entries) != 1 || entries[0].plugin == "summercms.lagoon" {
t.Fatalf("nil config: %+v (%v)", entries, err)
}
_, err = scheduleEntries(withConfig(t, map[string]any{"database.deferred_bindings.purge_at": "3am"}), plugins)
if err == nil || !strings.Contains(err.Error(), "database.deferred_bindings.purge_at") {
t.Fatalf("malformed purge_at = %v", err)
}
}