Files
gochat/backend/internal/ws/access_validator_test.go
Rogeeandrogee 6c78820a1f H-338: close H-335 release blockers (#59)
* H-16: align takeover with channel AI workflow (#2)

* feat(conversations): complete manual AI takeover

* fix(conversations): align AI takeover flow with channel AI

* fix(conversations): close takeover review gaps

---------

Co-authored-by: Rogee <rogee@ipao.vip>

* feat(shangwutong): sync customer names back to channel (#3)

Co-authored-by: Rogee <rogee@ipao.vip>

* fix(shangwutong): close contact sync review gaps (#4)

Co-authored-by: Rogee <rogee@ipao.vip>

* H-28: harden Shangwutong CID sync (#5)

* fix(shangwutong): close contact sync review gaps

* fix(shangwutong): harden CID sync boundaries

---------

Co-authored-by: Rogee <rogee@ipao.vip>

* fix(conversations): sync AI takeover exit in realtime (#6)

Co-authored-by: Rogee <rogee@ipao.vip>

* test(shangwutong): cover CID rename reliability (#7)

Co-authored-by: Rogee <rogee@ipao.vip>

* H-43: fix WEB Captain takeover E2E flow (#8)

* test(shangwutong): cover CID rename reliability

* H-43: fix WEB Captain takeover flow

* H-48: preserve compatible provider model

* H-49: make Captain takeover atomic

* H-50: prevent duplicate widget initialization

---------

Co-authored-by: Rogee <rogee@ipao.vip>

* H-55: make Captain bindings atomic (#9)

Co-authored-by: Rogee <rogee@ipao.vip>

* H-60: harden Captain migration rollback and concurrency

* chore(agent): baseline — uncommitted work from the local directory

* H-335: add safe Captain skills and user deactivation

* H-338: close auth and Captain review blockers

* H-338: close assignment and session races

* H-338: close assignment and websocket invalidation gaps

* H-338: enforce assignment write invariants

---------

Co-authored-by: Rogee <rogee@ipao.vip>
2026-08-20 10:21:19 +08:00

40 lines
1.6 KiB
Go

package ws
import (
"net/http/httptest"
"testing"
"github.com/gin-gonic/gin"
"github.com/gochat/gochat/internal/auth"
"github.com/gochat/gochat/internal/config"
"github.com/gochat/gochat/internal/model"
"github.com/stretchr/testify/require"
"gorm.io/driver/sqlite"
"gorm.io/gorm"
)
func TestWSAuthenticatorRejectsInactiveAndRevokedSessions(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.User{}, &model.UserSession{}))
user := &model.User{Name: "Agent", Email: "agent@example.com", Provider: "email", Active: true}
require.NoError(t, db.Create(user).Error)
require.NoError(t, db.Create(&model.UserSession{UserID: user.ID, ClientID: "browser"}).Error)
jwtService := auth.NewJWTService(&config.JWTConfig{Secret: "ws-test", ExpiryHours: 1, RefreshExpiryHours: 24})
pair, err := jwtService.GenerateTokenPairForClient(user, 1, "agent", "browser")
require.NoError(t, err)
authenticator := NewWSAuthenticator(jwtService, nil, db)
authenticate := func() error {
c, _ := gin.CreateTestContext(httptest.NewRecorder())
c.Request = httptest.NewRequest("GET", "/ws?token="+pair.AccessToken, nil)
_, err := authenticator.Authenticate(c)
return err
}
require.NoError(t, authenticate())
require.NoError(t, db.Model(user).Update("active", false).Error)
require.ErrorContains(t, authenticate(), "inactive")
require.NoError(t, db.Model(user).Update("active", true).Error)
require.NoError(t, db.Where("user_id = ?", user.ID).Delete(&model.UserSession{}).Error)
require.ErrorContains(t, authenticate(), "session revoked")
}