Files
fscan/common/parse_test.go
ZacharyZcR 71b92d4408 feat: v2.1.0 核心重构与功能增强
## 架构重构
- 全局变量消除,迁移至 Config/State 对象
- SMB 插件融合(smb/smb2/smbghost/smbinfo)
- 服务探测重构,实现 Nmap 风格 fallback 机制
- 输出系统重构,TXT 实时刷盘 + 双写机制
- i18n 框架升级至 go-i18n

## 性能优化
- 正则表达式预编译
- 内存优化 map[string]struct{}
- 并发指纹匹配
- SOCKS5 连接复用
- 滑动窗口调度 + 自适应线程池

## 新功能
- Web 管理界面
- 多格式 POC 适配(xray/afrog)
- 增强指纹库(3139条)
- Favicon hash 指纹识别
- 插件选择性编译(Build Tags)
- fscan-lab 靶场环境
- 默认端口扩展(62→133)

## 构建系统
- 添加 no_local tag 支持排除本地插件
- 多版本构建:fscan/fscan-nolocal/fscan-web
- CI 添加 snapshot 模式支持仅测试构建

## Bug 修复
- 修复 120+ 个问题,包括 RDP panic、批量扫描漏报、
  JSON 输出格式、Redis 检测、Context 超时等

## 测试增强
- 单元测试覆盖率 74-100%
- 并发安全测试
- 集成测试(Web/端口/服务/SSH/ICMP)
2026-01-11 20:16:23 +08:00

367 lines
9.6 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 common
import (
"reflect"
"testing"
)
/*
parse_test.go - 解析工具函数测试
测试目标RemoveDuplicate 函数
价值:去重逻辑影响用户输入处理,错误会导致:
- 重复扫描同一目标(性能浪费)
- 顺序错乱(某些场景依赖顺序)
"去重是经典算法问题。保留顺序、处理空值、全重复——
这些都是真实场景,必须验证。代码很简单,但边界情况会咬人。"
*/
// =============================================================================
// RemoveDuplicate - 切片去重测试
// =============================================================================
// TestRemoveDuplicate_BasicCases 测试基本去重功能
func TestRemoveDuplicate_BasicCases(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{
name: "无重复元素",
input: []string{"a", "b", "c"},
expected: []string{"a", "b", "c"},
},
{
name: "有重复元素-保留首次出现",
input: []string{"a", "b", "a", "c"},
expected: []string{"a", "b", "c"},
},
{
name: "连续重复",
input: []string{"a", "a", "b", "b", "c", "c"},
expected: []string{"a", "b", "c"},
},
{
name: "全部重复",
input: []string{"a", "a", "a", "a"},
expected: []string{"a"},
},
{
name: "空切片",
input: []string{},
expected: []string{},
},
{
name: "单个元素",
input: []string{"a"},
expected: []string{"a"},
},
{
name: "两个相同元素",
input: []string{"a", "a"},
expected: []string{"a"},
},
{
name: "两个不同元素",
input: []string{"a", "b"},
expected: []string{"a", "b"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := RemoveDuplicate(tt.input)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("RemoveDuplicate(%v) = %v, want %v",
tt.input, result, tt.expected)
}
})
}
}
// TestRemoveDuplicate_OrderPreservation 测试顺序保留
func TestRemoveDuplicate_OrderPreservation(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
note string
}{
{
name: "保留首次出现顺序",
input: []string{"b", "a", "c", "a", "b"},
expected: []string{"b", "a", "c"},
note: "b先出现应该在a前面",
},
{
name: "后续重复不影响顺序",
input: []string{"1", "2", "3", "2", "1"},
expected: []string{"1", "2", "3"},
note: "保持1,2,3的原始顺序",
},
{
name: "数字字符串顺序",
input: []string{"10", "2", "3", "2", "10"},
expected: []string{"10", "2", "3"},
note: "按出现顺序,不按数值排序",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := RemoveDuplicate(tt.input)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("RemoveDuplicate(%v) = %v, want %v\nNote: %s",
tt.input, result, tt.expected, tt.note)
}
})
}
}
// TestRemoveDuplicate_EdgeCases 测试边界情况
func TestRemoveDuplicate_EdgeCases(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{
name: "nil切片",
input: nil,
expected: nil,
},
{
name: "空字符串元素",
input: []string{"", "a", "", "b"},
expected: []string{"", "a", "b"},
},
{
name: "全是空字符串",
input: []string{"", "", ""},
expected: []string{""},
},
{
name: "包含空格的字符串",
input: []string{" ", "a", " ", "b"},
expected: []string{" ", "a", "b"},
},
{
name: "相似但不同的字符串",
input: []string{"a", "A", "a", "A"},
expected: []string{"a", "A"},
},
{
name: "长字符串",
input: []string{"very long string 1", "short", "very long string 1"},
expected: []string{"very long string 1", "short"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := RemoveDuplicate(tt.input)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("RemoveDuplicate(%v) = %v, want %v",
tt.input, result, tt.expected)
}
})
}
}
// TestRemoveDuplicate_ProductionScenarios 测试生产环境真实场景
func TestRemoveDuplicate_ProductionScenarios(t *testing.T) {
t.Run("IP地址去重", func(t *testing.T) {
// 用户可能输入重复的IP
input := []string{"192.168.1.1", "192.168.1.2", "192.168.1.1", "192.168.1.3"}
expected := []string{"192.168.1.1", "192.168.1.2", "192.168.1.3"}
result := RemoveDuplicate(input)
if !reflect.DeepEqual(result, expected) {
t.Errorf("IP去重失败: got %v, want %v", result, expected)
}
})
t.Run("域名去重", func(t *testing.T) {
// 用户可能从文件读取重复域名
input := []string{"example.com", "test.com", "example.com", "demo.com", "test.com"}
expected := []string{"example.com", "test.com", "demo.com"}
result := RemoveDuplicate(input)
if !reflect.DeepEqual(result, expected) {
t.Errorf("域名去重失败: got %v, want %v", result, expected)
}
})
t.Run("端口列表去重", func(t *testing.T) {
// 合并多个端口列表
input := []string{"80", "443", "8080", "80", "443", "3306"}
expected := []string{"80", "443", "8080", "3306"}
result := RemoveDuplicate(input)
if !reflect.DeepEqual(result, expected) {
t.Errorf("端口去重失败: got %v, want %v", result, expected)
}
})
t.Run("用户名去重", func(t *testing.T) {
// 字典文件可能有重复
input := []string{"admin", "root", "admin", "user", "root", "test"}
expected := []string{"admin", "root", "user", "test"}
result := RemoveDuplicate(input)
if !reflect.DeepEqual(result, expected) {
t.Errorf("用户名去重失败: got %v, want %v", result, expected)
}
})
}
// TestRemoveDuplicate_Performance 测试大规模数据性能
func TestRemoveDuplicate_Performance(t *testing.T) {
t.Run("1000个元素-50%重复", func(t *testing.T) {
// 构造测试数据1000个元素500个唯一
input := make([]string, 1000)
for i := 0; i < 1000; i++ {
input[i] = string(rune('A' + i%500))
}
result := RemoveDuplicate(input)
// 验证结果长度
if len(result) != 500 {
t.Errorf("去重后应该有500个唯一元素实际 %d", len(result))
}
// 验证无重复
seen := make(map[string]bool)
for _, item := range result {
if seen[item] {
t.Errorf("结果中仍有重复元素: %s", item)
break
}
seen[item] = true
}
})
t.Run("大规模-全不重复", func(t *testing.T) {
// 10000个唯一元素
input := make([]string, 10000)
for i := 0; i < 10000; i++ {
input[i] = string(rune(i))
}
result := RemoveDuplicate(input)
if len(result) != 10000 {
t.Errorf("全不重复应该保持原长度期望10000实际 %d", len(result))
}
})
t.Run("大规模-全重复", func(t *testing.T) {
// 10000个相同元素
input := make([]string, 10000)
for i := 0; i < 10000; i++ {
input[i] = "duplicate"
}
result := RemoveDuplicate(input)
if len(result) != 1 {
t.Errorf("全重复应该只剩1个元素实际 %d", len(result))
}
if result[0] != "duplicate" {
t.Errorf("结果应该是 'duplicate',实际 %q", result[0])
}
})
}
// TestRemoveDuplicate_ReturnValue 测试返回值特性
func TestRemoveDuplicate_ReturnValue(t *testing.T) {
t.Run("不修改原始切片", func(t *testing.T) {
input := []string{"a", "b", "a"}
inputCopy := make([]string, len(input))
copy(inputCopy, input)
_ = RemoveDuplicate(input)
// 验证原始切片未被修改
if !reflect.DeepEqual(input, inputCopy) {
t.Errorf("RemoveDuplicate修改了原始切片\n原始: %v\n修改后: %v", inputCopy, input)
}
})
t.Run("返回新切片", func(t *testing.T) {
input := []string{"a", "b", "c"}
result := RemoveDuplicate(input)
// 修改result不应影响input
result[0] = "modified"
if input[0] == "modified" {
t.Error("返回的切片与输入共享内存")
}
})
t.Run("空输入返回空切片", func(t *testing.T) {
result := RemoveDuplicate([]string{})
if result == nil {
t.Error("空输入应返回空切片而非nil")
}
if len(result) != 0 {
t.Errorf("空输入返回切片长度应为0实际 %d", len(result))
}
})
t.Run("单元素不分配新内存", func(t *testing.T) {
input := []string{"a"}
result := RemoveDuplicate(input)
// 根据实现,单元素直接返回原切片(优化)
// 这是实现细节,测试是否返回相同引用
if &input[0] != &result[0] {
t.Log("单元素时返回了新切片(也是正确的实现)")
}
})
}
// TestRemoveDuplicate_SpecialCharacters 测试特殊字符处理
func TestRemoveDuplicate_SpecialCharacters(t *testing.T) {
tests := []struct {
name string
input []string
expected []string
}{
{
name: "包含换行符",
input: []string{"a\n", "b", "a\n"},
expected: []string{"a\n", "b"},
},
{
name: "包含制表符",
input: []string{"a\t", "b", "a\t"},
expected: []string{"a\t", "b"},
},
{
name: "Unicode字符",
input: []string{"你好", "world", "你好", "世界"},
expected: []string{"你好", "world", "世界"},
},
{
name: "特殊符号",
input: []string{"!@#", "$%^", "!@#"},
expected: []string{"!@#", "$%^"},
},
{
name: "路径字符串",
input: []string{"/root/test", "/home/user", "/root/test"},
expected: []string{"/root/test", "/home/user"},
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := RemoveDuplicate(tt.input)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("RemoveDuplicate(%v) = %v, want %v",
tt.input, result, tt.expected)
}
})
}
}