// 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 batch import ( "context" "database/sql" "testing" "github.com/drone/drone/core" "github.com/drone/drone/store/perm" "github.com/drone/drone/store/repos" "github.com/drone/drone/store/shared/db" "github.com/drone/drone/store/shared/db/dbtest" "github.com/drone/drone/store/shared/encrypt" "github.com/drone/drone/store/user" ) var noContext = context.TODO() func TestBatch(t *testing.T) { conn, err := dbtest.Connect() if err != nil { t.Error(err) return } defer func() { dbtest.Reset(conn) dbtest.Disconnect(conn) }() batcher := New(conn).(*batchUpdater) repos := repos.New(conn) perms := perm.New(conn) user, err := seedUser(batcher.db) if err != nil { t.Error(err) } t.Run("Insert", testBatchInsert(batcher, repos, perms, user)) t.Run("Update", testBatchUpdate(batcher, repos, perms, user)) t.Run("Delete", testBatchDelete(batcher, repos, perms, user)) t.Run("DuplicateID", testBatchDuplicateID(batcher, repos, perms, user)) t.Run("DuplicateSlug", testBatchDuplicateSlug(batcher, repos, perms, user)) t.Run("DuplicateRename", testBatchDuplicateRename(batcher, repos, perms, user)) } func testBatchInsert( batcher core.Batcher, repos core.RepositoryStore, perms core.PermStore, user *core.User, ) func(t *testing.T) { return func(t *testing.T) { batch := &core.Batch{ Insert: []*core.Repository{ { UserID: 1, UID: "42", Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", Private: false, Visibility: "public", }, }, } err := batcher.Batch(noContext, user, batch) if err != nil { t.Error(err) } repo, err := repos.FindName(noContext, "octocat", "hello-world") if err != nil { t.Errorf("Want repository, got error %q", err) } _, err = perms.Find(noContext, repo.UID, user.ID) if err != nil { t.Errorf("Want permissions, got error %q", err) } } } func testBatchUpdate( batcher core.Batcher, repos core.RepositoryStore, perms core.PermStore, user *core.User, ) func(t *testing.T) { return func(t *testing.T) { before, err := repos.FindName(noContext, "octocat", "hello-world") if err != nil { t.Errorf("Want repository, got error %q", err) } batch := &core.Batch{ Update: []*core.Repository{ { ID: before.ID, UserID: 1, UID: "42", Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", Private: true, }, }, } err = batcher.Batch(noContext, user, batch) if err != nil { t.Error(err) } after, err := repos.FindName(noContext, "octocat", "hello-world") if err != nil { t.Errorf("Want repository, got error %q", err) } if got, want := after.Private, true; got != want { t.Errorf("Want repository Private %v, got %v", want, got) } } } func testBatchDelete( batcher core.Batcher, repos core.RepositoryStore, perms core.PermStore, user *core.User, ) func(t *testing.T) { return func(t *testing.T) { repo, err := repos.FindName(noContext, "octocat", "hello-world") if err != nil { t.Errorf("Want repository, got error %q", err) } _, err = perms.Find(noContext, repo.UID, user.ID) if err != nil { t.Errorf("Want permissions, got error %q", err) } batch := &core.Batch{ Revoke: []*core.Repository{ { ID: repo.ID, UserID: 1, UID: "42", Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", Private: true, }, }, } err = batcher.Batch(noContext, user, batch) if err != nil { t.Error(err) } _, err = perms.Find(noContext, repo.UID, user.ID) if err != sql.ErrNoRows { t.Errorf("Want sql.ErrNoRows got %v", err) } } } func testBatchDuplicateID( batcher core.Batcher, repos core.RepositoryStore, perms core.PermStore, user *core.User, ) func(t *testing.T) { return func(t *testing.T) { before, err := repos.FindName(noContext, "octocat", "hello-world") if err != nil { t.Errorf("Want repository, got error %q", err) } batch := &core.Batch{ Insert: []*core.Repository{ { ID: 0, UserID: 1, UID: "43", // Updated ID Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", }, { ID: 0, UserID: 1, UID: "43", // Updated ID Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", }, { ID: 0, UserID: 1, UID: "64778136", Namespace: "octocat", Name: "linguist", Slug: "octocat/linguist", }, }, Update: []*core.Repository{ { ID: before.ID, UserID: 1, UID: "44", // Updated ID Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", Private: true, }, }, } err = batcher.Batch(noContext, user, batch) if err != nil { t.Error(err) } added, err := repos.FindName(noContext, "octocat", "linguist") if err != nil { t.Errorf("Want repository, got error %q", err) } if got, want := added.UID, "64778136"; got != want { t.Errorf("Want added repository UID %v, got %v", want, got) } } } // the purpose of this unit test is to understand what happens // when a repository is deleted, re-created with the same name, // but has a different unique identifier. func testBatchDuplicateSlug( batcher core.Batcher, repos core.RepositoryStore, perms core.PermStore, user *core.User, ) func(t *testing.T) { return func(t *testing.T) { _, err := repos.FindName(noContext, "octocat", "hello-world") if err != nil { t.Errorf("Want repository, got error %q", err) } batch := &core.Batch{ Insert: []*core.Repository{ { ID: 0, UserID: 1, UID: "99", // Updated ID Namespace: "octocat", Name: "hello-world", Slug: "octocat/hello-world", }, }, } err = batcher.Batch(noContext, user, batch) if err != nil { t.Error(err) } } } // the purpose of this unit test is to understand what happens // when a repository is deleted, re-created with a new name, and // then updated back to the old name. // // TODO(bradrydzewski) for sqlite consider UPDATE OR REPLACE. // TODO(bradrydzewski) for mysql consider UPDATE IGNORE. // TODO(bradrydzewski) consider breaking rename into a separate set of logic that checks for existing records. func testBatchDuplicateRename( batcher core.Batcher, repos core.RepositoryStore, perms core.PermStore, user *core.User, ) func(t *testing.T) { return func(t *testing.T) { batch := &core.Batch{ Insert: []*core.Repository{ { ID: 0, UserID: 1, UID: "200", Namespace: "octocat", Name: "test-1", Slug: "octocat/test-1", }, { ID: 0, UserID: 1, UID: "201", Namespace: "octocat", Name: "test-2", Slug: "octocat/test-2", }, }, } err := batcher.Batch(noContext, user, batch) if err != nil { t.Error(err) return } before, err := repos.FindName(noContext, "octocat", "test-2") if err != nil { t.Errorf("Want repository, got error %q", err) return } before.Name = "test-1" before.Slug = "octocat/test-1" batch = &core.Batch{ Update: []*core.Repository{before}, } err = batcher.Batch(noContext, user, batch) if err != nil { t.Skip(err) } } } func seedUser(db *db.DB) (*core.User, error) { enc, _ := encrypt.New("") out := &core.User{Login: "octocat"} err := user.New(db, enc).Create(noContext, out) return out, err }