547 lines
15 KiB
Go
547 lines
15 KiB
Go
// Copyright 2019 Drone.IO Inc. All rights reserved.
|
|
// Use of this source code is governed by the Drone Non-Commercial License
|
|
// that can be found in the LICENSE file.
|
|
|
|
// +build !oss
|
|
|
|
package trigger
|
|
|
|
import (
|
|
"context"
|
|
"database/sql"
|
|
"io"
|
|
"io/ioutil"
|
|
"testing"
|
|
|
|
"github.com/drone/drone/core"
|
|
"github.com/drone/drone/mock"
|
|
"github.com/sirupsen/logrus"
|
|
|
|
"github.com/golang/mock/gomock"
|
|
"github.com/google/go-cmp/cmp"
|
|
"github.com/google/go-cmp/cmp/cmpopts"
|
|
)
|
|
|
|
var noContext = context.Background()
|
|
|
|
func init() {
|
|
logrus.SetOutput(ioutil.Discard)
|
|
}
|
|
|
|
func TestTrigger(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
checkBuild := func(_ context.Context, build *core.Build, stages []*core.Stage) {
|
|
if diff := cmp.Diff(build, dummyBuild, ignoreBuildFields); diff != "" {
|
|
t.Errorf(diff)
|
|
}
|
|
if diff := cmp.Diff(stages, dummyStages, ignoreStageFields); diff != "" {
|
|
t.Errorf(diff)
|
|
}
|
|
}
|
|
|
|
checkStatus := func(_ context.Context, _ *core.User, req *core.StatusInput) error {
|
|
if diff := cmp.Diff(req.Build, dummyBuild, ignoreBuildFields); diff != "" {
|
|
t.Errorf(diff)
|
|
}
|
|
if diff := cmp.Diff(req.Repo, dummyRepo, ignoreStageFields); diff != "" {
|
|
t.Errorf(diff)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(gomock.Any(), dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockRepos := mock.NewMockRepositoryStore(controller)
|
|
mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYaml, nil)
|
|
|
|
mockStatus := mock.NewMockStatusService(controller)
|
|
mockStatus.EXPECT().Send(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Do(checkStatus)
|
|
|
|
mockQueue := mock.NewMockScheduler(controller)
|
|
mockQueue.EXPECT().Schedule(gomock.Any(), gomock.Any()).Return(nil)
|
|
|
|
mockBuilds := mock.NewMockBuildStore(controller)
|
|
mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Do(checkBuild).Return(nil)
|
|
|
|
mockWebhooks := mock.NewMockWebhookSender(controller)
|
|
mockWebhooks.EXPECT().Send(gomock.Any(), gomock.Any()).Return(nil)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
mockStatus,
|
|
mockBuilds,
|
|
mockQueue,
|
|
mockRepos,
|
|
mockUsers,
|
|
mockWebhooks,
|
|
)
|
|
|
|
build, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != nil {
|
|
t.Error(err)
|
|
return
|
|
}
|
|
if diff := cmp.Diff(build, dummyBuild, ignoreBuildFields); diff != "" {
|
|
t.Errorf(diff)
|
|
}
|
|
}
|
|
|
|
// this test verifies that hook is ignored if the commit
|
|
// message includes the [CI SKIP] keyword.
|
|
func TestTrigger_SkipCI(t *testing.T) {
|
|
triggerer := New(
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
)
|
|
dummyHookSkip := *dummyHook
|
|
dummyHookSkip.Message = "foo [CI SKIP] bar"
|
|
triggerer.Trigger(noContext, dummyRepo, &dummyHookSkip)
|
|
}
|
|
|
|
// this test verifies that if the system cannot determine
|
|
// the repository owner, the function must exit with an error.
|
|
// The owner is required because we need an oauth token
|
|
// when fetching the configuration file.
|
|
func TestTrigger_NoOwner(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(nil, sql.ErrNoRows)
|
|
|
|
triggerer := New(
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
_, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != sql.ErrNoRows {
|
|
t.Errorf("Expect error when yaml not found")
|
|
}
|
|
}
|
|
|
|
// this test verifies that if the system cannot fetch the yaml
|
|
// configuration file, the function must exit with an error.
|
|
func TestTrigger_MissingYaml(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(nil, io.EOF)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
_, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err == nil {
|
|
t.Errorf("Expect error when yaml not found")
|
|
}
|
|
}
|
|
|
|
// this test verifies that if the system cannot parse the yaml
|
|
// configuration file, the function must exit with an error.
|
|
func TestTrigger_ErrorYaml(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlInvalid, nil)
|
|
|
|
mockRepos := mock.NewMockRepositoryStore(controller)
|
|
mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil)
|
|
|
|
mockBuilds := mock.NewMockBuildStore(controller)
|
|
mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil) // .Do(checkBuild).Return(nil)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
nil,
|
|
mockBuilds,
|
|
nil,
|
|
mockRepos,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
build, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != nil {
|
|
t.Error(err)
|
|
}
|
|
|
|
if got, want := build.Status, core.StatusError; got != want {
|
|
t.Errorf("Want status %s, got %s", want, got)
|
|
}
|
|
if got, want := build.Error, "yaml: found unknown directive name"; got != want {
|
|
t.Errorf("Want error %s, got %s", want, got)
|
|
}
|
|
if build.Finished == 0 {
|
|
t.Errorf("Want non-zero finished time")
|
|
}
|
|
}
|
|
|
|
// this test verifies that no build should be scheduled if the
|
|
// hook branch does not match the branches defined in the yaml.
|
|
func TestTrigger_SkipBranch(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlSkipBranch, nil)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
_, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != nil {
|
|
t.Errorf("Expect build silenty skipped if branch does not match")
|
|
}
|
|
}
|
|
|
|
// this test verifies that no build should be scheduled if the
|
|
// hook event does not match the events defined in the yaml.
|
|
func TestTrigger_SkipEvent(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlSkipEvent, nil)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
_, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != nil {
|
|
t.Errorf("Expect build silenty skipped if event does not match")
|
|
}
|
|
}
|
|
|
|
// this test verifies that no build should be scheduled if the
|
|
// hook action does not match the actions defined in the yaml.
|
|
func TestTrigger_SkipAction(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYamlSkipAction, nil)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
_, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != nil {
|
|
t.Errorf("Expect build silenty skipped if action does not match")
|
|
}
|
|
}
|
|
|
|
// this test verifies that if the system cannot increment the
|
|
// build number, the function must exit with error and must not
|
|
// schedule a new build.
|
|
func TestTrigger_ErrorIncrement(t *testing.T) {
|
|
controller := gomock.NewController(t)
|
|
defer controller.Finish()
|
|
|
|
mockUsers := mock.NewMockUserStore(controller)
|
|
mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
mockRepos := mock.NewMockRepositoryStore(controller)
|
|
mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(nil, sql.ErrNoRows)
|
|
|
|
mockConfigService := mock.NewMockConfigService(controller)
|
|
mockConfigService.EXPECT().Find(gomock.Any(), gomock.Any()).Return(dummyYaml, nil)
|
|
|
|
triggerer := New(
|
|
mockConfigService,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
nil,
|
|
mockRepos,
|
|
mockUsers,
|
|
nil,
|
|
)
|
|
|
|
_, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
if err != sql.ErrNoRows {
|
|
t.Errorf("Expect error when unable to increment build sequence")
|
|
}
|
|
}
|
|
|
|
func TestTrigger_ErrorCreate(t *testing.T) {
|
|
t.Skip()
|
|
// controller := gomock.NewController(t)
|
|
// defer controller.Finish()
|
|
|
|
// mockUsers := mock.NewMockUserStore(controller)
|
|
// mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
// mockTriggers := mock.NewMockTriggerStore(controller)
|
|
// mockTriggers.EXPECT().List(noContext, dummyRepo.ID).Return([]*core.Trigger{dummyTrigger}, nil)
|
|
|
|
// mockRepos := mock.NewMockRepositoryStore(controller)
|
|
// mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil)
|
|
|
|
// mockContents := mock.NewMockContentService(controller)
|
|
// mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummyTrigger.Path, dummyHook.After).Return(dummyYaml, nil, nil)
|
|
// mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummySignature.Path, dummyHook.After).Return(dummySignature, nil, nil)
|
|
|
|
// mockClient := new(scm.Client)
|
|
// mockClient.Contents = mockContents
|
|
|
|
// mockBuilds := mock.NewMockBuildStore(controller)
|
|
// mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any()).Return(sql.ErrNoRows)
|
|
|
|
// triggerer := New(
|
|
// mockClient,
|
|
// mockBuilds,
|
|
// nil,
|
|
// mockRepos,
|
|
// mockTriggers,
|
|
// mockUsers,
|
|
// )
|
|
|
|
// builds, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
// if err != sql.ErrNoRows {
|
|
// t.Error("Expect error when persisting the build fails")
|
|
// }
|
|
// if got, want := len(builds), 0; got != want {
|
|
// t.Errorf("Got build count %d, want %d", got, want)
|
|
// }
|
|
}
|
|
|
|
func TestTrigger_ErrorEnqueue(t *testing.T) {
|
|
t.Skip()
|
|
// controller := gomock.NewController(t)
|
|
// defer controller.Finish()
|
|
|
|
// mockUsers := mock.NewMockUserStore(controller)
|
|
// mockUsers.EXPECT().Find(noContext, dummyRepo.UserID).Return(dummyUser, nil)
|
|
|
|
// mockTriggers := mock.NewMockTriggerStore(controller)
|
|
// mockTriggers.EXPECT().List(noContext, dummyRepo.ID).Return([]*core.Trigger{dummyTrigger}, nil)
|
|
|
|
// mockRepos := mock.NewMockRepositoryStore(controller)
|
|
// mockRepos.EXPECT().Increment(gomock.Any(), dummyRepo).Return(dummyRepo, nil)
|
|
|
|
// mockContents := mock.NewMockContentService(controller)
|
|
// mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummyTrigger.Path, dummyHook.After).Return(dummyYaml, nil, nil)
|
|
// mockContents.EXPECT().Find(gomock.Any(), dummyRepo.Slug, dummySignature.Path, dummyHook.After).Return(dummySignature, nil, nil)
|
|
|
|
// mockClient := new(scm.Client)
|
|
// mockClient.Contents = mockContents
|
|
|
|
// mockQueue := mock.NewMockQueue(controller)
|
|
// mockQueue.EXPECT().Push(gomock.Any(), gomock.Any()).Return(sql.ErrNoRows)
|
|
|
|
// mockBuilds := mock.NewMockBuildStore(controller)
|
|
// mockBuilds.EXPECT().Create(gomock.Any(), gomock.Any()).Return(nil)
|
|
|
|
// triggerer := New(
|
|
// mockClient,
|
|
// mockBuilds,
|
|
// mockQueue,
|
|
// mockRepos,
|
|
// mockTriggers,
|
|
// mockUsers,
|
|
// )
|
|
|
|
// builds, err := triggerer.Trigger(noContext, dummyRepo, dummyHook)
|
|
// if err != sql.ErrNoRows {
|
|
// t.Error("Expect error when enqueueing the build fails")
|
|
// }
|
|
// if got, want := len(builds), 0; got != want {
|
|
// t.Errorf("Got build count %d, want %d", got, want)
|
|
// }
|
|
}
|
|
|
|
var (
|
|
dummyHook = &core.Hook{
|
|
Event: core.EventPush,
|
|
Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
|
|
Timestamp: 1299283200,
|
|
Message: "first commit",
|
|
Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e",
|
|
After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
|
|
Ref: "refs/heads/master",
|
|
Source: "master",
|
|
Target: "master",
|
|
Author: "octocat",
|
|
AuthorName: "The Octocat",
|
|
AuthorEmail: "octocat@hello-world.com",
|
|
AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231",
|
|
Sender: "octocat",
|
|
Action: "opened",
|
|
}
|
|
|
|
dummyBuild = &core.Build{
|
|
Number: dummyRepo.Counter,
|
|
RepoID: dummyRepo.ID,
|
|
Status: core.StatusPending,
|
|
Event: core.EventPush,
|
|
Link: "https://github.com/octocat/Hello-World/commit/7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
|
|
// Timestamp: 1299283200,
|
|
Message: "first commit",
|
|
Before: "553c2077f0edc3d5dc5d17262f6aa498e69d6f8e",
|
|
After: "7fd1a60b01f91b314f59955a4e4d4e80d8edf11d",
|
|
Ref: "refs/heads/master",
|
|
Source: "master",
|
|
Target: "master",
|
|
Author: "octocat",
|
|
AuthorName: "The Octocat",
|
|
AuthorEmail: "octocat@hello-world.com",
|
|
AuthorAvatar: "https://avatars3.githubusercontent.com/u/583231",
|
|
Sender: "octocat",
|
|
Action: "opened",
|
|
}
|
|
|
|
dummyRepo = &core.Repository{
|
|
ID: 1,
|
|
UID: "1296269",
|
|
UserID: 2,
|
|
Namespace: "octocat",
|
|
Name: "Hello-World",
|
|
Slug: "octocat/Hello-World",
|
|
SCM: "git",
|
|
HTTPURL: "https://github.com/octocat/Hello-World.git",
|
|
SSHURL: "git@github.com:octocat/Hello-World.git",
|
|
Link: "https://github.com/octocat/Hello-World",
|
|
Branch: "master",
|
|
Private: false,
|
|
Visibility: core.VisibilityPublic,
|
|
Active: true,
|
|
Counter: 42,
|
|
Secret: "g9dMChy22QutQM5lrpbe0yCR3f15t1gv",
|
|
Signer: "g9dMChy22QutQM5lrpbe0yCR3f15t1gv",
|
|
Config: ".drone.yml",
|
|
}
|
|
|
|
dummyStage = &core.Stage{
|
|
Kind: "pipeline",
|
|
Type: "docker",
|
|
RepoID: 1,
|
|
Name: "default",
|
|
Number: 1,
|
|
OS: "linux",
|
|
Arch: "amd64",
|
|
OnSuccess: true,
|
|
OnFailure: false,
|
|
Status: core.StatusPending,
|
|
}
|
|
|
|
dummyStages = []*core.Stage{
|
|
dummyStage,
|
|
}
|
|
|
|
dummyUser = &core.User{
|
|
ID: 2,
|
|
Login: "octocat",
|
|
Active: true,
|
|
}
|
|
|
|
dummyYaml = &core.Config{
|
|
Data: "kind: pipeline\nsteps: [ ]",
|
|
}
|
|
|
|
dummyYamlInvalid = &core.Config{
|
|
Data: "%ERROR",
|
|
}
|
|
|
|
dummyYamlSkipBranch = &core.Config{
|
|
Data: `
|
|
kind: pipeline
|
|
trigger:
|
|
branch:
|
|
exclude:
|
|
- master`,
|
|
}
|
|
|
|
dummyYamlSkipEvent = &core.Config{
|
|
Data: `
|
|
kind: pipeline
|
|
trigger:
|
|
event:
|
|
exclude:
|
|
- push`,
|
|
}
|
|
|
|
dummyYamlSkipAction = &core.Config{
|
|
Data: `
|
|
kind: pipeline
|
|
trigger:
|
|
action:
|
|
exclude:
|
|
- opened`,
|
|
}
|
|
|
|
ignoreBuildFields = cmpopts.IgnoreFields(core.Build{},
|
|
"Created", "Updated")
|
|
|
|
ignoreStageFields = cmpopts.IgnoreFields(core.Stage{},
|
|
"Created", "Updated")
|
|
)
|