300 lines
7.4 KiB
Go
300 lines
7.4 KiB
Go
package hipchat
|
|
|
|
import (
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"regexp"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
h "github.com/daneharrigan/hipchat"
|
|
|
|
"github.com/keel-hq/keel/approvals"
|
|
b "github.com/keel-hq/keel/bot"
|
|
"github.com/keel-hq/keel/pkg/store/sql"
|
|
|
|
"github.com/keel-hq/keel/provider/kubernetes"
|
|
"github.com/keel-hq/keel/types"
|
|
|
|
testutil "github.com/keel-hq/keel/util/testing"
|
|
|
|
log "github.com/sirupsen/logrus"
|
|
)
|
|
|
|
type fakeProvider struct {
|
|
submitted []types.Event
|
|
images []*types.TrackedImage
|
|
}
|
|
|
|
func (p *fakeProvider) Submit(event types.Event) error {
|
|
p.submitted = append(p.submitted, event)
|
|
return nil
|
|
}
|
|
|
|
func (p *fakeProvider) TrackedImages() ([]*types.TrackedImage, error) {
|
|
return p.images, nil
|
|
}
|
|
|
|
func (p *fakeProvider) List() []string {
|
|
return []string{"fakeprovider"}
|
|
}
|
|
func (p *fakeProvider) Stop() {
|
|
return
|
|
}
|
|
func (p *fakeProvider) GetName() string {
|
|
return "fp"
|
|
}
|
|
|
|
var botMessagesChannel chan *b.BotMessage
|
|
var approvalsRespCh chan *b.ApprovalResponse
|
|
|
|
type postedMessage struct {
|
|
channel string
|
|
text string
|
|
}
|
|
|
|
type fakeXmppImplementer struct {
|
|
postedMessages []postedMessage
|
|
messages chan *h.Message
|
|
}
|
|
|
|
func (i *fakeXmppImplementer) messageFromChat(message string) {
|
|
i.messages <- &h.Message{
|
|
Body: "@keel " + message,
|
|
From: "111111_approvals@conf.hipchat.com/test",
|
|
To: "222222_333333@chat.hipchat.com/bot",
|
|
}
|
|
}
|
|
|
|
func (i *fakeXmppImplementer) Say(roomID, name, body string) {
|
|
i.postedMessages = append(i.postedMessages, postedMessage{
|
|
text: body,
|
|
channel: roomID,
|
|
})
|
|
}
|
|
func (i *fakeXmppImplementer) Status(s string) {
|
|
}
|
|
func (i *fakeXmppImplementer) Join(roomID, resource string) {
|
|
}
|
|
func (i *fakeXmppImplementer) KeepAlive() {
|
|
}
|
|
func (i *fakeXmppImplementer) Messages() <-chan *h.Message {
|
|
return i.messages
|
|
}
|
|
|
|
func NewBot(k8sImplementer kubernetes.Implementer,
|
|
approvalsManager approvals.Manager, fi XmppImplementer) *Bot {
|
|
|
|
approvalsRespCh = make(chan *b.ApprovalResponse)
|
|
botMessagesChannel = make(chan *b.BotMessage)
|
|
fakeBot := &Bot{}
|
|
fakeBot.hipchatClient = fi
|
|
|
|
os.Setenv("HIPCHAT_APPROVALS_CHANNEL", "111111_approvals@conf.hipchat.com")
|
|
os.Setenv("HIPCHAT_APPROVALS_BOT_NAME", "keel")
|
|
os.Setenv("HIPCHAT_APPROVALS_USER_NAME", "111111_222222")
|
|
os.Setenv("HIPCHAT_APPROVALS_PASSWORT", "pass")
|
|
os.Setenv("HIPCHAT_CONNECTION_ATTEMPTS", "0")
|
|
|
|
b.RegisterBot("fakechat", fakeBot)
|
|
b.Run(k8sImplementer, approvalsManager)
|
|
return fakeBot
|
|
}
|
|
|
|
func init() {
|
|
log.SetLevel(log.DebugLevel)
|
|
}
|
|
|
|
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 TestHelpCommand(t *testing.T) {
|
|
f8s := &testutil.FakeK8sImplementer{}
|
|
fi := &fakeXmppImplementer{}
|
|
fi.messages = make(chan *h.Message)
|
|
|
|
store, teardown := newTestingUtils()
|
|
defer teardown()
|
|
am := approvals.New(&approvals.Opts{
|
|
Store: store,
|
|
})
|
|
|
|
NewBot(f8s, am, fi)
|
|
defer b.Stop()
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if len(fi.postedMessages) != 1 {
|
|
t.Errorf("expected to find 1 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
if !strings.HasPrefix(fi.postedMessages[0].text, "Keel bot was started") {
|
|
t.Errorf("expected to find greeting message, but got: %s", fi.postedMessages[0].text)
|
|
}
|
|
|
|
fi.messageFromChat("help")
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if len(fi.postedMessages) != 2 {
|
|
t.Errorf("expected to find 2 messages, but got: %d", len(fi.postedMessages))
|
|
}
|
|
if !strings.HasPrefix(fi.postedMessages[1].text, "/code Here's a list of supported commands") {
|
|
t.Errorf("expected to find help message, but got: %s", fi.postedMessages[1].text)
|
|
}
|
|
}
|
|
|
|
func TestBotAproval(t *testing.T) {
|
|
f8s := &testutil.FakeK8sImplementer{}
|
|
fi := &fakeXmppImplementer{}
|
|
fi.messages = make(chan *h.Message)
|
|
store, teardown := newTestingUtils()
|
|
defer teardown()
|
|
am := approvals.New(&approvals.Opts{
|
|
Store: store,
|
|
})
|
|
|
|
NewBot(f8s, am, fi)
|
|
defer b.Stop()
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
err := am.Create(&types.Approval{
|
|
Identifier: "k8s/project/repo:1.2.3",
|
|
VotesRequired: 1,
|
|
CurrentVersion: "2.3.4",
|
|
NewVersion: "3.4.5",
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "project/repo",
|
|
Tag: "2.3.4",
|
|
},
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error while creating : %s", err)
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if len(fi.postedMessages) != 2 {
|
|
t.Errorf("expected to find 2 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
if !strings.HasPrefix(fi.postedMessages[1].text, "/code Approval required!") {
|
|
t.Errorf("expected to find help message, but got: %s", fi.postedMessages[1].text)
|
|
}
|
|
|
|
// approve
|
|
fi.messageFromChat("approve k8s/project/repo:1.2.3")
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if len(fi.postedMessages) != 3 {
|
|
t.Errorf("expected to find 3 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
if !strings.HasPrefix(fi.postedMessages[2].text, "/code Update approved!") {
|
|
t.Errorf("expected to find message, but got: %s", fi.postedMessages[2].text)
|
|
}
|
|
|
|
// get approvals
|
|
fi.messageFromChat("get approvals")
|
|
time.Sleep(1 * time.Second)
|
|
if len(fi.postedMessages) != 4 {
|
|
t.Errorf("expected to find 4 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
resp := trimSpaces(fi.postedMessages[3].text)
|
|
|
|
if !strings.Contains(resp, "k8s/project/repo:1.2.3 2.3.4 -> 3.4.5 1/1 false") {
|
|
t.Errorf("expected to find message, but got: %s", resp)
|
|
}
|
|
}
|
|
|
|
func TestBotReject(t *testing.T) {
|
|
f8s := &testutil.FakeK8sImplementer{}
|
|
fi := &fakeXmppImplementer{}
|
|
fi.messages = make(chan *h.Message)
|
|
store, teardown := newTestingUtils()
|
|
defer teardown()
|
|
am := approvals.New(&approvals.Opts{
|
|
Store: store,
|
|
})
|
|
|
|
NewBot(f8s, am, fi)
|
|
defer b.Stop()
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
err := am.Create(&types.Approval{
|
|
Identifier: "k8s/project/repo:1.2.3",
|
|
VotesRequired: 1,
|
|
CurrentVersion: "2.3.4",
|
|
NewVersion: "3.4.5",
|
|
Event: &types.Event{
|
|
Repository: types.Repository{
|
|
Name: "project/repo",
|
|
Tag: "2.3.4",
|
|
},
|
|
},
|
|
})
|
|
|
|
if err != nil {
|
|
t.Fatalf("unexpected error while creating : %s", err)
|
|
}
|
|
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if len(fi.postedMessages) != 2 {
|
|
t.Errorf("expected to find 2 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
if !strings.HasPrefix(fi.postedMessages[1].text, "/code Approval required!") {
|
|
t.Errorf("expected to find help message, but got: %s", fi.postedMessages[1].text)
|
|
}
|
|
|
|
// reject
|
|
fi.messageFromChat("reject k8s/project/repo:1.2.3")
|
|
time.Sleep(1 * time.Second)
|
|
|
|
if len(fi.postedMessages) != 3 {
|
|
t.Errorf("expected to find 3 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
if !strings.HasPrefix(fi.postedMessages[2].text, "/code Change rejected") {
|
|
t.Errorf("expected to find message, but got: %s", fi.postedMessages[2].text)
|
|
}
|
|
|
|
// get approvals
|
|
fi.messageFromChat("get approvals")
|
|
time.Sleep(1 * time.Second)
|
|
if len(fi.postedMessages) != 4 {
|
|
t.Errorf("expected to find 4 message, but got: %d", len(fi.postedMessages))
|
|
}
|
|
resp := trimSpaces(fi.postedMessages[3].text)
|
|
|
|
if !strings.Contains(resp, "k8s/project/repo:1.2.3 2.3.4 -> 3.4.5 0/1 true") {
|
|
t.Errorf("expected to find message, but got: %s", resp)
|
|
}
|
|
}
|
|
|
|
func trimSpaces(input string) string {
|
|
reLeadcloseWhtsp := regexp.MustCompile(`^[\s\p{Zs}]+|[\s\p{Zs}]+$`)
|
|
reInsideWhtsp := regexp.MustCompile(`[\s\p{Zs}]{2,}`)
|
|
final := reLeadcloseWhtsp.ReplaceAllString(input, "")
|
|
final = reInsideWhtsp.ReplaceAllString(final, " ")
|
|
return final
|
|
}
|