mirror of
https://github.com/woodpecker-ci/woodpecker.git
synced 2024-11-27 04:11:03 +00:00
Merge pull request #994 from benschumacher/testify-mocks
Alternative pattern for API unit tests (versus #991)
This commit is contained in:
commit
0690a85bd3
4 changed files with 484 additions and 2 deletions
|
@ -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
304
mocks/Datastore.go
Normal 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
144
server/badge_test.go
Normal 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")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
33
server/responserecorder_test.go
Normal file
33
server/responserecorder_test.go
Normal 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 }
|
Loading…
Reference in a new issue