integrate request builder into HTTP client for googleapis support (#157)
Some checks failed
coverage / build (push) Successful in 2m19s
test / test (push) Failing after 17m15s

This commit is contained in:
2025-09-23 15:30:15 +05:00
committed by GitHub
parent b37fca95cf
commit 24801750a7
32 changed files with 9491 additions and 1736 deletions

401
client_helpers_test.go Normal file
View 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)
}
})
}
}