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) } } }