Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -11,9 +11,13 @@ import (
|
||||
"github.com/wangjia/jiu/backend/testutil"
|
||||
)
|
||||
|
||||
// 同平台类超配额时踢掉最旧会话。
|
||||
func TestLogin_PerClassQuota_KicksOldest(t *testing.T) {
|
||||
// 达到总配额时拒绝新登录(不踢最旧),已有会话保持在线。
|
||||
func TestLogin_TotalQuota_RejectsWhenFull(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
old := config.C.Session.LimitTotal
|
||||
config.C.Session.LimitTotal = 2
|
||||
defer func() { config.C.Session.LimitTotal = old }()
|
||||
|
||||
shop := testutil.CreateTestShop(db, "SESS01")
|
||||
testutil.CreateTestUser(db, shop.ID, "admin", "password123", "admin")
|
||||
svc := NewAuthService(db)
|
||||
@@ -23,38 +27,43 @@ func TestLogin_PerClassQuota_KicksOldest(t *testing.T) {
|
||||
require.NoError(t, err)
|
||||
p2, _, err := svc.Login("SESS01", "admin", "password123", dev)
|
||||
require.NoError(t, err)
|
||||
// 配额 2,第 3 次桌面端登录应踢掉最旧(p1)
|
||||
// 总配额 2 已满,第 3 次登录应被拒绝
|
||||
_, _, err = svc.Login("SESS01", "admin", "password123", dev)
|
||||
require.NoError(t, err)
|
||||
assert.ErrorIs(t, err, ErrDeviceLimitReached)
|
||||
|
||||
// 共 2 个未撤销会话
|
||||
// 仍为 2 个未撤销会话,两者都未被踢
|
||||
var active int64
|
||||
db.Model(&model.UserSession{}).Where("shop_id = ? AND revoked_at IS NULL", shop.ID).Count(&active)
|
||||
assert.Equal(t, int64(2), active)
|
||||
|
||||
// p1 的 refresh 应失败(会话被踢)
|
||||
_, err = svc.RefreshTokens(p1.RefreshToken)
|
||||
assert.ErrorIs(t, err, ErrSessionRevoked)
|
||||
// p2 仍可续期
|
||||
assert.NoError(t, err)
|
||||
_, err = svc.RefreshTokens(p2.RefreshToken)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 移动端与桌面端配额互不影响。
|
||||
func TestLogin_QuotaIsolatedByClass(t *testing.T) {
|
||||
// 总配额跨全部平台合并计数:不同平台类共用一个总上限,满额即拒。
|
||||
func TestLogin_TotalQuota_CountsAcrossClasses(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
old := config.C.Session.LimitTotal
|
||||
config.C.Session.LimitTotal = 2
|
||||
defer func() { config.C.Session.LimitTotal = old }()
|
||||
|
||||
shop := testutil.CreateTestShop(db, "SESS02")
|
||||
testutil.CreateTestUser(db, shop.ID, "admin", "password123", "admin")
|
||||
svc := NewAuthService(db)
|
||||
|
||||
// 桌面 + 移动 占满总配额 2
|
||||
_, _, err := svc.Login("SESS02", "admin", "password123", DeviceInfo{Platform: "windows"})
|
||||
require.NoError(t, err)
|
||||
_, _, err = svc.Login("SESS02", "admin", "password123", DeviceInfo{Platform: "android"})
|
||||
require.NoError(t, err)
|
||||
// 第三个不同平台(web)应被总配额拒绝
|
||||
_, _, err = svc.Login("SESS02", "admin", "password123", DeviceInfo{Platform: "web"})
|
||||
assert.ErrorIs(t, err, ErrDeviceLimitReached)
|
||||
|
||||
var active int64
|
||||
db.Model(&model.UserSession{}).Where("shop_id = ? AND revoked_at IS NULL", shop.ID).Count(&active)
|
||||
assert.Equal(t, int64(2), active) // 桌面 1 + 移动 1,都没被踢
|
||||
assert.Equal(t, int64(2), active) // 仅前两个在线
|
||||
}
|
||||
|
||||
// 配额为 0 的平台拒绝登录(如禁 web)。
|
||||
@@ -72,24 +81,23 @@ func TestLogin_PlatformDisabled(t *testing.T) {
|
||||
assert.ErrorIs(t, err, ErrPlatformNotAllowed)
|
||||
}
|
||||
|
||||
// 每店 session_policy 覆盖全局默认。
|
||||
// 每店 session_policy["total"] 覆盖全局总配额默认。
|
||||
func TestLogin_PerShopPolicyOverride(t *testing.T) {
|
||||
db := testutil.SetupTestDB()
|
||||
shop := testutil.CreateTestShop(db, "SESS04")
|
||||
// 该店限桌面 1
|
||||
// 该店总配额限 1
|
||||
db.Model(&model.Shop{}).Where("id = ?", shop.ID).
|
||||
Update("custom_fields", model.JSON{"session_policy": map[string]interface{}{"desktop": float64(1)}})
|
||||
Update("custom_fields", model.JSON{"session_policy": map[string]interface{}{"total": float64(1)}})
|
||||
testutil.CreateTestUser(db, shop.ID, "admin", "password123", "admin")
|
||||
svc := NewAuthService(db)
|
||||
|
||||
p1, _, err := svc.Login("SESS04", "admin", "password123", DeviceInfo{Platform: "windows"})
|
||||
require.NoError(t, err)
|
||||
_, _, err = svc.Login("SESS04", "admin", "password123", DeviceInfo{Platform: "windows"})
|
||||
require.NoError(t, err)
|
||||
|
||||
// 第 2 次登录应把 p1 踢掉(配额 1)
|
||||
// 总配额 1 已满,第 2 次登录(不同平台)应被拒绝,p1 保持在线
|
||||
_, _, err = svc.Login("SESS04", "admin", "password123", DeviceInfo{Platform: "android"})
|
||||
assert.ErrorIs(t, err, ErrDeviceLimitReached)
|
||||
_, err = svc.RefreshTokens(p1.RefreshToken)
|
||||
assert.ErrorIs(t, err, ErrSessionRevoked)
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
||||
// 被禁用用户无法用 refresh token 续命(修复历史漏洞)。
|
||||
|
||||
Reference in New Issue
Block a user