diff --git a/controllers/site_cache_test.go b/controllers/site_cache_test.go new file mode 100644 index 0000000..bf93773 --- /dev/null +++ b/controllers/site_cache_test.go @@ -0,0 +1,77 @@ +package controllers + +import ( + "context" + "net/http" + "net/http/httptest" + "sync/atomic" + "testing" + + "git.golem15.com/golem15/summercms/modules/backpack" + "git.golem15.com/golem15/summercms/modules/compass" + "git.golem15.com/golem15/summercms/modules/cooler" +) + +const purgeHelperToken = "controllers-purge-helper-token-0123456789" + +// purgeCounter is a purge receiver that counts requests. +func purgeCounter(t *testing.T) (*httptest.Server, *atomic.Int64) { + t.Helper() + var hits atomic.Int64 + srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) { + hits.Add(1) + _, _ = w.Write([]byte(`{"purged":1}`)) + })) + t.Cleanup(srv.Close) + return srv, &hits +} + +// helperApp returns an app on a config without a database, with the +// framework purge keys pointing at purgeURL when it is not empty. +func helperApp(t *testing.T, purgeURL string) *backpack.App { + t.Helper() + cfg, err := compass.Open(compass.Options{Dir: t.TempDir(), Environ: []string{}}) + if err != nil { + t.Fatal(err) + } + if purgeURL != "" { + if err := cfg.Set(cooler.SiteCachePurgeURLKey, purgeURL); err != nil { + t.Fatal(err) + } + if err := cfg.Set(cooler.SiteCachePurgeTokenKey, purgeHelperToken); err != nil { + t.Fatal(err) + } + } + return backpack.New(cfg) +} + +// TestPurgeSiteCacheWithoutTx: outside a save transaction the helper sends +// nothing, even with a purger configured; a purge only ever follows a commit. +func TestPurgeSiteCacheWithoutTx(t *testing.T) { + srv, hits := purgeCounter(t) + app := helperApp(t, srv.URL+"/api/__site__/cache/purge") + if cooler.SiteCacheFromApp(app) == nil { + t.Fatal("the test purger is not configured") + } + get := func() *backpack.App { return app } + purgeSiteCache(context.Background(), get, "faq", "shipping") + purgeSiteCache(context.TODO(), get, "menu", "main") + if n := hits.Load(); n != 0 { + t.Fatalf("%d purge requests outside a transaction, want 0", n) + } +} + +// TestPurgeSiteCacheWithoutPurger: without an app or a purge configuration +// the helper does nothing and never panics. (Inside a save transaction +// without a purger, the plugin's Docker admin suites run every save through +// it.) +func TestPurgeSiteCacheWithoutPurger(t *testing.T) { + ctx := context.Background() + purgeSiteCache(ctx, nil, "faq", "a") + purgeSiteCache(ctx, func() *backpack.App { return nil }, "faq", "b") + app := helperApp(t, "") + purgeSiteCache(ctx, func() *backpack.App { return app }, "faq", "c") + if p, ok := app.Lookup[*cooler.SiteCachePurger](); ok && p != nil { + t.Fatal("the helper published a purger without configuration") + } +}