package service import ( "context" "encoding/json" "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" "gorm.io/gorm/logger" "github.com/gochat/gochat/internal/model" "github.com/gochat/gochat/internal/repository" ) func setupShopifyIntegrationServiceTestDB(t *testing.T) *gorm.DB { t.Helper() db, err := gorm.Open(sqlite.Open("file::memory:"), &gorm.Config{ Logger: logger.Default.LogMode(logger.Silent), }) require.NoError(t, err, "failed to open SQLite test database") require.NoError(t, db.AutoMigrate( &model.Account{}, &model.IntegrationHook{}, &model.IntegrationApp{}, ), "failed to auto-migrate models") t.Cleanup(func() { sqlDB, _ := db.DB() sqlDB.Close() }) return db } func setupShopifyIntegrationService(t *testing.T) (*ShopifyIntegrationService, *gorm.DB) { t.Helper() db := setupShopifyIntegrationServiceTestDB(t) hookRepo := repository.NewIntegrationHookRepo(db) svc := NewShopifyIntegrationService(hookRepo) return svc, db } func seedShopifyAccount(db *gorm.DB, t *testing.T) uint { t.Helper() account := &model.Account{Name: "Test Account"} require.NoError(t, db.Create(account).Error, "failed to seed account") return account.ID } // ======================================== // ShopifyIntegrationService — Auth tests // ======================================== func TestShopifyIntegrationService_Auth_CreateNew(t *testing.T) { svc, db := setupShopifyIntegrationService(t) accountID := seedShopifyAccount(db, t) ctx := context.Background() req := CreateShopifyAuthRequest{ ShopDomain: "my-store.myshopify.com", AccessToken: "shpat_abc123", } hook, err := svc.Auth(ctx, accountID, req) require.NoError(t, err, "Auth should succeed for new integration") require.NotNil(t, hook, "Auth should return a hook") assert.Equal(t, accountID, hook.AccountID, "hook should belong to the account") assert.Equal(t, model.HookTypeShopify, hook.HookType, "hook type should be shopify") assert.Equal(t, model.HookStatusActive, hook.Status, "hook status should be active") assert.Equal(t, "https://my-store.myshopify.com/admin/api/webhooks.json", hook.URL, "hook URL should be constructed from shop domain") // Verify settings were stored correctly var settings model.ShopifySettings require.NoError(t, json.Unmarshal(hook.Settings, &settings), "settings should be valid JSON") assert.Equal(t, "my-store.myshopify.com", settings.ShopDomain, "settings shop_domain should match request") assert.Equal(t, "shpat_abc123", settings.AccessToken, "settings access_token should match request") // Verify the hook was persisted hooks, err := svc.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) require.NoError(t, err) assert.Len(t, hooks, 1, "one Shopify hook should exist for the account") } func TestShopifyIntegrationService_Auth_UpdateExisting(t *testing.T) { svc, db := setupShopifyIntegrationService(t) accountID := seedShopifyAccount(db, t) ctx := context.Background() // Create initial integration initialReq := CreateShopifyAuthRequest{ ShopDomain: "old-store.myshopify.com", AccessToken: "shpat_old_token", } initialHook, err := svc.Auth(ctx, accountID, initialReq) require.NoError(t, err, "initial Auth should succeed") require.NotNil(t, initialHook) // Update with new credentials updateReq := CreateShopifyAuthRequest{ ShopDomain: "new-store.myshopify.com", AccessToken: "shpat_new_token", } updatedHook, err := svc.Auth(ctx, accountID, updateReq) require.NoError(t, err, "Auth update should succeed") require.NotNil(t, updatedHook) // Should return the same hook (updated, not a new one) assert.Equal(t, initialHook.ID, updatedHook.ID, "update should return the same hook ID") assert.Equal(t, "https://new-store.myshopify.com/admin/api/webhooks.json", updatedHook.URL, "URL should reflect updated shop domain") // Verify updated settings var settings model.ShopifySettings require.NoError(t, json.Unmarshal(updatedHook.Settings, &settings), "settings should be valid JSON") assert.Equal(t, "new-store.myshopify.com", settings.ShopDomain, "shop_domain should be updated") assert.Equal(t, "shpat_new_token", settings.AccessToken, "access_token should be updated") // Verify only one hook exists (no duplicates) hooks, err := svc.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) require.NoError(t, err) assert.Len(t, hooks, 1, "still only one Shopify hook should exist after update") } // ======================================== // ShopifyIntegrationService — Delete tests // ======================================== func TestShopifyIntegrationService_Delete_Success(t *testing.T) { svc, db := setupShopifyIntegrationService(t) accountID := seedShopifyAccount(db, t) ctx := context.Background() // Create an integration first req := CreateShopifyAuthRequest{ ShopDomain: "my-store.myshopify.com", AccessToken: "shpat_abc123", } _, err := svc.Auth(ctx, accountID, req) require.NoError(t, err, "Auth should succeed before delete") // Verify hook exists before delete hooks, err := svc.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) require.NoError(t, err) assert.Len(t, hooks, 1, "hook should exist before delete") // Delete the integration err = svc.Delete(ctx, accountID) require.NoError(t, err, "Delete should succeed") // Verify hook no longer exists hooks, err = svc.hookRepo.FindByAccountAndType(ctx, accountID, model.HookTypeShopify) require.NoError(t, err) assert.Len(t, hooks, 0, "no Shopify hooks should remain after delete") } func TestShopifyIntegrationService_Delete_NotFound(t *testing.T) { svc, db := setupShopifyIntegrationService(t) accountID := seedShopifyAccount(db, t) ctx := context.Background() // Delete without any integration configured err := svc.Delete(ctx, accountID) assert.Error(t, err, "Delete should fail when no Shopify integration exists") assert.Contains(t, err.Error(), "not found", "error should indicate integration not found") }