Smail/backend/internal/passwordtest/hold.go
smail b4f5f51c0e
Some checks are pending
Security Checks / security (push) Waiting to run
保存认证容量保护整改检查点待远程复审
2026-09-17 12:00:45 +08:00

64 lines
1.4 KiB
Go

// Package passwordtest 为串行安全测试占用真实密码计算名额,不执行额外的昂贵计算。
package passwordtest
import (
"crypto/rand"
"io"
"sync"
"sync/atomic"
"testing"
"time"
"git.pcux.cn/xuans/Smail/utils/password"
)
type heldReader struct {
original io.Reader
entered chan struct{}
resume chan struct{}
enteredOnce atomic.Bool
}
func (r *heldReader) Read(p []byte) (int, error) {
if r.enteredOnce.CompareAndSwap(false, true) {
close(r.entered)
<-r.resume
panic(r)
}
return r.original.Read(p)
}
// Hold 仅供非并行测试使用,释放时同时验证异常路径没有泄漏计算名额。
func Hold(t *testing.T) func() {
t.Helper()
r := &heldReader{original: rand.Reader, entered: make(chan struct{}), resume: make(chan struct{})}
rand.Reader = r
done := make(chan any, 1)
go func() {
defer func() { done <- recover() }()
_, _ = password.Hash("HeldPass123")
}()
var once sync.Once
release := func() {
once.Do(func() {
close(r.resume)
defer func() { rand.Reader = r.original }()
select {
case result := <-done:
if result != r {
t.Errorf("密码测试占位未经过预期的异常释放路径")
}
case <-time.After(5 * time.Second):
t.Error("密码测试占位释放超时")
}
})
}
t.Cleanup(release)
select {
case <-r.entered:
case <-time.After(5 * time.Second):
t.Fatal("密码计算名额未被测试请求占用")
}
return release
}