Merge pull request #994 from benschumacher/testify-mocks

Alternative pattern for API unit tests (versus #991)
This commit is contained in:
Brad Rydzewski 2015-04-30 21:07:21 -07:00
commit 0690a85bd3
4 changed files with 484 additions and 2 deletions

View file

@ -1,14 +1,15 @@
package datastore package datastore
import ( import (
"errors"
"io" "io"
"github.com/drone/drone/common" "github.com/drone/drone/common"
) )
var ( var (
ErrConflict = "Key not unique" ErrConflict = errors.New("Key not unique")
ErrNotFound = "Key not found" ErrKeyNotFound = errors.New("Key not found")
) )
type Datastore interface { type Datastore interface {

304
mocks/Datastore.go Normal file
View file

@ -0,0 +1,304 @@
package mocks
import "github.com/stretchr/testify/mock"
import "io"
import "github.com/drone/drone/common"
type Datastore struct {
mock.Mock
}
func (m *Datastore) User(_a0 string) (*common.User, error) {
ret := m.Called(_a0)
var r0 *common.User
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.User)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) UserCount() (int, error) {
ret := m.Called()
r0 := ret.Get(0).(int)
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) UserList() ([]*common.User, error) {
ret := m.Called()
var r0 []*common.User
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*common.User)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetUser(_a0 *common.User) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetUserNotExists(_a0 *common.User) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) DelUser(_a0 *common.User) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) Token(_a0 string, _a1 string) (*common.Token, error) {
ret := m.Called(_a0, _a1)
var r0 *common.Token
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.Token)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) TokenList(_a0 string) ([]*common.Token, error) {
ret := m.Called(_a0)
var r0 []*common.Token
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*common.Token)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetToken(_a0 *common.Token) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) DelToken(_a0 *common.Token) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) Subscribed(_a0 string, _a1 string) (bool, error) {
ret := m.Called(_a0, _a1)
r0 := ret.Get(0).(bool)
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetSubscriber(_a0 string, _a1 string) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) DelSubscriber(_a0 string, _a1 string) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) Repo(_a0 string) (*common.Repo, error) {
ret := m.Called(_a0)
var r0 *common.Repo
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.Repo)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) RepoList(_a0 string) ([]*common.Repo, error) {
ret := m.Called(_a0)
var r0 []*common.Repo
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*common.Repo)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) RepoParams(_a0 string) (map[string]string, error) {
ret := m.Called(_a0)
var r0 map[string]string
if ret.Get(0) != nil {
r0 = ret.Get(0).(map[string]string)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) RepoKeypair(_a0 string) (*common.Keypair, error) {
ret := m.Called(_a0)
var r0 *common.Keypair
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.Keypair)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetRepo(_a0 *common.Repo) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetRepoNotExists(_a0 *common.User, _a1 *common.Repo) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetRepoParams(_a0 string, _a1 map[string]string) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetRepoKeypair(_a0 string, _a1 *common.Keypair) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) DelRepo(_a0 *common.Repo) error {
ret := m.Called(_a0)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) Build(_a0 string, _a1 int) (*common.Build, error) {
ret := m.Called(_a0, _a1)
var r0 *common.Build
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.Build)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) BuildList(_a0 string) ([]*common.Build, error) {
ret := m.Called(_a0)
var r0 []*common.Build
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*common.Build)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) BuildLast(_a0 string) (*common.Build, error) {
ret := m.Called(_a0)
var r0 *common.Build
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.Build)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetBuild(_a0 string, _a1 *common.Build) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) Status(_a0 string, _a1 int, _a2 string) (*common.Status, error) {
ret := m.Called(_a0, _a1, _a2)
var r0 *common.Status
if ret.Get(0) != nil {
r0 = ret.Get(0).(*common.Status)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) StatusList(_a0 string, _a1 int) ([]*common.Status, error) {
ret := m.Called(_a0, _a1)
var r0 []*common.Status
if ret.Get(0) != nil {
r0 = ret.Get(0).([]*common.Status)
}
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetStatus(_a0 string, _a1 int, _a2 *common.Status) error {
ret := m.Called(_a0, _a1, _a2)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) LogReader(_a0 string, _a1 int, _a2 int) (io.Reader, error) {
ret := m.Called(_a0, _a1, _a2)
r0 := ret.Get(0).(io.Reader)
r1 := ret.Error(1)
return r0, r1
}
func (m *Datastore) SetLogs(_a0 string, _a1 int, _a2 int, _a3 []byte) error {
ret := m.Called(_a0, _a1, _a2, _a3)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetBuildState(_a0 string, _a1 *common.Build) error {
ret := m.Called(_a0, _a1)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetBuildStatus(_a0 string, _a1 int, _a2 *common.Status) error {
ret := m.Called(_a0, _a1, _a2)
r0 := ret.Error(0)
return r0
}
func (m *Datastore) SetBuildTask(_a0 string, _a1 int, _a2 *common.Task) error {
ret := m.Called(_a0, _a1, _a2)
r0 := ret.Error(0)
return r0
}

144
server/badge_test.go Normal file
View file

@ -0,0 +1,144 @@
package server
import (
"encoding/xml"
"net/http"
"net/url"
"strings"
"testing"
"time"
"github.com/drone/drone/common"
"github.com/drone/drone/common/ccmenu"
"github.com/drone/drone/datastore"
"github.com/drone/drone/mocks"
. "github.com/franela/goblin"
"github.com/gin-gonic/gin"
)
func TestBadge(t *testing.T) {
g := Goblin(t)
g.Describe("Badge", func() {
var ctx gin.Context
owner := "Freya"
name := "Hello-World"
fullName := owner + "/" + name
repo := &common.Repo{Owner: owner, Name: name, FullName: fullName}
g.BeforeEach(func() {
ctx = gin.Context{Engine: gin.Default()}
url, _ := url.Parse("http://drone.local/badges/" + fullName)
ctx.Request = &http.Request{URL: url}
ctx.Set("repo", repo)
})
g.AfterEach(func() {
})
cycleStateTester := func(expector gin.HandlerFunc, handle gin.HandlerFunc, validator func(state string, w *ResponseRecorder)) {
for idx, state := range []string{"", common.StateError, common.StateFailure, common.StateKilled, common.StatePending, common.StateRunning, common.StateSuccess} {
w := NewResponseRecorder()
ctx.Writer = w
repo.Last = &common.Build{
Started: time.Now().UTC().Unix(),
Finished: time.Now().UTC().Unix(),
Number: idx,
State: state,
}
ctx.Set("repo", repo)
if expector != nil {
expector(&ctx)
}
handle(&ctx)
validator(state, w)
}
}
g.It("should provide SVG response", func() {
{
// 1. verify no "last" build
w := NewResponseRecorder()
ctx.Writer = w
ctx.Request.URL.Path += "/status.svg"
GetBadge(&ctx)
g.Assert(w.Status()).Equal(200)
g.Assert(w.HeaderMap.Get("content-type")).Equal("image/svg+xml")
g.Assert(strings.Contains(w.Body.String(), ">none")).IsTrue()
}
// 2. verify a variety of "last" build states
cycleStateTester(nil, GetBadge, func(state string, w *ResponseRecorder) {
g.Assert(w.Status()).Equal(200)
g.Assert(w.HeaderMap.Get("content-type")).Equal("image/svg+xml")
// this may be excessive, but does effectively verify behavior
switch state {
case common.StateSuccess:
g.Assert(strings.Contains(w.Body.String(), ">success")).IsTrue()
case common.StatePending, common.StateRunning:
g.Assert(strings.Contains(w.Body.String(), ">started")).IsTrue()
case common.StateError, common.StateKilled:
g.Assert(strings.Contains(w.Body.String(), ">error")).IsTrue()
case common.StateFailure:
g.Assert(strings.Contains(w.Body.String(), ">failure")).IsTrue()
default:
g.Assert(strings.Contains(w.Body.String(), ">none")).IsTrue()
}
})
})
g.It("should provide CCTray response", func() {
{
// 1. verify no "last" build
w := NewResponseRecorder()
ctx.Writer = w
ctx.Request.URL.Path += "/cc.xml"
ds := new(mocks.Datastore)
ctx.Set("datastore", ds)
ds.On("BuildLast", fullName).Return(nil, datastore.ErrKeyNotFound).Once()
GetCC(&ctx)
g.Assert(w.Status()).Equal(404)
}
// 2. verify a variety of "last" build states
cycleStateTester(func(c *gin.Context) {
repo := ToRepo(c)
ds := new(mocks.Datastore)
ctx.Set("datastore", ds)
ds.On("BuildLast", fullName).Return(repo.Last, nil).Once()
},
GetCC,
func(state string, w *ResponseRecorder) {
g.Assert(w.Status()).Equal(200)
v := ccmenu.CCProjects{}
xml.Unmarshal(w.Body.Bytes(), &v)
switch state {
case common.StateSuccess:
g.Assert(v.Project.Activity).Equal("Sleeping")
g.Assert(v.Project.LastBuildStatus).Equal("Success")
case common.StatePending, common.StateRunning:
g.Assert(v.Project.Activity).Equal("Building")
g.Assert(v.Project.LastBuildStatus).Equal("Unknown")
case common.StateError, common.StateKilled:
g.Assert(v.Project.Activity).Equal("Sleeping")
g.Assert(v.Project.LastBuildStatus).Equal("Exception")
case common.StateFailure:
g.Assert(v.Project.Activity).Equal("Sleeping")
g.Assert(v.Project.LastBuildStatus).Equal("Failure")
default:
g.Assert(v.Project.Activity).Equal("Sleeping")
g.Assert(v.Project.LastBuildStatus).Equal("Unknown")
}
})
})
})
}

View file

@ -0,0 +1,33 @@
package server
import (
"bufio"
"net"
"net/http"
"net/http/httptest"
)
type ResponseRecorder struct {
*httptest.ResponseRecorder
}
func NewResponseRecorder() *ResponseRecorder {
return &ResponseRecorder{httptest.NewRecorder()}
}
func (rr *ResponseRecorder) reset() {
rr.ResponseRecorder = httptest.NewRecorder()
}
func (rr *ResponseRecorder) CloseNotify() <-chan bool {
return http.ResponseWriter(rr).(http.CloseNotifier).CloseNotify()
}
func (rr *ResponseRecorder) Hijack() (net.Conn, *bufio.ReadWriter, error) {
return http.ResponseWriter(rr).(http.Hijacker).Hijack()
}
func (rr *ResponseRecorder) Size() int { return rr.Body.Len() }
func (rr *ResponseRecorder) Status() int { return rr.Code }
func (rr *ResponseRecorder) WriteHeaderNow() {}
func (rr *ResponseRecorder) Written() bool { return rr.Code != 0 }