starnet/utils_test.go

285 lines
6.1 KiB
Go
Raw Normal View History

2026-03-08 20:19:40 +08:00
package starnet
import (
"net/http"
"testing"
"time"
)
func TestUrlEncodeRaw(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "basic string with space",
input: "hello world",
expected: "hello%20world",
},
{
name: "special characters",
input: "hello world!@#$%^&*()_+-=~`",
expected: "hello%20world%21%40%23%24%25%5E%26%2A%28%29_%2B-%3D~%60",
},
{
name: "empty string",
input: "",
expected: "",
},
{
name: "chinese characters",
input: "你好世界",
expected: "%E4%BD%A0%E5%A5%BD%E4%B8%96%E7%95%8C",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := UrlEncodeRaw(tt.input)
if result != tt.expected {
t.Errorf("UrlEncodeRaw(%q) = %q; want %q", tt.input, result, tt.expected)
}
})
}
}
func TestUrlEncode(t *testing.T) {
tests := []struct {
name string
input string
expected string
}{
{
name: "space encoded as plus",
input: "hello world",
expected: "hello+world",
},
{
name: "special characters",
input: "hello world!@#$%^&*()_+-=~`",
expected: "hello+world%21%40%23%24%25%5E%26%2A%28%29_%2B-%3D~%60",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := UrlEncode(tt.input)
if result != tt.expected {
t.Errorf("UrlEncode(%q) = %q; want %q", tt.input, result, tt.expected)
}
})
}
}
func TestUrlDecode(t *testing.T) {
tests := []struct {
name string
input string
expected string
expectErr bool
}{
{
name: "basic decode",
input: "hello%20world",
expected: "hello world",
expectErr: false,
},
{
name: "plus to space",
input: "hello+world",
expected: "hello world",
expectErr: false,
},
{
name: "special characters",
input: "hello%20world%21%40%23%24%25%5E%26*%28%29_%2B-%3D~%60",
expected: "hello world!@#$%^&*()_+-=~`",
expectErr: false,
},
{
name: "invalid encoding",
input: "%zz",
expected: "",
expectErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result, err := UrlDecode(tt.input)
if tt.expectErr {
if err == nil {
t.Errorf("UrlDecode(%q) expected error, got nil", tt.input)
}
} else {
if err != nil {
t.Errorf("UrlDecode(%q) unexpected error: %v", tt.input, err)
}
if result != tt.expected {
t.Errorf("UrlDecode(%q) = %q; want %q", tt.input, result, tt.expected)
}
}
})
}
}
func TestBuildQuery(t *testing.T) {
tests := []struct {
name string
input map[string]string
expected string
}{
{
name: "single parameter",
input: map[string]string{
"key": "value",
},
expected: "key=value",
},
{
name: "empty map",
input: map[string]string{},
expected: "",
},
{
name: "nil map",
input: nil,
expected: "",
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := BuildQuery(tt.input)
if result != tt.expected {
t.Errorf("BuildQuery(%v) = %q; want %q", tt.input, result, tt.expected)
}
})
}
}
func TestBuildPostForm(t *testing.T) {
tests := []struct {
name string
input map[string]string
expected []byte
}{
{
name: "basic form",
input: map[string]string{
"key1": "value1",
},
expected: []byte("key1=value1"),
},
{
name: "empty map",
input: map[string]string{},
expected: []byte(""),
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := BuildPostForm(tt.input)
if string(result) != string(tt.expected) {
t.Errorf("BuildPostForm(%v) = %v; want %v", tt.input, result, tt.expected)
}
})
}
}
func TestValidMethod(t *testing.T) {
tests := []struct {
name string
method string
expected bool
}{
{"GET", "GET", true},
{"POST", "POST", true},
{"PUT", "PUT", true},
{"DELETE", "DELETE", true},
{"PATCH", "PATCH", true},
{"OPTIONS", "OPTIONS", true},
{"HEAD", "HEAD", true},
{"TRACE", "TRACE", true},
{"CONNECT", "CONNECT", true},
{"invalid with space", "GET POST", false},
{"invalid with special char", "GET<>", false},
{"empty", "", false},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := validMethod(tt.method)
if result != tt.expected {
t.Errorf("validMethod(%q) = %v; want %v", tt.method, result, tt.expected)
}
})
}
}
func TestCloneCookies_FullFields(t *testing.T) {
expire := time.Now().Add(2 * time.Hour)
src := []*http.Cookie{
{
Name: "sid",
Value: "abc123",
Path: "/",
Domain: "example.com",
Expires: expire,
RawExpires: expire.UTC().Format(time.RFC1123),
MaxAge: 3600,
Secure: true,
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Raw: "sid=abc123; Path=/; HttpOnly",
Unparsed: []string{"Priority=High", "Partitioned"},
},
}
got := cloneCookies(src)
if got == nil || len(got) != 1 {
t.Fatalf("cloneCookies() len=%v; want 1", len(got))
}
// 指针应不同(不是浅拷贝)
if got[0] == src[0] {
t.Fatal("cookie pointer should be different (deep copy expected)")
}
// 字段值应一致
s := src[0]
g := got[0]
if g.Name != s.Name ||
g.Value != s.Value ||
g.Path != s.Path ||
g.Domain != s.Domain ||
!g.Expires.Equal(s.Expires) ||
g.RawExpires != s.RawExpires ||
g.MaxAge != s.MaxAge ||
g.Secure != s.Secure ||
g.HttpOnly != s.HttpOnly ||
g.SameSite != s.SameSite ||
g.Raw != s.Raw {
t.Fatalf("cloned cookie fields mismatch:\n got=%+v\n src=%+v", g, s)
}
// Unparsed 内容一致
if len(g.Unparsed) != len(s.Unparsed) {
t.Fatalf("Unparsed len=%d; want %d", len(g.Unparsed), len(s.Unparsed))
}
for i := range s.Unparsed {
if g.Unparsed[i] != s.Unparsed[i] {
t.Fatalf("Unparsed[%d]=%q; want %q", i, g.Unparsed[i], s.Unparsed[i])
}
}
// 验证 Unparsed 是深拷贝(修改源不影响目标)
src[0].Unparsed[0] = "Modified=Yes"
if got[0].Unparsed[0] == "Modified=Yes" {
t.Fatal("Unparsed should be deep-copied, but was affected by source mutation")
}
}