github.com/mook-as/cf-cli@v7.0.0-beta.28.0.20200120190804-b91c115fae48+incompatible/cf/util/testhelpers/net/server.go (about) 1 package net 2 3 import ( 4 "fmt" 5 "net/http" 6 "net/http/httptest" 7 "net/url" 8 "strings" 9 10 "github.com/onsi/ginkgo" 11 ) 12 13 type TestRequest struct { 14 Method string 15 Path string 16 Header http.Header 17 Matcher RequestMatcher 18 Response TestResponse 19 } 20 21 type RequestMatcher func(*http.Request) 22 23 type TestResponse struct { 24 Body string 25 Status int 26 Header http.Header 27 } 28 29 type TestHandler struct { 30 Requests []TestRequest 31 CallCount int 32 } 33 34 func (h *TestHandler) AllRequestsCalled() bool { 35 return h.CallCount == len(h.Requests) 36 } 37 38 func urlQueryContains(container, containee url.Values) bool { 39 //Cloud Controller often uses "q" as a container for search queries, which may be semantically 40 //equivalent to CC but be actually different strings. 41 42 //Example: "foo:bar;baz:qux" is semantically the same as "baz:qux;foo:bar". CC doesn't care about order. 43 44 //Therefore, we crack apart "q" params on their seperator (a colon) and compare the resulting 45 //substrings. No other params seem to use semicolon separators AND are order-dependent, so we just 46 //run all params through the same process. 47 for key := range containee { 48 49 containerValues := strings.Split(container.Get(key), ";") 50 containeeValues := strings.Split(containee.Get(key), ";") 51 52 allValuesFound := make([]bool, len(containeeValues)) 53 54 for index, expected := range containeeValues { 55 for _, actual := range containerValues { 56 if expected == actual { 57 allValuesFound[index] = true 58 break 59 } 60 } 61 } 62 for _, ok := range allValuesFound { 63 if !ok { 64 return false 65 } 66 } 67 } 68 69 return true 70 } 71 72 func (h *TestHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { 73 defer ginkgo.GinkgoRecover() 74 75 if len(h.Requests) <= h.CallCount { 76 h.logError("Index out of range! Test server called too many times. Final Request:", r.Method, r.RequestURI) 77 return 78 } 79 80 tester := h.Requests[h.CallCount] 81 h.CallCount++ 82 83 // match method 84 if tester.Method != r.Method { 85 h.logError("Method does not match.\nExpected: %s\nActual: %s", tester.Method, r.Method) 86 } 87 88 // match path 89 paths := strings.Split(tester.Path, "?") 90 if paths[0] != r.URL.Path { 91 h.logError("Path does not match.\nExpected: %s\nActual: %s", paths[0], r.URL.Path) 92 } 93 // match query string 94 if len(paths) > 1 { 95 actualValues, _ := url.ParseQuery(r.URL.RawQuery) 96 expectedValues, _ := url.ParseQuery(paths[1]) 97 98 if !urlQueryContains(actualValues, expectedValues) { 99 h.logError("Query string does not match.\nExpected: %s\nActual: %s", paths[1], r.URL.RawQuery) 100 } 101 } 102 103 for key, values := range tester.Header { 104 key = http.CanonicalHeaderKey(key) 105 actualValues := strings.Join(r.Header[key], ";") 106 expectedValues := strings.Join(values, ";") 107 108 if key == "Authorization" && !strings.Contains(actualValues, expectedValues) { 109 h.logError("%s header is not contained in actual value.\nExpected: %s\nActual: %s", key, expectedValues, actualValues) 110 } 111 if key != "Authorization" && actualValues != expectedValues { 112 h.logError("%s header did not match.\nExpected: %s\nActual: %s", key, expectedValues, actualValues) 113 } 114 } 115 116 // match custom request matcher 117 if tester.Matcher != nil { 118 tester.Matcher(r) 119 } 120 121 // set response headers 122 header := w.Header() 123 for name, values := range tester.Response.Header { 124 if len(values) < 1 { 125 continue 126 } 127 header.Set(name, values[0]) 128 } 129 130 // write response 131 w.WriteHeader(tester.Response.Status) 132 fmt.Fprintln(w, tester.Response.Body) 133 } 134 135 func NewTLSServer(requests []TestRequest) (*httptest.Server, *TestHandler) { 136 handler := &TestHandler{Requests: requests} 137 return httptest.NewTLSServer(handler), handler 138 } 139 140 func NewServer(requests []TestRequest) (*httptest.Server, *TestHandler) { 141 handler := &TestHandler{Requests: requests} 142 return httptest.NewServer(handler), handler 143 } 144 145 func (h *TestHandler) logError(msg string, args ...interface{}) { 146 println(fmt.Sprintf(msg, args...)) 147 ginkgo.Fail("failed") 148 }