github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/apiv3/route/service_test.go (about)

     1  package route
     2  
     3  import (
     4  	"bytes"
     5  	"encoding/json"
     6  	"fmt"
     7  	"net/http"
     8  	"net/http/httptest"
     9  	"net/url"
    10  	"testing"
    11  	"time"
    12  
    13  	"github.com/evergreen-ci/evergreen/apiv3"
    14  	"github.com/evergreen-ci/evergreen/apiv3/model"
    15  	"github.com/evergreen-ci/evergreen/apiv3/servicecontext"
    16  	serviceModel "github.com/evergreen-ci/evergreen/model"
    17  	"github.com/evergreen-ci/evergreen/model/host"
    18  	"github.com/evergreen-ci/evergreen/model/task"
    19  	"github.com/evergreen-ci/evergreen/model/user"
    20  	"github.com/evergreen-ci/evergreen/util"
    21  	"github.com/gorilla/context"
    22  	"github.com/gorilla/mux"
    23  	. "github.com/smartystreets/goconvey/convey"
    24  )
    25  
    26  func TestHostParseAndValidate(t *testing.T) {
    27  	Convey("With a hostGetHandler and request", t, func() {
    28  		testStatus := "testStatus"
    29  		hgh := &hostGetHandler{}
    30  		hgh, ok := hgh.Handler().(*hostGetHandler)
    31  		So(ok, ShouldBeTrue)
    32  		u := url.URL{
    33  			RawQuery: fmt.Sprintf("status=%s", testStatus),
    34  		}
    35  		r := http.Request{
    36  			URL: &u,
    37  		}
    38  		Convey("parsing request should fetch status", func() {
    39  			err := hgh.ParseAndValidate(&r)
    40  			So(err, ShouldBeNil)
    41  			hga, ok := hgh.PaginationExecutor.Args.(hostGetArgs)
    42  			So(ok, ShouldBeTrue)
    43  			So(hga.status, ShouldEqual, testStatus)
    44  		})
    45  	})
    46  }
    47  
    48  func TestHostPaginator(t *testing.T) {
    49  	numHostsInDB := 300
    50  	Convey("When paginating with a ServiceContext", t, func() {
    51  		serviceContext := servicecontext.MockServiceContext{}
    52  		Convey("and there are hosts to be found", func() {
    53  			cachedHosts := []host.Host{}
    54  			for i := 0; i < numHostsInDB; i++ {
    55  				nextHost := host.Host{
    56  					Id: fmt.Sprintf("host%d", i),
    57  				}
    58  				cachedHosts = append(cachedHosts, nextHost)
    59  			}
    60  			serviceContext.MockHostConnector.CachedHosts = cachedHosts
    61  			Convey("then finding a key in the middle of the set should produce"+
    62  				" a full next and previous page and a full set of models", func() {
    63  				hostToStartAt := 100
    64  				limit := 100
    65  				expectedHosts := []model.Model{}
    66  				for i := hostToStartAt; i < hostToStartAt+limit; i++ {
    67  					nextModelHost := &model.APIHost{
    68  						Id: model.APIString(fmt.Sprintf("host%d", i)),
    69  					}
    70  					expectedHosts = append(expectedHosts, nextModelHost)
    71  				}
    72  				expectedPages := &PageResult{
    73  					Next: &Page{
    74  						Key:      fmt.Sprintf("host%d", hostToStartAt+limit),
    75  						Limit:    limit,
    76  						Relation: "next",
    77  					},
    78  					Prev: &Page{
    79  						Key:      fmt.Sprintf("host%d", hostToStartAt-limit),
    80  						Limit:    limit,
    81  						Relation: "prev",
    82  					},
    83  				}
    84  				checkPaginatorResultMatches(hostPaginator, fmt.Sprintf("host%d", hostToStartAt),
    85  					limit, &serviceContext, hostGetArgs{}, expectedPages, expectedHosts, nil)
    86  
    87  			})
    88  			Convey("then finding a key in the near the end of the set should produce"+
    89  				" a limited next and full previous page and a full set of models", func() {
    90  				hostToStartAt := 150
    91  				limit := 100
    92  				expectedHosts := []model.Model{}
    93  				for i := hostToStartAt; i < hostToStartAt+limit; i++ {
    94  					nextModelHost := &model.APIHost{
    95  						Id: model.APIString(fmt.Sprintf("host%d", i)),
    96  					}
    97  					expectedHosts = append(expectedHosts, nextModelHost)
    98  				}
    99  				expectedPages := &PageResult{
   100  					Next: &Page{
   101  						Key:      fmt.Sprintf("host%d", hostToStartAt+limit),
   102  						Limit:    50,
   103  						Relation: "next",
   104  					},
   105  					Prev: &Page{
   106  						Key:      fmt.Sprintf("host%d", hostToStartAt-limit),
   107  						Limit:    limit,
   108  						Relation: "prev",
   109  					},
   110  				}
   111  				checkPaginatorResultMatches(hostPaginator, fmt.Sprintf("host%d", hostToStartAt),
   112  					limit, &serviceContext, hostGetArgs{}, expectedPages, expectedHosts, nil)
   113  
   114  			})
   115  			Convey("then finding a key in the near the beginning of the set should produce"+
   116  				" a full next and a limited previous page and a full set of models", func() {
   117  				hostToStartAt := 50
   118  				limit := 100
   119  				expectedHosts := []model.Model{}
   120  				for i := hostToStartAt; i < hostToStartAt+limit; i++ {
   121  					nextModelHost := &model.APIHost{
   122  						Id: model.APIString(fmt.Sprintf("host%d", i)),
   123  					}
   124  					expectedHosts = append(expectedHosts, nextModelHost)
   125  				}
   126  				expectedPages := &PageResult{
   127  					Next: &Page{
   128  						Key:      fmt.Sprintf("host%d", hostToStartAt+limit),
   129  						Limit:    limit,
   130  						Relation: "next",
   131  					},
   132  					Prev: &Page{
   133  						Key:      fmt.Sprintf("host%d", 0),
   134  						Limit:    50,
   135  						Relation: "prev",
   136  					},
   137  				}
   138  				checkPaginatorResultMatches(hostPaginator, fmt.Sprintf("host%d", hostToStartAt),
   139  					limit, &serviceContext, hostGetArgs{}, expectedPages, expectedHosts, nil)
   140  
   141  			})
   142  			Convey("then finding a key in the last page should produce only a previous"+
   143  				" page and a limited set of models", func() {
   144  				hostToStartAt := 299
   145  				limit := 100
   146  				expectedHosts := []model.Model{}
   147  				for i := hostToStartAt; i < numHostsInDB; i++ {
   148  					nextModelHost := &model.APIHost{
   149  						Id: model.APIString(fmt.Sprintf("host%d", i)),
   150  					}
   151  					expectedHosts = append(expectedHosts, nextModelHost)
   152  				}
   153  				expectedPages := &PageResult{
   154  					Prev: &Page{
   155  						Key:      fmt.Sprintf("host%d", hostToStartAt-limit),
   156  						Limit:    limit,
   157  						Relation: "prev",
   158  					},
   159  				}
   160  				checkPaginatorResultMatches(hostPaginator, fmt.Sprintf("host%d", hostToStartAt),
   161  					limit, &serviceContext, hostGetArgs{}, expectedPages, expectedHosts, nil)
   162  
   163  			})
   164  			Convey("then finding the first key should produce only a next"+
   165  				" page and a full set of models", func() {
   166  				hostToStartAt := 0
   167  				limit := 100
   168  				expectedHosts := []model.Model{}
   169  				for i := hostToStartAt; i < hostToStartAt+limit; i++ {
   170  					nextModelHost := &model.APIHost{
   171  						Id: model.APIString(fmt.Sprintf("host%d", i)),
   172  					}
   173  					expectedHosts = append(expectedHosts, nextModelHost)
   174  				}
   175  				expectedPages := &PageResult{
   176  					Next: &Page{
   177  						Key:      fmt.Sprintf("host%d", hostToStartAt+limit),
   178  						Limit:    limit,
   179  						Relation: "next",
   180  					},
   181  				}
   182  				checkPaginatorResultMatches(hostPaginator, fmt.Sprintf("host%d", hostToStartAt),
   183  					limit, &serviceContext, hostGetArgs{}, expectedPages, expectedHosts, nil)
   184  
   185  			})
   186  		})
   187  	})
   188  }
   189  
   190  func TestTasksByProjectAndCommitPaginator(t *testing.T) {
   191  	numTasks := 300
   192  	projectName := "project_1"
   193  	commit := "commit_1"
   194  	Convey("When paginating with a ServiceContext", t, func() {
   195  		serviceContext := servicecontext.MockServiceContext{}
   196  		Convey("and there are tasks to be found", func() {
   197  			cachedTasks := []task.Task{}
   198  			for i := 0; i < numTasks; i++ {
   199  				nextTask := task.Task{
   200  					Id:       fmt.Sprintf("task_%d", i),
   201  					Revision: commit,
   202  					Project:  projectName,
   203  				}
   204  				cachedTasks = append(cachedTasks, nextTask)
   205  			}
   206  			serviceContext.MockTaskConnector.CachedTasks = cachedTasks
   207  			Convey("then finding a key in the middle of the set should produce"+
   208  				" a full next and previous page and a full set of models", func() {
   209  				taskToStartAt := 100
   210  				limit := 100
   211  				expectedTasks := []model.Model{}
   212  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   213  					nextModelTask := &model.APITask{
   214  						Id:       model.APIString(fmt.Sprintf("task_%d", i)),
   215  						Revision: model.APIString(commit),
   216  						Branch:   model.APIString(projectName),
   217  					}
   218  					err := nextModelTask.BuildFromService("")
   219  					So(err, ShouldBeNil)
   220  					expectedTasks = append(expectedTasks, nextModelTask)
   221  				}
   222  				expectedPages := &PageResult{
   223  					Next: &Page{
   224  						Key:      fmt.Sprintf("task_%d", taskToStartAt+limit),
   225  						Limit:    limit,
   226  						Relation: "next",
   227  					},
   228  					Prev: &Page{
   229  						Key:      fmt.Sprintf("task_%d", taskToStartAt-limit),
   230  						Limit:    limit,
   231  						Relation: "prev",
   232  					},
   233  				}
   234  				tphArgs := tasksByProjectArgs{
   235  					projectId:  projectName,
   236  					commitHash: commit,
   237  				}
   238  				checkPaginatorResultMatches(tasksByProjectPaginator, fmt.Sprintf("task_%d", taskToStartAt),
   239  					limit, &serviceContext, tphArgs, expectedPages, expectedTasks, nil)
   240  
   241  			})
   242  			Convey("then finding a key in the near the end of the set should produce"+
   243  				" a limited next and full previous page and a full set of models", func() {
   244  				taskToStartAt := 150
   245  				limit := 100
   246  				expectedTasks := []model.Model{}
   247  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   248  					nextModelTask := &model.APITask{
   249  						Id:       model.APIString(fmt.Sprintf("task_%d", i)),
   250  						Revision: model.APIString(commit),
   251  						Branch:   model.APIString(projectName),
   252  					}
   253  					err := nextModelTask.BuildFromService("")
   254  					So(err, ShouldBeNil)
   255  					expectedTasks = append(expectedTasks, nextModelTask)
   256  				}
   257  				expectedPages := &PageResult{
   258  					Next: &Page{
   259  						Key:      fmt.Sprintf("task_%d", taskToStartAt+limit),
   260  						Limit:    50,
   261  						Relation: "next",
   262  					},
   263  					Prev: &Page{
   264  						Key:      fmt.Sprintf("task_%d", taskToStartAt-limit),
   265  						Limit:    limit,
   266  						Relation: "prev",
   267  					},
   268  				}
   269  				tphArgs := tasksByProjectArgs{
   270  					projectId:  projectName,
   271  					commitHash: commit,
   272  				}
   273  				checkPaginatorResultMatches(tasksByProjectPaginator, fmt.Sprintf("task_%d", taskToStartAt),
   274  					limit, &serviceContext, tphArgs, expectedPages, expectedTasks, nil)
   275  
   276  			})
   277  			Convey("then finding a key in the near the beginning of the set should produce"+
   278  				" a full next and a limited previous page and a full set of models", func() {
   279  				taskToStartAt := 50
   280  				limit := 100
   281  				expectedTasks := []model.Model{}
   282  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   283  					nextModelTask := &model.APITask{
   284  						Id:       model.APIString(fmt.Sprintf("task_%d", i)),
   285  						Revision: model.APIString(commit),
   286  						Branch:   model.APIString(projectName),
   287  					}
   288  					err := nextModelTask.BuildFromService("")
   289  					So(err, ShouldBeNil)
   290  					expectedTasks = append(expectedTasks, nextModelTask)
   291  				}
   292  				expectedPages := &PageResult{
   293  					Next: &Page{
   294  						Key:      fmt.Sprintf("task_%d", taskToStartAt+limit),
   295  						Limit:    limit,
   296  						Relation: "next",
   297  					},
   298  					Prev: &Page{
   299  						Key:      fmt.Sprintf("task_%d", 0),
   300  						Limit:    50,
   301  						Relation: "prev",
   302  					},
   303  				}
   304  				tphArgs := tasksByProjectArgs{
   305  					projectId:  projectName,
   306  					commitHash: commit,
   307  				}
   308  				checkPaginatorResultMatches(tasksByProjectPaginator, fmt.Sprintf("task_%d", taskToStartAt),
   309  					limit, &serviceContext, tphArgs, expectedPages, expectedTasks, nil)
   310  
   311  			})
   312  			Convey("then finding a key in the last page should produce only a previous"+
   313  				" page and a limited set of models", func() {
   314  				taskToStartAt := 299
   315  				limit := 100
   316  				expectedTasks := []model.Model{}
   317  				for i := taskToStartAt; i < numTasks; i++ {
   318  					nextModelTask := &model.APITask{
   319  						Id:       model.APIString(fmt.Sprintf("task_%d", i)),
   320  						Revision: model.APIString(commit),
   321  						Branch:   model.APIString(projectName),
   322  					}
   323  					err := nextModelTask.BuildFromService("")
   324  					So(err, ShouldBeNil)
   325  					expectedTasks = append(expectedTasks, nextModelTask)
   326  				}
   327  				expectedPages := &PageResult{
   328  					Prev: &Page{
   329  						Key:      fmt.Sprintf("task_%d", taskToStartAt-limit),
   330  						Limit:    limit,
   331  						Relation: "prev",
   332  					},
   333  				}
   334  				tphArgs := tasksByProjectArgs{
   335  					projectId:  projectName,
   336  					commitHash: commit,
   337  				}
   338  				checkPaginatorResultMatches(tasksByProjectPaginator, fmt.Sprintf("task_%d", taskToStartAt),
   339  					limit, &serviceContext, tphArgs, expectedPages, expectedTasks, nil)
   340  
   341  			})
   342  			Convey("then finding the first key should produce only a next"+
   343  				" page and a full set of models", func() {
   344  				taskToStartAt := 0
   345  				limit := 100
   346  				expectedTasks := []model.Model{}
   347  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   348  					nextModelTask := &model.APITask{
   349  						Id:       model.APIString(fmt.Sprintf("task_%d", i)),
   350  						Revision: model.APIString(commit),
   351  						Branch:   model.APIString(projectName),
   352  					}
   353  					err := nextModelTask.BuildFromService("")
   354  					So(err, ShouldBeNil)
   355  					expectedTasks = append(expectedTasks, nextModelTask)
   356  				}
   357  				expectedPages := &PageResult{
   358  					Next: &Page{
   359  						Key:      fmt.Sprintf("task_%d", taskToStartAt+limit),
   360  						Limit:    limit,
   361  						Relation: "next",
   362  					},
   363  				}
   364  				tphArgs := tasksByProjectArgs{
   365  					projectId:  projectName,
   366  					commitHash: commit,
   367  				}
   368  				checkPaginatorResultMatches(tasksByProjectPaginator, fmt.Sprintf("task_%d", taskToStartAt),
   369  					limit, &serviceContext, tphArgs, expectedPages, expectedTasks, nil)
   370  			})
   371  			Convey("when APIError is returned from DB, should percolate upward", func() {
   372  				expectedErr := apiv3.APIError{
   373  					StatusCode: http.StatusNotFound,
   374  					Message:    "not found",
   375  				}
   376  				serviceContext.MockTaskConnector.StoredError = &expectedErr
   377  				checkPaginatorResultMatches(tasksByProjectPaginator, "",
   378  					0, &serviceContext, tasksByProjectArgs{}, nil, nil, &expectedErr)
   379  
   380  			})
   381  		})
   382  	})
   383  }
   384  
   385  func TestTaskByBuildPaginator(t *testing.T) {
   386  	numTasks := 300
   387  	Convey("When paginating with a ServiceContext", t, func() {
   388  		serviceContext := servicecontext.MockServiceContext{}
   389  		Convey("and there are tasks to be found", func() {
   390  			cachedTasks := []task.Task{}
   391  			for i := 0; i < numTasks; i++ {
   392  				nextTask := task.Task{
   393  					Id: fmt.Sprintf("build%d", i),
   394  				}
   395  				cachedTasks = append(cachedTasks, nextTask)
   396  			}
   397  			serviceContext.MockTaskConnector.CachedTasks = cachedTasks
   398  			Convey("then finding a key in the middle of the set should produce"+
   399  				" a full next and previous page and a full set of models", func() {
   400  				taskToStartAt := 100
   401  				limit := 100
   402  				expectedTasks := []model.Model{}
   403  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   404  					nextModelTask := &model.APITask{
   405  						Id: model.APIString(fmt.Sprintf("build%d", i)),
   406  					}
   407  					err := nextModelTask.BuildFromService("")
   408  					So(err, ShouldBeNil)
   409  					expectedTasks = append(expectedTasks, nextModelTask)
   410  				}
   411  				expectedPages := &PageResult{
   412  					Next: &Page{
   413  						Key:      fmt.Sprintf("build%d", taskToStartAt+limit),
   414  						Limit:    limit,
   415  						Relation: "next",
   416  					},
   417  					Prev: &Page{
   418  						Key:      fmt.Sprintf("build%d", taskToStartAt-limit),
   419  						Limit:    limit,
   420  						Relation: "prev",
   421  					},
   422  				}
   423  				checkPaginatorResultMatches(tasksByBuildPaginator, fmt.Sprintf("build%d", taskToStartAt),
   424  					limit, &serviceContext, tasksByBuildArgs{}, expectedPages, expectedTasks, nil)
   425  
   426  			})
   427  			Convey("then finding a key in the near the end of the set should produce"+
   428  				" a limited next and full previous page and a full set of models", func() {
   429  				taskToStartAt := 150
   430  				limit := 100
   431  				expectedTasks := []model.Model{}
   432  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   433  					nextModelTask := &model.APITask{
   434  						Id: model.APIString(fmt.Sprintf("build%d", i)),
   435  					}
   436  					err := nextModelTask.BuildFromService("")
   437  					So(err, ShouldBeNil)
   438  					expectedTasks = append(expectedTasks, nextModelTask)
   439  				}
   440  				expectedPages := &PageResult{
   441  					Next: &Page{
   442  						Key:      fmt.Sprintf("build%d", taskToStartAt+limit),
   443  						Limit:    50,
   444  						Relation: "next",
   445  					},
   446  					Prev: &Page{
   447  						Key:      fmt.Sprintf("build%d", taskToStartAt-limit),
   448  						Limit:    limit,
   449  						Relation: "prev",
   450  					},
   451  				}
   452  				checkPaginatorResultMatches(tasksByBuildPaginator, fmt.Sprintf("build%d", taskToStartAt),
   453  					limit, &serviceContext, tasksByBuildArgs{}, expectedPages, expectedTasks, nil)
   454  
   455  			})
   456  			Convey("then finding a key in the near the beginning of the set should produce"+
   457  				" a full next and a limited previous page and a full set of models", func() {
   458  				taskToStartAt := 50
   459  				limit := 100
   460  				expectedTasks := []model.Model{}
   461  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   462  					nextModelTask := &model.APITask{
   463  						Id: model.APIString(fmt.Sprintf("build%d", i)),
   464  					}
   465  					err := nextModelTask.BuildFromService("")
   466  					So(err, ShouldBeNil)
   467  					expectedTasks = append(expectedTasks, nextModelTask)
   468  				}
   469  				expectedPages := &PageResult{
   470  					Next: &Page{
   471  						Key:      fmt.Sprintf("build%d", taskToStartAt+limit),
   472  						Limit:    limit,
   473  						Relation: "next",
   474  					},
   475  					Prev: &Page{
   476  						Key:      fmt.Sprintf("build%d", 0),
   477  						Limit:    50,
   478  						Relation: "prev",
   479  					},
   480  				}
   481  				checkPaginatorResultMatches(tasksByBuildPaginator, fmt.Sprintf("build%d", taskToStartAt),
   482  					limit, &serviceContext, tasksByBuildArgs{}, expectedPages, expectedTasks, nil)
   483  
   484  			})
   485  			Convey("then finding a key in the last page should produce only a previous"+
   486  				" page and a limited set of models", func() {
   487  				taskToStartAt := 299
   488  				limit := 100
   489  				expectedTasks := []model.Model{}
   490  				for i := taskToStartAt; i < numTasks; i++ {
   491  					nextModelTask := &model.APITask{
   492  						Id: model.APIString(fmt.Sprintf("build%d", i)),
   493  					}
   494  					err := nextModelTask.BuildFromService("")
   495  					So(err, ShouldBeNil)
   496  					expectedTasks = append(expectedTasks, nextModelTask)
   497  				}
   498  				expectedPages := &PageResult{
   499  					Prev: &Page{
   500  						Key:      fmt.Sprintf("build%d", taskToStartAt-limit),
   501  						Limit:    limit,
   502  						Relation: "prev",
   503  					},
   504  				}
   505  				checkPaginatorResultMatches(tasksByBuildPaginator, fmt.Sprintf("build%d", taskToStartAt),
   506  					limit, &serviceContext, tasksByBuildArgs{}, expectedPages, expectedTasks, nil)
   507  
   508  			})
   509  			Convey("then finding the first key should produce only a next"+
   510  				" page and a full set of models", func() {
   511  				taskToStartAt := 0
   512  				limit := 100
   513  				expectedTasks := []model.Model{}
   514  				for i := taskToStartAt; i < taskToStartAt+limit; i++ {
   515  					nextModelTask := &model.APITask{
   516  						Id: model.APIString(fmt.Sprintf("build%d", i)),
   517  					}
   518  					err := nextModelTask.BuildFromService("")
   519  					So(err, ShouldBeNil)
   520  					expectedTasks = append(expectedTasks, nextModelTask)
   521  				}
   522  				expectedPages := &PageResult{
   523  					Next: &Page{
   524  						Key:      fmt.Sprintf("build%d", taskToStartAt+limit),
   525  						Limit:    limit,
   526  						Relation: "next",
   527  					},
   528  				}
   529  				checkPaginatorResultMatches(tasksByBuildPaginator, fmt.Sprintf("build%d", taskToStartAt),
   530  					limit, &serviceContext, tasksByBuildArgs{}, expectedPages, expectedTasks, nil)
   531  
   532  			})
   533  		})
   534  	})
   535  }
   536  
   537  func TestTestPaginator(t *testing.T) {
   538  	numTests := 300
   539  	Convey("When paginating with a ServiceContext", t, func() {
   540  		serviceContext := servicecontext.MockServiceContext{}
   541  		Convey("and there are tasks with tests to be found", func() {
   542  			cachedTests := []task.TestResult{}
   543  			for i := 0; i < numTests; i++ {
   544  				status := "pass"
   545  				if i%2 == 0 {
   546  					status = "fail"
   547  				}
   548  				nextTest := task.TestResult{
   549  					TestFile: fmt.Sprintf("test%d", i),
   550  					Status:   status,
   551  				}
   552  				cachedTests = append(cachedTests, nextTest)
   553  			}
   554  			serviceContext.MockTestConnector.CachedTests = cachedTests
   555  			Convey("then finding a key in the middle of the set should produce"+
   556  				" a full next and previous page and a full set of models", func() {
   557  				testToStartAt := 100
   558  				limit := 100
   559  				expectedTests := []model.Model{}
   560  				for i := testToStartAt; i < testToStartAt+limit; i++ {
   561  					status := "pass"
   562  					if i%2 == 0 {
   563  						status = "fail"
   564  					}
   565  					nextModelTest := &model.APITest{
   566  						TestFile:  model.APIString(fmt.Sprintf("test%d", i)),
   567  						StartTime: model.APITime(time.Unix(0, 0)),
   568  						EndTime:   model.APITime(time.Unix(0, 0)),
   569  						Status:    model.APIString(status),
   570  					}
   571  					expectedTests = append(expectedTests, nextModelTest)
   572  				}
   573  				expectedPages := &PageResult{
   574  					Next: &Page{
   575  						Key:      fmt.Sprintf("test%d", testToStartAt+limit),
   576  						Limit:    limit,
   577  						Relation: "next",
   578  					},
   579  					Prev: &Page{
   580  						Key:      fmt.Sprintf("test%d", testToStartAt-limit),
   581  						Limit:    limit,
   582  						Relation: "prev",
   583  					},
   584  				}
   585  				args := testGetHandlerArgs{}
   586  				checkPaginatorResultMatches(testPaginator, fmt.Sprintf("test%d", testToStartAt),
   587  					limit, &serviceContext, args, expectedPages, expectedTests, nil)
   588  
   589  			})
   590  			Convey("then finding a key in the near the end of the set should produce"+
   591  				" a limited next and full previous page and a full set of models", func() {
   592  				testToStartAt := 150
   593  				limit := 100
   594  				expectedTests := []model.Model{}
   595  				for i := testToStartAt; i < testToStartAt+limit; i++ {
   596  					status := "pass"
   597  					if i%2 == 0 {
   598  						status = "fail"
   599  					}
   600  					nextModelTest := &model.APITest{
   601  						TestFile:  model.APIString(fmt.Sprintf("test%d", i)),
   602  						StartTime: model.APITime(time.Unix(0, 0)),
   603  						EndTime:   model.APITime(time.Unix(0, 0)),
   604  						Status:    model.APIString(status),
   605  					}
   606  					expectedTests = append(expectedTests, nextModelTest)
   607  				}
   608  				expectedPages := &PageResult{
   609  					Next: &Page{
   610  						Key:      fmt.Sprintf("test%d", testToStartAt+limit),
   611  						Limit:    50,
   612  						Relation: "next",
   613  					},
   614  					Prev: &Page{
   615  						Key:      fmt.Sprintf("test%d", testToStartAt-limit),
   616  						Limit:    limit,
   617  						Relation: "prev",
   618  					},
   619  				}
   620  				args := testGetHandlerArgs{}
   621  				checkPaginatorResultMatches(testPaginator, fmt.Sprintf("test%d", testToStartAt),
   622  					limit, &serviceContext, args, expectedPages, expectedTests, nil)
   623  
   624  			})
   625  			Convey("then finding a key in the near the beginning of the set should produce"+
   626  				" a full next and a limited previous page and a full set of models", func() {
   627  				testToStartAt := 50
   628  				limit := 100
   629  				expectedTests := []model.Model{}
   630  				for i := testToStartAt; i < testToStartAt+limit; i++ {
   631  					status := "pass"
   632  					if i%2 == 0 {
   633  						status = "fail"
   634  					}
   635  					nextModelTest := &model.APITest{
   636  						TestFile:  model.APIString(fmt.Sprintf("test%d", i)),
   637  						StartTime: model.APITime(time.Unix(0, 0)),
   638  						EndTime:   model.APITime(time.Unix(0, 0)),
   639  						Status:    model.APIString(status),
   640  					}
   641  					expectedTests = append(expectedTests, nextModelTest)
   642  				}
   643  				expectedPages := &PageResult{
   644  					Next: &Page{
   645  						Key:      fmt.Sprintf("test%d", testToStartAt+limit),
   646  						Limit:    limit,
   647  						Relation: "next",
   648  					},
   649  					Prev: &Page{
   650  						Key:      fmt.Sprintf("test%d", 0),
   651  						Limit:    50,
   652  						Relation: "prev",
   653  					},
   654  				}
   655  				args := testGetHandlerArgs{}
   656  				checkPaginatorResultMatches(testPaginator, fmt.Sprintf("test%d", testToStartAt),
   657  					limit, &serviceContext, args, expectedPages, expectedTests, nil)
   658  
   659  			})
   660  			Convey("then finding a key in the last page should produce only a previous"+
   661  				" page and a limited set of models", func() {
   662  				testToStartAt := 299
   663  				limit := 100
   664  				expectedTests := []model.Model{}
   665  				for i := testToStartAt; i < numTests; i++ {
   666  					status := "pass"
   667  					if i%2 == 0 {
   668  						status = "fail"
   669  					}
   670  					nextModelTest := &model.APITest{
   671  						TestFile:  model.APIString(fmt.Sprintf("test%d", i)),
   672  						StartTime: model.APITime(time.Unix(0, 0)),
   673  						EndTime:   model.APITime(time.Unix(0, 0)),
   674  						Status:    model.APIString(status),
   675  					}
   676  					expectedTests = append(expectedTests, nextModelTest)
   677  				}
   678  				expectedPages := &PageResult{
   679  					Prev: &Page{
   680  						Key:      fmt.Sprintf("test%d", testToStartAt-limit),
   681  						Limit:    limit,
   682  						Relation: "prev",
   683  					},
   684  				}
   685  				args := testGetHandlerArgs{}
   686  				checkPaginatorResultMatches(testPaginator, fmt.Sprintf("test%d", testToStartAt),
   687  					limit, &serviceContext, args, expectedPages, expectedTests, nil)
   688  
   689  			})
   690  			Convey("then finding the first key should produce only a next"+
   691  				" page and a full set of models", func() {
   692  				testToStartAt := 0
   693  				limit := 100
   694  				expectedTests := []model.Model{}
   695  				for i := testToStartAt; i < testToStartAt+limit; i++ {
   696  					status := "pass"
   697  					if i%2 == 0 {
   698  						status = "fail"
   699  					}
   700  					nextModelTest := &model.APITest{
   701  						TestFile:  model.APIString(fmt.Sprintf("test%d", i)),
   702  						StartTime: model.APITime(time.Unix(0, 0)),
   703  						EndTime:   model.APITime(time.Unix(0, 0)),
   704  						Status:    model.APIString(status),
   705  					}
   706  					expectedTests = append(expectedTests, nextModelTest)
   707  				}
   708  				expectedPages := &PageResult{
   709  					Next: &Page{
   710  						Key:      fmt.Sprintf("test%d", testToStartAt+limit),
   711  						Limit:    limit,
   712  						Relation: "next",
   713  					},
   714  				}
   715  				args := testGetHandlerArgs{}
   716  				checkPaginatorResultMatches(testPaginator, fmt.Sprintf("test%d", testToStartAt),
   717  					limit, &serviceContext, args, expectedPages, expectedTests, nil)
   718  			})
   719  		})
   720  	})
   721  }
   722  
   723  func TestTaskExecutionPatchPrepare(t *testing.T) {
   724  	Convey("With handler and a project context and user", t, func() {
   725  		tep := &TaskExecutionPatchHandler{}
   726  
   727  		projCtx := serviceModel.Context{
   728  			Task: &task.Task{
   729  				Id:        "testTaskId",
   730  				Priority:  0,
   731  				Activated: false,
   732  			},
   733  		}
   734  		u := user.DBUser{
   735  			Id: "testUser",
   736  		}
   737  		Convey("then should error on empty body", func() {
   738  			req, err := http.NewRequest("PATCH", "task/testTaskId", &bytes.Buffer{})
   739  			So(err, ShouldBeNil)
   740  			context.Set(req, RequestUser, &u)
   741  			context.Set(req, RequestContext, &projCtx)
   742  			err = tep.ParseAndValidate(req)
   743  			So(err, ShouldNotBeNil)
   744  			expectedErr := apiv3.APIError{
   745  				Message:    "No request body sent",
   746  				StatusCode: http.StatusBadRequest,
   747  			}
   748  			So(err, ShouldResemble, expectedErr)
   749  		})
   750  		Convey("then should error on body with wrong type", func() {
   751  			str := "nope"
   752  			badBod := &struct {
   753  				Activated *string
   754  			}{
   755  				Activated: &str,
   756  			}
   757  			res, err := json.Marshal(badBod)
   758  			So(err, ShouldBeNil)
   759  			buf := bytes.NewBuffer(res)
   760  
   761  			req, err := http.NewRequest("PATCH", "task/testTaskId", buf)
   762  			So(err, ShouldBeNil)
   763  			context.Set(req, RequestUser, &u)
   764  			context.Set(req, RequestContext, &projCtx)
   765  			err = tep.ParseAndValidate(req)
   766  			So(err, ShouldNotBeNil)
   767  			expectedErr := apiv3.APIError{
   768  				Message: fmt.Sprintf("Incorrect type given, expecting '%s' "+
   769  					"but receieved '%s'",
   770  					"bool", "string"),
   771  				StatusCode: http.StatusBadRequest,
   772  			}
   773  			So(err, ShouldResemble, expectedErr)
   774  		})
   775  		Convey("then should error when fields not set", func() {
   776  			badBod := &struct {
   777  				Activated *string
   778  			}{}
   779  			res, err := json.Marshal(badBod)
   780  			So(err, ShouldBeNil)
   781  			buf := bytes.NewBuffer(res)
   782  
   783  			req, err := http.NewRequest("PATCH", "task/testTaskId", buf)
   784  			So(err, ShouldBeNil)
   785  			context.Set(req, RequestUser, &u)
   786  			context.Set(req, RequestContext, &projCtx)
   787  			err = tep.ParseAndValidate(req)
   788  			So(err, ShouldNotBeNil)
   789  			expectedErr := apiv3.APIError{
   790  				Message:    "Must set 'activated' or 'priority'",
   791  				StatusCode: http.StatusBadRequest,
   792  			}
   793  			So(err, ShouldResemble, expectedErr)
   794  		})
   795  		Convey("then should set it's Activated and Priority field when set", func() {
   796  			goodBod := &struct {
   797  				Activated bool
   798  				Priority  int
   799  			}{
   800  				Activated: true,
   801  				Priority:  100,
   802  			}
   803  			res, err := json.Marshal(goodBod)
   804  			So(err, ShouldBeNil)
   805  			buf := bytes.NewBuffer(res)
   806  
   807  			req, err := http.NewRequest("PATCH", "task/testTaskId", buf)
   808  			So(err, ShouldBeNil)
   809  			context.Set(req, RequestUser, &u)
   810  			context.Set(req, RequestContext, &projCtx)
   811  			err = tep.ParseAndValidate(req)
   812  			So(err, ShouldBeNil)
   813  			So(*tep.Activated, ShouldBeTrue)
   814  			So(*tep.Priority, ShouldEqual, 100)
   815  
   816  			Convey("and task and user should be set", func() {
   817  				So(tep.task, ShouldNotBeNil)
   818  				So(tep.task.Id, ShouldEqual, "testTaskId")
   819  				So(tep.user.Username(), ShouldEqual, "testUser")
   820  			})
   821  		})
   822  	})
   823  }
   824  
   825  func TestTaskExecutionPatchExecute(t *testing.T) {
   826  	Convey("With a task in the DB and a ServiceContext", t, func() {
   827  		sc := servicecontext.MockServiceContext{}
   828  		testTask := task.Task{
   829  			Id:        "testTaskId",
   830  			Activated: false,
   831  			Priority:  10,
   832  		}
   833  		sc.MockTaskConnector.CachedTasks = append(sc.MockTaskConnector.CachedTasks, testTask)
   834  
   835  		Convey("then setting priority should change it's priority", func() {
   836  			act := true
   837  			var prio int64 = 100
   838  
   839  			tep := &TaskExecutionPatchHandler{
   840  				Activated: &act,
   841  				Priority:  &prio,
   842  				task: &task.Task{
   843  					Id: "testTaskId",
   844  				},
   845  				user: &user.DBUser{
   846  					Id: "testUser",
   847  				},
   848  			}
   849  			res, err := tep.Execute(&sc)
   850  			So(err, ShouldBeNil)
   851  			So(len(res.Result), ShouldEqual, 1)
   852  			resModel := res.Result[0]
   853  			resTask, ok := resModel.(*model.APITask)
   854  			So(ok, ShouldBeTrue)
   855  			So(resTask.Priority, ShouldEqual, int64(100))
   856  			So(resTask.Activated, ShouldBeTrue)
   857  			So(resTask.ActivatedBy, ShouldEqual, "testUser")
   858  		})
   859  	})
   860  }
   861  
   862  func TestTaskResetPrepare(t *testing.T) {
   863  	Convey("With handler and a project context and user", t, func() {
   864  		trh := &TaskRestartHandler{}
   865  
   866  		projCtx := serviceModel.Context{
   867  			Task: &task.Task{
   868  				Id:        "testTaskId",
   869  				Priority:  0,
   870  				Activated: false,
   871  			},
   872  			Project: &serviceModel.Project{},
   873  		}
   874  		u := user.DBUser{
   875  			Id: "testUser",
   876  		}
   877  		Convey("should error on empty project", func() {
   878  			projCtx.Project = nil
   879  			req, err := http.NewRequest("POST", "task/testTaskId/restart", &bytes.Buffer{})
   880  			So(err, ShouldBeNil)
   881  			context.Set(req, RequestUser, &u)
   882  			context.Set(req, RequestContext, &projCtx)
   883  			err = trh.ParseAndValidate(req)
   884  			So(err, ShouldNotBeNil)
   885  			expectedErr := fmt.Errorf("Unable to fetch associated project")
   886  			So(err, ShouldResemble, expectedErr)
   887  		})
   888  		Convey("then should error on empty task", func() {
   889  			projCtx.Task = nil
   890  			req, err := http.NewRequest("POST", "task/testTaskId/restart", &bytes.Buffer{})
   891  			So(err, ShouldBeNil)
   892  			context.Set(req, RequestUser, &u)
   893  			context.Set(req, RequestContext, &projCtx)
   894  			err = trh.ParseAndValidate(req)
   895  			So(err, ShouldNotBeNil)
   896  			expectedErr := apiv3.APIError{
   897  				Message:    "Task not found",
   898  				StatusCode: http.StatusNotFound,
   899  			}
   900  
   901  			So(err, ShouldResemble, expectedErr)
   902  		})
   903  	})
   904  }
   905  
   906  func TestTaskGetHandler(t *testing.T) {
   907  	Convey("With test server with a handler and mock servicecontext", t, func() {
   908  		rm := getTaskRouteManager("/tasks/{task_id}", 2)
   909  		sc := &servicecontext.MockServiceContext{}
   910  		sc.SetPrefix("rest")
   911  		r := mux.NewRouter()
   912  
   913  		Convey("and task is in the service context", func() {
   914  			sc.MockTaskConnector.CachedTasks = []task.Task{
   915  				{Id: "testTaskId"},
   916  			}
   917  			rm.Register(r, sc)
   918  			Convey("a request with a user should then return no error and a task should"+
   919  				" should be returned", func() {
   920  				req, err := http.NewRequest("GET", "/rest/v2/tasks/testTaskId", nil)
   921  				So(err, ShouldBeNil)
   922  				req.Header.Add("Api-Key", "Key")
   923  				req.Header.Add("Api-User", "User")
   924  
   925  				sc.MockUserConnector.CachedUsers = map[string]*user.DBUser{
   926  					"User": &user.DBUser{
   927  						APIKey: "Key",
   928  						Id:     "User",
   929  					},
   930  				}
   931  
   932  				rr := httptest.NewRecorder()
   933  				r.ServeHTTP(rr, req)
   934  				So(rr.Code, ShouldEqual, http.StatusOK)
   935  
   936  				res := model.APITask{}
   937  				err = json.Unmarshal(rr.Body.Bytes(), &res)
   938  				So(err, ShouldBeNil)
   939  				So(res.Id, ShouldEqual, "testTaskId")
   940  			})
   941  			Convey("a request without a user should then return a 404 error and a task should"+
   942  				" should be not returned", func() {
   943  				req, err := http.NewRequest("GET", "/rest/v2/tasks/testTaskId", nil)
   944  				So(err, ShouldBeNil)
   945  				req.Header.Add("Api-Key", "Key")
   946  				req.Header.Add("Api-User", "User")
   947  
   948  				rr := httptest.NewRecorder()
   949  				r.ServeHTTP(rr, req)
   950  				So(rr.Code, ShouldEqual, http.StatusNotFound)
   951  			})
   952  		})
   953  	})
   954  }
   955  
   956  func TestTaskResetExecute(t *testing.T) {
   957  	Convey("With a task returned by the ServiceContext", t, func() {
   958  		sc := servicecontext.MockServiceContext{}
   959  		timeNow := time.Now()
   960  		testTask := task.Task{
   961  			Id:           "testTaskId",
   962  			Activated:    false,
   963  			Secret:       "initialSecret",
   964  			DispatchTime: timeNow,
   965  		}
   966  		sc.MockTaskConnector.CachedTasks = append(sc.MockTaskConnector.CachedTasks, testTask)
   967  		Convey("and an error from the service function", func() {
   968  			sc.MockTaskConnector.StoredError = fmt.Errorf("could not reset task")
   969  
   970  			trh := &TaskRestartHandler{
   971  				taskId:   "testTaskId",
   972  				project:  &serviceModel.Project{},
   973  				username: "testUser",
   974  			}
   975  
   976  			_, err := trh.Execute(&sc)
   977  			So(err, ShouldNotBeNil)
   978  			apiErr, ok := err.(apiv3.APIError)
   979  			So(ok, ShouldBeTrue)
   980  			So(apiErr.StatusCode, ShouldEqual, http.StatusBadRequest)
   981  
   982  		})
   983  
   984  		Convey("calling TryReset should reset the task", func() {
   985  
   986  			trh := &TaskRestartHandler{
   987  				taskId:   "testTaskId",
   988  				project:  &serviceModel.Project{},
   989  				username: "testUser",
   990  			}
   991  
   992  			res, err := trh.Execute(&sc)
   993  			So(err, ShouldBeNil)
   994  			So(len(res.Result), ShouldEqual, 1)
   995  			resModel := res.Result[0]
   996  			resTask, ok := resModel.(*model.APITask)
   997  			So(ok, ShouldBeTrue)
   998  			So(resTask.Activated, ShouldBeTrue)
   999  			So(time.Time(resTask.DispatchTime), ShouldResemble, util.ZeroTime)
  1000  			dbTask, err := sc.FindTaskById("testTaskId")
  1001  			So(err, ShouldBeNil)
  1002  			So(string(dbTask.Secret), ShouldNotResemble, "initialSecret")
  1003  		})
  1004  	})
  1005  
  1006  }
  1007  
  1008  func checkPaginatorResultMatches(paginator PaginatorFunc, key string, limit int,
  1009  	sc servicecontext.ServiceContext, args interface{}, expectedPages *PageResult,
  1010  	expectedModels []model.Model, expectedErr error) {
  1011  	res, pages, err := paginator(key, limit, args, sc)
  1012  	So(err, ShouldResemble, expectedErr)
  1013  	So(len(res), ShouldEqual, len(expectedModels))
  1014  	for ix := range expectedModels {
  1015  		So(res[ix], ShouldResemble, expectedModels[ix])
  1016  	}
  1017  	So(pages, ShouldResemble, expectedPages)
  1018  }