~evanj/cms

8a453a73244ce94c291204147a7ee2cbf61f7ab2 — Evan M Jones 3 months ago 5eb9775
Fix(testing): Refactor'd tests per RBAC feat breakage. All tests
passing.
14 files changed, 269 insertions(+), 211 deletions(-)

R internal/c/mock/{mock_c.go => k.go}
M internal/c/c_test.go
R internal/c/content/{mock.go => content_mock.go}
M internal/c/content/content_test.go
R internal/c/contenttype/mock/{mock_contenttype.go => nttype_mock.go}
M internal/c/contenttype/contenttype_test.go
R internal/c/hook/{mock.go => hook_mock.go}
M internal/c/hook/hook_test.go
R internal/c/space/mock/{mock_space.go => _mock.go}
M internal/c/space/space_test.go
R internal/c/user/{mock.go => user_mock.go}
M internal/m/user/mock.go
A internal/s/cache/cache_generic.go
A internal/s/cache/cache_generic.go2
R internal/c/mock/mock_c.go => internal/c/c_mock.go +2 -2
@@ 1,8 1,8 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: c.go

// Package mock_c is a generated GoMock package.
package mock_c
// Package c is a generated GoMock package.
package c

import (
	user "git.sr.ht/~evanj/cms/internal/m/user"

M internal/c/c_test.go => internal/c/c_test.go +8 -9
@@ 13,7 13,6 @@ import (
	"testing"

	"git.sr.ht/~evanj/cms/internal/c"
	mock_c "git.sr.ht/~evanj/cms/internal/c/mock"
	userT "git.sr.ht/~evanj/cms/internal/m/user"
	"github.com/bmizerany/assert"
	"github.com/golang/mock/gomock"


@@ 35,7 34,7 @@ func TestJSON(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
		s    = server{c.New(l, db, true, "test")}
		ts   = httptest.NewServer(s)


@@ 51,7 50,7 @@ func TestHTML(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
		s    = server{c.New(l, db, true, "test")}
		ts   = httptest.NewServer(s)


@@ 79,7 78,7 @@ func TestMethodHack(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
	)



@@ 141,7 140,7 @@ func TestRedirect(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
	)



@@ 201,7 200,7 @@ func TestUserFromBasicAuth(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
		s    = server4{c.New(l, db, true, "test")}
		ts   = httptest.NewServer(s)


@@ 225,7 224,7 @@ func TestUserFromFromCookie(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
		s    = server4{c.New(l, db, true, "test")}
		ts   = httptest.NewServer(s)


@@ 255,7 254,7 @@ func TestUserFromFromCookieFail(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
		s    = server4{c.New(l, db, true, "test")}
		ts   = httptest.NewServer(s)


@@ 298,7 297,7 @@ func TestStrAndErr(t *testing.T) {

	var (
		ctrl = gomock.NewController(t)
		db   = mock_c.NewMockdber(ctrl)
		db   = c.NewMockdber(ctrl)
		l    *log.Logger
	)


R internal/c/content/mock.go => internal/c/content/content_mock.go +28 -28
@@ 87,92 87,92 @@ func (mr *MockDBerMockRecorder) SpaceGet(user, spaceID interface{}) *gomock.Call
}

// ContentTypeGet mocks base method
func (m *MockDBer) ContentTypeGet(space space.Space, contenttypeID string) (contenttype.ContentType, error) {
func (m *MockDBer) ContentTypeGet(u user.User, space space.Space, contenttypeID string) (contenttype.ContentType, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypeGet", space, contenttypeID)
	ret := m.ctrl.Call(m, "ContentTypeGet", u, space, contenttypeID)
	ret0, _ := ret[0].(contenttype.ContentType)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentTypeGet indicates an expected call of ContentTypeGet
func (mr *MockDBerMockRecorder) ContentTypeGet(space, contenttypeID interface{}) *gomock.Call {
func (mr *MockDBerMockRecorder) ContentTypeGet(u, space, contenttypeID interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeGet", reflect.TypeOf((*MockDBer)(nil).ContentTypeGet), space, contenttypeID)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeGet", reflect.TypeOf((*MockDBer)(nil).ContentTypeGet), u, space, contenttypeID)
}

// ContentNew mocks base method
func (m *MockDBer) ContentNew(space space.Space, ct contenttype.ContentType, params []db.ContentNewParam) (content.Content, error) {
func (m *MockDBer) ContentNew(u user.User, space space.Space, ct contenttype.ContentType, params []db.ContentNewParam) (content.Content, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentNew", space, ct, params)
	ret := m.ctrl.Call(m, "ContentNew", u, space, ct, params)
	ret0, _ := ret[0].(content.Content)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentNew indicates an expected call of ContentNew
func (mr *MockDBerMockRecorder) ContentNew(space, ct, params interface{}) *gomock.Call {
func (mr *MockDBerMockRecorder) ContentNew(u, space, ct, params interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentNew", reflect.TypeOf((*MockDBer)(nil).ContentNew), space, ct, params)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentNew", reflect.TypeOf((*MockDBer)(nil).ContentNew), u, space, ct, params)
}

// ContentGet mocks base method
func (m *MockDBer) ContentGet(space space.Space, ct contenttype.ContentType, contentID string) (content.Content, error) {
func (m *MockDBer) ContentGet(u user.User, space space.Space, ct contenttype.ContentType, contentID string) (content.Content, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentGet", space, ct, contentID)
	ret := m.ctrl.Call(m, "ContentGet", u, space, ct, contentID)
	ret0, _ := ret[0].(content.Content)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentGet indicates an expected call of ContentGet
func (mr *MockDBerMockRecorder) ContentGet(space, ct, contentID interface{}) *gomock.Call {
func (mr *MockDBerMockRecorder) ContentGet(u, space, ct, contentID interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentGet", reflect.TypeOf((*MockDBer)(nil).ContentGet), space, ct, contentID)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentGet", reflect.TypeOf((*MockDBer)(nil).ContentGet), u, space, ct, contentID)
}

// ContentUpdate mocks base method
func (m *MockDBer) ContentUpdate(space space.Space, ct contenttype.ContentType, c content.Content, newParams []db.ContentNewParam, updateParams []db.ContentUpdateParam) (content.Content, error) {
func (m *MockDBer) ContentUpdate(u user.User, space space.Space, ct contenttype.ContentType, c content.Content, newParams []db.ContentNewParam, updateParams []db.ContentUpdateParam) (content.Content, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentUpdate", space, ct, c, newParams, updateParams)
	ret := m.ctrl.Call(m, "ContentUpdate", u, space, ct, c, newParams, updateParams)
	ret0, _ := ret[0].(content.Content)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentUpdate indicates an expected call of ContentUpdate
func (mr *MockDBerMockRecorder) ContentUpdate(space, ct, content, newParams, updateParams interface{}) *gomock.Call {
func (mr *MockDBerMockRecorder) ContentUpdate(u, space, ct, content, newParams, updateParams interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentUpdate", reflect.TypeOf((*MockDBer)(nil).ContentUpdate), space, ct, content, newParams, updateParams)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentUpdate", reflect.TypeOf((*MockDBer)(nil).ContentUpdate), u, space, ct, content, newParams, updateParams)
}

// ContentDelete mocks base method
func (m *MockDBer) ContentDelete(space space.Space, ct contenttype.ContentType, content content.Content) error {
func (m *MockDBer) ContentDelete(u user.User, space space.Space, ct contenttype.ContentType, content content.Content) error {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentDelete", space, ct, content)
	ret := m.ctrl.Call(m, "ContentDelete", u, space, ct, content)
	ret0, _ := ret[0].(error)
	return ret0
}

// ContentDelete indicates an expected call of ContentDelete
func (mr *MockDBerMockRecorder) ContentDelete(space, ct, content interface{}) *gomock.Call {
func (mr *MockDBerMockRecorder) ContentDelete(u, space, ct, content interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentDelete", reflect.TypeOf((*MockDBer)(nil).ContentDelete), space, ct, content)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentDelete", reflect.TypeOf((*MockDBer)(nil).ContentDelete), u, space, ct, content)
}

// ContentSearch mocks base method
func (m *MockDBer) ContentSearch(space space.Space, ct contenttype.ContentType, name, query string, before int) (content.ContentList, error) {
func (m *MockDBer) ContentSearch(u user.User, space space.Space, ct contenttype.ContentType, name, query string, before int) (content.ContentList, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentSearch", space, ct, name, query, before)
	ret := m.ctrl.Call(m, "ContentSearch", u, space, ct, name, query, before)
	ret0, _ := ret[0].(content.ContentList)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentSearch indicates an expected call of ContentSearch
func (mr *MockDBerMockRecorder) ContentSearch(space, ct, name, query, before interface{}) *gomock.Call {
func (mr *MockDBerMockRecorder) ContentSearch(u, space, ct, name, query, before interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentSearch", reflect.TypeOf((*MockDBer)(nil).ContentSearch), space, ct, name, query, before)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentSearch", reflect.TypeOf((*MockDBer)(nil).ContentSearch), u, space, ct, name, query, before)
}

// MockE3er is a mock of E3er interface


@@ 237,13 237,13 @@ func (m *MockHooker) EXPECT() *MockHookerMockRecorder {
}

// Do mocks base method
func (m *MockHooker) Do(space space.Space, content content.Content, ht hook.HookType) {
func (m *MockHooker) Do(user user.User, space space.Space, content content.Content, ht hook.HookType) {
	m.ctrl.T.Helper()
	m.ctrl.Call(m, "Do", space, content, ht)
	m.ctrl.Call(m, "Do", user, space, content, ht)
}

// Do indicates an expected call of Do
func (mr *MockHookerMockRecorder) Do(space, content, ht interface{}) *gomock.Call {
func (mr *MockHookerMockRecorder) Do(user, space, content, ht interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*MockHooker)(nil).Do), space, content, ht)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Do", reflect.TypeOf((*MockHooker)(nil).Do), user, space, content, ht)
}

M internal/c/content/content_test.go => internal/c/content/content_test.go +31 -31
@@ 181,10 181,10 @@ func TestCreateGood(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentNew(s, ct, nmatcher{newParams}).Return(c, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentNew(u, s, ct, nmatcher{newParams}).Return(c, nil).AnyTimes()
	e3.EXPECT().Upload(gomock.Any(), false, gomock.Any(), gomock.Any()).Return(fileURL, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookNew).Return().AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookNew).Return().AnyTimes()

	bod, headerCT := bodyWithFile("File-image", form)
	req, _ := http.NewRequest("POST", ts.URL, bod)


@@ 238,11 238,11 @@ func TestUpdateGood(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(s, ct, c.ID()).Return(c, nil).AnyTimes()
	db.EXPECT().ContentUpdate(s, ct, c, nmatcher{newParams}, umatcher{updateParams}).Return(c, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(u, s, ct, c.ID()).Return(c, nil).AnyTimes()
	db.EXPECT().ContentUpdate(u, s, ct, c, nmatcher{newParams}, umatcher{updateParams}).Return(c, nil).AnyTimes()
	e3.EXPECT().Upload(gomock.Any(), false, gomock.Any(), gomock.Any()).Return(fileURL, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookUpdate).Return().AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookUpdate).Return().AnyTimes()

	bod, headerCT := bodyWithFile("File-image", form)
	req, _ := http.NewRequest("POST", ts.URL, bod)


@@ 296,11 296,11 @@ func TestUpdateGood2(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(s, ct, c.ID()).Return(c, nil).AnyTimes()
	db.EXPECT().ContentUpdate(s, ct, c, nmatcher{newParams}, umatcher{updateParams}).Return(c, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(u, s, ct, c.ID()).Return(c, nil).AnyTimes()
	db.EXPECT().ContentUpdate(u, s, ct, c, nmatcher{newParams}, umatcher{updateParams}).Return(c, nil).AnyTimes()
	e3.EXPECT().Upload(gomock.Any(), false, gomock.Any(), gomock.Any()).Return(fileURL, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookUpdate).Return().AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookUpdate).Return().AnyTimes()

	bod, headerCT := bodyWithFile("value_update_File-4-image", form)
	req, _ := http.NewRequest("POST", ts.URL, bod)


@@ 337,9 337,9 @@ func TestServeGood(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(s, ct, c.ID()).Return(c, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookNew).Return().AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(u, s, ct, c.ID()).Return(c, nil).AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookNew).Return().AnyTimes()

	req, _ := http.NewRequest("POST", ts.URL,
		strings.NewReader(form.Encode()))


@@ 375,9 375,9 @@ func TestServeGood2(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(s, ct, c.ID()).Return(c, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookNew).Return().AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(u, s, ct, c.ID()).Return(c, nil).AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookNew).Return().AnyTimes()

	req, _ := http.NewRequest("GET", ts.URL+"?"+form.Encode(), nil)
	req.SetBasicAuth(u.Name(), u.Pass())


@@ 404,9 404,9 @@ func TestServeGood3(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(s, ct, c.ID()).Return(c, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookNew).Return().AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(u, s, ct, c.ID()).Return(c, nil).AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookNew).Return().AnyTimes()

	req, _ := http.NewRequest("GET", fmt.Sprintf("%s/content/%s/%s/%s", ts.URL, s.ID(), ct.ID(), c.ID()), nil)
	req.SetBasicAuth(u.Name(), u.Pass())


@@ 439,10 439,10 @@ func TestDeleteGood(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(s, ct, c.ID()).Return(c, nil).AnyTimes()
	db.EXPECT().ContentDelete(s, ct, c).Return(nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookDelete).Return().AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentGet(u, s, ct, c.ID()).Return(c, nil).AnyTimes()
	db.EXPECT().ContentDelete(u, s, ct, c).Return(nil).AnyTimes()
	h.EXPECT().Do(u, s, c, webhook.HookDelete).Return().AnyTimes()

	req, _ := http.NewRequest("DELETE", ts.URL+"?"+form.Encode(), nil)
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")


@@ 478,8 478,8 @@ func TestSearchGood(t *testing.T) {

	db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
	db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentSearch(s, ct, "name", "garfield", 0).Return(cl, nil).AnyTimes()
	db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(ct, nil).AnyTimes()
	db.EXPECT().ContentSearch(u, s, ct, "name", "garfield", 0).Return(cl, nil).AnyTimes()

	req, _ := http.NewRequest("GET", ts.URL+"?"+form.Encode(), nil)
	req.SetBasicAuth(u.Name(), u.Pass())


@@ 633,7 633,7 @@ func TestBad(t *testing.T) {
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
				db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{


@@ 648,7 648,7 @@ func TestBad(t *testing.T) {
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
				db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{


@@ 662,7 662,7 @@ func TestBad(t *testing.T) {
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
				db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{


@@ 676,7 676,7 @@ func TestBad(t *testing.T) {
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
				db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{


@@ 690,7 690,7 @@ func TestBad(t *testing.T) {
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.Pass()).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
				db.EXPECT().ContentTypeGet(u, s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
	}

R internal/c/contenttype/mock/mock_contenttype.go => internal/c/contenttype/contenttype_mock.go +26 -26
@@ 1,8 1,8 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: contenttype.go

// Package mock_contenttype is a generated GoMock package.
package mock_contenttype
// Package contenttype is a generated GoMock package.
package contenttype

import (
	reflect "reflect"


@@ 84,90 84,90 @@ func (mr *MockdberMockRecorder) SpaceGet(user, spaceID interface{}) *gomock.Call
}

// ContentTypeNew mocks base method
func (m *Mockdber) ContentTypeNew(space space.Space, name string, params []db.ContentTypeNewParam) (contenttype.ContentType, error) {
func (m *Mockdber) ContentTypeNew(user user.User, space space.Space, name string, params []db.ContentTypeNewParam) (contenttype.ContentType, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypeNew", space, name, params)
	ret := m.ctrl.Call(m, "ContentTypeNew", user, space, name, params)
	ret0, _ := ret[0].(contenttype.ContentType)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentTypeNew indicates an expected call of ContentTypeNew
func (mr *MockdberMockRecorder) ContentTypeNew(space, name, params interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentTypeNew(user, space, name, params interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeNew", reflect.TypeOf((*Mockdber)(nil).ContentTypeNew), space, name, params)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeNew", reflect.TypeOf((*Mockdber)(nil).ContentTypeNew), user, space, name, params)
}

// ContentTypeGet mocks base method
func (m *Mockdber) ContentTypeGet(space space.Space, contenttypeID string) (contenttype.ContentType, error) {
func (m *Mockdber) ContentTypeGet(user user.User, space space.Space, contenttypeID string) (contenttype.ContentType, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypeGet", space, contenttypeID)
	ret := m.ctrl.Call(m, "ContentTypeGet", user, space, contenttypeID)
	ret0, _ := ret[0].(contenttype.ContentType)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentTypeGet indicates an expected call of ContentTypeGet
func (mr *MockdberMockRecorder) ContentTypeGet(space, contenttypeID interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentTypeGet(user, space, contenttypeID interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeGet", reflect.TypeOf((*Mockdber)(nil).ContentTypeGet), space, contenttypeID)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeGet", reflect.TypeOf((*Mockdber)(nil).ContentTypeGet), user, space, contenttypeID)
}

// ContentTypeUpdate mocks base method
func (m *Mockdber) ContentTypeUpdate(space space.Space, ct contenttype.ContentType, name string, newParams []db.ContentTypeNewParam, updateParams []db.ContentTypeUpdateParam) (contenttype.ContentType, error) {
func (m *Mockdber) ContentTypeUpdate(user user.User, space space.Space, ct contenttype.ContentType, name string, newParams []db.ContentTypeNewParam, updateParams []db.ContentTypeUpdateParam) (contenttype.ContentType, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypeUpdate", space, ct, name, newParams, updateParams)
	ret := m.ctrl.Call(m, "ContentTypeUpdate", user, space, ct, name, newParams, updateParams)
	ret0, _ := ret[0].(contenttype.ContentType)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentTypeUpdate indicates an expected call of ContentTypeUpdate
func (mr *MockdberMockRecorder) ContentTypeUpdate(space, contenttype, name, newParams, updateParams interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentTypeUpdate(user, space, contenttype, name, newParams, updateParams interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeUpdate", reflect.TypeOf((*Mockdber)(nil).ContentTypeUpdate), space, contenttype, name, newParams, updateParams)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeUpdate", reflect.TypeOf((*Mockdber)(nil).ContentTypeUpdate), user, space, contenttype, name, newParams, updateParams)
}

// ContentTypeDelete mocks base method
func (m *Mockdber) ContentTypeDelete(space space.Space, ct contenttype.ContentType) error {
func (m *Mockdber) ContentTypeDelete(user user.User, space space.Space, ct contenttype.ContentType) error {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypeDelete", space, ct)
	ret := m.ctrl.Call(m, "ContentTypeDelete", user, space, ct)
	ret0, _ := ret[0].(error)
	return ret0
}

// ContentTypeDelete indicates an expected call of ContentTypeDelete
func (mr *MockdberMockRecorder) ContentTypeDelete(space, ct interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentTypeDelete(user, space, ct interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeDelete", reflect.TypeOf((*Mockdber)(nil).ContentTypeDelete), space, ct)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeDelete", reflect.TypeOf((*Mockdber)(nil).ContentTypeDelete), user, space, ct)
}

// ContentTypeSearch mocks base method
func (m *Mockdber) ContentTypeSearch(space space.Space, query string, before int) (contenttype.ContentTypeList, error) {
func (m *Mockdber) ContentTypeSearch(user user.User, space space.Space, query string, before int) (contenttype.ContentTypeList, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypeSearch", space, query, before)
	ret := m.ctrl.Call(m, "ContentTypeSearch", user, space, query, before)
	ret0, _ := ret[0].(contenttype.ContentTypeList)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentTypeSearch indicates an expected call of ContentTypeSearch
func (mr *MockdberMockRecorder) ContentTypeSearch(space, query, before interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentTypeSearch(user, space, query, before interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeSearch", reflect.TypeOf((*Mockdber)(nil).ContentTypeSearch), space, query, before)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypeSearch", reflect.TypeOf((*Mockdber)(nil).ContentTypeSearch), user, space, query, before)
}

// ContentPerContentType mocks base method
func (m *Mockdber) ContentPerContentType(space space.Space, ct contenttype.ContentType, before int, order db.OrderType, sortField string) (content.ContentList, error) {
func (m *Mockdber) ContentPerContentType(user user.User, space space.Space, ct contenttype.ContentType, before int, order db.OrderType, sortField string) (content.ContentList, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentPerContentType", space, ct, before, order, sortField)
	ret := m.ctrl.Call(m, "ContentPerContentType", user, space, ct, before, order, sortField)
	ret0, _ := ret[0].(content.ContentList)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentPerContentType indicates an expected call of ContentPerContentType
func (mr *MockdberMockRecorder) ContentPerContentType(space, ct, before, order, sortField interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentPerContentType(user, space, ct, before, order, sortField interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentPerContentType", reflect.TypeOf((*Mockdber)(nil).ContentPerContentType), space, ct, before, order, sortField)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentPerContentType", reflect.TypeOf((*Mockdber)(nil).ContentPerContentType), user, space, ct, before, order, sortField)
}

M internal/c/contenttype/contenttype_test.go => internal/c/contenttype/contenttype_test.go +36 -37
@@ 12,7 12,6 @@ import (

	"git.sr.ht/~evanj/cms/internal/c"
	"git.sr.ht/~evanj/cms/internal/c/contenttype"
	mock_contenttype "git.sr.ht/~evanj/cms/internal/c/contenttype/mock"
	"git.sr.ht/~evanj/cms/internal/m/content"
	spaceT "git.sr.ht/~evanj/cms/internal/m/space"
	userT "git.sr.ht/~evanj/cms/internal/m/user"


@@ 28,7 27,7 @@ func TestNoUser(t *testing.T) {

	var (
		ctrl    = gomock.NewController(t)
		db      = mock_contenttype.NewMockdber(ctrl)
		db      = contenttype.NewMockdber(ctrl)
		l       *log.Logger
		s       = contenttype.New(c.New(l, db, true, "test"), l, db)
		ts      = httptest.NewServer(s)


@@ 75,7 74,7 @@ func TestContentTypeHappyPath(t *testing.T) {
	type spec struct {
		expect int
		form   url.Values
		mock   func(db *mock_contenttype.Mockdber)
		mock   func(db *contenttype.Mockdber)
	}

	tests := []spec{


@@ 88,13 87,13 @@ func TestContentTypeHappyPath(t *testing.T) {
				"space":        {sItem.ID()},
				"method":       {"POST"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(uItem, sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
			},
		},
		{


@@ 110,7 109,7 @@ func TestContentTypeHappyPath(t *testing.T) {
				"space":               {sItem.ID()},
				"method":              {"PATCH"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}


@@ 119,8 118,8 @@ func TestContentTypeHappyPath(t *testing.T) {
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeUpdate(sItem, ctItemUpdate, ctItemUpdate.Name(), newParams, updateParams).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItemUpdate.ID()).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeUpdate(uItem, sItem, ctItemUpdate, ctItemUpdate.Name(), newParams, updateParams).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItemUpdate.ID()).Return(ctItemUpdate, nil).AnyTimes()
			},
		},
		{


@@ 130,11 129,11 @@ func TestContentTypeHappyPath(t *testing.T) {
				"space":       {sItem.ID()},
				"method":      {"GET"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentPerContentType(sItem, ctItem, 0, db.OrderAsc, "name").Return(cl, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentPerContentType(uItem, sItem, ctItem, 0, db.OrderAsc, "name").Return(cl, nil).AnyTimes()
			},
		},
		{


@@ 144,11 143,11 @@ func TestContentTypeHappyPath(t *testing.T) {
				"space":  {sItem.ID()},
				"method": {"GET"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeSearch(sItem, "post", 0).Return(ctl, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeSearch(uItem, sItem, "post", 0).Return(ctl, nil).AnyTimes()
			},
		},
		{


@@ 159,11 158,11 @@ func TestContentTypeHappyPath(t *testing.T) {
				"order":       {"desc"},
				"method":      {"GET"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentPerContentType(sItem, ctItem, 0, db.OrderDesc, "name").Return(cl, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentPerContentType(uItem, sItem, ctItem, 0, db.OrderDesc, "name").Return(cl, nil).AnyTimes()
			},
		},
		{


@@ 173,11 172,11 @@ func TestContentTypeHappyPath(t *testing.T) {
				"space":       {sItem.ID()},
				"method":      {"DELETE"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeDelete(sItem, ctItem).Return(nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItem.ID()).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeDelete(uItem, sItem, ctItem).Return(nil).AnyTimes()
			},
		},
	}


@@ 185,7 184,7 @@ func TestContentTypeHappyPath(t *testing.T) {
	for _, test := range tests {
		var (
			ctrl = gomock.NewController(t)
			db   = mock_contenttype.NewMockdber(ctrl)
			db   = contenttype.NewMockdber(ctrl)
			l    *log.Logger
			s    = contenttype.New(c.New(l, db, true, "test"), l, db)
			ts   = httptest.NewServer(s)


@@ 223,7 222,7 @@ func TestContentTypeBadPath(t *testing.T) {
		expect int
		err    error
		form   url.Values
		mock   func(db *mock_contenttype.Mockdber)
		mock   func(db *contenttype.Mockdber)
	}

	tests := []spec{


@@ 235,13 234,13 @@ func TestContentTypeBadPath(t *testing.T) {
				"space":  {sItem.ID()},
				"method": {"POST"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(uItem, sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
			},
		},
		{


@@ 254,13 253,13 @@ func TestContentTypeBadPath(t *testing.T) {
				"space":        {sItem.ID()},
				"method":       {"POST"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(uItem, sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
			},
		},
		{


@@ 273,13 272,13 @@ func TestContentTypeBadPath(t *testing.T) {
				"space":        {sItem.ID()},
				"method":       {"POST"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(uItem, sItem, ctItem.Name(), newParams).Return(ctItem, nil).AnyTimes()
			},
		},
		{


@@ 292,13 291,13 @@ func TestContentTypeBadPath(t *testing.T) {
				"space":        {sItem.ID()},
				"method":       {"POST"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeNew(sItem, ctItem.Name(), newParams).Return(nil, err).AnyTimes()
				m.EXPECT().ContentTypeNew(uItem, sItem, ctItem.Name(), newParams).Return(nil, err).AnyTimes()
			},
		},
		{


@@ 315,7 314,7 @@ func TestContentTypeBadPath(t *testing.T) {
				"space":               {sItem.ID()},
				"method":              {"PATCH"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}


@@ 324,8 323,8 @@ func TestContentTypeBadPath(t *testing.T) {
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeUpdate(sItem, ctItemUpdate, ctItemUpdate.Name(), newParams, updateParams).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItemUpdate.ID()).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeUpdate(uItem, sItem, ctItemUpdate, ctItemUpdate.Name(), newParams, updateParams).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItemUpdate.ID()).Return(ctItemUpdate, nil).AnyTimes()
			},
		},
		{


@@ 342,7 341,7 @@ func TestContentTypeBadPath(t *testing.T) {
				"space":               {sItem.ID()},
				"method":              {"PATCH"}, // By default net/http doesn't parse body on DELETE.
			},
			func(m *mock_contenttype.Mockdber) {
			func(m *contenttype.Mockdber) {
				newParams := []db.ContentTypeNewParam{
					{"name", string(valuetype.StringSmall)},
				}


@@ 351,8 350,8 @@ func TestContentTypeBadPath(t *testing.T) {
				}
				m.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				m.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				m.EXPECT().ContentTypeUpdate(sItem, ctItemUpdate, ctItemUpdate.Name(), newParams, updateParams).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(sItem, ctItemUpdate.ID()).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeUpdate(uItem, sItem, ctItemUpdate, ctItemUpdate.Name(), newParams, updateParams).Return(ctItemUpdate, nil).AnyTimes()
				m.EXPECT().ContentTypeGet(uItem, sItem, ctItemUpdate.ID()).Return(ctItemUpdate, nil).AnyTimes()
			},
		},
	}


@@ 360,7 359,7 @@ func TestContentTypeBadPath(t *testing.T) {
	for _, test := range tests {
		var (
			ctrl = gomock.NewController(t)
			db   = mock_contenttype.NewMockdber(ctrl)
			db   = contenttype.NewMockdber(ctrl)
			l    *log.Logger
			s    = contenttype.New(c.New(l, db, true, "test"), l, db)
			ts   = httptest.NewServer(s)

R internal/c/hook/mock.go => internal/c/hook/hook_mock.go +12 -12
@@ 81,45 81,45 @@ func (mr *MockdberMockRecorder) SpaceGet(user, spaceID interface{}) *gomock.Call
}

// HookNew mocks base method
func (m *Mockdber) HookNew(space space.Space, url string) (hook.Hook, error) {
func (m *Mockdber) HookNew(user user.User, space space.Space, url string) (hook.Hook, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "HookNew", space, url)
	ret := m.ctrl.Call(m, "HookNew", user, space, url)
	ret0, _ := ret[0].(hook.Hook)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// HookNew indicates an expected call of HookNew
func (mr *MockdberMockRecorder) HookNew(space, url interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) HookNew(user, space, url interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HookNew", reflect.TypeOf((*Mockdber)(nil).HookNew), space, url)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HookNew", reflect.TypeOf((*Mockdber)(nil).HookNew), user, space, url)
}

// HookGet mocks base method
func (m *Mockdber) HookGet(space space.Space, id string) (hook.Hook, error) {
func (m *Mockdber) HookGet(user user.User, space space.Space, id string) (hook.Hook, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "HookGet", space, id)
	ret := m.ctrl.Call(m, "HookGet", user, space, id)
	ret0, _ := ret[0].(hook.Hook)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// HookGet indicates an expected call of HookGet
func (mr *MockdberMockRecorder) HookGet(space, id interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) HookGet(user, space, id interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HookGet", reflect.TypeOf((*Mockdber)(nil).HookGet), space, id)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HookGet", reflect.TypeOf((*Mockdber)(nil).HookGet), user, space, id)
}

// HookDelete mocks base method
func (m *Mockdber) HookDelete(space space.Space, hook hook.Hook) error {
func (m *Mockdber) HookDelete(user user.User, space space.Space, hook hook.Hook) error {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "HookDelete", space, hook)
	ret := m.ctrl.Call(m, "HookDelete", user, space, hook)
	ret0, _ := ret[0].(error)
	return ret0
}

// HookDelete indicates an expected call of HookDelete
func (mr *MockdberMockRecorder) HookDelete(space, hook interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) HookDelete(user, space, hook interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HookDelete", reflect.TypeOf((*Mockdber)(nil).HookDelete), space, hook)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HookDelete", reflect.TypeOf((*Mockdber)(nil).HookDelete), user, space, hook)
}

M internal/c/hook/hook_test.go => internal/c/hook/hook_test.go +17 -17
@@ 76,7 76,7 @@ func TestHookHappyPath(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookNew(sItem, hItem.URL()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookNew(uItem, sItem, hItem.URL()).Return(hItem, nil).AnyTimes()
			},
		},
		{


@@ 89,7 89,7 @@ func TestHookHappyPath(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
			},
		},
		{


@@ 102,8 102,8 @@ func TestHookHappyPath(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookDelete(sItem, hItem).Return(nil).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookDelete(uItem, sItem, hItem).Return(nil).AnyTimes()
			},
		},
	}


@@ 161,7 161,7 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(nil, err).AnyTimes()
				// db.EXPECT().HookNew(sItem, hItem.URL()).Return(hItem, nil).AnyTimes()
				// db.EXPECT().HookNew(uItem, sItem, hItem.URL()).Return(hItem, nil).AnyTimes()
			},
		},
		{


@@ 175,7 175,7 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookNew(sItem, hItem.URL()).Return(nil, err).AnyTimes()
				db.EXPECT().HookNew(uItem, sItem, hItem.URL()).Return(nil, err).AnyTimes()
			},
		},
		// GET


@@ 190,7 190,7 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(nil, err).AnyTimes()
				// db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				// db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
			},
		},
		{


@@ 204,7 204,7 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(nil, err).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(nil, err).AnyTimes()
			},
		},
		// DELETE


@@ 219,8 219,8 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(nil, err).AnyTimes()
				// db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				// db.EXPECT().HookDelete(sItem, hItem).Return(nil).AnyTimes()
				// db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				// db.EXPECT().HookDelete(uItem, sItem, hItem).Return(nil).AnyTimes()
			},
		},
		{


@@ 234,8 234,8 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(nil, err).AnyTimes()
				// db.EXPECT().HookDelete(sItem, hItem).Return(nil).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(nil, err).AnyTimes()
				// db.EXPECT().HookDelete(uItem, sItem, hItem).Return(nil).AnyTimes()
			},
		},
		{


@@ 249,8 249,8 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookDelete(sItem, hItem).Return(err).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookDelete(uItem, sItem, hItem).Return(err).AnyTimes()
			},
		},
		// PATCH / NOT FOUND


@@ 265,8 265,8 @@ func TestHookBadPaths(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookDelete(sItem, hItem).Return(err).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookDelete(uItem, sItem, hItem).Return(err).AnyTimes()
			},
		},
	}


@@ 314,7 314,7 @@ func TestHookWeirdPath(t *testing.T) {
			func(db *hook.Mockdber) {
				db.EXPECT().UserGet(uItem.Name(), uItem.Pass()).Return(uItem, nil).AnyTimes()
				db.EXPECT().SpaceGet(uItem, sItem.ID()).Return(sItem, nil).AnyTimes()
				db.EXPECT().HookGet(sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
				db.EXPECT().HookGet(uItem, sItem, hItem.ID()).Return(hItem, nil).AnyTimes()
			},
		},
	}

R internal/c/space/mock/mock_space.go => internal/c/space/space_mock.go +22 -22
@@ 1,15 1,15 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: space.go

// Package mock_space is a generated GoMock package.
package mock_space
// Package space is a generated GoMock package.
package space

import (
	reflect "reflect"

	contenttype "git.sr.ht/~evanj/cms/internal/m/contenttype"
	hook "git.sr.ht/~evanj/cms/internal/m/hook"
	spaceT "git.sr.ht/~evanj/cms/internal/m/space"
	space "git.sr.ht/~evanj/cms/internal/m/space"
	user "git.sr.ht/~evanj/cms/internal/m/user"
	gomock "github.com/golang/mock/gomock"
)


@@ 68,10 68,10 @@ func (mr *MockdberMockRecorder) UserGetFromToken(token interface{}) *gomock.Call
}

// SpaceNew mocks base method
func (m *Mockdber) SpaceNew(user user.User, name, desc string) (spaceT.Space, error) {
func (m *Mockdber) SpaceNew(user user.User, name, desc string) (space.Space, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "SpaceNew", user, name, desc)
	ret0, _ := ret[0].(spaceT.Space)
	ret0, _ := ret[0].(space.Space)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}


@@ 83,10 83,10 @@ func (mr *MockdberMockRecorder) SpaceNew(user, name, desc interface{}) *gomock.C
}

// SpaceGet mocks base method
func (m *Mockdber) SpaceGet(user user.User, spaceID string) (spaceT.Space, error) {
func (m *Mockdber) SpaceGet(user user.User, spaceID string) (space.Space, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "SpaceGet", user, spaceID)
	ret0, _ := ret[0].(spaceT.Space)
	ret0, _ := ret[0].(space.Space)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}


@@ 98,10 98,10 @@ func (mr *MockdberMockRecorder) SpaceGet(user, spaceID interface{}) *gomock.Call
}

// SpaceUpdate mocks base method
func (m *Mockdber) SpaceUpdate(user user.User, space spaceT.Space, name, desc string) (spaceT.Space, error) {
func (m *Mockdber) SpaceUpdate(user user.User, s space.Space, name, desc string) (space.Space, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "SpaceUpdate", user, space, name, desc)
	ret0, _ := ret[0].(spaceT.Space)
	ret := m.ctrl.Call(m, "SpaceUpdate", user, s, name, desc)
	ret0, _ := ret[0].(space.Space)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}


@@ 113,10 113,10 @@ func (mr *MockdberMockRecorder) SpaceUpdate(user, space, name, desc interface{})
}

// SpaceCopy mocks base method
func (m *Mockdber) SpaceCopy(user user.User, space spaceT.Space, name, desc string) (spaceT.Space, error) {
func (m *Mockdber) SpaceCopy(user user.User, s space.Space, name, desc string) (space.Space, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "SpaceCopy", user, space, name, desc)
	ret0, _ := ret[0].(spaceT.Space)
	ret := m.ctrl.Call(m, "SpaceCopy", user, s, name, desc)
	ret0, _ := ret[0].(space.Space)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}


@@ 128,7 128,7 @@ func (mr *MockdberMockRecorder) SpaceCopy(user, space, name, desc interface{}) *
}

// SpaceDelete mocks base method
func (m *Mockdber) SpaceDelete(user user.User, space spaceT.Space) error {
func (m *Mockdber) SpaceDelete(user user.User, space space.Space) error {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "SpaceDelete", user, space)
	ret0, _ := ret[0].(error)


@@ 142,31 142,31 @@ func (mr *MockdberMockRecorder) SpaceDelete(user, space interface{}) *gomock.Cal
}

// ContentTypesPerSpace mocks base method
func (m *Mockdber) ContentTypesPerSpace(space spaceT.Space, before int) (contenttype.ContentTypeList, error) {
func (m *Mockdber) ContentTypesPerSpace(user user.User, space space.Space, before int) (contenttype.ContentTypeList, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentTypesPerSpace", space, before)
	ret := m.ctrl.Call(m, "ContentTypesPerSpace", user, space, before)
	ret0, _ := ret[0].(contenttype.ContentTypeList)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentTypesPerSpace indicates an expected call of ContentTypesPerSpace
func (mr *MockdberMockRecorder) ContentTypesPerSpace(space, before interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) ContentTypesPerSpace(user, space, before interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypesPerSpace", reflect.TypeOf((*Mockdber)(nil).ContentTypesPerSpace), space, before)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentTypesPerSpace", reflect.TypeOf((*Mockdber)(nil).ContentTypesPerSpace), user, space, before)
}

// HooksPerSpace mocks base method
func (m *Mockdber) HooksPerSpace(space spaceT.Space, before int) (hook.HookList, error) {
func (m *Mockdber) HooksPerSpace(user user.User, space space.Space, before int) (hook.HookList, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "HooksPerSpace", space, before)
	ret := m.ctrl.Call(m, "HooksPerSpace", user, space, before)
	ret0, _ := ret[0].(hook.HookList)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// HooksPerSpace indicates an expected call of HooksPerSpace
func (mr *MockdberMockRecorder) HooksPerSpace(space, before interface{}) *gomock.Call {
func (mr *MockdberMockRecorder) HooksPerSpace(user, space, before interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HooksPerSpace", reflect.TypeOf((*Mockdber)(nil).HooksPerSpace), space, before)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "HooksPerSpace", reflect.TypeOf((*Mockdber)(nil).HooksPerSpace), user, space, before)
}

M internal/c/space/space_test.go => internal/c/space/space_test.go +24 -25
@@ 13,7 13,6 @@ import (

	"git.sr.ht/~evanj/cms/internal/c"
	"git.sr.ht/~evanj/cms/internal/c/space"
	mock_space "git.sr.ht/~evanj/cms/internal/c/space/mock"
	"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"


@@ 68,7 67,7 @@ func TestNoUser(t *testing.T) {

	var (
		ctrl    = gomock.NewController(t)
		db      = mock_space.NewMockdber(ctrl)
		db      = space.NewMockdber(ctrl)
		l       *log.Logger
		s       = space.New(c.New(l, db, true, "test"), l, db)
		ts      = httptest.NewServer(s)


@@ 111,46 110,46 @@ func TestAll(t *testing.T) {

	type SpaceTest struct {
		createSC int
		create   func(*mock_space.Mockdber)
		create   func(*space.Mockdber)
		getSC    int
		get      func(*mock_space.Mockdber)
		get      func(*space.Mockdber)
		updateSC int
		update   func(*mock_space.Mockdber)
		update   func(*space.Mockdber)
		copySC   int
		copy     func(*mock_space.Mockdber)
		copy     func(*space.Mockdber)
		deleteSC int
		delete   func(*mock_space.Mockdber)
		delete   func(*space.Mockdber)
	}

	tests := []SpaceTest{

		SpaceTest{
			createSC: http.StatusFound,
			create: func(db *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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(sItem, 0).Return(contentTypeList, nil).AnyTimes()
				db.EXPECT().HooksPerSpace(sItem, 0).Return(hookList, 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 *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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()


@@ 159,50 158,50 @@ func TestAll(t *testing.T) {

		SpaceTest{
			createSC: http.StatusBadRequest,
			create: func(db *mock_space.Mockdber) {
			create: func(db *space.Mockdber) {
				db.EXPECT().UserGet(uname, upass).Return(nil, c.ErrNoLogin).AnyTimes()
			},
			getSC: http.StatusBadRequest,
			get: func(db *mock_space.Mockdber) {
			get: func(db *space.Mockdber) {
				db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
			},
			updateSC: http.StatusBadRequest,
			update: func(db *mock_space.Mockdber) {
			update: func(db *space.Mockdber) {
				db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
			},
			copySC: http.StatusBadRequest,
			copy: func(db *mock_space.Mockdber) {
			copy: func(db *space.Mockdber) {
				db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
			},
			deleteSC: http.StatusBadRequest,
			delete: func(db *mock_space.Mockdber) {
			delete: func(db *space.Mockdber) {
				db.EXPECT().UserGet(uname, upass).Return(uItem, nil).AnyTimes()
			},
		},

		SpaceTest{
			createSC: http.StatusBadRequest,
			create: func(db *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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 *mock_space.Mockdber) {
			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()
			},


@@ 215,7 214,7 @@ func TestAll(t *testing.T) {

		var (
			ctrl = gomock.NewController(t)
			db   = mock_space.NewMockdber(ctrl)
			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)

R internal/c/user/mock.go => internal/c/user/user_mock.go +0 -0

M internal/m/user/mock.go => internal/m/user/mock.go +9 -2
@@ 2,6 2,7 @@ package user

import (
	"git.sr.ht/~evanj/cms/internal/m/org"
	"git.sr.ht/~evanj/cms/internal/m/role"
	"git.sr.ht/~evanj/cms/internal/m/tier"
	"github.com/google/uuid"
)


@@ 10,6 11,7 @@ type _user struct {
	id, name, tok, email string
	org                  _org
	pass                 string
	role                 role.Role
}

type _org struct {


@@ 17,11 19,15 @@ type _org struct {
}

func NewMock(id, name string) _user {
	return _user{id, name, uuid.New().String(), "", _org{id}, ""}
	return _user{id, name, uuid.New().String(), "", _org{id}, "", role.Admin}
}

func NewMockWithEmail(id, name, email string) _user {
	return _user{id, name, uuid.New().String(), email, _org{id}, ""}
	return _user{id, name, uuid.New().String(), email, _org{id}, "", role.Admin}
}

func NewMockWithRole(id, name string, r role.Role) _user {
	return _user{id, name, uuid.New().String(), "", _org{id}, "", r}
}

func (u _user) ID() string             { return u.id }


@@ 32,6 38,7 @@ func (u _user) Email() string          { return u.email }
func (u _user) Org() org.Org           { return u.org }
func (u _user) SetPass(p string) _user { u.pass = p; return u }
func (u _user) Pass() string           { return u.pass }
func (u _user) Role() role.Role        { return u.role }

func (o _org) ID() string                { return o.id }
func (o _org) HasPaymentCustomer() bool  { return false }

A internal/s/cache/cache_generic.go => internal/s/cache/cache_generic.go +23 -0
@@ 0,0 1,23 @@
// Code generated by go2go; DO NOT EDIT.


//line cache_generic.go2:1
package cache

//line cache_generic.go2:1
import (
//line cache_generic.go2:1
 "encoding/json"
//line cache_generic.go2:1
 "github.com/bradfitz/gomemcache/memcache"
//line cache_generic.go2:1
)

//line cache_generic.go2:1
type Importableā­Ķ int

//line cache_generic.go2:1
var _ = json.Compact

//line cache_generic.go2:1
type _ memcache.Client

A internal/s/cache/cache_generic.go2 => internal/s/cache/cache_generic.go2 +31 -0
@@ 0,0 1,31 @@
package cache

import (
	"encoding/json"
	"github.com/bradfitz/gomemcache/memcache"
)

func StoreAndLoad[type T](mc *memcache.Client, breakCache bool, key string, val T, getter func() (T, error)) (it T, err error) { 
  cached, err := mc.Get(key)
  if err != nil || breakCache { 
    it, err = getter()
    if err != nil { 
      return it, err
    }

		bytes, err := json.Marshal(it)
		if err != nil {
      return it, err
		}

    if err := mc.Set(&memcache.Item{Key: key, Value: bytes}); err != nil { 
      return it, err
    }

    return it, nil
  }

	err = json.Unmarshal(cached.Value, &it)
  return it, err
}