Skip to content

Commit 3a85474

Browse files
committed
Add unit tests for HTTP request to Envoy request conversion
Signed-off-by: Pushpalanka Jayawardhana <[email protected]>
1 parent f6f9efb commit 3a85474

File tree

1 file changed

+199
-0
lines changed

1 file changed

+199
-0
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,199 @@
1+
package envoy
2+
3+
import (
4+
ext_authz_v3_core "github.com/envoyproxy/go-control-plane/envoy/config/core/v3"
5+
ext_authz_v3 "github.com/envoyproxy/go-control-plane/envoy/service/auth/v3"
6+
"github.com/stretchr/testify/assert"
7+
"google.golang.org/protobuf/types/known/structpb"
8+
"net/http"
9+
"net/url"
10+
"reflect"
11+
"strings"
12+
"testing"
13+
)
14+
15+
func TestAdaptToExtAuthRequest(t *testing.T) {
16+
tests := []struct {
17+
name string
18+
req *http.Request
19+
metadata *ext_authz_v3_core.Metadata
20+
contextExtensions map[string]string
21+
rawBody []byte
22+
want *ext_authz_v3.CheckRequest
23+
wantErr bool
24+
}{
25+
{
26+
name: "valid request with headers and metadata",
27+
req: &http.Request{
28+
Method: "GET",
29+
Host: "example-app",
30+
URL: &url.URL{Path: "/users/profile/amal#segment?param=yes"},
31+
Header: createHeaders(map[string]string{
32+
"accept": "*/*",
33+
"user-agent": "curl/7.68.0",
34+
"x-request-id": "1455bbb0-0623-4810-a2c6-df73ffd8863a",
35+
"x-forwarded-proto": "http",
36+
":authority": "example-app",
37+
}),
38+
Proto: "HTTP/1.1",
39+
ContentLength: 100,
40+
},
41+
metadata: createFilterMetadata(map[string]map[string]string{
42+
"envoy.filters.http.header_to_metadata": {"policy_type": "ingress"},
43+
}),
44+
contextExtensions: map[string]string{
45+
"key1": "value1",
46+
"key2": "value2",
47+
},
48+
rawBody: []byte(`{"key":"value"}`),
49+
want: &ext_authz_v3.CheckRequest{
50+
Attributes: &ext_authz_v3.AttributeContext{
51+
Request: &ext_authz_v3.AttributeContext_Request{
52+
Http: &ext_authz_v3.AttributeContext_HttpRequest{
53+
Host: "example-app",
54+
Method: "GET",
55+
Path: "/users/profile/amal%23segment%3Fparam=yes", //URL encoded
56+
Headers: map[string]string{
57+
"accept": "*/*",
58+
"user-agent": "curl/7.68.0",
59+
"x-request-id": "1455bbb0-0623-4810-a2c6-df73ffd8863a",
60+
"x-forwarded-proto": "http",
61+
":authority": "example-app",
62+
},
63+
RawBody: []byte(`{"key":"value"}`),
64+
},
65+
},
66+
ContextExtensions: map[string]string{
67+
"key1": "value1",
68+
"key2": "value2",
69+
},
70+
MetadataContext: createFilterMetadata(map[string]map[string]string{
71+
"envoy.filters.http.header_to_metadata": {"policy_type": "ingress"},
72+
}),
73+
},
74+
},
75+
wantErr: false,
76+
},
77+
}
78+
79+
for _, tt := range tests {
80+
t.Run(tt.name, func(t *testing.T) {
81+
got, err := AdaptToExtAuthRequest(tt.req, tt.metadata, tt.contextExtensions, tt.rawBody)
82+
83+
// Assert error
84+
assert.Equal(t, tt.wantErr, err != nil, "Unexpected error state")
85+
86+
if err == nil {
87+
// Validate the transformed request using `want` fields
88+
assert.Equal(t, tt.want.Attributes.Request.Http.Host, got.Attributes.Request.Http.Host, "Mismatch in Host")
89+
assert.Equal(t, tt.want.Attributes.Request.Http.Method, got.Attributes.Request.Http.Method, "Mismatch in Method")
90+
assert.Equal(t, tt.want.Attributes.Request.Http.Path, got.Attributes.Request.Http.Path, "Mismatch in Path")
91+
92+
// Headers comparison
93+
assert.Equal(t, tt.want.Attributes.Request.Http.Headers, got.Attributes.Request.Http.Headers, "Mismatch in Headers")
94+
95+
// Metadata comparison
96+
assert.True(t, compareMetadata(tt.want.Attributes.MetadataContext, got.Attributes.MetadataContext), "Mismatch in MetadataContext")
97+
98+
// Context extensions comparison
99+
assert.Equal(t, tt.want.Attributes.ContextExtensions, got.Attributes.ContextExtensions, "Mismatch in ContextExtensions")
100+
101+
// RawBody comparison
102+
assert.Equal(t, tt.want.Attributes.Request.Http.RawBody, got.Attributes.Request.Http.RawBody, "Mismatch in RawBody")
103+
}
104+
})
105+
}
106+
}
107+
108+
// Helper to compare Envoy metadata objects
109+
func compareMetadata(expected *ext_authz_v3_core.Metadata, actual *ext_authz_v3_core.Metadata) bool {
110+
if len(expected.FilterMetadata) != len(actual.FilterMetadata) {
111+
return false
112+
}
113+
for key, expectedStruct := range expected.FilterMetadata {
114+
actualStruct, ok := actual.FilterMetadata[key]
115+
if !ok || !reflect.DeepEqual(expectedStruct.Fields, actualStruct.Fields) {
116+
return false
117+
}
118+
}
119+
return true
120+
}
121+
122+
// Helper to generate HTTP headers
123+
func createHeaders(headerMap map[string]string) http.Header {
124+
headers := make(http.Header)
125+
for key, value := range headerMap {
126+
headers.Add(key, value)
127+
}
128+
return headers
129+
}
130+
131+
// Helper to generate Envoy filter metadata
132+
func createFilterMetadata(filterMap map[string]map[string]string) *ext_authz_v3_core.Metadata {
133+
filterMetadata := make(map[string]*structpb.Struct)
134+
for key, subMap := range filterMap {
135+
fields := make(map[string]*structpb.Value)
136+
for subKey, subValue := range subMap {
137+
fields[subKey] = structpb.NewStringValue(subValue)
138+
}
139+
filterMetadata[key] = &structpb.Struct{Fields: fields}
140+
}
141+
return &ext_authz_v3_core.Metadata{FilterMetadata: filterMetadata}
142+
}
143+
144+
func Test_validateURLForInvalidUTF8(t *testing.T) {
145+
type args struct {
146+
u *url.URL
147+
}
148+
tests := []struct {
149+
name string
150+
args args
151+
wantErr bool
152+
errMsg string
153+
}{
154+
{
155+
name: "valid UTF-8 path and query",
156+
args: args{u: parseURL(t, "https://example.com/path?query=value")},
157+
wantErr: false,
158+
},
159+
{
160+
name: "invalid UTF-8 in path",
161+
args: args{u: parseURL(t, "https://example.com/%C3%28")}, // Invalid UTF-8 sequence
162+
wantErr: true,
163+
errMsg: `invalid utf8 in path: "/\xc3("`,
164+
},
165+
{
166+
name: "valid UTF-8 path with invalid UTF-8 in query",
167+
args: args{u: parseURL(t, "https://example.com/path?query=%C3%28")}, // Invalid UTF-8 in query
168+
wantErr: true,
169+
errMsg: `invalid utf8 in query: "query=%C3%28"`,
170+
},
171+
{
172+
name: "invalid UTF-8 in query and path",
173+
args: args{u: parseURL(t, "https://example.com/%C3%28?query=%C3%28")},
174+
wantErr: true,
175+
errMsg: `invalid utf8 in path: "/\xc3("`,
176+
},
177+
}
178+
179+
for _, tt := range tests {
180+
t.Run(tt.name, func(t *testing.T) {
181+
err := validateURLForInvalidUTF8(tt.args.u)
182+
if (err != nil) != tt.wantErr {
183+
t.Errorf("validateURLForInvalidUTF8() error = %v, wantErr %v", err, tt.wantErr)
184+
}
185+
if tt.wantErr && err != nil && !strings.Contains(err.Error(), tt.errMsg) {
186+
t.Errorf("validateURLForInvalidUTF8() error message = %v, expected to contain %v", err.Error(), tt.errMsg)
187+
}
188+
})
189+
}
190+
}
191+
192+
// Helper function to parse a URL for testing
193+
func parseURL(t *testing.T, rawurl string) *url.URL {
194+
u, err := url.Parse(rawurl)
195+
if err != nil {
196+
t.Fatalf("Failed to parse URL %s: %v", rawurl, err)
197+
}
198+
return u
199+
}

0 commit comments

Comments
 (0)