mirror of
https://github.com/Buriburizaem0n/nezha_domains.git
synced 2026-09-19 17:50:12 +00:00
87 lines
2.2 KiB
Go
87 lines
2.2 KiB
Go
package model
|
||
|
||
import (
|
||
"net/http/httptest"
|
||
"sync"
|
||
"testing"
|
||
|
||
"github.com/gin-gonic/gin"
|
||
)
|
||
|
||
// Server.UserID 在 server-transfer rotation 流程里会被 ServerTransfer 的
|
||
// Register/revertTransition 改写以反映新所有者,同时 authorizeAgentForUUID
|
||
// 在每次 agent RPC 里读取它。原实现两处都是裸字段访问,race detector 会
|
||
// 报告 data race;这是 review 评分 75 的真实问题。
|
||
//
|
||
// 修复后所有并发读写都走 SetUserID/GetUserID 的 atomic 包装,本测试在
|
||
// `go test -race` 下应该完全跑干净。
|
||
func TestServerUserIDConcurrentAccessIsRaceFree(t *testing.T) {
|
||
s := &Server{}
|
||
|
||
const (
|
||
writers = 4
|
||
readers = 8
|
||
rounds = 500
|
||
)
|
||
var wg sync.WaitGroup
|
||
wg.Add(writers + readers)
|
||
|
||
for i := 0; i < writers; i++ {
|
||
uid := uint64(i + 1)
|
||
go func() {
|
||
defer wg.Done()
|
||
for j := 0; j < rounds; j++ {
|
||
s.SetUserID(uid)
|
||
}
|
||
}()
|
||
}
|
||
for i := 0; i < readers; i++ {
|
||
go func() {
|
||
defer wg.Done()
|
||
for j := 0; j < rounds; j++ {
|
||
_ = s.GetUserID()
|
||
}
|
||
}()
|
||
}
|
||
wg.Wait()
|
||
}
|
||
|
||
// Common.HasPermission 是 server-transfer 旋转下与 SetUserID 并发的主要读者
|
||
// 之一:dashboard 各 controller 的 listHandler post-filter 在 transfer 窗口
|
||
// 内不断对同一 *Server 调用 HasPermission,而 Register/revertTransition 同
|
||
// 时通过 SetUserID 改写所属用户。原实现的 `user.ID == c.UserID` 是裸读,会
|
||
// 与 atomic.StoreUint64 形成 data race(go test -race 必爆)。修复后改成走
|
||
// GetUserID() 走 atomic 协议。这个测试就是用来在 -race 下钉死该不变量的。
|
||
func TestCommonHasPermissionConcurrentWithSetUserIDIsRaceFree(t *testing.T) {
|
||
s := &Server{Common: Common{ID: 1}}
|
||
|
||
const (
|
||
writers = 4
|
||
readers = 8
|
||
rounds = 500
|
||
)
|
||
var wg sync.WaitGroup
|
||
wg.Add(writers + readers)
|
||
|
||
for i := 0; i < writers; i++ {
|
||
uid := uint64(i + 1)
|
||
go func() {
|
||
defer wg.Done()
|
||
for j := 0; j < rounds; j++ {
|
||
s.SetUserID(uid)
|
||
}
|
||
}()
|
||
}
|
||
for i := 0; i < readers; i++ {
|
||
go func() {
|
||
defer wg.Done()
|
||
ctx, _ := gin.CreateTestContext(httptest.NewRecorder())
|
||
ctx.Set(CtxKeyAuthorizedUser, &User{Common: Common{ID: 2}, Role: RoleMember})
|
||
for j := 0; j < rounds; j++ {
|
||
_ = s.HasPermission(ctx)
|
||
}
|
||
}()
|
||
}
|
||
wg.Wait()
|
||
}
|