test(19.3-05): Journal site-cache keys: framework keys win, alias keys deprecated
- site_cache_keys_test.go: wireSiteCache on a config without a database - rename the Docker admin test to TestCategoriesAdminFrameworkKeysWin so the unit test owns TestSiteCacheFrameworkKeysWin (one name per test binary)
This commit is contained in:
@@ -330,10 +330,10 @@ func TestPostsAdminPurgesSiteCache(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
// TestSiteCacheFrameworkKeysWin proves the framework keys site_cache.* take
|
||||
// TestCategoriesAdminFrameworkKeysWin proves the framework keys site_cache.* take
|
||||
// precedence over the Journal's deprecated keys: with both set, a category
|
||||
// save purges through the framework target only.
|
||||
func TestSiteCacheFrameworkKeysWin(t *testing.T) {
|
||||
func TestCategoriesAdminFrameworkKeysWin(t *testing.T) {
|
||||
framework := newPurgeReceiver(t)
|
||||
alias := newPurgeReceiver(t)
|
||||
env := newPostsEnv(t, append(alias.env(),
|
||||
|
||||
222
site_cache_keys_test.go
Normal file
222
site_cache_keys_test.go
Normal file
@@ -0,0 +1,222 @@
|
||||
package journal
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"log/slog"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
|
||||
"git.golem15.com/golem15/sm-journal-plugin/classes"
|
||||
"git.golem15.com/golem15/summercms/modules/backpack"
|
||||
"git.golem15.com/golem15/summercms/modules/compass"
|
||||
"git.golem15.com/golem15/summercms/modules/cooler"
|
||||
)
|
||||
|
||||
const siteCacheKeysToken = "journal-site-cache-keys-token-0123456789"
|
||||
|
||||
type keysLogBuffer struct {
|
||||
mu sync.Mutex
|
||||
buf bytes.Buffer
|
||||
}
|
||||
|
||||
func (b *keysLogBuffer) Write(p []byte) (int, error) {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
return b.buf.Write(p)
|
||||
}
|
||||
|
||||
func (b *keysLogBuffer) lines() []string {
|
||||
b.mu.Lock()
|
||||
defer b.mu.Unlock()
|
||||
var out []string
|
||||
for _, l := range strings.Split(strings.TrimSpace(b.buf.String()), "\n") {
|
||||
if l != "" {
|
||||
out = append(out, l)
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// siteCacheApp returns an app on a config without a database holding values,
|
||||
// with slog.Default captured for the test.
|
||||
func siteCacheApp(t *testing.T, values map[string]string) (*backpack.App, *keysLogBuffer) {
|
||||
t.Helper()
|
||||
cfg, err := compass.Open(compass.Options{Dir: t.TempDir(), Environ: []string{}})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
for k, v := range values {
|
||||
if err := cfg.Set(k, v); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
buf := &keysLogBuffer{}
|
||||
prev := slog.Default()
|
||||
slog.SetDefault(slog.New(slog.NewTextHandler(buf, nil)))
|
||||
t.Cleanup(func() { slog.SetDefault(prev) })
|
||||
return backpack.New(cfg), buf
|
||||
}
|
||||
|
||||
func assertNoPurgeToken(t *testing.T, lines []string) {
|
||||
t.Helper()
|
||||
for _, l := range lines {
|
||||
if strings.Contains(l, siteCacheKeysToken) {
|
||||
t.Fatalf("log line carries the token: %s", l)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestSiteCacheFrameworkKeysWin(t *testing.T) {
|
||||
framework := map[string]string{
|
||||
cooler.SiteCachePurgeURLKey: "http://127.0.0.1:3000/api/__site__/cache/purge",
|
||||
cooler.SiteCachePurgeTokenKey: siteCacheKeysToken,
|
||||
}
|
||||
|
||||
t.Run("framework keys only: the Journal publishes nothing and stays quiet", func(t *testing.T) {
|
||||
app, logs := siteCacheApp(t, framework)
|
||||
if err := (&Plugin{}).wireSiteCache(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p, ok := app.Lookup[*cooler.SiteCachePurger](); ok && p != nil {
|
||||
t.Fatal("the Journal published a purger although the framework keys are set")
|
||||
}
|
||||
if lines := logs.lines(); len(lines) != 0 {
|
||||
t.Fatalf("log = %q, want nothing", lines)
|
||||
}
|
||||
// The framework builds the purger from its own keys.
|
||||
if p := cooler.SiteCacheFromApp(app); p == nil || p.Endpoint() != "127.0.0.1:3000/api/__site__/cache/purge" {
|
||||
t.Fatalf("cooler.SiteCacheFromApp = %v", p)
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("alias keys too: one warning that they are ignored", func(t *testing.T) {
|
||||
values := map[string]string{
|
||||
classes.SiteCachePurgeURLKey: "http://127.0.0.1:4000/purge",
|
||||
classes.SiteCachePurgeTokenKey: "x", // invalid, but ignored
|
||||
}
|
||||
for k, v := range framework {
|
||||
values[k] = v
|
||||
}
|
||||
app, logs := siteCacheApp(t, values)
|
||||
if err := (&Plugin{}).wireSiteCache(app); err != nil {
|
||||
t.Fatalf("ignored alias keys must not fail boot: %v", err)
|
||||
}
|
||||
if p, ok := app.Lookup[*cooler.SiteCachePurger](); ok && p != nil {
|
||||
t.Fatal("the Journal published a purger from ignored keys")
|
||||
}
|
||||
lines := logs.lines()
|
||||
if len(lines) != 1 || !strings.Contains(lines[0], "level=WARN") || !strings.Contains(lines[0], "ignored") ||
|
||||
!strings.Contains(lines[0], cooler.SiteCachePurgeURLKey) {
|
||||
t.Fatalf("log = %q, want one warning naming the framework keys", lines)
|
||||
}
|
||||
assertNoPurgeToken(t, lines)
|
||||
})
|
||||
|
||||
t.Run("only the framework token set still counts as framework keys", func(t *testing.T) {
|
||||
app, _ := siteCacheApp(t, map[string]string{
|
||||
cooler.SiteCachePurgeTokenKey: siteCacheKeysToken,
|
||||
classes.SiteCachePurgeURLKey: "http://127.0.0.1:4000/purge",
|
||||
classes.SiteCachePurgeTokenKey: siteCacheKeysToken,
|
||||
})
|
||||
if err := (&Plugin{}).wireSiteCache(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p, ok := app.Lookup[*cooler.SiteCachePurger](); ok && p != nil {
|
||||
t.Fatal("alias keys were used although a framework key is set")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSiteCacheAliasKeysDeprecated(t *testing.T) {
|
||||
t.Run("valid alias keys publish a purger with one deprecation warning", func(t *testing.T) {
|
||||
app, logs := siteCacheApp(t, map[string]string{
|
||||
classes.SiteCachePurgeURLKey: "http://127.0.0.1:4000/api/__site__/cache/purge",
|
||||
classes.SiteCachePurgeTokenKey: siteCacheKeysToken,
|
||||
})
|
||||
if err := (&Plugin{}).wireSiteCache(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
p, ok := app.Lookup[*cooler.SiteCachePurger]()
|
||||
if !ok || p == nil || p.Endpoint() != "127.0.0.1:4000/api/__site__/cache/purge" {
|
||||
t.Fatalf("published purger = %v", p)
|
||||
}
|
||||
if got := cooler.SiteCacheFromApp(app); got != p {
|
||||
t.Fatal("cooler.SiteCacheFromApp did not return the alias purger")
|
||||
}
|
||||
lines := logs.lines()
|
||||
if len(lines) != 1 || !strings.Contains(lines[0], "level=WARN") || !strings.Contains(lines[0], "deprecated") ||
|
||||
!strings.Contains(lines[0], cooler.SiteCachePurgeURLKey) || !strings.Contains(lines[0], cooler.SiteCachePurgeTokenKey) {
|
||||
t.Fatalf("log = %q, want one deprecation warning naming both framework keys", lines)
|
||||
}
|
||||
assertNoPurgeToken(t, lines)
|
||||
})
|
||||
|
||||
for _, tc := range []struct {
|
||||
name, url, token, key string
|
||||
}{
|
||||
{"only the alias URL", "http://127.0.0.1:4000/purge", "", classes.SiteCachePurgeTokenKey},
|
||||
{"only the alias token", "", siteCacheKeysToken, classes.SiteCachePurgeURLKey},
|
||||
{"http to a remote host", "http://example.test/purge", siteCacheKeysToken, classes.SiteCachePurgeURLKey},
|
||||
{"a short token", "http://127.0.0.1:4000/purge", "short", classes.SiteCachePurgeTokenKey},
|
||||
} {
|
||||
t.Run("invalid alias keys fail boot: "+tc.name, func(t *testing.T) {
|
||||
app, _ := siteCacheApp(t, map[string]string{
|
||||
classes.SiteCachePurgeURLKey: tc.url,
|
||||
classes.SiteCachePurgeTokenKey: tc.token,
|
||||
})
|
||||
err := (&Plugin{}).wireSiteCache(app)
|
||||
if err == nil || !strings.Contains(err.Error(), tc.key) {
|
||||
t.Fatalf("wireSiteCache = %v, want an error naming %s", err, tc.key)
|
||||
}
|
||||
if tc.token != "" && strings.Contains(err.Error(), tc.token) {
|
||||
t.Fatalf("error %q carries the token", err)
|
||||
}
|
||||
if p, ok := app.Lookup[*cooler.SiteCachePurger](); ok && p != nil {
|
||||
t.Fatal("an invalid alias configuration published a purger")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
t.Run("an already published purger is kept", func(t *testing.T) {
|
||||
app, _ := siteCacheApp(t, map[string]string{
|
||||
classes.SiteCachePurgeURLKey: "http://127.0.0.1:4000/purge",
|
||||
classes.SiteCachePurgeTokenKey: siteCacheKeysToken,
|
||||
})
|
||||
first, err := cooler.NewSiteCachePurger("http://127.0.0.1:5000/purge", siteCacheKeysToken)
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := app.Publish(first); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if err := (&Plugin{}).wireSiteCache(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if got, _ := app.Lookup[*cooler.SiteCachePurger](); got != first {
|
||||
t.Fatal("wireSiteCache replaced a published purger")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func TestSiteCacheNoKeys(t *testing.T) {
|
||||
app, logs := siteCacheApp(t, nil)
|
||||
if err := (&Plugin{}).wireSiteCache(app); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if p, ok := app.Lookup[*cooler.SiteCachePurger](); ok && p != nil {
|
||||
t.Fatal("no keys published a purger")
|
||||
}
|
||||
if p := cooler.SiteCacheFromApp(app); p != nil {
|
||||
t.Fatalf("cooler.SiteCacheFromApp = %v, want nil", p)
|
||||
}
|
||||
if lines := logs.lines(); len(lines) != 0 {
|
||||
t.Fatalf("log = %q, want nothing", lines)
|
||||
}
|
||||
if err := (&Plugin{}).wireSiteCache(nil); err != nil {
|
||||
t.Fatalf("wireSiteCache(nil) = %v", err)
|
||||
}
|
||||
if err := (&Plugin{}).wireSiteCache(backpack.New(nil)); err != nil {
|
||||
t.Fatalf("wireSiteCache(no config) = %v", err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user