github.com/billybanfield/evergreen@v0.0.0-20170525200750-eeee692790f7/repotracker/repotracker_test.go (about)

     1  package repotracker
     2  
     3  import (
     4  	"testing"
     5  	"time"
     6  
     7  	"github.com/evergreen-ci/evergreen"
     8  	"github.com/evergreen-ci/evergreen/db"
     9  	"github.com/evergreen-ci/evergreen/model"
    10  	"github.com/evergreen-ci/evergreen/model/distro"
    11  	modelutil "github.com/evergreen-ci/evergreen/model/testutil"
    12  	"github.com/evergreen-ci/evergreen/model/version"
    13  	"github.com/evergreen-ci/evergreen/testutil"
    14  	"github.com/mongodb/grip"
    15  	"github.com/pkg/errors"
    16  	. "github.com/smartystreets/goconvey/convey"
    17  )
    18  
    19  func init() {
    20  	db.SetGlobalSessionProvider(db.SessionFactoryFromConfig(testConfig))
    21  }
    22  
    23  func TestFetchRevisions(t *testing.T) {
    24  	dropTestDB(t)
    25  	testutil.ConfigureIntegrationTest(t, testConfig, "TestFetchRevisions")
    26  	Convey("With a GithubRepositoryPoller with a valid OAuth token...", t, func() {
    27  		err := modelutil.CreateTestLocalConfig(testConfig, "mci-test", "")
    28  		So(err, ShouldBeNil)
    29  
    30  		resetProjectRefs()
    31  
    32  		grip.Alertf("project: %+v", projectRef)
    33  		repoTracker := RepoTracker{
    34  			testConfig,
    35  			projectRef,
    36  			NewGithubRepositoryPoller(projectRef, testConfig.Credentials["github"]),
    37  		}
    38  
    39  		Convey("Fetching commits from the repository should not return any errors", func() {
    40  			So(repoTracker.FetchRevisions(10), ShouldBeNil)
    41  		})
    42  
    43  		Convey("Only get 3 revisions from the given repository if given a "+
    44  			"limit of 4 commits where only 3 exist", func() {
    45  			testutil.HandleTestingErr(repoTracker.FetchRevisions(4), t,
    46  				"Error running repository process %v")
    47  			numVersions, err := version.Count(version.All)
    48  			testutil.HandleTestingErr(err, t, "Error finding all versions")
    49  			So(numVersions, ShouldEqual, 3)
    50  		})
    51  
    52  		Convey("Only get 2 revisions from the given repository if given a "+
    53  			"limit of 2 commits where 3 exist", func() {
    54  			testutil.HandleTestingErr(repoTracker.FetchRevisions(2), t,
    55  				"Error running repository process %v")
    56  			numVersions, err := version.Count(version.All)
    57  			testutil.HandleTestingErr(err, t, "Error finding all versions")
    58  			So(numVersions, ShouldEqual, 2)
    59  		})
    60  
    61  		Reset(func() {
    62  			dropTestDB(t)
    63  		})
    64  	})
    65  }
    66  
    67  func TestStoreRepositoryRevisions(t *testing.T) {
    68  	dropTestDB(t)
    69  	testutil.ConfigureIntegrationTest(t, testConfig, "TestStoreRepositoryRevisions")
    70  	Convey("When storing revisions gotten from a repository...", t, func() {
    71  		err := modelutil.CreateTestLocalConfig(testConfig, "mci-test", "")
    72  		So(err, ShouldBeNil)
    73  		repoTracker := RepoTracker{testConfig, projectRef, NewGithubRepositoryPoller(projectRef,
    74  			testConfig.Credentials["github"])}
    75  
    76  		// insert distros used in testing.
    77  		d := distro.Distro{Id: "test-distro-one"}
    78  		So(d.Insert(), ShouldBeNil)
    79  		d.Id = "test-distro-two"
    80  		So(d.Insert(), ShouldBeNil)
    81  
    82  		Convey("On storing a single repo revision, we expect a version to be created"+
    83  			" in the database for this project, which should be retrieved when we search"+
    84  			" for this project's most recent version", func() {
    85  			createTime := time.Now()
    86  			revisionOne := *createTestRevision("firstRevision", createTime)
    87  			revisions := []model.Revision{revisionOne}
    88  
    89  			resultVersion, err := repoTracker.StoreRevisions(revisions)
    90  			testutil.HandleTestingErr(err, t, "Error storing repository revisions %v")
    91  
    92  			newestVersion, err := version.FindOne(version.ByMostRecentForRequester(projectRef.String(), evergreen.RepotrackerVersionRequester))
    93  			testutil.HandleTestingErr(err, t, "Error retreiving newest version %v")
    94  
    95  			So(resultVersion, ShouldResemble, newestVersion)
    96  		})
    97  
    98  		Convey("On storing several repo revisions, we expect a version to be created "+
    99  			"for each revision", func() {
   100  			createTime := time.Now()
   101  			laterCreateTime := createTime.Add(time.Duration(4 * time.Hour))
   102  
   103  			revisionOne := *createTestRevision("one", laterCreateTime)
   104  			revisionTwo := *createTestRevision("two", createTime)
   105  
   106  			revisions := []model.Revision{revisionOne, revisionTwo}
   107  
   108  			_, err := repoTracker.StoreRevisions(revisions)
   109  			testutil.HandleTestingErr(err, t, "Error storing repository revisions %v")
   110  
   111  			versionOne, err := version.FindOne(version.ByProjectIdAndRevision(projectRef.Identifier, revisionOne.Revision))
   112  			testutil.HandleTestingErr(err, t, "Error retrieving first stored version %v")
   113  			versionTwo, err := version.FindOne(version.ByProjectIdAndRevision(projectRef.Identifier, revisionTwo.Revision))
   114  			testutil.HandleTestingErr(err, t, "Error retreiving second stored version %v")
   115  
   116  			So(versionOne.Revision, ShouldEqual, revisionOne.Revision)
   117  			So(versionTwo.Revision, ShouldEqual, revisionTwo.Revision)
   118  		})
   119  
   120  		Reset(func() {
   121  			dropTestDB(t)
   122  		})
   123  	})
   124  
   125  	Convey("When storing versions from repositories with remote configuration files...", t, func() {
   126  
   127  		project := createTestProject(nil, nil)
   128  
   129  		revisions := []model.Revision{
   130  			*createTestRevision("foo", time.Now().Add(1*time.Minute)),
   131  		}
   132  
   133  		poller := NewMockRepoPoller(project, revisions)
   134  
   135  		repoTracker := RepoTracker{
   136  			testConfig,
   137  			&model.ProjectRef{
   138  				Identifier: "testproject",
   139  				BatchTime:  10,
   140  			},
   141  			poller,
   142  		}
   143  
   144  		// insert distros used in testing.
   145  		d := distro.Distro{Id: "test-distro-one"}
   146  		So(d.Insert(), ShouldBeNil)
   147  		d.Id = "test-distro-two"
   148  		So(d.Insert(), ShouldBeNil)
   149  
   150  		Convey("We should not fetch configs for versions we already have stored.",
   151  			func() {
   152  				So(poller.ConfigGets, ShouldBeZeroValue)
   153  				// Store revisions the first time
   154  				_, err := repoTracker.StoreRevisions(revisions)
   155  				So(err, ShouldBeNil)
   156  				// We should have fetched the config once for each revision
   157  				So(poller.ConfigGets, ShouldEqual, len(revisions))
   158  
   159  				// Store them again
   160  				_, err = repoTracker.StoreRevisions(revisions)
   161  				So(err, ShouldBeNil)
   162  				// We shouldn't have fetched the config any additional times
   163  				// since we have already stored these versions
   164  				So(poller.ConfigGets, ShouldEqual, len(revisions))
   165  			},
   166  		)
   167  
   168  		Convey("We should handle invalid configuration files gracefully by storing a stub version", func() {
   169  			errStrs := []string{"Someone dun' goof'd"}
   170  			poller.setNextError(projectConfigError{errStrs, []string{}})
   171  			stubVersion, err := repoTracker.StoreRevisions(revisions)
   172  			// We want this error to get swallowed so a config error
   173  			// doesn't stop additional versions from getting created
   174  			So(err, ShouldBeNil)
   175  			So(stubVersion.Errors, ShouldResemble, errStrs)
   176  			So(len(stubVersion.BuildVariants), ShouldEqual, 0)
   177  		})
   178  
   179  		Convey("Project configuration files with missing distros should still create versions", func() {
   180  			poller.addBadDistro("Cray-Y-MP")
   181  			v, err := repoTracker.StoreRevisions(revisions)
   182  			So(err, ShouldBeNil)
   183  			So(v, ShouldNotBeNil)
   184  			So(len(v.BuildVariants), ShouldBeGreaterThan, 0)
   185  
   186  			Convey("and log a warning", func() {
   187  				So(len(v.Warnings), ShouldEqual, 1)
   188  				So(v.Errors, ShouldBeNil)
   189  			})
   190  		})
   191  
   192  		Convey("If there is an error other than a config error while fetching a config, we should fail hard",
   193  			func() {
   194  				unexpectedError := errors.New("Something terrible has happened!!")
   195  				poller.setNextError(unexpectedError)
   196  				v, err := repoTracker.StoreRevisions(revisions)
   197  				So(v, ShouldBeNil)
   198  				So(err.Error(), ShouldEqual, unexpectedError.Error())
   199  			},
   200  		)
   201  
   202  		Reset(func() {
   203  			dropTestDB(t)
   204  		})
   205  
   206  	})
   207  }
   208  
   209  func TestBatchTimes(t *testing.T) {
   210  	Convey("When deciding whether or not to activate variants for the most recently stored version", t, func() {
   211  		// We create a version with an activation time of now so that all the bvs have a last activation time of now.
   212  		previouslyActivatedVersion := version.Version{
   213  			Id:         "previously activated",
   214  			Identifier: "testproject",
   215  			BuildVariants: []version.BuildStatus{
   216  				{
   217  					BuildVariant: "bv1",
   218  					Activated:    true,
   219  					ActivateAt:   time.Now(),
   220  				},
   221  				{
   222  					BuildVariant: "bv2",
   223  					Activated:    true,
   224  					ActivateAt:   time.Now(),
   225  				},
   226  			},
   227  			RevisionOrderNumber: 0,
   228  			Requester:           evergreen.RepotrackerVersionRequester,
   229  		}
   230  
   231  		So(previouslyActivatedVersion.Insert(), ShouldBeNil)
   232  
   233  		// insert distros used in testing.
   234  		d := distro.Distro{Id: "test-distro-one"}
   235  		So(d.Insert(), ShouldBeNil)
   236  		d.Id = "test-distro-two"
   237  		So(d.Insert(), ShouldBeNil)
   238  
   239  		Convey("If the project's batch time has not elapsed, and no buildvariants "+
   240  			"have overridden their batch times, no variants should be activated", func() {
   241  			project := createTestProject(nil, nil)
   242  			revisions := []model.Revision{
   243  				*createTestRevision("foo", time.Now()),
   244  			}
   245  
   246  			repoTracker := RepoTracker{
   247  				testConfig,
   248  				&model.ProjectRef{
   249  					Identifier: "testproject",
   250  					BatchTime:  1,
   251  				},
   252  				NewMockRepoPoller(project, revisions),
   253  			}
   254  			v, err := repoTracker.StoreRevisions(revisions)
   255  			So(v, ShouldNotBeNil)
   256  			So(err, ShouldBeNil)
   257  			So(len(v.BuildVariants), ShouldEqual, 2)
   258  			So(repoTracker.activateElapsedBuilds(v), ShouldBeNil)
   259  			So(v.BuildVariants[0].Activated, ShouldBeFalse)
   260  			So(v.BuildVariants[1].Activated, ShouldBeFalse)
   261  		})
   262  
   263  		Convey("If the project's batch time has elapsed, and no buildvariants "+
   264  			"have overridden their batch times, all variants should be activated", func() {
   265  			project := createTestProject(nil, nil)
   266  			revisions := []model.Revision{
   267  				*createTestRevision("bar", time.Now().Add(time.Duration(-6*time.Minute))),
   268  			}
   269  			repoTracker := RepoTracker{
   270  				testConfig,
   271  				&model.ProjectRef{
   272  					Identifier: "testproject",
   273  					BatchTime:  0,
   274  				},
   275  				NewMockRepoPoller(project, revisions),
   276  			}
   277  			version, err := repoTracker.StoreRevisions(revisions)
   278  			So(version, ShouldNotBeNil)
   279  			So(err, ShouldBeNil)
   280  			So(repoTracker.activateElapsedBuilds(version), ShouldBeNil)
   281  			bv1, found := findStatus(version, "bv1")
   282  			So(found, ShouldBeTrue)
   283  			So(bv1.Activated, ShouldBeTrue)
   284  			bv2, found := findStatus(version, "bv2")
   285  			So(found, ShouldBeTrue)
   286  			So(bv2.Activated, ShouldBeTrue)
   287  		})
   288  
   289  		Convey("If the project's batch time has elapsed, but both variants "+
   290  			"have overridden their batch times (which have not elapsed)"+
   291  			", no variants should be activated", func() {
   292  			// need to assign pointer vals
   293  			twoforty := 240
   294  			onetwenty := 120
   295  
   296  			project := createTestProject(&twoforty, &onetwenty)
   297  
   298  			revisions := []model.Revision{
   299  				*createTestRevision("baz", time.Now()),
   300  			}
   301  
   302  			repoTracker := RepoTracker{
   303  				testConfig,
   304  				&model.ProjectRef{
   305  					Identifier: "testproject",
   306  					BatchTime:  60,
   307  				},
   308  				NewMockRepoPoller(project, revisions),
   309  			}
   310  			version, err := repoTracker.StoreRevisions(revisions)
   311  			So(version, ShouldNotBeNil)
   312  			So(err, ShouldBeNil)
   313  			So(repoTracker.activateElapsedBuilds(version), ShouldBeNil)
   314  			bv1, found := findStatus(version, "bv1")
   315  			So(found, ShouldBeTrue)
   316  			So(bv1.Activated, ShouldBeFalse)
   317  			bv2, found := findStatus(version, "bv2")
   318  			So(found, ShouldBeTrue)
   319  			So(bv2.Activated, ShouldBeFalse)
   320  		})
   321  
   322  		Convey("If the project's batch time has not elapsed, but one variant "+
   323  			"has overridden their batch times to be shorter"+
   324  			", that variant should be activated", func() {
   325  			zero := 0
   326  
   327  			project := createTestProject(&zero, nil)
   328  
   329  			revisions := []model.Revision{
   330  				*createTestRevision("garply", time.Now()),
   331  			}
   332  
   333  			repoTracker := RepoTracker{
   334  				testConfig,
   335  				&model.ProjectRef{
   336  					Identifier: "testproject",
   337  					BatchTime:  60,
   338  				},
   339  				NewMockRepoPoller(project, revisions),
   340  			}
   341  			version, err := repoTracker.StoreRevisions(revisions)
   342  			So(version, ShouldNotBeNil)
   343  			So(err, ShouldBeNil)
   344  			So(repoTracker.activateElapsedBuilds(version), ShouldBeNil)
   345  			bv1, found := findStatus(version, "bv1")
   346  			So(found, ShouldBeTrue)
   347  			So(bv1.Activated, ShouldBeTrue)
   348  			bv2, found := findStatus(version, "bv2")
   349  			So(found, ShouldBeTrue)
   350  			So(bv2, ShouldNotBeNil)
   351  			So(bv2.Activated, ShouldBeFalse)
   352  		})
   353  
   354  		Reset(func() {
   355  			dropTestDB(t)
   356  		})
   357  	})
   358  
   359  	Convey("If the new revision adds a variant", t, func() {
   360  		previouslyActivatedVersion := version.Version{
   361  			Id:         "previously activated",
   362  			Identifier: "testproject",
   363  			BuildVariants: []version.BuildStatus{
   364  				{
   365  					BuildVariant: "bv1",
   366  					Activated:    true,
   367  					ActivateAt:   time.Now(),
   368  				},
   369  				// "bv2" will be added in a later revision
   370  			},
   371  			RevisionOrderNumber: 0,
   372  			Requester:           evergreen.RepotrackerVersionRequester,
   373  		}
   374  		So(previouslyActivatedVersion.Insert(), ShouldBeNil)
   375  		// insert distros used in testing.
   376  		d := distro.Distro{Id: "test-distro-one"}
   377  		So(d.Insert(), ShouldBeNil)
   378  		d.Id = "test-distro-two"
   379  		So(d.Insert(), ShouldBeNil)
   380  		zero := 0
   381  		project := createTestProject(&zero, nil)
   382  		revisions := []model.Revision{
   383  			*createTestRevision("garply", time.Now()),
   384  		}
   385  		repoTracker := RepoTracker{
   386  			testConfig,
   387  			&model.ProjectRef{
   388  				Identifier: "testproject",
   389  				BatchTime:  60,
   390  			},
   391  			NewMockRepoPoller(project, revisions),
   392  		}
   393  		version, err := repoTracker.StoreRevisions(revisions)
   394  		So(version, ShouldNotBeNil)
   395  		So(err, ShouldBeNil)
   396  
   397  		Convey("the new variant should activate immediately", func() {
   398  			So(repoTracker.activateElapsedBuilds(version), ShouldBeNil)
   399  			bv1, found := findStatus(version, "bv1")
   400  			So(found, ShouldBeTrue)
   401  			So(bv1.Activated, ShouldBeTrue)
   402  			bv2, found := findStatus(version, "bv2")
   403  			So(found, ShouldBeTrue)
   404  			So(bv2, ShouldNotBeNil)
   405  			So(bv2.Activated, ShouldBeTrue)
   406  			So(bv2.ActivateAt, ShouldResemble, bv1.ActivateAt)
   407  		})
   408  
   409  		Reset(func() {
   410  			dropTestDB(t)
   411  		})
   412  	})
   413  }
   414  
   415  func findStatus(v *version.Version, buildVariant string) (*version.BuildStatus, bool) {
   416  	for _, status := range v.BuildVariants {
   417  		if status.BuildVariant == buildVariant {
   418  			return &status, true
   419  		}
   420  	}
   421  	return nil, false
   422  }
   423  
   424  func createTestRevision(revision string,
   425  	createTime time.Time) *model.Revision {
   426  	return &model.Revision{
   427  		Author:          "author",
   428  		AuthorEmail:     "authorEmail",
   429  		RevisionMessage: "revisionMessage",
   430  		Revision:        revision,
   431  		CreateTime:      createTime,
   432  	}
   433  }
   434  
   435  func createTestProject(override1, override2 *int) *model.Project {
   436  	return &model.Project{
   437  		BuildVariants: []model.BuildVariant{
   438  			{
   439  				Name:        "bv1",
   440  				DisplayName: "bv1",
   441  				BatchTime:   override1,
   442  				Tasks: []model.BuildVariantTask{
   443  					{
   444  						Name:    "Unabhaengigkeitserklaerungen",
   445  						Distros: []string{"test-distro-one"},
   446  					},
   447  				},
   448  			},
   449  			{
   450  				Name:        "bv2",
   451  				DisplayName: "bv2",
   452  				BatchTime:   override2,
   453  				Tasks: []model.BuildVariantTask{
   454  					{
   455  						Name:    "Unabhaengigkeitserklaerungen",
   456  						Distros: []string{"test-distro-one"},
   457  					},
   458  				},
   459  			},
   460  		},
   461  		Tasks: []model.ProjectTask{
   462  			{
   463  				Name:     "Unabhaengigkeitserklaerungen",
   464  				Commands: []model.PluginCommandConf{},
   465  			},
   466  		},
   467  	}
   468  }