472 lines
9.9 KiB
Go
472 lines
9.9 KiB
Go
package approvals
|
|
|
|
import (
|
|
"context"
|
|
"io/ioutil"
|
|
"log"
|
|
"os"
|
|
"path/filepath"
|
|
"testing"
|
|
"time"
|
|
|
|
_ "github.com/jinzhu/gorm/dialects/sqlite"
|
|
|
|
"github.com/keel-hq/keel/pkg/store/sql"
|
|
"github.com/keel-hq/keel/types"
|
|
)
|
|
|
|
func NewTestingUtils() (*sql.SQLStore, func()) {
|
|
dir, err := ioutil.TempDir("", "whstoretest")
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
tmpfn := filepath.Join(dir, "gorm.db")
|
|
// defer
|
|
store, err := sql.New(sql.Opts{DatabaseType: "sqlite3", URI: tmpfn})
|
|
if err != nil {
|
|
log.Fatal(err)
|
|
}
|
|
|
|
teardown := func() {
|
|
os.RemoveAll(dir) // clean up
|
|
}
|
|
|
|
return store, teardown
|
|
}
|
|
|
|
func TestCreateApproval(t *testing.T) {
|
|
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
stored, err := am.Get("xxx/app-1")
|
|
if err != nil {
|
|
t.Fatalf("failed to get approval: %s", err)
|
|
}
|
|
|
|
if stored.CurrentVersion != "1.2.3" {
|
|
t.Errorf("unexpected version: %s", stored.CurrentVersion)
|
|
}
|
|
}
|
|
|
|
func TestDeleteApproval(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
stored, err := am.Get("xxx/app-1")
|
|
if err != nil {
|
|
t.Fatalf("didn't find approval: %s", err)
|
|
}
|
|
|
|
err = am.Delete(stored)
|
|
if err != nil {
|
|
t.Errorf("failed to delete approval: %s", err)
|
|
}
|
|
|
|
_, err = am.Get("xxx/app-1")
|
|
if err == nil {
|
|
t.Errorf("expected to get an error when retrieving deleted approval")
|
|
}
|
|
|
|
}
|
|
|
|
func TestUpdateApproval(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
VotesRequired: 1,
|
|
VotesReceived: 0,
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "very/repo",
|
|
Tag: "1.2.5",
|
|
},
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
ch, err := am.SubscribeApproved(ctx)
|
|
if err != nil {
|
|
t.Fatalf("failed to subscribe: %s", err)
|
|
}
|
|
|
|
err = am.Update(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
VotesRequired: 1,
|
|
VotesReceived: 1,
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "very/repo",
|
|
Tag: "1.2.5",
|
|
},
|
|
},
|
|
})
|
|
|
|
approved := <-ch
|
|
|
|
if approved.Event.Repository.Name != "very/repo" {
|
|
t.Errorf("unexpected repo name in re-submitted event: %s", approved.Event.Repository.Name)
|
|
}
|
|
if approved.Event.Repository.Tag != "1.2.5" {
|
|
t.Errorf("unexpected repo tag in re-submitted event: %s", approved.Event.Repository.Tag)
|
|
}
|
|
}
|
|
|
|
func TestUpdateApprovalRejected(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
VotesRequired: 1,
|
|
VotesReceived: 0,
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "very/repo",
|
|
Tag: "1.2.5",
|
|
},
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
created, _ := am.Get("xxx/app-1")
|
|
|
|
ctx, cancel := context.WithCancel(context.Background())
|
|
defer cancel()
|
|
ch, err := am.SubscribeApproved(ctx)
|
|
if err != nil {
|
|
t.Fatalf("failed to subscribe: %s", err)
|
|
}
|
|
|
|
// rejecting
|
|
err = am.Update(&types.Approval{
|
|
ID: created.ID,
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
VotesRequired: 1,
|
|
VotesReceived: 0,
|
|
Rejected: true,
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "very/repo",
|
|
Tag: "1.2.5",
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to update approval: %s", err)
|
|
}
|
|
|
|
// sending vote
|
|
err = am.Update(&types.Approval{
|
|
ID: created.ID,
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
VotesRequired: 1,
|
|
VotesReceived: 1,
|
|
Rejected: true,
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "very/repo",
|
|
Tag: "1.2.5",
|
|
},
|
|
},
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("failed to update approval: %s", err)
|
|
}
|
|
|
|
select {
|
|
case <-time.After(500 * time.Millisecond):
|
|
// success
|
|
return
|
|
case approval := <-ch:
|
|
t.Errorf("unexpected approval got: %s", approval.Identifier)
|
|
}
|
|
|
|
}
|
|
|
|
func TestApprove(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1:1.2.5",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
am.Approve("xxx/app-1:1.2.5", "warda")
|
|
|
|
stored, err := am.Get("xxx/app-1:1.2.5")
|
|
if err != nil {
|
|
t.Fatalf("failed to get approval: %s", err)
|
|
}
|
|
|
|
if stored.VotesReceived != 1 {
|
|
t.Errorf("unexpected number of received votes: %d", stored.VotesReceived)
|
|
}
|
|
}
|
|
|
|
func TestApproveTwiceSameVoter(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1:1.2.5",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
am.Approve("xxx/app-1:1.2.5", "warda")
|
|
am.Approve("xxx/app-1:1.2.5", "warda")
|
|
|
|
stored, err := am.Get("xxx/app-1:1.2.5")
|
|
if err != nil {
|
|
t.Fatalf("failed to get approval: %s", err)
|
|
}
|
|
|
|
// should still be the same
|
|
if stored.VotesReceived != 1 {
|
|
t.Errorf("unexpected number of received votes: %d", stored.VotesReceived)
|
|
}
|
|
}
|
|
|
|
func TestApproveTwoVoters(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1:1.2.5",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
am.Approve("xxx/app-1:1.2.5", "w")
|
|
am.Approve("xxx/app-1:1.2.5", "k")
|
|
|
|
stored, err := am.Get("xxx/app-1:1.2.5")
|
|
if err != nil {
|
|
t.Fatalf("failed to get approval: %s", err)
|
|
}
|
|
|
|
// should still be the same
|
|
if stored.VotesReceived != 2 {
|
|
t.Errorf("unexpected number of received votes: %d", stored.VotesReceived)
|
|
}
|
|
}
|
|
|
|
func TestReject(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(5 * time.Minute),
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
am.Reject("xxx/app-1")
|
|
|
|
stored, err := am.Get("xxx/app-1")
|
|
if err != nil {
|
|
t.Fatalf("failed to get approval: %s", err)
|
|
}
|
|
|
|
if !stored.Rejected {
|
|
t.Errorf("unexpected approval to be rejected")
|
|
}
|
|
}
|
|
|
|
func TestExpire(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Deadline: time.Now().Add(-5 * time.Minute),
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create approval: %s", err)
|
|
}
|
|
|
|
err = am.expireEntries()
|
|
if err != nil {
|
|
t.Errorf("got error while expiring entries: %s", err)
|
|
}
|
|
|
|
_, err = am.Get("xxx/app-1")
|
|
if err == nil {
|
|
t.Errorf("expected approval to be deleted but didn't get an error")
|
|
}
|
|
}
|
|
|
|
func TestGetArchived(t *testing.T) {
|
|
store, teardown := NewTestingUtils()
|
|
defer teardown()
|
|
|
|
am := New(&Opts{
|
|
Store: store,
|
|
})
|
|
|
|
err := am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Archived: true,
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create first approval: %s", err)
|
|
}
|
|
|
|
err = am.Create(&types.Approval{
|
|
Provider: types.ProviderTypeKubernetes,
|
|
Identifier: "xxx/app-1",
|
|
CurrentVersion: "1.2.3",
|
|
NewVersion: "1.2.5",
|
|
Archived: false,
|
|
VotesRequired: 2,
|
|
VotesReceived: 0,
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("failed to create second (not archived )approval: %s", err)
|
|
}
|
|
|
|
secondStore, err := am.Get("xxx/app-1")
|
|
if err != nil {
|
|
t.Errorf("expected approval to be deleted but didn't get an error")
|
|
}
|
|
if secondStore.Archived {
|
|
t.Errorf("didn't expect approval to be archived")
|
|
}
|
|
}
|