package delivery_test import ( "fmt" "net/http" "path/filepath" "testing" "time" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "sneak.berlin/go/webhooker/internal/database" "sneak.berlin/go/webhooker/internal/delivery" ) // evictTestEngine builds an engine backed by a temporary data // directory and returns it along with that directory. func evictTestEngine(t *testing.T) (*delivery.Engine, string) { t.Helper() dataDir := t.TempDir() eng := delivery.NewTestEngineWithDB( nil, database.NewTestWebhookDBManager(dataDir), archiveTestLogger(), &http.Client{Timeout: 5 * time.Second}, 1, ) return eng, dataDir } // TestEvictWebhook_ClosesAndRemovesWriter proves that evicting // a webhook drops its archive writer from the registry and // closes the open archive handle, rather than leaving both // alive for the process lifetime. func TestEvictWebhook_ClosesAndRemovesWriter(t *testing.T) { t.Parallel() eng, dataDir := evictTestEngine(t) webhookDB := testWebhookDB(t) event := seedEvent(t, webhookDB, `{"archived":true}`) d := seedDatabaseTargetDelivery(t, webhookDB, event, "") eng.ExportDeliverDatabase(webhookDB, d) webhookID := event.WebhookID require.True( t, eng.ExportHasArchiveWriter(webhookID), "a delivery should have cached an archive writer", ) require.True( t, eng.ExportArchiveHandleOpen(webhookID), "the writer should hold an open handle after a write", ) eng.EvictWebhook(webhookID) assert.False( t, eng.ExportHasArchiveWriter(webhookID), "eviction should remove the registry entry", ) assert.False( t, eng.ExportArchiveHandleOpen(webhookID), "eviction should close the archive handle", ) archivePath := filepath.Join( dataDir, fmt.Sprintf("archive-%s.db", webhookID), ) assert.FileExists( t, archivePath, "eviction must not delete the archive file", ) } // TestEvictWebhook_UnknownWebhookIsNoOp proves eviction is safe // for the common case of a webhook that never had a database // target, and that repeating it does not panic. func TestEvictWebhook_UnknownWebhookIsNoOp(t *testing.T) { t.Parallel() eng, _ := evictTestEngine(t) assert.NotPanics(t, func() { eng.EvictWebhook("no-such-webhook") eng.EvictWebhook("no-such-webhook") }) assert.False( t, eng.ExportHasArchiveWriter("no-such-webhook"), "eviction must not create a writer", ) } // TestEvictWebhook_EvictedWriterDoesNotReopen proves an evicted // writer refuses further writes instead of silently reopening // the archive file: nothing holds it any more, so a reopened // handle would leak. func TestEvictWebhook_EvictedWriterDoesNotReopen(t *testing.T) { t.Parallel() eng, _ := evictTestEngine(t) webhookDB := testWebhookDB(t) event := seedEvent(t, webhookDB, `{"archived":true}`) d := seedDatabaseTargetDelivery(t, webhookDB, event, "") eng.ExportDeliverDatabase(webhookDB, d) require.True( t, eng.ExportHasArchiveWriter(event.WebhookID), ) eng.EvictWebhook(event.WebhookID) // A fresh delivery for the same webhook gets a brand new // writer from the registry, so archiving keeps working. second := seedDatabaseTargetDelivery( t, webhookDB, event, "", ) eng.ExportDeliverDatabase(webhookDB, second) assert.True( t, eng.ExportHasArchiveWriter(event.WebhookID), "a later delivery should recreate the writer", ) }