package space_test
import (
"bytes"
"io/ioutil"
"log"
"net/http"
"net/http/httptest"
"net/url"
"os"
"strings"
"testing"
"git.sr.ht/~evanj/cms/internal/c"
"git.sr.ht/~evanj/cms/internal/c/space"
"git.sr.ht/~evanj/cms/internal/m/contenttype"
"git.sr.ht/~evanj/cms/internal/m/hook"
spaceT "git.sr.ht/~evanj/cms/internal/m/space"
userT "git.sr.ht/~evanj/cms/internal/m/user"
"github.com/bmizerany/assert"
"github.com/golang/mock/gomock"
"github.com/google/uuid"
)
var client = &http.Client{
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
func emptyreq(ts *httptest.Server, method string) (*http.Response, error) {
req, err := newrequest(method, ts.URL, nil)
if err != nil {
return nil, err
}
return client.Do(req)
}
type FakeContentTypeList struct{}
func fakecontenttypelist() FakeContentTypeList { return FakeContentTypeList{} }
func (_ FakeContentTypeList) List() (r []contenttype.ContentType) { return r }
func (_ FakeContentTypeList) More() bool { return false }
func (_ FakeContentTypeList) Before() int { return 0 }
type FakeHookList struct{}
func fakehooklist() FakeHookList { return FakeHookList{} }
func (_ FakeHookList) List() (r []hook.Hook) { return r }
func (_ FakeHookList) More() bool { return false }
func (_ FakeHookList) Before() int { return 0 }
func newrequest(method, url string, bod url.Values) (*http.Request, error) {
// Fake the method. This is used for two reasons.
// 1. On the frontend we use a hidden method field in form to mock/use REST.
// It ends up making cURL use much more ergonomic.
// 2. When testing, net/http doesn't read full body of DELETE/PUT. Incredibly
// annoying.
if bod != nil {
bod.Add("method", method)
}
return http.NewRequest("POST", url, strings.NewReader(bod.Encode()))
}
func TestNoUser(t *testing.T) {
t.Parallel()
var (
ctrl = gomock.NewController(t)
db = space.NewMockdber(ctrl)
l *log.Logger
s = space.New(c.New(l, db, true, "test"), l, db)
ts = httptest.NewServer(s)
methods = []string{"GET", "POST", "PUT", "PATCH", "DELETE"}
)
for _, method := range methods {
res, err := emptyreq(ts, method)
assert.Equal(t, err, nil)
bytes, err := ioutil.ReadAll(res.Body)
assert.Equal(t, err, nil)
assert.Equal(t, true, strings.Contains(string(bytes), c.ErrNoLogin.Error()))
}
}
func TestAll(t *testing.T) {
t.Parallel()
var (
uname = "test"
upass = "test"
uItem = userT.NewMock(uname, upass)
sname = uuid.New().String()
sdesc = uuid.New().String()
sItem = spaceT.NewMock(sname, sdesc, "")
snameUpdate = uuid.New().String()
sdescUpdate = uuid.New().String()
sItemUpdate = spaceT.NewMock(snameUpdate, sdescUpdate, "")
snameCopy = uuid.New().String()
sdescCopy = uuid.New().String()
sItemCopy = spaceT.NewMock(snameCopy, sdescCopy, "")
contentTypeList = fakecontenttypelist()
hookList = fakehooklist()
)
type SpaceTest struct {
createSC int
create func(*space.Mockdber)
getSC int
get func(*space.Mockdber)
updateSC int
update func(*space.Mockdber)
copySC int
copy func(*space.Mockdber)
deleteSC int
delete func(*space.Mockdber)
}
tests := []SpaceTest{
SpaceTest{
createSC: http.StatusFound,
create: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceNew(uItem, sname, sdesc).Return(sItem, nil).AnyTimes()
},
getSC: http.StatusOK,
get: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
db.EXPECT().ContentTypesPerSpace(uItem, sItem, 0).Return(contentTypeList, nil).AnyTimes()
db.EXPECT().HooksPerSpace(uItem, sItem, 0).Return(hookList, nil).AnyTimes()
},
updateSC: http.StatusFound,
update: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
db.EXPECT().SpaceUpdate(uItem, sItem, snameUpdate, sdescUpdate).Return(sItemUpdate, nil).AnyTimes()
},
copySC: http.StatusFound,
copy: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItemUpdate.ID()).Return(sItemUpdate, nil).AnyTimes()
db.EXPECT().SpaceCopy(uItem, sItemUpdate, snameCopy, sdescCopy).Return(sItemCopy, nil).AnyTimes()
},
deleteSC: http.StatusFound,
delete: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
db.EXPECT().SpaceDelete(uItem, sItem).Return(nil).AnyTimes()
},
},
SpaceTest{
createSC: http.StatusBadRequest,
create: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(nil, c.ErrNoLogin).AnyTimes()
},
getSC: http.StatusBadRequest,
get: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
},
updateSC: http.StatusBadRequest,
update: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
},
copySC: http.StatusBadRequest,
copy: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
},
deleteSC: http.StatusBadRequest,
delete: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
},
},
SpaceTest{
createSC: http.StatusBadRequest,
create: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceNew(uItem, sname, sdesc).Return(nil, space.ErrNoSpace).AnyTimes()
},
getSC: http.StatusNotFound,
get: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(nil, space.ErrNoSpace).AnyTimes()
},
updateSC: http.StatusNotFound,
update: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(nil, space.ErrNoSpace).AnyTimes()
},
copySC: http.StatusNotFound,
copy: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItemUpdate.ID()).Return(nil, space.ErrNoSpace).AnyTimes()
},
deleteSC: http.StatusNotFound,
delete: func(db *space.Mockdber) {
db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(nil, space.ErrNoSpace).AnyTimes()
},
},
}
for _, test := range tests {
// NOTE: If we run each iteration in their own t.Run it kills test
// coverage...
var (
ctrl = gomock.NewController(t)
db = space.NewMockdber(ctrl)
l = log.New(bytes.NewBufferString(os.DevNull), "", 0)
s = space.New(c.New(l, db, true, "test"), l, db)
ts = httptest.NewServer(s)
)
// Create
{
test.create(db)
req, _ := newrequest("POST", ts.URL, url.Values{"name": []string{sname}, "desc": []string{sdesc}})
req.SetBasicAuth(uname, upass)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "text/html")
res, _ := client.Do(req)
assert.Equal(t, res.StatusCode, test.createSC)
}
// Get
{
test.get(db)
req, _ := newrequest("GET", ts.URL, url.Values{"space": []string{sItem.ID()}})
req.SetBasicAuth(uname, upass)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "text/html")
res, _ := client.Do(req)
assert.Equal(t, res.StatusCode, test.getSC)
}
// Update
{
test.update(db)
req, _ := newrequest("PATCH", ts.URL,
url.Values{
"space": []string{sItem.ID()},
"name": []string{snameUpdate},
"desc": []string{sdescUpdate}})
req.SetBasicAuth(uname, upass)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "text/html")
res, _ := client.Do(req)
assert.Equal(t, res.StatusCode, test.updateSC)
}
// Copy
{
test.copy(db)
req, _ := newrequest("PUT", ts.URL,
url.Values{
"space": []string{sItemUpdate.ID()},
"name": []string{snameCopy},
"desc": []string{sdescCopy}})
req.SetBasicAuth(uname, upass)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "text/html")
res, _ := client.Do(req)
assert.Equal(t, res.StatusCode, test.copySC)
}
// Delete
{
test.delete(db)
req, _ := newrequest("DELETE", ts.URL,
url.Values{
"space": []string{sItem.ID()},
"method": []string{"DELETE"},
})
req.SetBasicAuth(uname, upass)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
req.Header.Set("Accept", "text/html")
res, _ := client.Do(req)
assert.Equal(t, res.StatusCode, test.deleteSC)
}
}
}