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  }