Add test coverage for buildUpstreamRequest

Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
This commit is contained in:
Alex Ellis (VMware)
2018-08-24 10:28:10 +01:00
committed by Alex Ellis
parent 2f98ca8802
commit 4367fc4e35
3 changed files with 132 additions and 34 deletions

View File

@ -19,7 +19,7 @@ if [ "$1" ] ; then
fi fi
fi fi
NS=openfaas NS=alexellis
echo Building $NS/gateway:$eTAG echo Building $NS/gateway:$eTAG

View File

@ -16,15 +16,15 @@ import (
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
) )
// Parse out the service name (group 1) and rest of path (group 2). // functionMatcher parses out the service name (group 1) and rest of path (group 2).
var functionMatcher = regexp.MustCompile("^/?(?:async-)?function/([^/?]+)([^?]*)") var functionMatcher = regexp.MustCompile("^/?(?:async-)?function/([^/?]+)([^?]*)")
// Indicies and meta-data for functionMatcher regex parts // Indicies and meta-data for functionMatcher regex parts
const ( const (
hasPathCount = 3 hasPathCount = 3
routeIndex = 0 routeIndex = 0 // routeIndex corresponds to /function/ or /async-function/
nameIndex = 1 nameIndex = 1 // nameIndex is the function name
pathIndex = 2 pathIndex = 2 // pathIndex is the path i.e. /employee/:id/
) )
// HTTPNotifier notify about HTTP request/response // HTTPNotifier notify about HTTP request/response
@ -218,28 +218,6 @@ func (f TransparentURLPathTransformer) Transform(r *http.Request) string {
return r.URL.Path return r.URL.Path
} }
// FunctionPathTruncatingURLPathTransformer always truncated the path to "/".
type FunctionPathTruncatingURLPathTransformer struct {
}
// Transform always return a path of "/".
func (f FunctionPathTruncatingURLPathTransformer) Transform(r *http.Request) string {
ret := r.URL.Path
if ret != "" {
matcher := functionMatcher.Copy()
parts := matcher.FindStringSubmatch(ret)
// In the following regex, in the case of a match the r.URL.Path will be at `0`,
// the function name at `1` and the rest of the path (the part we are interested in)
// at `2`. For this transformer, all we need to do is confirm it is a function.
if len(parts) == hasPathCount {
ret = "/"
}
}
return ret
}
// FunctionPrefixTrimmingURLPathTransformer removes the "/function/servicename/" prefix from the URL path. // FunctionPrefixTrimmingURLPathTransformer removes the "/function/servicename/" prefix from the URL path.
type FunctionPrefixTrimmingURLPathTransformer struct { type FunctionPrefixTrimmingURLPathTransformer struct {
} }

View File

@ -2,6 +2,7 @@ package handlers
import ( import (
"bytes" "bytes"
"fmt"
"io/ioutil" "io/ioutil"
"net/http" "net/http"
"net/url" "net/url"
@ -151,22 +152,31 @@ func Test_getServiceName(t *testing.T) {
} }
} }
func Test_buildUpstreamRequest_Body_Method_Query_Path(t *testing.T) { func Test_buildUpstreamRequest_WithPathNoQuery(t *testing.T) {
srcBytes := []byte("hello world") srcBytes := []byte("hello world")
path := "/my/deep/path" functionPath := "/employee/info/300"
requestPath := fmt.Sprintf("/function/xyz%s", functionPath)
reader := bytes.NewReader(srcBytes) reader := bytes.NewReader(srcBytes)
request, _ := http.NewRequest(http.MethodPost, "/function/xyz"+path+"?code=1", reader) request, _ := http.NewRequest(http.MethodPost, requestPath, reader)
request.Header.Set("X-Source", "unit-test") request.Header.Set("X-Source", "unit-test")
if request.URL.RawQuery != "code=1" { queryWant := ""
t.Errorf("Query - want: %s, got: %s", "code=1", request.URL.RawQuery) if request.URL.RawQuery != queryWant {
t.Errorf("Query - want: %s, got: %s", queryWant, request.URL.RawQuery)
t.Fail() t.Fail()
} }
transformer := FunctionPrefixTrimmingURLPathTransformer{} transformer := FunctionPrefixTrimmingURLPathTransformer{}
transformedPath := transformer.Transform(request) transformedPath := transformer.Transform(request)
wantTransformedPath := functionPath
if transformedPath != wantTransformedPath {
t.Errorf("transformedPath want: %s, got %s", wantTransformedPath, transformedPath)
}
upstream := buildUpstreamRequest(request, "http://xyz:8080", transformedPath) upstream := buildUpstreamRequest(request, "http://xyz:8080", transformedPath)
if request.Method != upstream.Method { if request.Method != upstream.Method {
@ -191,8 +201,118 @@ func Test_buildUpstreamRequest_Body_Method_Query_Path(t *testing.T) {
t.Fail() t.Fail()
} }
if path != upstream.URL.Path { if functionPath != upstream.URL.Path {
t.Errorf("URL.Path - want: %s, got: %s", path, upstream.URL.Path) t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
t.Fail()
}
}
func Test_buildUpstreamRequest_WithNoPathNoQuery(t *testing.T) {
srcBytes := []byte("hello world")
functionPath := "/"
requestPath := fmt.Sprintf("/function/xyz%s", functionPath)
reader := bytes.NewReader(srcBytes)
request, _ := http.NewRequest(http.MethodPost, requestPath, reader)
request.Header.Set("X-Source", "unit-test")
queryWant := ""
if request.URL.RawQuery != queryWant {
t.Errorf("Query - want: %s, got: %s", queryWant, request.URL.RawQuery)
t.Fail()
}
transformer := FunctionPrefixTrimmingURLPathTransformer{}
transformedPath := transformer.Transform(request)
wantTransformedPath := "/"
if transformedPath != wantTransformedPath {
t.Errorf("transformedPath want: %s, got %s", wantTransformedPath, transformedPath)
}
upstream := buildUpstreamRequest(request, "http://xyz:8080", transformedPath)
if request.Method != upstream.Method {
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
t.Fail()
}
upstreamBytes, _ := ioutil.ReadAll(upstream.Body)
if string(upstreamBytes) != string(srcBytes) {
t.Errorf("Body - want: %s, got: %s", string(upstreamBytes), string(srcBytes))
t.Fail()
}
if request.Header.Get("X-Source") != upstream.Header.Get("X-Source") {
t.Errorf("Header X-Source - want: %s, got: %s", request.Header.Get("X-Source"), upstream.Header.Get("X-Source"))
t.Fail()
}
if request.URL.RawQuery != upstream.URL.RawQuery {
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
t.Fail()
}
if functionPath != upstream.URL.Path {
t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
t.Fail()
}
}
func Test_buildUpstreamRequest_WithPathAndQuery(t *testing.T) {
srcBytes := []byte("hello world")
functionPath := "/employee/info/300"
requestPath := fmt.Sprintf("/function/xyz%s?code=1", functionPath)
reader := bytes.NewReader(srcBytes)
request, _ := http.NewRequest(http.MethodPost, requestPath, reader)
request.Header.Set("X-Source", "unit-test")
if request.URL.RawQuery != "code=1" {
t.Errorf("Query - want: %s, got: %s", "code=1", request.URL.RawQuery)
t.Fail()
}
transformer := FunctionPrefixTrimmingURLPathTransformer{}
transformedPath := transformer.Transform(request)
wantTransformedPath := functionPath
if transformedPath != wantTransformedPath {
t.Errorf("transformedPath want: %s, got %s", wantTransformedPath, transformedPath)
}
upstream := buildUpstreamRequest(request, "http://xyz:8080", transformedPath)
if request.Method != upstream.Method {
t.Errorf("Method - want: %s, got: %s", request.Method, upstream.Method)
t.Fail()
}
upstreamBytes, _ := ioutil.ReadAll(upstream.Body)
if string(upstreamBytes) != string(srcBytes) {
t.Errorf("Body - want: %s, got: %s", string(upstreamBytes), string(srcBytes))
t.Fail()
}
if request.Header.Get("X-Source") != upstream.Header.Get("X-Source") {
t.Errorf("Header X-Source - want: %s, got: %s", request.Header.Get("X-Source"), upstream.Header.Get("X-Source"))
t.Fail()
}
if request.URL.RawQuery != upstream.URL.RawQuery {
t.Errorf("URL.RawQuery - want: %s, got: %s", request.URL.RawQuery, upstream.URL.RawQuery)
t.Fail()
}
if functionPath != upstream.URL.Path {
t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
t.Fail() t.Fail()
} }