func TestExistingFailedTaskTriggers(t *testing.T) { Convey("With a previously failed instance of task in the database", t, func() { db.Clear(task.Collection) db.Clear(model.ProjectRefCollection) db.Clear(version.Collection) So(testProject.Insert(), ShouldBeNil) So(testVersion.Insert(), ShouldBeNil) testTask.Status = evergreen.TaskFailed err := testTask.Insert() So(err, ShouldBeNil) Convey("a newly failed task should trigger TaskFailed but *not* TaskFailTransition", func() { t2 := &task.Task{ Id: "testTask2", Status: evergreen.TaskFailed, DisplayName: testTask.DisplayName, Project: testTask.Project, BuildVariant: testTask.BuildVariant, Version: "testVersion2", RevisionOrderNumber: testTask.RevisionOrderNumber + 2, } ctx, err := getTaskTriggerContext(t2) So(err, ShouldBeNil) triggers, err := getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(len(triggers), ShouldEqual, 1) So(triggers[0].Id(), ShouldEqual, TaskFailed{}.Id()) }) }) }
func TestPatchPluginAPI(t *testing.T) { testConfig := evergreen.TestConfig() cwd := testutil.GetDirectoryOfFile() Convey("With a running api server and installed plugin", t, func() { registry := plugin.NewSimpleRegistry() gitPlugin := &GitPlugin{} err := registry.Register(gitPlugin) testutil.HandleTestingErr(err, t, "Couldn't register patch plugin") server, err := service.CreateTestServer(testConfig, nil, plugin.APIPlugins, false) testutil.HandleTestingErr(err, t, "Couldn't set up testing server") taskConfig, _ := plugintest.CreateTestConfig(filepath.Join(cwd, "testdata", "plugin_patch.yml"), t) testCommand := GitGetProjectCommand{Directory: "dir"} _, _, err = plugintest.SetupAPITestData("testTask", filepath.Join(cwd, "testdata", "testmodule.patch"), t) testutil.HandleTestingErr(err, t, "Couldn't set up test documents") testTask, err := task.FindOne(task.ById("testTaskId")) testutil.HandleTestingErr(err, t, "Couldn't set up test patch task") sliceAppender := &evergreen.SliceAppender{[]*slogger.Log{}} logger := agentutil.NewTestLogger(sliceAppender) Convey("calls to existing tasks with patches should succeed", func() { httpCom := plugintest.TestAgentCommunicator(testTask.Id, testTask.Secret, server.URL) pluginCom := &comm.TaskJSONCommunicator{gitPlugin.Name(), httpCom} patch, err := testCommand.GetPatch(taskConfig, pluginCom, logger) So(err, ShouldBeNil) So(patch, ShouldNotBeNil) testutil.HandleTestingErr(db.Clear(version.Collection), t, "unable to clear versions collection") }) Convey("calls to non-existing tasks should fail", func() { v := version.Version{Id: ""} testutil.HandleTestingErr(v.Insert(), t, "Couldn't insert dummy version") httpCom := plugintest.TestAgentCommunicator("BAD_TASK_ID", "", server.URL) pluginCom := &comm.TaskJSONCommunicator{gitPlugin.Name(), httpCom} patch, err := testCommand.GetPatch(taskConfig, pluginCom, logger) So(err.Error(), ShouldContainSubstring, "not found") So(err, ShouldNotBeNil) So(patch, ShouldBeNil) testutil.HandleTestingErr(db.Clear(version.Collection), t, "unable to clear versions collection") }) Convey("calls to existing tasks without patches should fail", func() { noPatchTask := task.Task{Id: "noPatchTask", BuildId: "a"} testutil.HandleTestingErr(noPatchTask.Insert(), t, "Couldn't insert patch task") noPatchVersion := version.Version{Id: "noPatchVersion", BuildIds: []string{"a"}} testutil.HandleTestingErr(noPatchVersion.Insert(), t, "Couldn't insert patch version") v := version.Version{Id: ""} testutil.HandleTestingErr(v.Insert(), t, "Couldn't insert dummy version") httpCom := plugintest.TestAgentCommunicator(noPatchTask.Id, "", server.URL) pluginCom := &comm.TaskJSONCommunicator{gitPlugin.Name(), httpCom} patch, err := testCommand.GetPatch(taskConfig, pluginCom, logger) So(err, ShouldNotBeNil) So(err.Error(), ShouldContainSubstring, "no patch found for task") So(patch, ShouldBeNil) testutil.HandleTestingErr(db.Clear(version.Collection), t, "unable to clear versions collection") }) }) }
func setupCLITestHarness() cliTestHarness { // create a test API server testServer, err := service.CreateTestServer(testConfig, nil, plugin.APIPlugins, true) So(err, ShouldBeNil) So( db.ClearCollections( task.Collection, build.Collection, user.Collection, patch.Collection, model.ProjectRefCollection, artifact.Collection, ), ShouldBeNil) So(db.Clear(patch.Collection), ShouldBeNil) So(db.Clear(model.ProjectRefCollection), ShouldBeNil) So((&user.DBUser{Id: "testuser", APIKey: "testapikey", EmailAddress: "*****@*****.**"}).Insert(), ShouldBeNil) localConfBytes, err := ioutil.ReadFile(filepath.Join(testutil.GetDirectoryOfFile(), "testdata", "sample.yml")) So(err, ShouldBeNil) projectRef := &model.ProjectRef{ Identifier: "sample", Owner: "evergreen-ci", Repo: "sample", RepoKind: "github", Branch: "master", RemotePath: "evergreen.yml", LocalConfig: string(localConfBytes), Enabled: true, BatchTime: 180, } So(projectRef.Insert(), ShouldBeNil) // create a settings file for the command line client settings := model.CLISettings{ APIServerHost: testServer.URL + "/api", UIServerHost: "http://dev-evg.mongodb.com", APIKey: "testapikey", User: "******", } settingsFile, err := ioutil.TempFile("", "settings") So(err, ShouldBeNil) settingsBytes, err := yaml.Marshal(settings) So(err, ShouldBeNil) _, err = settingsFile.Write(settingsBytes) So(err, ShouldBeNil) settingsFile.Close() return cliTestHarness{testServer, settingsFile.Name()} }
func TestSpawnExpireWarningTrigger(t *testing.T) { Convey("With a spawnhost due to expire in two hours", t, func() { db.Clear(host.Collection) testHost := host.Host{ Id: "testhost", StartedBy: "test_user", Status: "running", ExpirationTime: time.Now().Add(1 * time.Hour), } trigger := SpawnTwoHourWarning{} ctx := triggerContext{host: &testHost} shouldExec, err := trigger.ShouldExecute(ctx) So(err, ShouldBeNil) So(shouldExec, ShouldBeTrue) // run bookkeeping err = storeTriggerBookkeeping(ctx, []Trigger{trigger}) So(err, ShouldBeNil) // should exec should now return false shouldExec, err = trigger.ShouldExecute(ctx) So(err, ShouldBeNil) So(shouldExec, ShouldBeFalse) }) }
func TestGenericBuildUpdating(t *testing.T) { Convey("When updating builds", t, func() { Reset(func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error clearing '%v' collection", Collection) }) Convey("updating a single build should update the specified build"+ " in the database", func() { buildOne := &Build{Id: "buildOne"} So(buildOne.Insert(), ShouldBeNil) err := UpdateOne( bson.M{IdKey: buildOne.Id}, bson.M{"$set": bson.M{ProjectKey: "blah"}}, ) So(err, ShouldBeNil) buildOne, err = FindOne(ById(buildOne.Id)) So(err, ShouldBeNil) So(buildOne.Project, ShouldEqual, "blah") }) }) }
func TestFindTasksByIds(t *testing.T) { Convey("When calling FindTasksByIds...", t, func() { So(db.Clear(TasksCollection), ShouldBeNil) Convey("only tasks with the specified ids should be returned", func() { tasks := []Task{ Task{ Id: "one", }, Task{ Id: "two", }, Task{ Id: "three", }, } for _, task := range tasks { So(task.Insert(), ShouldBeNil) } dbTasks, err := FindTasksByIds([]string{"one", "two"}) So(err, ShouldBeNil) So(len(dbTasks), ShouldEqual, 2) So(dbTasks[0].Id, ShouldNotEqual, "three") So(dbTasks[1].Id, ShouldNotEqual, "three") }) }) }
func TestTaskSetPriority(t *testing.T) { Convey("With a task", t, func() { testutil.HandleTestingErr(db.Clear(TasksCollection), t, "Error clearing"+ " '%v' collection", TasksCollection) task := &Task{ Id: "task", } So(task.Insert(), ShouldBeNil) Convey("setting its priority should update it both in-memory"+ " and in the database", func() { So(task.SetPriority(1), ShouldBeNil) So(task.Priority, ShouldEqual, 1) task, err := FindTask(task.Id) So(err, ShouldBeNil) So(task, ShouldNotBeNil) So(task.Priority, ShouldEqual, 1) }) }) }
func TestFindOneProjectRef(t *testing.T) { Convey("With an existing repository ref", t, func() { testutil.HandleTestingErr(db.Clear(ProjectRefCollection), t, "Error clearing collection") projectRef := &ProjectRef{ Owner: "mongodb", Repo: "mci", Branch: "master", RepoKind: "github", Enabled: true, BatchTime: 10, Identifier: "ident", } Convey("all fields should be returned accurately for the "+ "corresponding project ref", func() { So(projectRef.Insert(), ShouldBeNil) projectRefFromDB, err := FindOneProjectRef("ident") So(err, ShouldBeNil) So(projectRefFromDB, ShouldNotEqual, nil) So(projectRefFromDB.Owner, ShouldEqual, "mongodb") So(projectRefFromDB.Repo, ShouldEqual, "mci") So(projectRefFromDB.Branch, ShouldEqual, "master") So(projectRefFromDB.RepoKind, ShouldEqual, "github") So(projectRefFromDB.Enabled, ShouldEqual, true) So(projectRefFromDB.BatchTime, ShouldEqual, 10) So(projectRefFromDB.Identifier, ShouldEqual, "ident") }) }) }
func TestBuildMarkFinished(t *testing.T) { Convey("With a build", t, func() { testutil.HandleTestingErr(db.Clear(build.Collection), t, "Error clearing"+ " '%v' collection", build.Collection) startTime := time.Now() b := &build.Build{ Id: "build", StartTime: startTime, } So(b.Insert(), ShouldBeNil) Convey("marking it as finished should update the status,"+ " finish time, and duration, both in memory and in the"+ " database", func() { finishTime := time.Now() So(b.MarkFinished(evergreen.BuildSucceeded, finishTime), ShouldBeNil) So(b.Status, ShouldEqual, evergreen.BuildSucceeded) So(b.FinishTime.Equal(finishTime), ShouldBeTrue) So(b.TimeTaken, ShouldEqual, finishTime.Sub(startTime)) // refresh from db and check again b, err := build.FindOne(build.ById(b.Id)) So(err, ShouldBeNil) So(b.Status, ShouldEqual, evergreen.BuildSucceeded) So(b.FinishTime.Round(time.Second).Equal( finishTime.Round(time.Second)), ShouldBeTrue) So(b.TimeTaken, ShouldEqual, finishTime.Sub(startTime)) }) }) }
func TestFindLastPassingVersionForBuildVariants(t *testing.T) { Convey("works", t, func() { So(db.Clear(TaskQueuesCollection), ShouldBeNil) project := "MyProject" bv1 := "linux" bv2 := "windows" projectObj := Project{ Identifier: project, } insertVersion("1", 1, project) insertVersion("2", 2, project) insertVersion("3", 3, project) insertBuild("1a", project, bv1, evergreen.BuildSucceeded, 1) insertBuild("1b", project, bv2, evergreen.BuildSucceeded, 1) insertBuild("2a", project, bv1, evergreen.BuildSucceeded, 2) insertBuild("2b", project, bv2, evergreen.BuildSucceeded, 2) insertBuild("3a", project, bv1, evergreen.BuildSucceeded, 3) insertBuild("3b", project, bv2, evergreen.BuildFailed, 3) version, err := FindLastPassingVersionForBuildVariants(projectObj, []string{bv1, bv2}) So(err, ShouldBeNil) So(version, ShouldNotBeNil) So(version.Id, ShouldEqual, "2") So(version.RevisionOrderNumber, ShouldEqual, 2) }) }
func TestCheckProjectSemantics(t *testing.T) { Convey("When validating a project's semantics", t, func() { Convey("if the project passes all of the validation funcs, no errors"+ " should be returned", func() { distros := []distro.Distro{ {Id: "test-distro-one"}, {Id: "test-distro-two"}, } for _, d := range distros { So(d.Insert(), ShouldBeNil) } projectRef := &model.ProjectRef{ Identifier: "project_test", LocalConfig: "test: testing", } project, err := model.FindProject("", projectRef) So(err, ShouldBeNil) So(CheckProjectSemantics(project), ShouldResemble, []ValidationError{}) }) Reset(func() { db.Clear(distro.Collection) }) }) }
func TestBuildMarkStarted(t *testing.T) { Convey("With a build", t, func() { testutil.HandleTestingErr(db.Clear(build.Collection), t, "Error clearing"+ " '%v' collection", build.Collection) b := &build.Build{ Id: "build", Status: evergreen.BuildCreated, } So(b.Insert(), ShouldBeNil) Convey("marking it as started should update the status and"+ " start time, both in memory and in the database", func() { startTime := time.Now() So(build.TryMarkStarted(b.Id, startTime), ShouldBeNil) // refresh from db and check again b, err := build.FindOne(build.ById(b.Id)) So(err, ShouldBeNil) So(b.Status, ShouldEqual, evergreen.BuildStarted) So(b.StartTime.Round(time.Second).Equal( startTime.Round(time.Second)), ShouldBeTrue) }) }) }
func TestFindRunningSpawnedHosts(t *testing.T) { testConfig := evergreen.TestConfig() db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig)) testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) Convey("With calling FindRunningSpawnedHosts...", t, func() { Convey("if there are no spawned hosts, nothing should be returned", func() { spawnedHosts, err := Find(IsRunningAndSpawned) So(err, ShouldBeNil) // make sure we only returned no document So(len(spawnedHosts), ShouldEqual, 0) }) Convey("if there are spawned hosts, they should be returned", func() { host := &Host{} host.Id = "spawned-1" host.Status = "running" host.StartedBy = "user1" testutil.HandleTestingErr(host.Insert(), t, "error from "+ "FindRunningSpawnedHosts") spawnedHosts, err := Find(IsRunningAndSpawned) testutil.HandleTestingErr(err, t, "error from "+ "FindRunningSpawnedHosts: %v", err) // make sure we only returned no document So(len(spawnedHosts), ShouldEqual, 1) }) }) }
func TestHostSetRunningTask(t *testing.T) { Convey("With a host", t, func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) host := &Host{ Id: "hostOne", } So(host.Insert(), ShouldBeNil) Convey("setting the running task for the host should set the running"+ " task and task dispatch time for both the in-memory and database"+ " copies of the host", func() { taskDispatchTime := time.Now() So(host.SetRunningTask("taskId", "c", taskDispatchTime), ShouldBeNil) So(host.RunningTask, ShouldEqual, "taskId") So(host.AgentRevision, ShouldEqual, "c") So(host.TaskDispatchTime.Round(time.Second).Equal( taskDispatchTime.Round(time.Second)), ShouldBeTrue) host, err := FindOne(ById(host.Id)) So(err, ShouldBeNil) So(host.RunningTask, ShouldEqual, "taskId") So(host.AgentRevision, ShouldEqual, "c") So(host.TaskDispatchTime.Round(time.Second).Equal( taskDispatchTime.Round(time.Second)), ShouldBeTrue) }) }) }
func TestNoteStorage(t *testing.T) { Convey("With a test note to save", t, func() { db.Clear(NotesCollection) n := Note{ TaskId: "t1", UnixNanoTime: time.Now().UnixNano(), Content: "test note", } Convey("saving the note should work without error", func() { So(n.Upsert(), ShouldBeNil) Convey("the note should be retrievable", func() { n2, err := NoteForTask("t1") So(err, ShouldBeNil) So(n2, ShouldNotBeNil) So(*n2, ShouldResemble, n) }) Convey("saving the note again should overwrite the existing note", func() { n3 := n n3.Content = "new content" So(n3.Upsert(), ShouldBeNil) n4, err := NoteForTask("t1") So(err, ShouldBeNil) So(n4, ShouldNotBeNil) So(n4.TaskId, ShouldEqual, "t1") So(n4.Content, ShouldEqual, n3.Content) }) }) }) }
func TestMarkAsProvisioned(t *testing.T) { Convey("With a host", t, func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) host := &Host{ Id: "hostOne", } So(host.Insert(), ShouldBeNil) Convey("marking the host as provisioned should update the status,"+ " provisioned, and host name fields in both the in-memory and"+ " database copies of the host", func() { So(host.MarkAsProvisioned(), ShouldBeNil) So(host.Status, ShouldEqual, evergreen.HostRunning) So(host.Provisioned, ShouldEqual, true) host, err := FindOne(ById(host.Id)) So(err, ShouldBeNil) So(host.Status, ShouldEqual, evergreen.HostRunning) So(host.Provisioned, ShouldEqual, true) }) }) }
func TestHostSetDNSName(t *testing.T) { Convey("With a host", t, func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) host := &Host{ Id: "hostOne", } So(host.Insert(), ShouldBeNil) Convey("setting the hostname should update both the in-memory and"+ " database copies of the host", func() { So(host.SetDNSName("hostname"), ShouldBeNil) So(host.Host, ShouldEqual, "hostname") host, err := FindOne(ById(host.Id)) So(err, ShouldBeNil) So(host.Host, ShouldEqual, "hostname") // if the host is already updated, no new updates should work So(host.SetDNSName("hostname2"), ShouldBeNil) So(host.Host, ShouldEqual, "hostname") host, err = FindOne(ById(host.Id)) So(err, ShouldBeNil) So(host.Host, ShouldEqual, "hostname") }) }) }
func TestSetHostTerminated(t *testing.T) { Convey("With a host", t, func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) host := &Host{ Id: "hostOne", } So(host.Insert(), ShouldBeNil) Convey("setting the host as terminated should set the status and the"+ " termination time in both the in-memory and database copies of"+ " the host", func() { So(host.Terminate(), ShouldBeNil) So(host.Status, ShouldEqual, evergreen.HostTerminated) So(host.TerminationTime.IsZero(), ShouldBeFalse) host, err := FindOne(ById(host.Id)) So(err, ShouldBeNil) So(host.Status, ShouldEqual, evergreen.HostTerminated) So(host.TerminationTime.IsZero(), ShouldBeFalse) }) }) }
func TestCheckProjectSyntax(t *testing.T) { Convey("When validating a project's syntax", t, func() { Convey("if the project passes all of the validation funcs, no errors"+ " should be returned", func() { distros := []distro.Distro{ {Id: "test-distro-one"}, {Id: "test-distro-two"}, } err := testutil.CreateTestLocalConfig(projectValidatorConf, "project_test", "") So(err, ShouldBeNil) projectRef, err := model.FindOneProjectRef("project_test") So(err, ShouldBeNil) for _, d := range distros { So(d.Insert(), ShouldBeNil) } project, err := model.FindProject("", projectRef) So(err, ShouldBeNil) So(CheckProjectSyntax(project), ShouldResemble, []ValidationError{}) }) Reset(func() { db.Clear(distro.Collection) }) }) }
func TestAlertQueue(t *testing.T) { Convey("After queueing a few alerts", t, func() { db.Clear(alert.Collection) now := time.Now() // a bunch of alerts, in order of oldest to newest testAlerts := []*alert.AlertRequest{ {Id: bson.NewObjectId(), Display: "test1", CreatedAt: now.Add(time.Millisecond)}, {Id: bson.NewObjectId(), Display: "test2", CreatedAt: now.Add(2 * time.Millisecond)}, {Id: bson.NewObjectId(), Display: "test3", CreatedAt: now.Add(3 * time.Millisecond)}, } for _, a := range testAlerts { err := alert.EnqueueAlertRequest(a) So(err, ShouldBeNil) } Convey("dequeuing should return them in order, oldest first", func() { found := 0 for { nextAlert, err := alert.DequeueAlertRequest() So(err, ShouldBeNil) if nextAlert == nil { break } So(nextAlert.Display, ShouldEqual, testAlerts[found].Display) So(nextAlert.QueueStatus, ShouldEqual, alert.InProgress) found++ } So(found, ShouldEqual, len(testAlerts)) }) }) }
func TestHostClearRunningTask(t *testing.T) { Convey("With a host", t, func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) host := &Host{ Id: "hostOne", RunningTask: "taskId", StartedBy: evergreen.User, Status: evergreen.HostRunning, TaskDispatchTime: time.Now(), Pid: "12345", } So(host.Insert(), ShouldBeNil) Convey("host statistics should properly count this host as active"+ " but not idle", func() { count, err := Count(IsActive) So(err, ShouldBeNil) So(count, ShouldEqual, 1) count, err = Count(IsIdle) So(err, ShouldBeNil) So(count, ShouldEqual, 0) }) Convey("clearing the running task should clear the running task, pid,"+ " and task dispatch time fields from both the in-memory and"+ " database copies of the host", func() { So(host.ClearRunningTask(), ShouldBeNil) So(host.RunningTask, ShouldEqual, "") So(host.TaskDispatchTime.Equal(time.Unix(0, 0)), ShouldBeTrue) So(host.Pid, ShouldEqual, "") host, err := FindOne(ById(host.Id)) So(err, ShouldBeNil) So(host.RunningTask, ShouldEqual, "") So(host.TaskDispatchTime.Equal(time.Unix(0, 0)), ShouldBeTrue) So(host.Pid, ShouldEqual, "") Convey("the count of idle hosts should go up", func() { count, err := Count(IsIdle) So(err, ShouldBeNil) So(count, ShouldEqual, 1) Convey("but the active host count should remain the same", func() { count, err = Count(IsActive) So(err, ShouldBeNil) So(count, ShouldEqual, 1) }) }) }) }) }
func TestExistingPassedTaskTriggers(t *testing.T) { Convey("With a previously passing instance of task in the database", t, func() { db.Clear(task.Collection) db.Clear(alertrecord.Collection) testTask.Status = evergreen.TaskSucceeded err := testTask.Insert() So(err, ShouldBeNil) t2 := &task.Task{ Id: "testTask2", Status: evergreen.TaskFailed, DisplayName: testTask.DisplayName, Project: testTask.Project, BuildVariant: testTask.BuildVariant, Version: "testVersion2", RevisionOrderNumber: testTask.RevisionOrderNumber + 2, } Convey("a newly failed task should trigger TaskFailed and TaskFailTransition", func() { ctx, err := getTaskTriggerContext(t2) So(err, ShouldBeNil) triggers, err := getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(hasTrigger(triggers, TaskFailed{}), ShouldBeTrue) So(hasTrigger(triggers, TaskFailTransition{}), ShouldBeTrue) }) Convey("a newly failed task should not trigger TaskFailTransition after bookkeeping is already done", func() { // Pre-bookkeeping ctx, err := getTaskTriggerContext(t2) So(err, ShouldBeNil) triggers, err := getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(hasTrigger(triggers, TaskFailed{}), ShouldBeTrue) So(hasTrigger(triggers, TaskFailTransition{}), ShouldBeTrue) // Post-bookkeeping err = storeTriggerBookkeeping(*ctx, triggers) So(err, ShouldBeNil) triggers, err = getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(hasTrigger(triggers, TaskFailed{}), ShouldBeTrue) So(hasTrigger(triggers, TaskFailTransition{}), ShouldBeFalse) }) }) }
func TestDequeueTask(t *testing.T) { var taskIds []string var distroId string var taskQueue *TaskQueue Convey("When attempting to pull a task from a task queue", t, func() { taskIds = []string{"t1", "t2", "t3"} distroId = "d1" taskQueue = &TaskQueue{ Distro: distroId, Queue: []TaskQueueItem{}, } So(db.Clear(TaskQueuesCollection), ShouldBeNil) Convey("if the task queue is empty, an error should be thrown", func() { So(taskQueue.Save(), ShouldBeNil) So(taskQueue.DequeueTask(taskIds[0]), ShouldNotBeNil) }) Convey("if the task is not present in the queue, an error should be"+ " thrown", func() { taskQueue.Queue = append(taskQueue.Queue, TaskQueueItem{Id: taskIds[1]}) So(taskQueue.Save(), ShouldBeNil) So(taskQueue.DequeueTask(taskIds[0]), ShouldNotBeNil) }) Convey("if the task is present in the queue, it should be removed"+ " from the in-memory and db versions of the queue", func() { taskQueue.Queue = []TaskQueueItem{ TaskQueueItem{Id: taskIds[0]}, TaskQueueItem{Id: taskIds[1]}, TaskQueueItem{Id: taskIds[2]}, } So(taskQueue.Save(), ShouldBeNil) So(taskQueue.DequeueTask(taskIds[1]), ShouldBeNil) // make sure the queue was updated in memory So(taskQueue.Length(), ShouldEqual, 2) So(taskQueue.Queue[0].Id, ShouldEqual, taskIds[0]) So(taskQueue.Queue[1].Id, ShouldEqual, taskIds[2]) // make sure the db representation was updated taskQueue, err := FindTaskQueueForDistro(distroId) So(err, ShouldBeNil) So(taskQueue.Length(), ShouldEqual, 2) So(taskQueue.Queue[0].Id, ShouldEqual, taskIds[0]) So(taskQueue.Queue[1].Id, ShouldEqual, taskIds[2]) }) }) }
func TestLoggingDistroEvents(t *testing.T) { Convey("When logging distro events, ", t, func() { So(db.Clear(Collection), ShouldBeNil) Convey("logged events should be stored and queryable in sorted order", func() { distroId := "distro_id" userId := "user_id" // log some events, sleeping in between to make sure the times are different LogDistroAdded(distroId, userId, nil) time.Sleep(1 * time.Millisecond) LogDistroModified(distroId, userId, "update") time.Sleep(1 * time.Millisecond) LogDistroRemoved(distroId, userId, nil) time.Sleep(1 * time.Millisecond) // fetch all the events from the database, make sure they are // persisted correctly eventsForDistro, err := Find(DistroEventsInOrder(distroId)) So(err, ShouldBeNil) event := eventsForDistro[0] So(event.EventType, ShouldEqual, EventDistroAdded) So(event.ResourceId, ShouldEqual, distroId) eventData, ok := event.Data.Data.(*DistroEventData) So(ok, ShouldBeTrue) So(eventData.ResourceType, ShouldEqual, ResourceTypeDistro) So(eventData.UserId, ShouldEqual, userId) So(eventData.Data, ShouldBeNil) event = eventsForDistro[1] So(event.EventType, ShouldEqual, EventDistroModified) So(event.ResourceId, ShouldEqual, distroId) eventData, ok = event.Data.Data.(*DistroEventData) So(ok, ShouldBeTrue) So(eventData.ResourceType, ShouldEqual, ResourceTypeDistro) So(eventData.UserId, ShouldEqual, userId) So(eventData.Data.(string), ShouldEqual, "update") event = eventsForDistro[2] So(event.EventType, ShouldEqual, EventDistroRemoved) So(event.ResourceId, ShouldEqual, distroId) eventData, ok = event.Data.Data.(*DistroEventData) So(ok, ShouldBeTrue) So(eventData.ResourceType, ShouldEqual, ResourceTypeDistro) So(eventData.UserId, ShouldEqual, userId) So(eventData.Data, ShouldBeNil) }) }) }
func TestDeletingBuild(t *testing.T) { Convey("With a build", t, func() { testutil.HandleTestingErr(db.Clear(build.Collection), t, "Error clearing"+ " '%v' collection", build.Collection) b := &build.Build{ Id: "build", } So(b.Insert(), ShouldBeNil) Convey("deleting it should remove it and all its associated"+ " tasks from the database", func() { testutil.HandleTestingErr(db.ClearCollections(task.Collection), t, "Error"+ " clearing '%v' collection", task.Collection) // insert two tasks that are part of the build, and one that isn't matchingTaskOne := &task.Task{ Id: "matchingOne", BuildId: b.Id, } So(matchingTaskOne.Insert(), ShouldBeNil) matchingTaskTwo := &task.Task{ Id: "matchingTwo", BuildId: b.Id, } So(matchingTaskTwo.Insert(), ShouldBeNil) nonMatchingTask := &task.Task{ Id: "nonMatching", BuildId: "blech", } So(nonMatchingTask.Insert(), ShouldBeNil) // delete the build, make sure only it and its tasks are deleted So(DeleteBuild(b.Id), ShouldBeNil) b, err := build.FindOne(build.ById(b.Id)) So(err, ShouldBeNil) So(b, ShouldBeNil) matchingTasks, err := task.Find(task.ByBuildId("build")) So(err, ShouldBeNil) So(len(matchingTasks), ShouldEqual, 0) nonMatchingTask, err = task.FindOne(task.ById(nonMatchingTask.Id)) So(err, ShouldBeNil) So(nonMatchingTask, ShouldNotBeNil) }) }) }
func TestFinalizePatch(t *testing.T) { testutil.ConfigureIntegrationTest(t, patchTestConfig, "TestFinalizePatch") Convey("With FinalizePatch on a project and commit event generated from GetPatchedProject path", t, func() { configPatch := resetPatchSetup(t, configFilePath) Convey("a patched config should drive version creation", func() { project, err := GetPatchedProject(configPatch, patchTestConfig) So(err, ShouldBeNil) yamlBytes, err := yaml.Marshal(project) So(err, ShouldBeNil) configPatch.PatchedConfig = string(yamlBytes) version, err := model.FinalizePatch(configPatch, patchTestConfig) So(err, ShouldBeNil) So(version, ShouldNotBeNil) // ensure the relevant builds/tasks were created builds, err := build.Find(build.All) So(err, ShouldBeNil) So(len(builds), ShouldEqual, 1) So(len(builds[0].Tasks), ShouldEqual, 2) tasks, err := task.Find(task.All) So(err, ShouldBeNil) So(len(tasks), ShouldEqual, 2) }) Convey("a patch that does not include the remote config should not "+ "drive version creation", func() { patchedConfigFile := "fakeInPatchSoNotPatched" configPatch := resetPatchSetup(t, patchedConfigFile) project, err := GetPatchedProject(configPatch, patchTestConfig) So(err, ShouldBeNil) yamlBytes, err := yaml.Marshal(project) So(err, ShouldBeNil) configPatch.PatchedConfig = string(yamlBytes) version, err := model.FinalizePatch(configPatch, patchTestConfig) So(err, ShouldBeNil) So(version, ShouldNotBeNil) So(err, ShouldBeNil) So(version, ShouldNotBeNil) // ensure the relevant builds/tasks were created builds, err := build.Find(build.All) So(err, ShouldBeNil) So(len(builds), ShouldEqual, 1) So(len(builds[0].Tasks), ShouldEqual, 1) tasks, err := task.Find(task.All) So(err, ShouldBeNil) So(len(tasks), ShouldEqual, 1) }) Reset(func() { db.Clear(distro.Collection) }) }) }
func TestEmptyTaskTriggers(t *testing.T) { db.Clear(task.Collection) db.Clear(alertrecord.Collection) Convey("With no existing tasks in the database", t, func() { Convey("a newly failed task should return all triggers", func() { // pre-bookkeeping ctx, err := getTaskTriggerContext(testTask) So(err, ShouldBeNil) So(ctx.previousCompleted, ShouldBeNil) triggers, err := getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(len(triggers), ShouldEqual, 5) So(hasTrigger(triggers, TaskFailed{}), ShouldBeTrue) So(hasTrigger(triggers, TaskFailTransition{}), ShouldBeTrue) So(hasTrigger(triggers, FirstFailureInVersion{}), ShouldBeTrue) So(hasTrigger(triggers, FirstFailureInVariant{}), ShouldBeTrue) So(hasTrigger(triggers, FirstFailureInTaskType{}), ShouldBeTrue) // post-bookkeeping err = storeTriggerBookkeeping(*ctx, triggers) So(err, ShouldBeNil) triggers, err = getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(len(triggers), ShouldEqual, 2) So(hasTrigger(triggers, TaskFailed{}), ShouldBeTrue) // The previous task doesn't exist, so this will still be true So(hasTrigger(triggers, TaskFailTransition{}), ShouldBeTrue) }) Convey("a successful task should trigger nothing", func() { testTask.Status = evergreen.TaskSucceeded ctx, err := getTaskTriggerContext(testTask) So(err, ShouldBeNil) triggers, err := getActiveTaskFailureTriggers(*ctx) So(err, ShouldBeNil) So(len(triggers), ShouldEqual, 0) }) }) }
func TestLastKnownGoodConfig(t *testing.T) { Convey("When calling LastKnownGoodConfig..", t, func() { identifier := "identifier" Convey("no versions should be returned if there're no good "+ "last known configurations", func() { v := &version.Version{ Identifier: identifier, Requester: evergreen.RepotrackerVersionRequester, Errors: []string{"error 1", "error 2"}, } testutil.HandleTestingErr(v.Insert(), t, "Error inserting test version: %v") lastGood, err := version.FindOne(version.ByLastKnownGoodConfig(identifier)) testutil.HandleTestingErr(err, t, "error finding last known good: %v") So(lastGood, ShouldBeNil) }) Convey("a version should be returned if there is a last known good configuration", func() { v := &version.Version{ Identifier: identifier, Requester: evergreen.RepotrackerVersionRequester, } testutil.HandleTestingErr(v.Insert(), t, "Error inserting test version: %v") lastGood, err := version.FindOne(version.ByLastKnownGoodConfig(identifier)) testutil.HandleTestingErr(err, t, "error finding last known good: %v") So(lastGood, ShouldNotBeNil) }) Convey("most recent version should be found if there are several recent good configs", func() { v := &version.Version{ Id: "1", Identifier: identifier, Requester: evergreen.RepotrackerVersionRequester, RevisionOrderNumber: 1, Config: "1", } testutil.HandleTestingErr(v.Insert(), t, "Error inserting test version: %v") v.Id = "5" v.RevisionOrderNumber = 5 v.Config = "5" testutil.HandleTestingErr(v.Insert(), t, "Error inserting test version: %v") v.Id = "2" v.RevisionOrderNumber = 2 v.Config = "2" testutil.HandleTestingErr(v.Insert(), t, "Error inserting test version: %v") lastGood, err := version.FindOne(version.ByLastKnownGoodConfig(identifier)) testutil.HandleTestingErr(err, t, "error finding last known good: %v") So(lastGood, ShouldNotBeNil) So(lastGood.Config, ShouldEqual, "5") }) Reset(func() { db.Clear(version.Collection) }) }) }
func TestReachedFailureLimit(t *testing.T) { Convey("With 3 failed task and relevant project variants", t, func() { db.Clear(task.Collection) db.Clear(model.ProjectRefCollection) db.Clear(version.Collection) t := task.Task{ Id: "t1", Revision: "aaa", Project: "testProject", BuildVariant: "bv1", FinishTime: time.Now().Add(-time.Hour), } So(t.Insert(), ShouldBeNil) t.Id = "t2" t.BuildVariant = "bv2" So(t.Insert(), ShouldBeNil) t.Id = "t3" t.BuildVariant = "bv3" So(t.Insert(), ShouldBeNil) So(testProject.Insert(), ShouldBeNil) So(testVersion.Insert(), ShouldBeNil) Convey("a variant with batchtime of 60 should not hit the limit", func() { out, err := reachedFailureLimit("t1") So(err, ShouldBeNil) So(out, ShouldBeFalse) }) Convey("a variant with batchtime of 1 should hit the limit", func() { out, err := reachedFailureLimit("t2") So(err, ShouldBeNil) So(out, ShouldBeTrue) }) Convey("a fallback batchtime of 5 should hit the limit", func() { out, err := reachedFailureLimit("t3") So(err, ShouldBeNil) So(out, ShouldBeTrue) }) }) }
func TestHostSetExpirationTime(t *testing.T) { Convey("With a host", t, func() { testutil.HandleTestingErr(db.Clear(Collection), t, "Error"+ " clearing '%v' collection", Collection) initialExpirationTime := time.Now() notifications := make(map[string]bool) notifications["2h"] = true memHost := &Host{ Id: "hostOne", ExpirationTime: initialExpirationTime, Notifications: notifications, } So(memHost.Insert(), ShouldBeNil) Convey("setting the expiration time for the host should change the "+ " expiration time for both the in-memory and database"+ " copies of the host and unset the notifications", func() { dbHost, err := FindOne(ById(memHost.Id)) // ensure the db entries are as expected So(err, ShouldBeNil) So(memHost.ExpirationTime.Round(time.Second).Equal( initialExpirationTime.Round(time.Second)), ShouldBeTrue) So(dbHost.ExpirationTime.Round(time.Second).Equal( initialExpirationTime.Round(time.Second)), ShouldBeTrue) So(memHost.Notifications, ShouldResemble, notifications) So(dbHost.Notifications, ShouldResemble, notifications) // now update the expiration time newExpirationTime := time.Now() So(memHost.SetExpirationTime(newExpirationTime), ShouldBeNil) dbHost, err = FindOne(ById(memHost.Id)) // ensure the db entries are as expected So(err, ShouldBeNil) So(memHost.ExpirationTime.Round(time.Second).Equal( newExpirationTime.Round(time.Second)), ShouldBeTrue) So(dbHost.ExpirationTime.Round(time.Second).Equal( newExpirationTime.Round(time.Second)), ShouldBeTrue) So(memHost.Notifications, ShouldResemble, make(map[string]bool)) So(dbHost.Notifications, ShouldEqual, nil) }) }) }