package service import ( "context" "testing" "github.com/alicebob/miniredis/v2" "github.com/gochat/gochat/internal/auth" "github.com/gochat/gochat/internal/config" "github.com/gochat/gochat/internal/model" "github.com/gochat/gochat/internal/repository" "github.com/redis/go-redis/v9" "github.com/stretchr/testify/require" "gorm.io/driver/sqlite" "gorm.io/gorm" ) func TestAgentDeactivationPermanentlyRevokesSessionsAndRefreshTokens(t *testing.T) { db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=private"), &gorm.Config{}) require.NoError(t, err) require.NoError(t, db.AutoMigrate(&model.Account{}, &model.User{}, &model.AccountUser{}, &model.UserSession{})) account := &model.Account{Name: "Account"} user := &model.User{Name: "Agent", Email: "agent@example.com", Provider: "email", Active: true} require.NoError(t, db.Create(account).Error) require.NoError(t, db.Create(user).Error) require.NoError(t, db.Create(&model.AccountUser{AccountID: account.ID, UserID: user.ID, Role: "agent"}).Error) require.NoError(t, db.Create(&model.UserSession{UserID: user.ID, ClientID: "browser"}).Error) store := auth.NewRefreshTokenStore(nil, &config.JWTConfig{RefreshExpiryHours: 24}) require.NoError(t, store.StoreForClient(context.Background(), user.ID, "browser", "old-refresh")) disconnected := uint(0) svc := NewAgentService(repository.NewAgentRepo(db), db).WithDeactivation(store, func(userID uint) { disconnected = userID }) inactive := false _, err = svc.Update(context.Background(), user.ID, account.ID, UpdateAgentRequest{Active: &inactive}) require.NoError(t, err) require.Equal(t, user.ID, disconnected) var sessions int64 require.NoError(t, db.Model(&model.UserSession{}).Where("user_id = ?", user.ID).Count(&sessions).Error) require.Zero(t, sessions) active := true _, err = svc.Update(context.Background(), user.ID, account.ID, UpdateAgentRequest{Active: &active}) require.NoError(t, err) valid, err := store.ValidateForClient(context.Background(), user.ID, "browser", "old-refresh") require.NoError(t, err) require.False(t, valid) } func TestAgentDeactivationRollsBackWhenRefreshRevocationFails(t *testing.T) { db, err := gorm.Open(sqlite.Open("file:"+t.Name()+"?mode=memory&cache=private"), &gorm.Config{}) require.NoError(t, err) require.NoError(t, db.AutoMigrate(&model.Account{}, &model.User{}, &model.AccountUser{}, &model.UserSession{})) account := &model.Account{Name: "Account"} user := &model.User{Name: "Agent", Email: "rollback@example.com", Provider: "email", Active: true} require.NoError(t, db.Create(account).Error) require.NoError(t, db.Create(user).Error) require.NoError(t, db.Create(&model.AccountUser{AccountID: account.ID, UserID: user.ID, Role: "agent"}).Error) require.NoError(t, db.Create(&model.UserSession{UserID: user.ID, ClientID: "browser"}).Error) mr := miniredis.RunT(t) rdb := redis.NewClient(&redis.Options{Addr: mr.Addr()}) store := auth.NewRefreshTokenStore(rdb, &config.JWTConfig{RefreshExpiryHours: 24}) require.NoError(t, store.StoreForClient(context.Background(), user.ID, "browser", "old-refresh")) require.NoError(t, rdb.Close()) svc := NewAgentService(repository.NewAgentRepo(db), db).WithDeactivation(store, nil) inactive := false _, err = svc.Update(context.Background(), user.ID, account.ID, UpdateAgentRequest{Active: &inactive}) require.Error(t, err) require.NoError(t, db.First(user, user.ID).Error) require.True(t, user.Active) var sessions int64 require.NoError(t, db.Model(&model.UserSession{}).Where("user_id = ?", user.ID).Count(&sessions).Error) require.EqualValues(t, 1, sessions) }