From 4367fc4e359472db43d509f6b550535d63ef0166 Mon Sep 17 00:00:00 2001
From: "Alex Ellis (VMware)" <alexellis2@gmail.com>
Date: Fri, 24 Aug 2018 10:28:10 +0100
Subject: [PATCH] Add test coverage for buildUpstreamRequest

Signed-off-by: Alex Ellis (VMware) <alexellis2@gmail.com>
---
 gateway/build.sh                          |   2 +-
 gateway/handlers/forwarding_proxy.go      |  30 +----
 gateway/handlers/forwarding_proxy_test.go | 130 +++++++++++++++++++++-
 3 files changed, 130 insertions(+), 32 deletions(-)

diff --git a/gateway/build.sh b/gateway/build.sh
index 763879e9..37b70f12 100755
--- a/gateway/build.sh
+++ b/gateway/build.sh
@@ -19,7 +19,7 @@ if [ "$1" ] ; then
   fi
 fi
 
-NS=openfaas
+NS=alexellis
 
 echo Building $NS/gateway:$eTAG
 
diff --git a/gateway/handlers/forwarding_proxy.go b/gateway/handlers/forwarding_proxy.go
index cff99ea8..4eac6f5b 100644
--- a/gateway/handlers/forwarding_proxy.go
+++ b/gateway/handlers/forwarding_proxy.go
@@ -16,15 +16,15 @@ import (
 	"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/([^/?]+)([^?]*)")
 
 // Indicies and meta-data for functionMatcher regex parts
 const (
 	hasPathCount = 3
-	routeIndex   = 0
-	nameIndex    = 1
-	pathIndex    = 2
+	routeIndex   = 0 // routeIndex corresponds to /function/ or /async-function/
+	nameIndex    = 1 // nameIndex is the function name
+	pathIndex    = 2 // pathIndex is the path i.e. /employee/:id/
 )
 
 // HTTPNotifier notify about HTTP request/response
@@ -218,28 +218,6 @@ func (f TransparentURLPathTransformer) Transform(r *http.Request) string {
 	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.
 type FunctionPrefixTrimmingURLPathTransformer struct {
 }
diff --git a/gateway/handlers/forwarding_proxy_test.go b/gateway/handlers/forwarding_proxy_test.go
index 963d6b74..3cb50762 100644
--- a/gateway/handlers/forwarding_proxy_test.go
+++ b/gateway/handlers/forwarding_proxy_test.go
@@ -2,6 +2,7 @@ package handlers
 
 import (
 	"bytes"
+	"fmt"
 	"io/ioutil"
 	"net/http"
 	"net/url"
@@ -151,12 +152,126 @@ 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")
-	path := "/my/deep/path"
+	functionPath := "/employee/info/300"
+
+	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 := 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()
+	}
+
+}
+
+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, "/function/xyz"+path+"?code=1", reader)
+	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" {
@@ -167,6 +282,11 @@ func Test_buildUpstreamRequest_Body_Method_Query_Path(t *testing.T) {
 	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 {
@@ -191,8 +311,8 @@ func Test_buildUpstreamRequest_Body_Method_Query_Path(t *testing.T) {
 		t.Fail()
 	}
 
-	if path != upstream.URL.Path {
-		t.Errorf("URL.Path - want: %s, got: %s", path, upstream.URL.Path)
+	if functionPath != upstream.URL.Path {
+		t.Errorf("URL.Path - want: %s, got: %s", functionPath, upstream.URL.Path)
 		t.Fail()
 	}
 
-- 
GitLab