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") } }