mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-25 20:51:52 +08:00
test: 补充单元测试覆盖率 29.9% → 36.6%
新建 18 个测试文件,追加 30 个已有测试文件,覆盖协议解析、 错误分类、CEL 表达式求值、YAML 反序列化、字节编码等纯函数。
This commit is contained in:
@@ -215,6 +215,300 @@ func TestState_ConcurrentTaskCounters(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
// TestState_GetOutputMutex 测试获取输出互斥锁指针
|
||||
func TestState_GetOutputMutex(t *testing.T) {
|
||||
s := NewState()
|
||||
mu := s.GetOutputMutex()
|
||||
if mu == nil {
|
||||
t.Fatal("GetOutputMutex returned nil")
|
||||
}
|
||||
// 验证返回的指针可以正常加锁
|
||||
mu.Lock()
|
||||
mu.Unlock()
|
||||
}
|
||||
|
||||
// TestState_GetICMPLimiter 测试 ICMP 限速器延迟初始化
|
||||
func TestState_GetICMPLimiter(t *testing.T) {
|
||||
s := NewState()
|
||||
|
||||
limiter := s.GetICMPLimiter(0.1)
|
||||
if limiter == nil {
|
||||
t.Fatal("GetICMPLimiter returned nil")
|
||||
}
|
||||
|
||||
// 再次调用应返回同一个实例(sync.Once 保证)
|
||||
limiter2 := s.GetICMPLimiter(0.5)
|
||||
if limiter != limiter2 {
|
||||
t.Fatal("GetICMPLimiter should return the same instance on repeated calls")
|
||||
}
|
||||
}
|
||||
|
||||
// TestState_GetICMPLimiterMinRate 测试极低速率下的 ICMP 限速器
|
||||
func TestState_GetICMPLimiterMinRate(t *testing.T) {
|
||||
s := NewState()
|
||||
// 极低速率(packetsPerSecond < 1)应被钳位到 1
|
||||
limiter := s.GetICMPLimiter(0.000001)
|
||||
if limiter == nil {
|
||||
t.Fatal("GetICMPLimiter with tiny rate returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
// TestState_GetPerfStats 测试性能统计数据
|
||||
func TestState_GetPerfStats(t *testing.T) {
|
||||
s := NewState()
|
||||
|
||||
// 初始状态:全零
|
||||
stats := s.GetPerfStats()
|
||||
if stats.TotalPackets != 0 {
|
||||
t.Errorf("初始 TotalPackets 应为 0, 实际 %d", stats.TotalPackets)
|
||||
}
|
||||
if stats.SuccessRate != 0 {
|
||||
t.Errorf("初始 SuccessRate 应为 0, 实际 %f", stats.SuccessRate)
|
||||
}
|
||||
|
||||
// 增加一些计数后验证统计
|
||||
s.IncrementTCPSuccessPacketCount()
|
||||
s.IncrementTCPSuccessPacketCount()
|
||||
s.IncrementTCPFailedPacketCount()
|
||||
s.SetNum(3)
|
||||
|
||||
stats = s.GetPerfStats()
|
||||
if stats.TotalPackets != 3 {
|
||||
t.Errorf("TotalPackets 期望 3, 实际 %d", stats.TotalPackets)
|
||||
}
|
||||
if stats.TCPSuccess != 2 {
|
||||
t.Errorf("TCPSuccess 期望 2, 实际 %d", stats.TCPSuccess)
|
||||
}
|
||||
if stats.TCPFailed != 1 {
|
||||
t.Errorf("TCPFailed 期望 1, 实际 %d", stats.TCPFailed)
|
||||
}
|
||||
if stats.TargetsScanned != 3 {
|
||||
t.Errorf("TargetsScanned 期望 3, 实际 %d", stats.TargetsScanned)
|
||||
}
|
||||
// success rate = 2/3 * 100 ≈ 66.67%
|
||||
if stats.SuccessRate < 66 || stats.SuccessRate > 67 {
|
||||
t.Errorf("SuccessRate 期望约 66.67, 实际 %f", stats.SuccessRate)
|
||||
}
|
||||
}
|
||||
|
||||
// TestState_GetPerfStatsJSON 测试性能统计 JSON 序列化
|
||||
func TestState_GetPerfStatsJSON(t *testing.T) {
|
||||
s := NewState()
|
||||
s.IncrementTCPSuccessPacketCount()
|
||||
|
||||
json := s.GetPerfStatsJSON()
|
||||
if json == "" || json == "{}" {
|
||||
t.Fatalf("GetPerfStatsJSON 返回空: %q", json)
|
||||
}
|
||||
if len(json) < 10 {
|
||||
t.Fatalf("GetPerfStatsJSON 内容过短: %q", json)
|
||||
}
|
||||
// 验证包含关键字段
|
||||
for _, key := range []string{"total_packets", "tcp_success", "success_rate"} {
|
||||
if !containsStr(json, key) {
|
||||
t.Errorf("GetPerfStatsJSON 缺少字段 %q", key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func containsStr(s, sub string) bool {
|
||||
return len(s) >= len(sub) && (s == sub || len(s) > 0 && stringContains(s, sub))
|
||||
}
|
||||
|
||||
func stringContains(s, sub string) bool {
|
||||
for i := 0; i <= len(s)-len(sub); i++ {
|
||||
if s[i:i+len(sub)] == sub {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// TestState_GetPacketLimiter 测试通用发包限速器
|
||||
func TestState_GetPacketLimiter(t *testing.T) {
|
||||
t.Run("零速率返回nil", func(t *testing.T) {
|
||||
s := NewState()
|
||||
limiter := s.GetPacketLimiter(0)
|
||||
if limiter != nil {
|
||||
t.Fatal("零速率应返回 nil limiter")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("负速率返回nil", func(t *testing.T) {
|
||||
s := NewState()
|
||||
limiter := s.GetPacketLimiter(-1)
|
||||
if limiter != nil {
|
||||
t.Fatal("负速率应返回 nil limiter")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("正速率初始化限速器", func(t *testing.T) {
|
||||
s := NewState()
|
||||
limiter := s.GetPacketLimiter(600) // 600/min = 10/s
|
||||
if limiter == nil {
|
||||
t.Fatal("正速率应返回非 nil limiter")
|
||||
}
|
||||
// 再次调用返回同一实例
|
||||
limiter2 := s.GetPacketLimiter(1200)
|
||||
if limiter != limiter2 {
|
||||
t.Fatal("GetPacketLimiter 应通过 sync.Once 复用实例")
|
||||
}
|
||||
})
|
||||
|
||||
t.Run("低速率被钳位到1pps", func(t *testing.T) {
|
||||
s := NewState()
|
||||
// 1/min < 1/s,应被钳位
|
||||
limiter := s.GetPacketLimiter(1)
|
||||
if limiter == nil {
|
||||
t.Fatal("低速率钳位后应返回非 nil limiter")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
// TestState_CacheService 测试服务识别缓存
|
||||
func TestState_CacheService(t *testing.T) {
|
||||
s := NewState()
|
||||
|
||||
// 未缓存时查询返回 false
|
||||
_, ok := s.GetCachedService("192.168.1.1:80")
|
||||
if ok {
|
||||
t.Fatal("未缓存的 key 不应返回 ok=true")
|
||||
}
|
||||
|
||||
// 缓存并查询
|
||||
type fakeInfo struct{ Name string }
|
||||
info := &fakeInfo{Name: "http"}
|
||||
s.CacheService("192.168.1.1:80", info)
|
||||
|
||||
got, ok := s.GetCachedService("192.168.1.1:80")
|
||||
if !ok {
|
||||
t.Fatal("已缓存的 key 应返回 ok=true")
|
||||
}
|
||||
if got != info {
|
||||
t.Fatalf("GetCachedService 返回 %v, 期望 %v", got, info)
|
||||
}
|
||||
|
||||
// 不同 key 互不干扰
|
||||
_, ok = s.GetCachedService("192.168.1.1:443")
|
||||
if ok {
|
||||
t.Fatal("不同 key 不应命中缓存")
|
||||
}
|
||||
}
|
||||
|
||||
// =============================================================================
|
||||
// CheckAndIncrementPacketRate 测试
|
||||
// =============================================================================
|
||||
|
||||
// TestCheckAndIncrementPacketRate_ZeroLimit 速率为 0 时无限制
|
||||
func TestCheckAndIncrementPacketRate_ZeroLimit(t *testing.T) {
|
||||
s := NewState()
|
||||
for i := 0; i < 1000; i++ {
|
||||
ok, err := s.CheckAndIncrementPacketRate(0)
|
||||
if !ok || err != nil {
|
||||
t.Fatalf("零速率限制应始终允许: ok=%v err=%v", ok, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckAndIncrementPacketRate_NegativeLimit 负速率等同于无限制
|
||||
func TestCheckAndIncrementPacketRate_NegativeLimit(t *testing.T) {
|
||||
s := NewState()
|
||||
ok, err := s.CheckAndIncrementPacketRate(-1)
|
||||
if !ok || err != nil {
|
||||
t.Fatalf("负速率应允许: ok=%v err=%v", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckAndIncrementPacketRate_AllowsWhenTokensAvailable 有令牌时返回 true
|
||||
func TestCheckAndIncrementPacketRate_AllowsWhenTokensAvailable(t *testing.T) {
|
||||
s := NewState()
|
||||
// 600/min = 10/s,桶容量 20,初始满桶
|
||||
ok, err := s.CheckAndIncrementPacketRate(600)
|
||||
if !ok || err != nil {
|
||||
t.Fatalf("初始应有令牌: ok=%v err=%v", ok, err)
|
||||
}
|
||||
}
|
||||
|
||||
// TestCheckAndIncrementPacketRate_RateLimitedAfterExhaustion 耗尽令牌后返回 false 和 PacketLimitError
|
||||
func TestCheckAndIncrementPacketRate_RateLimitedAfterExhaustion(t *testing.T) {
|
||||
s := NewState()
|
||||
// 极低速率:1/min,桶容量为 1(钳位后 packetsPerSecond=1,capacity=2)
|
||||
// 消耗掉所有令牌后应被限速
|
||||
const limit int64 = 1
|
||||
|
||||
// 初始化限速器(第一次调用触发 sync.Once)
|
||||
s.GetPacketLimiter(limit)
|
||||
|
||||
// 消耗完所有令牌(容量 <= 2)
|
||||
for i := 0; i < 10; i++ {
|
||||
s.CheckAndIncrementPacketRate(limit) //nolint: errcheck
|
||||
}
|
||||
|
||||
// 此时令牌应已耗尽,下一次调用应被限速
|
||||
ok, err := s.CheckAndIncrementPacketRate(limit)
|
||||
if ok {
|
||||
// 桶可能还剩令牌(容量 2),多耗几次再判断
|
||||
for i := 0; i < 20; i++ {
|
||||
ok, err = s.CheckAndIncrementPacketRate(limit)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ok {
|
||||
t.Fatal("令牌耗尽后应返回 ok=false")
|
||||
}
|
||||
if err == nil {
|
||||
t.Fatal("令牌耗尽后应返回 error")
|
||||
}
|
||||
if !isPacketLimitError(err) {
|
||||
t.Errorf("error 类型应为 PacketLimitError, 实际 %T: %v", err, err)
|
||||
}
|
||||
}
|
||||
|
||||
// isPacketLimitError 检查是否为 PacketLimitError
|
||||
func isPacketLimitError(err error) bool {
|
||||
_, ok := err.(*PacketLimitError)
|
||||
return ok
|
||||
}
|
||||
|
||||
// TestCheckAndIncrementPacketRate_ErrorUnwrapsToSentinel 验证 error 可 unwrap 到 sentinel
|
||||
func TestCheckAndIncrementPacketRate_ErrorUnwrapsToSentinel(t *testing.T) {
|
||||
s := NewState()
|
||||
const limit int64 = 1
|
||||
|
||||
// 耗尽令牌
|
||||
for i := 0; i < 50; i++ {
|
||||
s.CheckAndIncrementPacketRate(limit) //nolint: errcheck
|
||||
}
|
||||
|
||||
var lastErr error
|
||||
for i := 0; i < 10; i++ {
|
||||
ok, err := s.CheckAndIncrementPacketRate(limit)
|
||||
if !ok {
|
||||
lastErr = err
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if lastErr == nil {
|
||||
t.Skip("未能触发限速(可能令牌桶容量较大),跳过 unwrap 测试")
|
||||
}
|
||||
|
||||
// 验证可 unwrap 到 ErrPacketRateLimited
|
||||
pErr, ok := lastErr.(*PacketLimitError)
|
||||
if !ok {
|
||||
t.Fatalf("期望 *PacketLimitError, 实际 %T", lastErr)
|
||||
}
|
||||
if pErr.Sentinel != ErrPacketRateLimited {
|
||||
t.Errorf("Sentinel = %v, 期望 ErrPacketRateLimited", pErr.Sentinel)
|
||||
}
|
||||
if pErr.Limit != limit {
|
||||
t.Errorf("Limit = %d, 期望 %d", pErr.Limit, limit)
|
||||
}
|
||||
}
|
||||
|
||||
// TestState_OutputMutex 测试输出互斥锁
|
||||
func TestState_OutputMutex(t *testing.T) {
|
||||
s := NewState()
|
||||
|
||||
Reference in New Issue
Block a user