389 lines
11 KiB
Go
389 lines
11 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.
|
||
|
|
||
|
package syncer
|
||
|
|
||
|
import (
|
||
|
"context"
|
||
|
"database/sql"
|
||
|
"io/ioutil"
|
||
|
"testing"
|
||
|
|
||
|
"github.com/drone/drone/core"
|
||
|
"github.com/drone/drone/mock"
|
||
|
"github.com/drone/go-scm/scm"
|
||
|
"github.com/sirupsen/logrus"
|
||
|
|
||
|
"github.com/golang/mock/gomock"
|
||
|
"github.com/google/go-cmp/cmp"
|
||
|
"github.com/google/go-cmp/cmp/cmpopts"
|
||
|
)
|
||
|
|
||
|
// TODO(bradrydzewski) test failure to update user
|
||
|
// TODO(bradrydzewski) test recover from unexpected panic
|
||
|
|
||
|
var noContext = context.Background()
|
||
|
|
||
|
func init() {
|
||
|
logrus.SetOutput(ioutil.Discard)
|
||
|
}
|
||
|
|
||
|
func TestSync(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
batcher := mock.NewMockBatcher(controller)
|
||
|
batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{}, nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{
|
||
|
{
|
||
|
UID: "1",
|
||
|
Slug: "octocat/hello-world",
|
||
|
Namespace: "octocat",
|
||
|
Name: "hello-world",
|
||
|
Private: false,
|
||
|
Visibility: core.VisibilityPublic,
|
||
|
},
|
||
|
}, nil)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
repoStore,
|
||
|
userStore,
|
||
|
batcher,
|
||
|
)
|
||
|
got, err := s.Sync(context.Background(), user)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
want := &core.Batch{
|
||
|
Insert: []*core.Repository{
|
||
|
{
|
||
|
UID: "1",
|
||
|
Namespace: "octocat",
|
||
|
Name: "hello-world",
|
||
|
Slug: "octocat/hello-world",
|
||
|
Visibility: core.VisibilityPublic,
|
||
|
Version: 1,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
ignore := cmpopts.IgnoreFields(core.Repository{},
|
||
|
"Synced", "Created", "Updated")
|
||
|
if diff := cmp.Diff(got, want, ignore); len(diff) != 0 {
|
||
|
t.Errorf(diff)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that we are able to recognize when
|
||
|
// a repository has been updated.
|
||
|
func TestSync_Update(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
batcher := mock.NewMockBatcher(controller)
|
||
|
batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{
|
||
|
{UID: "1", Namespace: "octocat", Name: "hello-world"},
|
||
|
{UID: "2", Namespace: "octocat", Name: "Spoon-Knife", Private: false},
|
||
|
}, nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{
|
||
|
{
|
||
|
UID: "1",
|
||
|
Slug: "octocat/hello-world",
|
||
|
Namespace: "octocat",
|
||
|
Name: "hello-world",
|
||
|
},
|
||
|
{
|
||
|
UID: "2",
|
||
|
Slug: "octocat/Spoon-Knife",
|
||
|
Namespace: "octocat",
|
||
|
Name: "Spoon-Knife",
|
||
|
Private: true,
|
||
|
},
|
||
|
}, nil)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
repoStore,
|
||
|
userStore,
|
||
|
batcher,
|
||
|
)
|
||
|
got, err := s.Sync(context.Background(), user)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
|
||
|
want := &core.Batch{
|
||
|
Update: []*core.Repository{
|
||
|
{
|
||
|
UID: "2",
|
||
|
Namespace: "octocat",
|
||
|
Name: "Spoon-Knife",
|
||
|
Slug: "octocat/Spoon-Knife",
|
||
|
Private: true,
|
||
|
},
|
||
|
},
|
||
|
}
|
||
|
|
||
|
ignore := cmpopts.IgnoreFields(core.Repository{},
|
||
|
"Synced", "Created", "Updated")
|
||
|
if diff := cmp.Diff(got, want, ignore); len(diff) != 0 {
|
||
|
t.Errorf(diff)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that we are able to recognize when
|
||
|
// a repository has been renamed.
|
||
|
func TestSync_Rename(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
batcher := mock.NewMockBatcher(controller)
|
||
|
batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{
|
||
|
{ID: 101, UID: "1", Namespace: "octocat", Name: "hello-world"},
|
||
|
{ID: 102, UID: "2", Namespace: "octocat", Name: "Spoon-Knife"},
|
||
|
}, nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{
|
||
|
{
|
||
|
UID: "1",
|
||
|
Slug: "octocat/hello-world",
|
||
|
Namespace: "octocat",
|
||
|
Name: "hello-world",
|
||
|
},
|
||
|
{
|
||
|
UID: "2",
|
||
|
Slug: "octocat/Spoon-Knife",
|
||
|
Namespace: "octocat",
|
||
|
Name: "Spork-Knife",
|
||
|
},
|
||
|
}, nil)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
repoStore,
|
||
|
userStore,
|
||
|
batcher,
|
||
|
)
|
||
|
got, err := s.Sync(context.Background(), user)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
want := &core.Batch{
|
||
|
Update: []*core.Repository{
|
||
|
{ID: 102, UID: "2", Namespace: "octocat", Name: "Spork-Knife", Slug: "octocat/Spork-Knife"},
|
||
|
},
|
||
|
}
|
||
|
ignore := cmpopts.IgnoreFields(core.Repository{},
|
||
|
"Synced", "Created", "Updated")
|
||
|
if diff := cmp.Diff(got, want, ignore); len(diff) != 0 {
|
||
|
t.Errorf(diff)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that we are able to recognize when
|
||
|
// the user permission to the repository have been revoked.
|
||
|
func TestSync_Revoke(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
batcher := mock.NewMockBatcher(controller)
|
||
|
batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return([]*core.Repository{
|
||
|
{UID: "1", Namespace: "octocat", Name: "hello-world"},
|
||
|
}, nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{}, nil)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
repoStore,
|
||
|
userStore,
|
||
|
batcher,
|
||
|
)
|
||
|
got, err := s.Sync(context.Background(), user)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
want := &core.Batch{
|
||
|
Revoke: []*core.Repository{
|
||
|
{UID: "1", Namespace: "octocat", Name: "hello-world"},
|
||
|
},
|
||
|
}
|
||
|
if diff := cmp.Diff(got, want); len(diff) != 0 {
|
||
|
t.Errorf(diff)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that we invoke the batch update even
|
||
|
// if there are no batch updates to make. This is important
|
||
|
// because the batcher resets permissions and forces Drone
|
||
|
// to re-synchrnoize.
|
||
|
func TestSync_EmptyBatch(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
batcher := mock.NewMockBatcher(controller)
|
||
|
batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return(nil, nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, nil)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
repoStore,
|
||
|
userStore,
|
||
|
batcher,
|
||
|
)
|
||
|
batch, err := s.Sync(context.Background(), user)
|
||
|
if err != nil {
|
||
|
t.Error(err)
|
||
|
}
|
||
|
if want, got := len(batch.Insert), 0; got != want {
|
||
|
t.Errorf("Want %d batch inserts, got %d", want, got)
|
||
|
}
|
||
|
if want, got := len(batch.Update), 0; got != want {
|
||
|
t.Errorf("Want %d batch updates, got %d", want, got)
|
||
|
}
|
||
|
if want, got := len(batch.Revoke), 0; got != want {
|
||
|
t.Errorf("Want %d batch revokes, got %d", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that an error returned by the source
|
||
|
// code management system causes the synchronization process to
|
||
|
// exit and is returned to the caller.
|
||
|
func TestSync_RemoteError(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return(nil, scm.ErrNotFound)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
nil,
|
||
|
userStore,
|
||
|
nil,
|
||
|
)
|
||
|
_, err := s.Sync(context.Background(), user)
|
||
|
if got, want := err, scm.ErrNotFound; got != want {
|
||
|
t.Errorf("Want error %s, got %s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that an error returned by the internal
|
||
|
// repository datastore causes the synchronization process to
|
||
|
// exit and is returned to the caller.
|
||
|
func TestSync_StoreError(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{}, nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, sql.ErrNoRows)
|
||
|
|
||
|
s := Synchronizer{
|
||
|
repoz: repoService,
|
||
|
users: userStore,
|
||
|
repos: repoStore,
|
||
|
}
|
||
|
_, err := s.Sync(context.Background(), user)
|
||
|
if got, want := err, sql.ErrNoRows; got != want {
|
||
|
t.Errorf("Want error %s, got %s", want, got)
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// this test verifies that an error returned by the batcher
|
||
|
// causes the synchronization process to exit and is returned
|
||
|
// to the caller.
|
||
|
func TestSync_BatchError(t *testing.T) {
|
||
|
controller := gomock.NewController(t)
|
||
|
defer controller.Finish()
|
||
|
|
||
|
user := &core.User{ID: 1}
|
||
|
userStore := mock.NewMockUserStore(controller)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
userStore.EXPECT().Update(gomock.Any(), user).Return(nil)
|
||
|
|
||
|
repoService := mock.NewMockRepositoryService(controller)
|
||
|
repoService.EXPECT().List(gomock.Any(), user).Return([]*core.Repository{}, nil)
|
||
|
|
||
|
repoStore := mock.NewMockRepositoryStore(controller)
|
||
|
repoStore.EXPECT().List(gomock.Any(), gomock.Any()).Return(nil, nil)
|
||
|
|
||
|
batcher := mock.NewMockBatcher(controller)
|
||
|
batcher.EXPECT().Batch(gomock.Any(), gomock.Any(), gomock.Any()).Return(sql.ErrNoRows)
|
||
|
|
||
|
s := New(
|
||
|
repoService,
|
||
|
repoStore,
|
||
|
userStore,
|
||
|
batcher,
|
||
|
)
|
||
|
_, err := s.Sync(context.Background(), user)
|
||
|
if got, want := err, sql.ErrNoRows; got != want {
|
||
|
t.Errorf("Want error %s, got %s", want, got)
|
||
|
}
|
||
|
}
|