Files
lijiaoqiao/supply-api/internal/audit/service/batch_buffer_test.go
Your Name d5b5a8ece0 fix: 系统性修复安全问题、性能问题和错误处理
安全问题修复:
- 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错误码集中管理
2026-04-07 07:41:25 +08:00

250 lines
5.7 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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))
}
}