64 lines
1.4 KiB
Go
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
|
|
}
|