github.com/iron-io/functions@v0.0.0-20180820112432-d59d7d1c40b2/api/server/runner_test.go (about) 1 // +build server 2 3 package server 4 5 import ( 6 "bytes" 7 "context" 8 "net/http" 9 "strings" 10 "testing" 11 12 "github.com/iron-io/functions/api/datastore" 13 "github.com/iron-io/functions/api/models" 14 "github.com/iron-io/functions/api/mqs" 15 "github.com/iron-io/functions/api/runner" 16 "github.com/iron-io/functions/api/runner/task" 17 ) 18 19 func testRunner(t *testing.T) (*runner.Runner, context.CancelFunc) { 20 ctx, cancel := context.WithCancel(context.Background()) 21 r, err := runner.New(ctx, runner.NewFuncLogger(), runner.NewMetricLogger()) 22 if err != nil { 23 t.Fatal("Test: failed to create new runner") 24 } 25 return r, cancel 26 } 27 28 func TestRouteRunnerGet(t *testing.T) { 29 buf := setLogBuffer() 30 tasks := mockTasksConduit() 31 32 rnr, cancel := testRunner(t) 33 defer cancel() 34 35 srv := testServer(datastore.NewMockInit( 36 []*models.App{ 37 {Name: "myapp", Config: models.Config{}}, 38 }, nil, 39 ), &mqs.Mock{}, rnr, tasks) 40 41 for i, test := range []struct { 42 path string 43 body string 44 expectedCode int 45 expectedError error 46 }{ 47 {"/route", "", http.StatusNotFound, nil}, 48 {"/r/app/route", "", http.StatusNotFound, models.ErrAppsNotFound}, 49 {"/r/myapp/route", "", http.StatusNotFound, models.ErrRunnerRouteNotFound}, 50 } { 51 _, rec := routerRequest(t, srv.Router, "GET", test.path, nil) 52 53 if rec.Code != test.expectedCode { 54 t.Log(buf.String()) 55 t.Errorf("Test %d: Expected status code to be %d but was %d", 56 i, test.expectedCode, rec.Code) 57 } 58 59 if test.expectedError != nil { 60 resp := getErrorResponse(t, rec) 61 62 if !strings.Contains(resp.Error.Message, test.expectedError.Error()) { 63 t.Log(buf.String()) 64 t.Errorf("Test %d: Expected error message to have `%s`", 65 i, test.expectedError.Error()) 66 } 67 } 68 } 69 } 70 71 func TestRouteRunnerPost(t *testing.T) { 72 buf := setLogBuffer() 73 tasks := mockTasksConduit() 74 75 rnr, cancel := testRunner(t) 76 defer cancel() 77 78 srv := testServer(datastore.NewMockInit( 79 []*models.App{ 80 {Name: "myapp", Config: models.Config{}}, 81 }, nil, 82 ), &mqs.Mock{}, rnr, tasks) 83 84 for i, test := range []struct { 85 path string 86 body string 87 expectedCode int 88 expectedError error 89 }{ 90 {"/route", `{ "payload": "" }`, http.StatusNotFound, nil}, 91 {"/r/app/route", `{ "payload": "" }`, http.StatusNotFound, models.ErrAppsNotFound}, 92 {"/r/myapp/route", `{ "payload": "" }`, http.StatusNotFound, models.ErrRunnerRouteNotFound}, 93 } { 94 body := bytes.NewBuffer([]byte(test.body)) 95 _, rec := routerRequest(t, srv.Router, "POST", test.path, body) 96 97 if rec.Code != test.expectedCode { 98 t.Log(buf.String()) 99 t.Errorf("Test %d: Expected status code to be %d but was %d", 100 i, test.expectedCode, rec.Code) 101 } 102 103 if test.expectedError != nil { 104 resp := getErrorResponse(t, rec) 105 respMsg := resp.Error.Message 106 expMsg := test.expectedError.Error() 107 if respMsg != expMsg && !strings.Contains(respMsg, expMsg) { 108 t.Log(buf.String()) 109 t.Errorf("Test %d: Expected error message to have `%s`", 110 i, test.expectedError.Error()) 111 } 112 } 113 } 114 } 115 116 func TestRouteRunnerExecution(t *testing.T) { 117 buf := setLogBuffer() 118 119 tasks := make(chan task.Request) 120 ctx, cancel := context.WithCancel(context.Background()) 121 defer cancel() 122 123 rnr, cancelrnr := testRunner(t) 124 defer cancelrnr() 125 126 go runner.StartWorkers(ctx, rnr, tasks) 127 128 srv := testServer(datastore.NewMockInit( 129 []*models.App{ 130 {Name: "myapp", Config: models.Config{}}, 131 }, 132 []*models.Route{ 133 {Path: "/myroute", AppName: "myapp", Image: "iron/hello", Headers: map[string][]string{"X-Function": {"Test"}}}, 134 {Path: "/myerror", AppName: "myapp", Image: "iron/error", Headers: map[string][]string{"X-Function": {"Test"}}}, 135 }, 136 ), &mqs.Mock{}, rnr, tasks) 137 138 for i, test := range []struct { 139 path string 140 body string 141 method string 142 expectedCode int 143 expectedHeaders map[string][]string 144 }{ 145 {"/r/myapp/myroute", ``, "GET", http.StatusOK, map[string][]string{"X-Function": {"Test"}}}, 146 {"/r/myapp/myerror", ``, "GET", http.StatusInternalServerError, map[string][]string{"X-Function": {"Test"}}}, 147 148 // Added same tests again to check if time is reduced by the auth cache 149 {"/r/myapp/myroute", ``, "GET", http.StatusOK, map[string][]string{"X-Function": {"Test"}}}, 150 {"/r/myapp/myerror", ``, "GET", http.StatusInternalServerError, map[string][]string{"X-Function": {"Test"}}}, 151 } { 152 body := strings.NewReader(test.body) 153 _, rec := routerRequest(t, srv.Router, test.method, test.path, body) 154 155 if rec.Code != test.expectedCode { 156 t.Log(buf.String()) 157 t.Errorf("Test %d: Expected status code to be %d but was %d", 158 i, test.expectedCode, rec.Code) 159 } 160 161 if test.expectedHeaders == nil { 162 continue 163 } 164 for name, header := range test.expectedHeaders { 165 if header[0] != rec.Header().Get(name) { 166 t.Log(buf.String()) 167 t.Errorf("Test %d: Expected header `%s` to be %s but was %s", 168 i, name, header[0], rec.Header().Get(name)) 169 } 170 } 171 } 172 } 173 174 func TestRouteRunnerTimeout(t *testing.T) { 175 t.Skip("doesn't work on old Ubuntu") 176 buf := setLogBuffer() 177 178 tasks := make(chan task.Request) 179 ctx, cancel := context.WithCancel(context.Background()) 180 defer cancel() 181 182 rnr, cancelrnr := testRunner(t) 183 defer cancelrnr() 184 go runner.StartWorkers(ctx, rnr, tasks) 185 186 srv := testServer(datastore.NewMockInit( 187 []*models.App{ 188 {Name: "myapp", Config: models.Config{}}, 189 }, 190 []*models.Route{ 191 {Path: "/sleeper", AppName: "myapp", Image: "iron/sleeper", Timeout: 1}, 192 }, 193 ), &mqs.Mock{}, rnr, tasks) 194 195 for i, test := range []struct { 196 path string 197 body string 198 method string 199 expectedCode int 200 expectedHeaders map[string][]string 201 }{ 202 {"/r/myapp/sleeper", `{"sleep": 0}`, "POST", http.StatusOK, nil}, 203 {"/r/myapp/sleeper", `{"sleep": 2}`, "POST", http.StatusGatewayTimeout, nil}, 204 } { 205 body := strings.NewReader(test.body) 206 _, rec := routerRequest(t, srv.Router, test.method, test.path, body) 207 208 if rec.Code != test.expectedCode { 209 t.Log(buf.String()) 210 t.Errorf("Test %d: Expected status code to be %d but was %d", 211 i, test.expectedCode, rec.Code) 212 } 213 214 if test.expectedHeaders == nil { 215 continue 216 } 217 for name, header := range test.expectedHeaders { 218 if header[0] != rec.Header().Get(name) { 219 t.Log(buf.String()) 220 t.Errorf("Test %d: Expected header `%s` to be %s but was %s", 221 i, name, header[0], rec.Header().Get(name)) 222 } 223 } 224 } 225 } 226 227 func TestMatchRoute(t *testing.T) { 228 buf := setLogBuffer() 229 for i, test := range []struct { 230 baseRoute string 231 route string 232 expectedParams []Param 233 }{ 234 {"/myroute/", `/myroute/`, nil}, 235 {"/myroute/:mybigparam", `/myroute/1`, []Param{{"mybigparam", "1"}}}, 236 {"/:param/*test", `/1/2`, []Param{{"param", "1"}, {"test", "/2"}}}, 237 } { 238 if params, match := matchRoute(test.baseRoute, test.route); match { 239 if test.expectedParams != nil { 240 for j, param := range test.expectedParams { 241 if params[j].Key != param.Key || params[j].Value != param.Value { 242 t.Log(buf.String()) 243 t.Errorf("Test %d: expected param %d, key = %s, value = %s", i, j, param.Key, param.Value) 244 } 245 } 246 } 247 } else { 248 t.Log(buf.String()) 249 t.Errorf("Test %d: %s should match %s", i, test.route, test.baseRoute) 250 } 251 } 252 }