- Webhook event logging repo and subscription webhook idempotency - Pagination helper (echohelpers) with cursor/offset support - Request ID and structured logging middleware - Push client improvements (FCM HTTP v1, better error handling) - Task model version column, business constraint migrations, targeted indexes - Expanded categorization chain tests - Email service and config hardening - CI workflow updates, .gitignore additions, .env.example updates Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
105 lines
3.7 KiB
Go
105 lines
3.7 KiB
Go
package repositories
|
|
|
|
import (
|
|
"testing"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"gorm.io/driver/sqlite"
|
|
"gorm.io/gorm"
|
|
"gorm.io/gorm/logger"
|
|
)
|
|
|
|
// setupWebhookTestDB creates an in-memory SQLite database with the
|
|
// WebhookEvent table auto-migrated. This is separate from testutil.SetupTestDB
|
|
// because WebhookEvent lives in the repositories package (not models/) and
|
|
// only needs its own table for testing.
|
|
func setupWebhookTestDB(t *testing.T) *gorm.DB {
|
|
t.Helper()
|
|
db, err := gorm.Open(sqlite.Open(":memory:"), &gorm.Config{
|
|
Logger: logger.Default.LogMode(logger.Silent),
|
|
})
|
|
require.NoError(t, err)
|
|
|
|
err = db.AutoMigrate(&WebhookEvent{})
|
|
require.NoError(t, err)
|
|
|
|
return db
|
|
}
|
|
|
|
func TestWebhookEventRepo_RecordAndCheck(t *testing.T) {
|
|
db := setupWebhookTestDB(t)
|
|
repo := NewWebhookEventRepository(db)
|
|
|
|
// Record an event
|
|
err := repo.RecordEvent("apple", "evt_001", "INITIAL_BUY", "abc123hash")
|
|
require.NoError(t, err)
|
|
|
|
// HasProcessed should return true for the same provider + event ID
|
|
processed, err := repo.HasProcessed("apple", "evt_001")
|
|
require.NoError(t, err)
|
|
assert.True(t, processed, "expected HasProcessed to return true for a recorded event")
|
|
|
|
// HasProcessed should return false for a different event ID
|
|
processed, err = repo.HasProcessed("apple", "evt_999")
|
|
require.NoError(t, err)
|
|
assert.False(t, processed, "expected HasProcessed to return false for an unrecorded event ID")
|
|
|
|
// HasProcessed should return false for a different provider
|
|
processed, err = repo.HasProcessed("google", "evt_001")
|
|
require.NoError(t, err)
|
|
assert.False(t, processed, "expected HasProcessed to return false for a different provider")
|
|
}
|
|
|
|
func TestWebhookEventRepo_DuplicateInsert(t *testing.T) {
|
|
db := setupWebhookTestDB(t)
|
|
repo := NewWebhookEventRepository(db)
|
|
|
|
// First insert should succeed
|
|
err := repo.RecordEvent("apple", "evt_dup", "RENEWAL", "hash1")
|
|
require.NoError(t, err)
|
|
|
|
// Second insert with the same provider + event ID should fail (unique constraint)
|
|
err = repo.RecordEvent("apple", "evt_dup", "RENEWAL", "hash1")
|
|
require.Error(t, err, "expected an error when inserting a duplicate provider + event_id")
|
|
|
|
// Verify only one row exists
|
|
var count int64
|
|
db.Model(&WebhookEvent{}).Where("provider = ? AND event_id = ?", "apple", "evt_dup").Count(&count)
|
|
assert.Equal(t, int64(1), count, "expected exactly one row for the duplicated event")
|
|
}
|
|
|
|
func TestWebhookEventRepo_DifferentProviders(t *testing.T) {
|
|
db := setupWebhookTestDB(t)
|
|
repo := NewWebhookEventRepository(db)
|
|
|
|
sharedEventID := "evt_shared_123"
|
|
|
|
// Record event for "apple" provider
|
|
err := repo.RecordEvent("apple", sharedEventID, "INITIAL_BUY", "applehash")
|
|
require.NoError(t, err)
|
|
|
|
// HasProcessed should return true for "apple"
|
|
processed, err := repo.HasProcessed("apple", sharedEventID)
|
|
require.NoError(t, err)
|
|
assert.True(t, processed, "expected HasProcessed to return true for apple provider")
|
|
|
|
// HasProcessed should return false for "google" with the same event ID
|
|
processed, err = repo.HasProcessed("google", sharedEventID)
|
|
require.NoError(t, err)
|
|
assert.False(t, processed, "expected HasProcessed to return false for google provider with the same event ID")
|
|
|
|
// Recording the same event ID under "google" should succeed (different provider)
|
|
err = repo.RecordEvent("google", sharedEventID, "INITIAL_BUY", "googlehash")
|
|
require.NoError(t, err)
|
|
|
|
// Now both providers should show as processed
|
|
processed, err = repo.HasProcessed("apple", sharedEventID)
|
|
require.NoError(t, err)
|
|
assert.True(t, processed, "expected apple to still be processed")
|
|
|
|
processed, err = repo.HasProcessed("google", sharedEventID)
|
|
require.NoError(t, err)
|
|
assert.True(t, processed, "expected google to now be processed")
|
|
}
|