integrate request builder into HTTP client for googleapis support (#157)
This commit is contained in:
401
client_helpers_test.go
Normal file
401
client_helpers_test.go
Normal file
@@ -0,0 +1,401 @@
|
||||
package http
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
jsoncodec "go.unistack.org/micro-codec-json/v4"
|
||||
"google.golang.org/protobuf/proto"
|
||||
|
||||
pb "go.unistack.org/micro-client-http/v4/builder/proto"
|
||||
)
|
||||
|
||||
func TestJoinURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
addr string
|
||||
path string
|
||||
want string
|
||||
}{
|
||||
{
|
||||
name: "both without slash",
|
||||
addr: "http://example.com",
|
||||
path: "api/v1",
|
||||
want: "http://example.com/api/v1",
|
||||
},
|
||||
{
|
||||
name: "addr with slash, path without slash",
|
||||
addr: "http://example.com/",
|
||||
path: "api/v1",
|
||||
want: "http://example.com/api/v1",
|
||||
},
|
||||
{
|
||||
name: "addr without slash, path with slash",
|
||||
addr: "http://example.com",
|
||||
path: "/api/v1",
|
||||
want: "http://example.com/api/v1",
|
||||
},
|
||||
{
|
||||
name: "both with slash",
|
||||
addr: "http://example.com/",
|
||||
path: "/api/v1",
|
||||
want: "http://example.com/api/v1",
|
||||
},
|
||||
{
|
||||
name: "empty addr",
|
||||
addr: "",
|
||||
path: "/api/v1",
|
||||
want: "/api/v1",
|
||||
},
|
||||
{
|
||||
name: "empty path",
|
||||
addr: "http://example.com",
|
||||
path: "",
|
||||
want: "http://example.com",
|
||||
},
|
||||
{
|
||||
name: "both empty",
|
||||
addr: "",
|
||||
path: "",
|
||||
want: "",
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
require.Equal(t, tt.want, joinURL(tt.addr, tt.path))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNormalizeURL(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
input string
|
||||
want string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "host with port",
|
||||
input: "localhost:8080",
|
||||
want: "http://localhost:8080",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "http with host",
|
||||
input: "http://example.com",
|
||||
want: "http://example.com",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "http with no host",
|
||||
input: "http://",
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "https with host",
|
||||
input: "https://example.com",
|
||||
want: "https://example.com",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "https with no host",
|
||||
input: "https://",
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "invalid scheme",
|
||||
input: "ftp://example.com",
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "IPv4 without scheme",
|
||||
input: "127.0.0.1:9000",
|
||||
want: "http://127.0.0.1:9000",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "IPv4 with scheme",
|
||||
input: "http://127.0.0.1:8080",
|
||||
want: "http://127.0.0.1:8080",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "IPv6 without scheme",
|
||||
input: "[::1]:8080",
|
||||
want: "http://[::1]:8080",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "IPv6 with scheme",
|
||||
input: "https://[::1]:443",
|
||||
want: "https://[::1]:443",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "hostname only",
|
||||
input: "my-service",
|
||||
want: "http://my-service",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "hostname with path",
|
||||
input: "service.local/api/v1",
|
||||
want: "http://service.local/api/v1",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "hostname with dash and port",
|
||||
input: "api-service.local:8080",
|
||||
want: "http://api-service.local:8080",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "just path",
|
||||
input: "/api/v1",
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "empty string",
|
||||
input: "",
|
||||
want: "",
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "http with query params",
|
||||
input: "http://example.com?x=1&y=2",
|
||||
want: "http://example.com?x=1&y=2",
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "http with fragment",
|
||||
input: "http://example.com/path#section1",
|
||||
want: "http://example.com/path#section1",
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := normalizeURL(tt.input)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.Nil(t, result)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.want, result.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestMarshallMsg(t *testing.T) {
|
||||
type request = pb.Test_Client_Call_Request
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
msg proto.Message
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
msg: &request{},
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "nil",
|
||||
msg: nil,
|
||||
expected: "",
|
||||
},
|
||||
{
|
||||
name: "valid",
|
||||
msg: &request{UserId: "123", OrderId: 456},
|
||||
expected: `{"userId":"123","orderId":456}`,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
result, err := marshallMsg(jsoncodec.NewCodec(), tt.msg)
|
||||
require.NoError(t, err)
|
||||
require.Equal(t, tt.expected, string(result))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestApplyCookies(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
rawCookies []string
|
||||
want []*http.Cookie
|
||||
}{
|
||||
{
|
||||
name: "empty",
|
||||
rawCookies: []string{},
|
||||
want: []*http.Cookie{},
|
||||
},
|
||||
{
|
||||
name: "single cookie",
|
||||
rawCookies: []string{"session=abc123"},
|
||||
want: []*http.Cookie{
|
||||
{Name: "session", Value: "abc123"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple cookies separate items",
|
||||
rawCookies: []string{"session=abc123", "user=john"},
|
||||
want: []*http.Cookie{
|
||||
{Name: "session", Value: "abc123"},
|
||||
{Name: "user", Value: "john"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "multiple cookies in one item",
|
||||
rawCookies: []string{"a=1; b=2"},
|
||||
want: []*http.Cookie{
|
||||
{Name: "a", Value: "1"},
|
||||
{Name: "b", Value: "2"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "mix of combined and separate cookies",
|
||||
rawCookies: []string{"a=1; b=2", "c=3"},
|
||||
want: []*http.Cookie{
|
||||
{Name: "a", Value: "1"},
|
||||
{Name: "b", Value: "2"},
|
||||
{Name: "c", Value: "3"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "duplicate cookies",
|
||||
rawCookies: []string{"session=abc123", "session=xyz"},
|
||||
want: []*http.Cookie{
|
||||
{Name: "session", Value: "abc123"},
|
||||
{Name: "session", Value: "xyz"},
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "cookie with spaces",
|
||||
rawCookies: []string{"token=abc 123"},
|
||||
want: []*http.Cookie{
|
||||
{Name: "token", Value: "abc 123", Quoted: true},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
applyCookies(req, tt.rawCookies)
|
||||
require.Equal(t, tt.want, req.Cookies())
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateHeadersAndCookies(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
prepareRequest func() *http.Request
|
||||
parameters map[string]map[string]string
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "all required headers and cookies present",
|
||||
prepareRequest: func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("My-Header-1", "Header-Value-1")
|
||||
req.Header.Set("My-Header-2", "Header-Value-2")
|
||||
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
|
||||
req.AddCookie(&http.Cookie{Name: "session-2", Value: "abc-2"})
|
||||
return req
|
||||
},
|
||||
parameters: map[string]map[string]string{
|
||||
"header": {"My-Header-1": "true", "My-Header-2": "true"},
|
||||
"cookie": {"session-1": "true", "session-2": "true"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "missing required header",
|
||||
prepareRequest: func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("My-Header-1", "Header-Value-1")
|
||||
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
|
||||
req.AddCookie(&http.Cookie{Name: "session-2", Value: "abc-2"})
|
||||
return req
|
||||
},
|
||||
parameters: map[string]map[string]string{
|
||||
"header": {"My-Header-1": "true", "My-Header-2": "true"},
|
||||
"cookie": {"session-1": "true", "session-2": "true"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "missing required cookie",
|
||||
prepareRequest: func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("My-Header-1", "Header-Value-1")
|
||||
req.Header.Set("My-Header-2", "Header-Value-2")
|
||||
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
|
||||
return req
|
||||
},
|
||||
parameters: map[string]map[string]string{
|
||||
"header": {"My-Header-1": "true", "My-Header-2": "true"},
|
||||
"cookie": {"session-1": "true", "session-2": "true"},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "optional header and cookie not provided partially",
|
||||
prepareRequest: func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req.Header.Set("My-Header-1", "Header-Value-1")
|
||||
req.AddCookie(&http.Cookie{Name: "session-1", Value: "abc-1"})
|
||||
return req
|
||||
},
|
||||
parameters: map[string]map[string]string{
|
||||
"header": {"My-Header-1": "true", "My-Header-2": "false"},
|
||||
"cookie": {"session-1": "true", "session-2": "false"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "optional header and cookie not provided",
|
||||
prepareRequest: func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
return req
|
||||
},
|
||||
parameters: map[string]map[string]string{
|
||||
"header": {"My-Header-1": "false", "My-Header-2": "false"},
|
||||
"cookie": {"session-1": "false", "session-2": "false"},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "no headers or cookies required",
|
||||
prepareRequest: func() *http.Request {
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
return req
|
||||
},
|
||||
parameters: map[string]map[string]string{},
|
||||
wantErr: false,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := validateHeadersAndCookies(tt.prepareRequest(), tt.parameters)
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user