- Priority 1: Test NewSendEmailTask + NewSendPushTask (5 tests) - Priority 2: Test customHTTPErrorHandler — all 15+ branches (21 tests) - Priority 3: Extract Enqueuer interface + payload builders in worker pkg (5 tests) - Priority 4: Extract ClassifyFile/ComputeRelPath in migrate-encrypt (6 tests) - Priority 5: Define Handler interfaces, refactor to accept them, mock-based tests (14 tests) - Fix .gitignore: /worker instead of worker to stop ignoring internal/worker/ Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
419 lines
13 KiB
Go
419 lines
13 KiB
Go
package repositories
|
|
|
|
import (
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
|
|
"github.com/treytartt/honeydue-api/internal/models"
|
|
"github.com/treytartt/honeydue-api/internal/testutil"
|
|
)
|
|
|
|
func TestSubscriptionRepository_FindByUserID(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
sub := &models.UserSubscription{
|
|
UserID: user.ID,
|
|
Tier: models.TierPro,
|
|
}
|
|
require.NoError(t, db.Create(sub).Error)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.TierPro, found.Tier)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByUserID_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
_, err := repo.FindByUserID(9999)
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSubscriptionRepository_Update(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
sub, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
sub.Tier = models.TierPro
|
|
err = repo.Update(sub)
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.TierPro, found.Tier)
|
|
}
|
|
|
|
func TestSubscriptionRepository_UpgradeToPro(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
expiresAt := time.Now().UTC().AddDate(1, 0, 0)
|
|
err = repo.UpgradeToPro(user.ID, expiresAt, "apple")
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.TierPro, found.Tier)
|
|
assert.Equal(t, "apple", found.Platform)
|
|
assert.True(t, found.AutoRenew)
|
|
require.NotNil(t, found.SubscribedAt)
|
|
require.NotNil(t, found.ExpiresAt)
|
|
}
|
|
|
|
func TestSubscriptionRepository_DowngradeToFree(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
// First upgrade
|
|
err = repo.UpgradeToPro(user.ID, time.Now().UTC().AddDate(1, 0, 0), "apple")
|
|
require.NoError(t, err)
|
|
|
|
// Then downgrade
|
|
err = repo.DowngradeToFree(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
assert.Equal(t, models.TierFree, found.Tier)
|
|
assert.False(t, found.AutoRenew)
|
|
require.NotNil(t, found.CancelledAt)
|
|
}
|
|
|
|
func TestSubscriptionRepository_SetAutoRenew(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
err = repo.SetAutoRenew(user.ID, false)
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
assert.False(t, found.AutoRenew)
|
|
}
|
|
|
|
func TestSubscriptionRepository_UpdateReceiptData(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
err = repo.UpdateReceiptData(user.ID, "receipt_data_abc123")
|
|
require.NoError(t, err)
|
|
|
|
var sub models.UserSubscription
|
|
db.Where("user_id = ?", user.ID).First(&sub)
|
|
require.NotNil(t, sub.AppleReceiptData)
|
|
assert.Equal(t, "receipt_data_abc123", *sub.AppleReceiptData)
|
|
}
|
|
|
|
func TestSubscriptionRepository_UpdatePurchaseToken(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
err = repo.UpdatePurchaseToken(user.ID, "google_token_xyz")
|
|
require.NoError(t, err)
|
|
|
|
var sub models.UserSubscription
|
|
db.Where("user_id = ?", user.ID).First(&sub)
|
|
require.NotNil(t, sub.GooglePurchaseToken)
|
|
assert.Equal(t, "google_token_xyz", *sub.GooglePurchaseToken)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByAppleReceiptContains(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
// Use raw SQL to ensure apple_receipt_data is stored correctly (GORM may omit pointer fields)
|
|
require.NoError(t, db.Exec(
|
|
"INSERT INTO subscription_usersubscription (user_id, tier, apple_receipt_data, created_at, updated_at) VALUES (?, ?, ?, datetime('now'), datetime('now'))",
|
|
user.ID, models.TierPro, "transactionid=txnabc123data",
|
|
).Error)
|
|
|
|
// Avoid underscores in search term: escapeLikeWildcards escapes _ with \
|
|
// which PostgreSQL handles but SQLite does not (no default ESCAPE clause)
|
|
found, err := repo.FindByAppleReceiptContains("txnabc123")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, user.ID, found.UserID)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByAppleReceiptContains_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
_, err := repo.FindByAppleReceiptContains("nonexistent_txn")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByGoogleToken(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
purchaseToken := "google_purchase_abc"
|
|
sub := &models.UserSubscription{
|
|
UserID: user.ID,
|
|
Tier: models.TierPro,
|
|
GooglePurchaseToken: &purchaseToken,
|
|
}
|
|
require.NoError(t, db.Create(sub).Error)
|
|
|
|
found, err := repo.FindByGoogleToken("google_purchase_abc")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, user.ID, found.UserID)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByGoogleToken_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
_, err := repo.FindByGoogleToken("nonexistent_token")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSubscriptionRepository_SetCancelledAt(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
cancelTime := time.Now().UTC()
|
|
err = repo.SetCancelledAt(user.ID, cancelTime)
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
require.NotNil(t, found.CancelledAt)
|
|
}
|
|
|
|
func TestSubscriptionRepository_ClearCancelledAt(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
_, err := repo.GetOrCreate(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
// Set then clear
|
|
err = repo.SetCancelledAt(user.ID, time.Now().UTC())
|
|
require.NoError(t, err)
|
|
|
|
err = repo.ClearCancelledAt(user.ID)
|
|
require.NoError(t, err)
|
|
|
|
found, err := repo.FindByUserID(user.ID)
|
|
require.NoError(t, err)
|
|
assert.Nil(t, found.CancelledAt)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetTierLimits_Defaults(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
// No tier limits in DB, should return defaults
|
|
freeLimits, err := repo.GetTierLimits(models.TierFree)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, freeLimits)
|
|
|
|
proLimits, err := repo.GetTierLimits(models.TierPro)
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, proLimits)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetAllTierLimits(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
// No limits seeded = empty
|
|
limits, err := repo.GetAllTierLimits()
|
|
require.NoError(t, err)
|
|
assert.Empty(t, limits)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetSettings_Defaults(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
// No settings in DB, should return default (limitations disabled)
|
|
settings, err := repo.GetSettings()
|
|
require.NoError(t, err)
|
|
assert.NotNil(t, settings)
|
|
assert.False(t, settings.EnableLimitations)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetUpgradeTrigger(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
trigger := &models.UpgradeTrigger{
|
|
TriggerKey: "max_residences",
|
|
Title: "Upgrade for more homes",
|
|
Message: "Free tier supports 1 home",
|
|
IsActive: true,
|
|
}
|
|
require.NoError(t, db.Create(trigger).Error)
|
|
|
|
found, err := repo.GetUpgradeTrigger("max_residences")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "max_residences", found.TriggerKey)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetUpgradeTrigger_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
_, err := repo.GetUpgradeTrigger("nonexistent_key")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetAllUpgradeTriggers(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
trigger1 := &models.UpgradeTrigger{TriggerKey: "key1", Title: "T1", Message: "M1", IsActive: true}
|
|
trigger2 := &models.UpgradeTrigger{TriggerKey: "key2", Title: "T2", Message: "M2", IsActive: true}
|
|
require.NoError(t, db.Create(trigger1).Error)
|
|
require.NoError(t, db.Create(trigger2).Error)
|
|
// Use raw SQL for inactive record to avoid GORM default:true overriding IsActive=false
|
|
require.NoError(t, db.Exec(
|
|
"INSERT INTO subscription_upgradetrigger (trigger_key, title, message, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, datetime('now'), datetime('now'))",
|
|
"key3", "T3", "M3", false,
|
|
).Error)
|
|
|
|
triggers, err := repo.GetAllUpgradeTriggers()
|
|
require.NoError(t, err)
|
|
assert.Len(t, triggers, 2) // Only active
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetFeatureBenefits(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
b1 := &models.FeatureBenefit{FeatureName: "Benefit 1", FreeTierText: "1 home", ProTierText: "Unlimited", DisplayOrder: 1, IsActive: true}
|
|
require.NoError(t, db.Create(b1).Error)
|
|
// Use raw SQL for inactive record to avoid GORM default:true overriding IsActive=false
|
|
require.NoError(t, db.Exec(
|
|
"INSERT INTO subscription_featurebenefit (feature_name, free_tier_text, pro_tier_text, display_order, is_active, created_at, updated_at) VALUES (?, ?, ?, ?, ?, datetime('now'), datetime('now'))",
|
|
"Benefit 2", "3 tasks", "Unlimited", 2, false,
|
|
).Error)
|
|
|
|
benefits, err := repo.GetFeatureBenefits()
|
|
require.NoError(t, err)
|
|
assert.Len(t, benefits, 1) // Only active
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetActivePromotions(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
now := time.Now().UTC()
|
|
active := &models.Promotion{
|
|
PromotionID: "promo_active",
|
|
TargetTier: models.TierPro,
|
|
Title: "Active Promo",
|
|
Message: "Get Pro now!",
|
|
StartDate: now.AddDate(0, 0, -1),
|
|
EndDate: now.AddDate(0, 0, 1),
|
|
IsActive: true,
|
|
}
|
|
expired := &models.Promotion{
|
|
PromotionID: "promo_expired",
|
|
TargetTier: models.TierPro,
|
|
Title: "Expired Promo",
|
|
Message: "Too late!",
|
|
StartDate: now.AddDate(0, 0, -10),
|
|
EndDate: now.AddDate(0, 0, -5),
|
|
IsActive: true,
|
|
}
|
|
require.NoError(t, db.Create(active).Error)
|
|
require.NoError(t, db.Create(expired).Error)
|
|
|
|
promos, err := repo.GetActivePromotions(models.TierPro)
|
|
require.NoError(t, err)
|
|
assert.Len(t, promos, 1)
|
|
assert.Equal(t, "promo_active", promos[0].PromotionID)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetPromotionByID(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
promo := &models.Promotion{
|
|
PromotionID: "promo_find",
|
|
TargetTier: models.TierPro,
|
|
Title: "Find Me",
|
|
Message: "Found!",
|
|
StartDate: time.Now().UTC(),
|
|
EndDate: time.Now().UTC().AddDate(0, 0, 30),
|
|
IsActive: true,
|
|
}
|
|
require.NoError(t, db.Create(promo).Error)
|
|
|
|
found, err := repo.GetPromotionByID("promo_find")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, "Find Me", found.Title)
|
|
}
|
|
|
|
func TestSubscriptionRepository_GetPromotionByID_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
_, err := repo.GetPromotionByID("nonexistent")
|
|
assert.Error(t, err)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByStripeSubscriptionID(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
user := testutil.CreateTestUser(t, db, "owner", "owner@test.com", "Password123")
|
|
subID := "sub_test_123"
|
|
sub := &models.UserSubscription{
|
|
UserID: user.ID,
|
|
Tier: models.TierPro,
|
|
StripeSubscriptionID: &subID,
|
|
}
|
|
require.NoError(t, db.Create(sub).Error)
|
|
|
|
found, err := repo.FindByStripeSubscriptionID("sub_test_123")
|
|
require.NoError(t, err)
|
|
assert.Equal(t, user.ID, found.UserID)
|
|
}
|
|
|
|
func TestSubscriptionRepository_FindByStripeSubscriptionID_NotFound(t *testing.T) {
|
|
db := testutil.SetupTestDB(t)
|
|
repo := NewSubscriptionRepository(db)
|
|
|
|
_, err := repo.FindByStripeSubscriptionID("sub_nonexistent")
|
|
assert.Error(t, err)
|
|
}
|