安全问题修复: - X-Forwarded-For越界检查(auth.go) - checkTokenStatus Context参数传递(auth.go) - Type Assertion安全检查(auth.go) 性能问题修复: - TokenCache过期清理机制 - BruteForceProtection过期清理 - InMemoryIdempotencyStore过期清理 错误处理修复: - AuditStore.Emit返回error - domain层emitAudit辅助方法 - List方法返回空slice而非nil - 金额/价格负数验证 架构一致性: - 统一使用model.RoleHierarchyLevels 新增功能: - Alert API完整实现(CRUD+Resolve) - pkg/error错误码集中管理
250 lines
5.7 KiB
Go
250 lines
5.7 KiB
Go
package service
|
||
|
||
import (
|
||
"context"
|
||
"sync"
|
||
"testing"
|
||
"time"
|
||
|
||
"lijiaoqiao/supply-api/internal/audit/model"
|
||
)
|
||
|
||
// TestBatchBuffer_BatchSize 测试50条/批刷新
|
||
func TestBatchBuffer_BatchSize(t *testing.T) {
|
||
const batchSize = 50
|
||
|
||
buffer := NewBatchBuffer(batchSize, 100*time.Millisecond) // 100ms超时防止测试卡住
|
||
ctx := context.Background()
|
||
|
||
err := buffer.Start(ctx)
|
||
if err != nil {
|
||
t.Fatalf("Start failed: %v", err)
|
||
}
|
||
defer buffer.Close()
|
||
|
||
// 收集器:接收批量事件
|
||
var receivedBatches [][]*model.AuditEvent
|
||
var mu sync.Mutex
|
||
|
||
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
|
||
mu.Lock()
|
||
receivedBatches = append(receivedBatches, events)
|
||
mu.Unlock()
|
||
return nil
|
||
})
|
||
|
||
// 添加50条事件,应该触发一次批量刷新
|
||
for i := 0; i < batchSize; i++ {
|
||
event := &model.AuditEvent{
|
||
EventID: "batch-test-001",
|
||
EventName: "TEST-EVENT",
|
||
}
|
||
if err := buffer.Add(event); err != nil {
|
||
t.Errorf("Add failed: %v", err)
|
||
}
|
||
}
|
||
|
||
// 等待刷新完成
|
||
time.Sleep(50 * time.Millisecond)
|
||
|
||
// 验证:应该收到恰好一个批次
|
||
mu.Lock()
|
||
if len(receivedBatches) != 1 {
|
||
t.Errorf("expected 1 batch, got %d", len(receivedBatches))
|
||
}
|
||
if len(receivedBatches) > 0 && len(receivedBatches[0]) != batchSize {
|
||
t.Errorf("expected batch size %d, got %d", batchSize, len(receivedBatches[0]))
|
||
}
|
||
mu.Unlock()
|
||
}
|
||
|
||
// TestBatchBuffer_TimeoutFlush 测试5ms超时刷新
|
||
func TestBatchBuffer_TimeoutFlush(t *testing.T) {
|
||
const batchSize = 100 // 大于我们添加的数量
|
||
const flushInterval = 5 * time.Millisecond
|
||
|
||
buffer := NewBatchBuffer(batchSize, flushInterval)
|
||
ctx := context.Background()
|
||
|
||
err := buffer.Start(ctx)
|
||
if err != nil {
|
||
t.Fatalf("Start failed: %v", err)
|
||
}
|
||
defer buffer.Close()
|
||
|
||
// 收集器
|
||
var receivedBatches [][]*model.AuditEvent
|
||
var mu sync.Mutex
|
||
|
||
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
|
||
mu.Lock()
|
||
receivedBatches = append(receivedBatches, events)
|
||
mu.Unlock()
|
||
return nil
|
||
})
|
||
|
||
// 只添加3条事件,不满50条
|
||
for i := 0; i < 3; i++ {
|
||
event := &model.AuditEvent{
|
||
EventID: "batch-test-002",
|
||
EventName: "TEST-TIMEOUT",
|
||
}
|
||
if err := buffer.Add(event); err != nil {
|
||
t.Errorf("Add failed: %v", err)
|
||
}
|
||
}
|
||
|
||
// 等待5ms超时刷新
|
||
time.Sleep(20 * time.Millisecond)
|
||
|
||
// 验证:应该收到一个批次,包含3条事件
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
if len(receivedBatches) != 1 {
|
||
t.Errorf("expected 1 batch (timeout flush), got %d", len(receivedBatches))
|
||
}
|
||
if len(receivedBatches) > 0 && len(receivedBatches[0]) != 3 {
|
||
t.Errorf("expected 3 events in batch, got %d", len(receivedBatches[0]))
|
||
}
|
||
}
|
||
|
||
// TestBatchBuffer_ConcurrentAccess 测试并发安全性
|
||
func TestBatchBuffer_ConcurrentAccess(t *testing.T) {
|
||
const batchSize = 50
|
||
const numGoroutines = 10
|
||
const eventsPerGoroutine = 100
|
||
|
||
buffer := NewBatchBuffer(batchSize, 10*time.Millisecond)
|
||
ctx := context.Background()
|
||
|
||
err := buffer.Start(ctx)
|
||
if err != nil {
|
||
t.Fatalf("Start failed: %v", err)
|
||
}
|
||
defer buffer.Close()
|
||
|
||
var totalReceived int
|
||
var mu sync.Mutex
|
||
|
||
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
|
||
mu.Lock()
|
||
totalReceived += len(events)
|
||
mu.Unlock()
|
||
return nil
|
||
})
|
||
|
||
// 并发添加事件
|
||
var wg sync.WaitGroup
|
||
for g := 0; g < numGoroutines; g++ {
|
||
wg.Add(1)
|
||
go func(goroutineID int) {
|
||
defer wg.Done()
|
||
for i := 0; i < eventsPerGoroutine; i++ {
|
||
event := &model.AuditEvent{
|
||
EventID: "batch-test-concurrent",
|
||
EventName: "TEST-CONCURRENT",
|
||
}
|
||
if err := buffer.Add(event); err != nil {
|
||
t.Errorf("Add failed: %v", err)
|
||
}
|
||
}
|
||
}(g)
|
||
}
|
||
|
||
wg.Wait()
|
||
time.Sleep(50 * time.Millisecond) // 等待所有刷新完成
|
||
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
expectedTotal := numGoroutines * eventsPerGoroutine
|
||
if totalReceived != expectedTotal {
|
||
t.Errorf("expected %d total events, got %d", expectedTotal, totalReceived)
|
||
}
|
||
}
|
||
|
||
// TestBatchBuffer_Close 测试关闭
|
||
func TestBatchBuffer_Close(t *testing.T) {
|
||
buffer := NewBatchBuffer(50, 10*time.Millisecond)
|
||
ctx := context.Background()
|
||
|
||
err := buffer.Start(ctx)
|
||
if err != nil {
|
||
t.Fatalf("Start failed: %v", err)
|
||
}
|
||
|
||
// 添加一些事件
|
||
for i := 0; i < 5; i++ {
|
||
event := &model.AuditEvent{
|
||
EventID: "batch-test-close",
|
||
EventName: "TEST-CLOSE",
|
||
}
|
||
if err := buffer.Add(event); err != nil {
|
||
t.Errorf("Add failed: %v", err)
|
||
}
|
||
}
|
||
|
||
// 关闭缓冲区
|
||
err = buffer.Close()
|
||
if err != nil {
|
||
t.Errorf("Close failed: %v", err)
|
||
}
|
||
|
||
// 关闭后添加应该失败
|
||
event := &model.AuditEvent{
|
||
EventID: "batch-test-after-close",
|
||
EventName: "TEST-AFTER-CLOSE",
|
||
}
|
||
if err := buffer.Add(event); err == nil {
|
||
t.Errorf("Add after Close should fail")
|
||
}
|
||
}
|
||
|
||
// TestBatchBuffer_FlushNow 测试手动刷新
|
||
func TestBatchBuffer_FlushNow(t *testing.T) {
|
||
const batchSize = 100 // 足够大,不会自动触发
|
||
|
||
buffer := NewBatchBuffer(batchSize, 100*time.Millisecond) // 100ms才自动刷新
|
||
ctx := context.Background()
|
||
|
||
err := buffer.Start(ctx)
|
||
if err != nil {
|
||
t.Fatalf("Start failed: %v", err)
|
||
}
|
||
defer buffer.Close()
|
||
|
||
var receivedBatches [][]*model.AuditEvent
|
||
var mu sync.Mutex
|
||
|
||
buffer.SetFlushHandler(func(events []*model.AuditEvent) error {
|
||
mu.Lock()
|
||
receivedBatches = append(receivedBatches, events)
|
||
mu.Unlock()
|
||
return nil
|
||
})
|
||
|
||
// 添加少量事件
|
||
for i := 0; i < 3; i++ {
|
||
event := &model.AuditEvent{
|
||
EventID: "batch-test-manual",
|
||
EventName: "TEST-MANUAL",
|
||
}
|
||
if err := buffer.Add(event); err != nil {
|
||
t.Errorf("Add failed: %v", err)
|
||
}
|
||
}
|
||
|
||
// 立即手动刷新
|
||
err = buffer.FlushNow()
|
||
if err != nil {
|
||
t.Errorf("FlushNow failed: %v", err)
|
||
}
|
||
|
||
time.Sleep(10 * time.Millisecond)
|
||
|
||
mu.Lock()
|
||
defer mu.Unlock()
|
||
if len(receivedBatches) != 1 {
|
||
t.Errorf("expected 1 batch after FlushNow, got %d", len(receivedBatches))
|
||
}
|
||
}
|