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" ) // ======================================== // Shared test helpers // ======================================== func setupLinearNotionTestDB(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{}, ), "failed to auto-migrate models") t.Cleanup(func() { sqlDB, _ := db.DB() sqlDB.Close() }) return db } func seedLinearNotionAccount(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 } func seedLinearHook(db *gorm.DB, t *testing.T, accountID uint) uint { t.Helper() settings := model.LinearSettings{ TeamID: "team-1", TeamName: "Engineering", AccessToken: "lin_token_123", } settingsJSON, err := json.Marshal(settings) require.NoError(t, err, "failed to marshal Linear settings") hook := &model.IntegrationHook{ AccountID: accountID, HookType: model.HookTypeLinear, Status: model.HookStatusActive, AccessToken: "lin_token_123", Settings: settingsJSON, } require.NoError(t, db.Create(hook).Error, "failed to seed Linear hook") return hook.ID } func seedNotionHook(db *gorm.DB, t *testing.T, accountID uint) uint { t.Helper() settings := model.NotionSettings{ DatabaseID: "db-1", AccessToken: "notion_token_123", } settingsJSON, err := json.Marshal(settings) require.NoError(t, err, "failed to marshal Notion settings") hook := &model.IntegrationHook{ AccountID: accountID, HookType: model.HookTypeNotion, Status: model.HookStatusActive, AccessToken: "notion_token_123", Settings: settingsJSON, } require.NoError(t, db.Create(hook).Error, "failed to seed Notion hook") return hook.ID } func setupLinearService(t *testing.T) (*LinearIntegrationService, *gorm.DB) { t.Helper() db := setupLinearNotionTestDB(t) hookRepo := repository.NewIntegrationHookRepo(db) svc := NewLinearIntegrationService(hookRepo) return svc, db } func setupNotionService(t *testing.T) (*NotionIntegrationService, *gorm.DB) { t.Helper() db := setupLinearNotionTestDB(t) hookRepo := repository.NewIntegrationHookRepo(db) svc := NewNotionIntegrationService(hookRepo) return svc, db } // ======================================== // LinearIntegrationService tests // ======================================== func TestLinearIntegrationService_Delete_Success(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) seedLinearHook(db, t, accountID) err := svc.Delete(context.Background(), accountID) assert.NoError(t, err) // Verify the hook is gone var count int64 db.Model(&model.IntegrationHook{}).Where("account_id = ? AND hook_type = ?", accountID, model.HookTypeLinear).Count(&count) assert.Equal(t, int64(0), count) } func TestLinearIntegrationService_Delete_NotFound(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) // No Linear hook seeded err := svc.Delete(context.Background(), accountID) assert.Error(t, err) assert.Contains(t, err.Error(), "Linear integration not found") } func TestLinearIntegrationService_GetTeams_Success(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) seedLinearHook(db, t, accountID) teams, err := svc.GetTeams(context.Background(), accountID) assert.NoError(t, err) assert.Len(t, teams, 1) assert.Equal(t, "team-1", teams[0]["id"]) assert.Equal(t, "Engineering", teams[0]["name"]) } func TestLinearIntegrationService_GetTeams_NotFound(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) // No Linear hook seeded teams, err := svc.GetTeams(context.Background(), accountID) assert.Error(t, err) assert.Nil(t, teams) assert.Contains(t, err.Error(), "Linear integration not found") } func TestLinearIntegrationService_CreateIssue_Success(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) seedLinearHook(db, t, accountID) req := CreateIssueRequest{ Title: "Bug in login flow", Description: "Users cannot log in after password reset", TeamID: "team-1", } result, err := svc.CreateIssue(context.Background(), accountID, req) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, "Bug in login flow", result["title"]) } func TestLinearIntegrationService_CreateIssue_NotFound(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) // No Linear hook seeded req := CreateIssueRequest{ Title: "Some issue", TeamID: "team-1", } result, err := svc.CreateIssue(context.Background(), accountID, req) assert.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "Linear integration not found") } func TestLinearIntegrationService_LinkIssue_Success(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) seedLinearHook(db, t, accountID) req := LinkIssueRequest{ IssueID: "LIN-42", ConversationID: 10, } result, err := svc.LinkIssue(context.Background(), accountID, req) assert.NoError(t, err) assert.NotNil(t, result) assert.Equal(t, "linked", result["status"]) assert.Equal(t, "LIN-42", result["issue_id"]) } func TestLinearIntegrationService_LinkIssue_NotFound(t *testing.T) { svc, db := setupLinearService(t) accountID := seedLinearNotionAccount(db, t) // No Linear hook seeded req := LinkIssueRequest{ IssueID: "LIN-42", ConversationID: 10, } result, err := svc.LinkIssue(context.Background(), accountID, req) assert.Error(t, err) assert.Nil(t, result) assert.Contains(t, err.Error(), "Linear integration not found") } // ======================================== // NotionIntegrationService tests // ======================================== func TestNotionIntegrationService_Delete_Success(t *testing.T) { svc, db := setupNotionService(t) accountID := seedLinearNotionAccount(db, t) seedNotionHook(db, t, accountID) err := svc.Delete(context.Background(), accountID) assert.NoError(t, err) // Verify the hook is gone var count int64 db.Model(&model.IntegrationHook{}).Where("account_id = ? AND hook_type = ?", accountID, model.HookTypeNotion).Count(&count) assert.Equal(t, int64(0), count) } func TestNotionIntegrationService_Delete_NotFound(t *testing.T) { svc, db := setupNotionService(t) accountID := seedLinearNotionAccount(db, t) // No Notion hook seeded err := svc.Delete(context.Background(), accountID) assert.Error(t, err) assert.Contains(t, err.Error(), "Notion integration not found") }