mirror of
https://github.com/shadow1ng/fscan.git
synced 2026-09-23 19:51:52 +08:00
test: 补充单元测试覆盖率 29.9% → 36.6%
新建 18 个测试文件,追加 30 个已有测试文件,覆盖协议解析、 错误分类、CEL 表达式求值、YAML 反序列化、字节编码等纯函数。
This commit is contained in:
@@ -101,3 +101,194 @@ func TestReadWebTitleBodyIsBounded(t *testing.T) {
|
||||
t.Fatalf("body len = %d, want %d", len(got), maxWebTitleBodyBytes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRedirectURL(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
base := "http://example.com/path"
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
location string
|
||||
want string
|
||||
}{
|
||||
{"absolute http", "http://other.com/page", "http://other.com/page"},
|
||||
{"absolute https", "https://other.com/page", "https://other.com/page"},
|
||||
{"relative path", "/admin/login", "http://example.com/admin/login"},
|
||||
{"relative no slash", "login", "http://example.com/login"},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := p.resolveRedirectURL(base, tt.location)
|
||||
if got != tt.want {
|
||||
t.Fatalf("resolveRedirectURL(%q, %q) = %q, want %q", base, tt.location, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRedirectURLInvalidBase(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
got := p.resolveRedirectURL("://bad-url", "/path")
|
||||
if got != "" {
|
||||
t.Fatalf("expected empty string for invalid base, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveRedirectURLInvalidLocation(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
// 百分号开头的无效 URL
|
||||
got := p.resolveRedirectURL("http://example.com", "://")
|
||||
// net/url.Parse 对 "://" 不一定报错,只要不 panic 即可
|
||||
_ = got
|
||||
}
|
||||
|
||||
func TestFormatHeaders(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
|
||||
// 空 header
|
||||
if got := p.formatHeaders(http.Header{}); got != "" {
|
||||
t.Fatalf("empty headers = %q, want empty string", got)
|
||||
}
|
||||
|
||||
// 单个 header
|
||||
h := http.Header{}
|
||||
h.Set("Content-Type", "text/html")
|
||||
got := p.formatHeaders(h)
|
||||
if !strings.Contains(got, "Content-Type") || !strings.Contains(got, "text/html") {
|
||||
t.Fatalf("formatHeaders missing expected content: %q", got)
|
||||
}
|
||||
|
||||
// 多值 header
|
||||
h2 := http.Header{}
|
||||
h2.Add("X-Custom", "val1")
|
||||
h2.Add("X-Custom", "val2")
|
||||
got2 := p.formatHeaders(h2)
|
||||
if !strings.Contains(got2, "val1") || !strings.Contains(got2, "val2") {
|
||||
t.Fatalf("formatHeaders missing multi-value: %q", got2)
|
||||
}
|
||||
}
|
||||
|
||||
func TestURLHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
input string
|
||||
want string
|
||||
}{
|
||||
{"127.0.0.1", "127.0.0.1"},
|
||||
{"example.com", "example.com"},
|
||||
{"::1", "[::1]"},
|
||||
{"[::1]", "[::1]"}, // 已经括起来的不要双重括号
|
||||
}
|
||||
for _, tt := range tests {
|
||||
got := urlHost(tt.input)
|
||||
if got != tt.want {
|
||||
t.Fatalf("urlHost(%q) = %q, want %q", tt.input, got, tt.want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTruncateRunes(t *testing.T) {
|
||||
// 负数 maxRunes → 原样返回
|
||||
s := "hello"
|
||||
if got := truncateRunes(s, -1); got != s {
|
||||
t.Fatalf("truncateRunes negative = %q, want %q", got, s)
|
||||
}
|
||||
|
||||
// 短于 maxRunes → 原样返回
|
||||
if got := truncateRunes("ab", 10); got != "ab" {
|
||||
t.Fatalf("truncateRunes short = %q, want %q", got, "ab")
|
||||
}
|
||||
|
||||
// 超过 maxRunes → 截断加 "..."
|
||||
long := strings.Repeat("x", 5)
|
||||
got := truncateRunes(long, 3)
|
||||
if got != "xxx..." {
|
||||
t.Fatalf("truncateRunes long = %q, want %q", got, "xxx...")
|
||||
}
|
||||
|
||||
// maxRunes=0 → 立刻截断
|
||||
if got := truncateRunes("hello", 0); got != "..." {
|
||||
t.Fatalf("truncateRunes zero = %q, want %q", got, "...")
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractTitleInvalidUTF8(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
// 构造含非法 UTF-8 字节的 title
|
||||
html := "<html><title>\xff\xfe</title></html>"
|
||||
got := p.extractTitle(html)
|
||||
// 非法 UTF-8 应返回空
|
||||
if got != "" {
|
||||
t.Fatalf("extractTitle with invalid UTF-8 = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExtractTitleNoMatch(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
got := p.extractTitle("<html><body>no title here</body></html>")
|
||||
if got != "" {
|
||||
t.Fatalf("extractTitle no match = %q, want empty", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestWebTitleHTTPClientsGM(t *testing.T) {
|
||||
previousGM, previousNoRedirectGM := lib.ClientGM, lib.ClientNoRedirectGM
|
||||
defer func() {
|
||||
lib.ClientGM, lib.ClientNoRedirectGM = previousGM, previousNoRedirectGM
|
||||
}()
|
||||
|
||||
// 设置 GM 客户端为非 nil
|
||||
lib.ClientGM = &http.Client{}
|
||||
lib.ClientNoRedirectGM = &http.Client{
|
||||
CheckRedirect: func(*http.Request, []*http.Request) error {
|
||||
return http.ErrUseLastResponse
|
||||
},
|
||||
}
|
||||
|
||||
clientNR, clientR := webTitleHTTPClients(true)
|
||||
if clientNR == nil || clientR == nil {
|
||||
t.Fatal("webTitleHTTPClients(GM) returned nil")
|
||||
}
|
||||
}
|
||||
|
||||
func TestFirstHTTPClientAllNil(t *testing.T) {
|
||||
got := firstHTTPClient(nil, nil, nil)
|
||||
if got != http.DefaultClient {
|
||||
t.Fatalf("firstHTTPClient all nil = %v, want http.DefaultClient", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchFaviconHashNon200(t *testing.T) {
|
||||
previous := lib.Client
|
||||
lib.Client = &http.Client{
|
||||
Transport: roundTripFunc(func(req *http.Request) (*http.Response, error) {
|
||||
return &http.Response{
|
||||
StatusCode: http.StatusNotFound,
|
||||
Body: http.NoBody,
|
||||
}, nil
|
||||
}),
|
||||
}
|
||||
defer func() { lib.Client = previous }()
|
||||
|
||||
p := NewWebTitlePlugin()
|
||||
hashes := p.fetchFaviconHash(context.Background(), "http://example.com")
|
||||
if len(hashes.MMH3) != 0 || len(hashes.MD5) != 0 {
|
||||
t.Fatalf("fetchFaviconHash non-200 returned hashes: %#v", hashes)
|
||||
}
|
||||
}
|
||||
|
||||
func TestFetchFaviconHashBadURL(t *testing.T) {
|
||||
p := NewWebTitlePlugin()
|
||||
// 无效 URL 应返回空 hash,不 panic
|
||||
hashes := p.fetchFaviconHash(context.Background(), "://bad")
|
||||
if len(hashes.MMH3) != 0 || len(hashes.MD5) != 0 {
|
||||
t.Fatalf("fetchFaviconHash bad URL returned hashes: %#v", hashes)
|
||||
}
|
||||
}
|
||||
|
||||
// roundTripFunc 允许用函数实现 http.RoundTripper
|
||||
type roundTripFunc func(*http.Request) (*http.Response, error)
|
||||
|
||||
func (f roundTripFunc) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
return f(req)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user