Files
fscan/webscan/lib/eval_string_test.go
T
ZacharyZcR 353d525642
测试构建 / 代码检查 (push) Has been cancelled
测试构建 / 单元测试和构建 (push) Has been cancelled
测试构建 / 构建验证 (push) Has been cancelled
test: 补充单元测试覆盖率 29.9% → 36.6%
新建 18 个测试文件,追加 30 个已有测试文件,覆盖协议解析、
错误分类、CEL 表达式求值、YAML 反序列化、字节编码等纯函数。
2026-06-15 19:11:15 +08:00

338 lines
9.8 KiB
Go

package lib
import (
"testing"
"github.com/google/cel-go/common/types"
"github.com/google/cel-go/common/types/ref"
)
func TestRegisterStringImplementations(t *testing.T) {
overloads := registerStringImplementations()
idx := make(map[string]int, len(overloads))
for i, o := range overloads {
idx[o.Operator] = i
}
t.Run("bytes_bcontains_bytes", func(t *testing.T) {
binary := overloads[idx["bytes_bcontains_bytes"]].Binary
tests := []struct {
name string
lhs types.Bytes
rhs types.Bytes
want types.Bool
}{
{"contains", types.Bytes([]byte("hello world")), types.Bytes([]byte("world")), true},
{"not_contains", types.Bytes([]byte("hello world")), types.Bytes([]byte("xyz")), false},
{"empty_needle", types.Bytes([]byte("hello")), types.Bytes([]byte{}), true},
{"both_empty", types.Bytes([]byte{}), types.Bytes([]byte{}), true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := binary(tc.lhs, tc.rhs)
if types.IsError(result) {
t.Fatalf("unexpected error: %v", result)
}
got, ok := result.(types.Bool)
if !ok {
t.Fatalf("expected types.Bool, got %T", result)
}
if got != tc.want {
t.Errorf("bcontains = %v, want %v", got, tc.want)
}
})
}
})
t.Run("bytes_bcontains_bytes_wrong_lhs", func(t *testing.T) {
result := overloads[idx["bytes_bcontains_bytes"]].Binary(types.String("hello"), types.Bytes([]byte("x")))
if !types.IsError(result) {
t.Error("expected error for non-Bytes lhs")
}
})
t.Run("bytes_bcontains_bytes_wrong_rhs", func(t *testing.T) {
result := overloads[idx["bytes_bcontains_bytes"]].Binary(types.Bytes([]byte("hello")), types.String("x"))
if !types.IsError(result) {
t.Error("expected error for non-Bytes rhs")
}
})
t.Run("string_bmatches_bytes", func(t *testing.T) {
binary := overloads[idx["string_bmatches_bytes"]].Binary
tests := []struct {
name string
pattern types.String
input types.Bytes
want types.Bool
}{
{"digits_match", `\d+`, types.Bytes([]byte("abc123")), true},
{"digits_no_match", `\d+`, types.Bytes([]byte("abc")), false},
{"any", `.*`, types.Bytes([]byte("hello")), true},
{"empty_pattern", ``, types.Bytes([]byte("hello")), true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := binary(tc.pattern, tc.input)
if types.IsError(result) {
t.Fatalf("unexpected error: %v", result)
}
got, ok := result.(types.Bool)
if !ok {
t.Fatalf("expected types.Bool, got %T", result)
}
if got != tc.want {
t.Errorf("bmatches(%q, %q) = %v, want %v", tc.pattern, tc.input, got, tc.want)
}
})
}
})
t.Run("string_bmatches_bytes_invalid_regex", func(t *testing.T) {
result := overloads[idx["string_bmatches_bytes"]].Binary(types.String(`[invalid`), types.Bytes([]byte("hello")))
if !types.IsError(result) {
t.Error("expected error for invalid regex pattern")
}
})
t.Run("string_bmatches_bytes_wrong_lhs", func(t *testing.T) {
result := overloads[idx["string_bmatches_bytes"]].Binary(types.Int(0), types.Bytes([]byte("hello")))
if !types.IsError(result) {
t.Error("expected error for non-String lhs")
}
})
t.Run("string_bmatches_bytes_wrong_rhs", func(t *testing.T) {
result := overloads[idx["string_bmatches_bytes"]].Binary(types.String(`\d+`), types.String("123"))
if !types.IsError(result) {
t.Error("expected error for non-Bytes rhs")
}
})
t.Run("icontains_string", func(t *testing.T) {
binary := overloads[idx["icontains_string"]].Binary
tests := []struct {
name string
lhs types.String
rhs types.String
want types.Bool
}{
{"case_insensitive_match", "Hello World", "hello", true},
{"exact_match", "Hello World", "Hello", true},
{"upper_needle", "hello world", "WORLD", true},
{"not_contains", "hello world", "xyz", false},
{"empty_needle", "hello", "", true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := binary(tc.lhs, tc.rhs)
if types.IsError(result) {
t.Fatalf("unexpected error: %v", result)
}
got, ok := result.(types.Bool)
if !ok {
t.Fatalf("expected types.Bool, got %T", result)
}
if got != tc.want {
t.Errorf("icontains(%q, %q) = %v, want %v", tc.lhs, tc.rhs, got, tc.want)
}
})
}
})
t.Run("icontains_string_wrong_lhs", func(t *testing.T) {
result := overloads[idx["icontains_string"]].Binary(types.Bool(true), types.String("x"))
if !types.IsError(result) {
t.Error("expected error for non-String lhs")
}
})
t.Run("icontains_string_wrong_rhs", func(t *testing.T) {
result := overloads[idx["icontains_string"]].Binary(types.String("hello"), types.Int(1))
if !types.IsError(result) {
t.Error("expected error for non-String rhs")
}
})
t.Run("substr_string_int_int", func(t *testing.T) {
fn := overloads[idx["substr_string_int_int"]].Function
tests := []struct {
name string
str types.String
start types.Int
length types.Int
want types.String
}{
{"basic", "hello world", 0, 5, "hello"},
{"middle", "hello world", 6, 5, "world"},
{"single_char", "hello", 1, 1, "e"},
{"full", "hello", 0, 5, "hello"},
{"zero_length", "hello", 2, 0, ""},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := fn(tc.str, tc.start, tc.length)
if types.IsError(result) {
t.Fatalf("substr(%q, %d, %d): unexpected error %v", tc.str, tc.start, tc.length, result)
}
got, ok := result.(types.String)
if !ok {
t.Fatalf("expected types.String, got %T", result)
}
if got != tc.want {
t.Errorf("substr(%q, %d, %d) = %q, want %q", tc.str, tc.start, tc.length, got, tc.want)
}
})
}
})
t.Run("substr_out_of_bounds", func(t *testing.T) {
fn := overloads[idx["substr_string_int_int"]].Function
oob := []struct {
name string
str types.String
start types.Int
length types.Int
}{
{"negative_start", "hello", -1, 2},
{"negative_length", "hello", 0, -1},
{"start_too_large", "hello", 10, 1},
{"length_overflow", "hello", 3, 10},
}
for _, tc := range oob {
t.Run(tc.name, func(t *testing.T) {
result := fn(tc.str, tc.start, tc.length)
if !types.IsError(result) {
t.Errorf("expected error for substr(%q, %d, %d), got %v", tc.str, tc.start, tc.length, result)
}
})
}
})
t.Run("substr_wrong_arg_count", func(t *testing.T) {
fn := overloads[idx["substr_string_int_int"]].Function
result := fn(types.String("hello"), types.Int(0))
if !types.IsError(result) {
t.Error("expected error for wrong argument count")
}
})
t.Run("substr_wrong_types", func(t *testing.T) {
fn := overloads[idx["substr_string_int_int"]].Function
cases := []struct {
name string
args []ref.Val
}{
{"wrong_str", []ref.Val{types.Int(0), types.Int(0), types.Int(1)}},
{"wrong_start", []ref.Val{types.String("hello"), types.String("x"), types.Int(1)}},
{"wrong_length", []ref.Val{types.String("hello"), types.Int(0), types.String("x")}},
}
for _, tc := range cases {
t.Run(tc.name, func(t *testing.T) {
result := fn(tc.args...)
if !types.IsError(result) {
t.Errorf("expected error, got %v", result)
}
})
}
})
t.Run("startsWith_bytes", func(t *testing.T) {
binary := overloads[idx["startsWith_bytes"]].Binary
tests := []struct {
name string
lhs types.Bytes
rhs types.Bytes
want types.Bool
}{
{"match", types.Bytes([]byte("hello world")), types.Bytes([]byte("hello")), true},
{"no_match", types.Bytes([]byte("hello world")), types.Bytes([]byte("world")), false},
{"empty_prefix", types.Bytes([]byte("hello")), types.Bytes([]byte{}), true},
{"exact", types.Bytes([]byte("hello")), types.Bytes([]byte("hello")), true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := binary(tc.lhs, tc.rhs)
if types.IsError(result) {
t.Fatalf("unexpected error: %v", result)
}
got, ok := result.(types.Bool)
if !ok {
t.Fatalf("expected types.Bool, got %T", result)
}
if got != tc.want {
t.Errorf("startsWith_bytes = %v, want %v", got, tc.want)
}
})
}
})
t.Run("startsWith_bytes_wrong_lhs", func(t *testing.T) {
result := overloads[idx["startsWith_bytes"]].Binary(types.String("hello"), types.Bytes([]byte("h")))
if !types.IsError(result) {
t.Error("expected error for non-Bytes lhs")
}
})
t.Run("startsWith_bytes_wrong_rhs", func(t *testing.T) {
result := overloads[idx["startsWith_bytes"]].Binary(types.Bytes([]byte("hello")), types.String("h"))
if !types.IsError(result) {
t.Error("expected error for non-Bytes rhs")
}
})
t.Run("startsWith_string", func(t *testing.T) {
binary := overloads[idx["startsWith_string"]].Binary
tests := []struct {
name string
lhs types.String
rhs types.String
want types.Bool
}{
{"case_insensitive_match", "Hello World", "hello", true},
{"upper_prefix", "hello world", "HELLO", true},
{"no_match", "hello world", "world", false},
{"empty_prefix", "hello", "", true},
{"exact", "Hello", "Hello", true},
}
for _, tc := range tests {
t.Run(tc.name, func(t *testing.T) {
result := binary(tc.lhs, tc.rhs)
if types.IsError(result) {
t.Fatalf("unexpected error: %v", result)
}
got, ok := result.(types.Bool)
if !ok {
t.Fatalf("expected types.Bool, got %T", result)
}
if got != tc.want {
t.Errorf("startsWith_string(%q, %q) = %v, want %v", tc.lhs, tc.rhs, got, tc.want)
}
})
}
})
t.Run("startsWith_string_wrong_lhs", func(t *testing.T) {
result := overloads[idx["startsWith_string"]].Binary(types.Int(0), types.String("h"))
if !types.IsError(result) {
t.Error("expected error for non-String lhs")
}
})
t.Run("startsWith_string_wrong_rhs", func(t *testing.T) {
result := overloads[idx["startsWith_string"]].Binary(types.String("hello"), types.Bool(true))
if !types.IsError(result) {
t.Error("expected error for non-String rhs")
}
})
}