~evanj/cms

81dac613a00686c518230f2688a0893a658b61e3 — Evan M Jones 4 months ago 8509c41
Feat(c/content): Testing complete for controllers.
5 files changed, 899 insertions(+), 197 deletions(-)

M internal/c/content/content.go
M internal/c/content/content_test.go
R internal/c/content/{content_mock.go => mock.go}
A internal/c/content/testfiles/zoidberg.png
M internal/c/contenttype/contenttype_test.go
M internal/c/content/content.go => internal/c/content/content.go +82 -91
@@ 25,7 25,10 @@ import (
var (
	contentHTML = tmpl.MustParse("html/content.html")

	ErrNoLogin = errors.New("must be logged in")
	ErrNoLogin = c.ErrNoLogin
	ErrNoSpace = errors.New("failed to find required space")
	ErrNoCT    = errors.New("failed to find required contenttype")
	ErrNoC     = errors.New("failed to find desired content")
)

type Content struct {


@@ 90,17 93,17 @@ func (c *Content) tree(w http.ResponseWriter, r *http.Request, spaceID, contentt

	space, err := c.db.SpaceGet(user, spaceID)
	if err != nil {
		return nil, nil, nil, nil, fmt.Errorf("failed to find required space")
		return nil, nil, nil, nil, ErrNoSpace
	}

	ct, err := c.db.ContentTypeGet(space, contenttypeID)
	if err != nil {
		return nil, nil, nil, nil, fmt.Errorf("failed to find required contenttype")
		return nil, nil, nil, nil, ErrNoCT
	}

	content, err := c.db.ContentGet(space, ct, contentID)
	if err != nil {
		return nil, nil, nil, nil, fmt.Errorf("failed to find required content")
		return nil, nil, nil, nil, ErrNoC
	}

	return user, space, ct, content, nil


@@ 112,19 115,19 @@ func (c *Content) create(w http.ResponseWriter, r *http.Request) {

	user, err := c.GetCookieUser(w, r)
	if err != nil {
		c.Error(w, r, http.StatusBadRequest, ErrNoLogin.Error())
		c.Error2(w, r, http.StatusBadRequest, ErrNoLogin)
		return
	}

	space, err := c.db.SpaceGet(user, spaceID)
	if err != nil {
		c.Error(w, r, http.StatusInternalServerError, "failed to find required space")
		c.Error2(w, r, http.StatusBadRequest, ErrNoSpace)
		return
	}

	ct, err := c.db.ContentTypeGet(space, contenttypeID)
	if err != nil {
		c.Error(w, r, http.StatusInternalServerError, "failed to find required contenttype")
		c.Error2(w, r, http.StatusBadRequest, ErrNoCT)
		return
	}



@@ 154,46 157,45 @@ func (c *Content) create(w http.ResponseWriter, r *http.Request) {

	// TODO: Upload concurrently.
	_, _, _ = r.FormFile("") // Dummy read, loads internal r.MulitpartForm state.
	for key := range r.MultipartForm.File {
		file, header, err := r.FormFile(key)
		if err != nil {
			c.Error(w, r, http.StatusInternalServerError, "failed to retreive file")
			return
		}
	if r.MultipartForm != nil {
		for key := range r.MultipartForm.File {
			file, header, err := r.FormFile(key)
			if err != nil {
				c.Error(w, r, http.StatusInternalServerError, "failed to retreive file")
				return
			}

		url, err := c.upload(r.Context(), header.Filename, file)
		if err != nil {
			c.log.Println("failed to upload file", err)
			c.Error(w, r, http.StatusInternalServerError, "failed to upload file")
			return
		}
			url, err := c.upload(r.Context(), header.Filename, file)
			if err != nil {
				c.Error(w, r, http.StatusInternalServerError, "failed to upload file")
				return
			}

		parts := strings.Split(key, "-")
		if len(parts) < 2 {
			c.Error(w, r, http.StatusInternalServerError, "invalid name field for value")
			return
		}
			parts := strings.Split(key, "-")
			if len(parts) < 2 {
				c.Error(w, r, http.StatusInternalServerError, "invalid name field for value")
				return
			}

		typ, name := parts[0], parts[1]
			typ, name := parts[0], parts[1]

		params = append(params, db.ContentNewParam{
			Type:  typ,
			Name:  name,
			Value: url,
		})
			params = append(params, db.ContentNewParam{
				Type:  typ,
				Name:  name,
				Value: url,
			})
		}
	}

	content, err := c.db.ContentNew(space, ct, params)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, err.Error())
		c.Error2(w, r, http.StatusInternalServerError, err)
		return
	}

	go c.hook.Do(space, content, webhook.HookNew)

	url := fmt.Sprintf("/content/%s/%s/%s", space.ID(), ct.ID(), content.ID())
	c.log.Println("successfully created new content for user", user.Name(), "in space", space.Name(), "for contenttype", ct.Name(), "redirecting to", url)
	c.Redirect(w, r, url)
}



@@ 211,26 213,25 @@ func (c *Content) serve(w http.ResponseWriter, r *http.Request) {

	user, err := c.GetCookieUser(w, r)
	if err != nil {
		c.Error(w, r, http.StatusBadRequest, ErrNoLogin.Error())
		c.Error2(w, r, http.StatusBadRequest, ErrNoLogin)
		return
	}

	space, err := c.db.SpaceGet(user, spaceID)
	if err != nil {
		c.Error(w, r, http.StatusInternalServerError, "failed to find required space")
		c.Error2(w, r, http.StatusBadRequest, ErrNoSpace)
		return
	}

	ct, err := c.db.ContentTypeGet(space, contenttypeID)
	if err != nil {
		c.Error(w, r, http.StatusInternalServerError, "failed to find required contenttype")
		c.Error2(w, r, http.StatusBadRequest, ErrNoCT)
		return
	}

	content, err := c.db.ContentGet(space, ct, contentID)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, "failed to find desired content")
		c.Error2(w, r, http.StatusBadRequest, ErrNoC)
		return
	}



@@ 247,10 248,9 @@ func (c *Content) update(w http.ResponseWriter, r *http.Request) {
	contenttypeID := r.FormValue("contenttype")
	contentID := r.FormValue("content")

	user, space, ct, content, err := c.tree(w, r, spaceID, contenttypeID, contentID)
	_, space, ct, content, err := c.tree(w, r, spaceID, contenttypeID, contentID)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, err.Error())
		c.Error2(w, r, http.StatusBadRequest, err)
		return
	}



@@ 311,58 311,58 @@ func (c *Content) update(w http.ResponseWriter, r *http.Request) {

	// TODO: Upload concurrently.
	_, _, _ = r.FormFile("") // Dummy read, loads internal r.MulitpartForm state.
	for key := range r.MultipartForm.File {
		file, header, err := r.FormFile(key)
		if err != nil {
			c.Error(w, r, http.StatusInternalServerError, "failed to retreive file")
			return
		}
	if r.MultipartForm != nil {
		for key := range r.MultipartForm.File {
			file, header, err := r.FormFile(key)
			if err != nil {
				c.Error(w, r, http.StatusInternalServerError, "failed to retreive file")
				return
			}

		url, err := c.upload(r.Context(), header.Filename, file)
		if err != nil {
			c.log.Println("failed to upload file", err)
			c.Error(w, r, http.StatusInternalServerError, "failed to upload file")
			return
		}
			url, err := c.upload(r.Context(), header.Filename, file)
			if err != nil {
				c.Error(w, r, http.StatusInternalServerError, "failed to upload file")
				return
			}

		// Check if we're update image value.
		if strings.Contains(key, "value_update_") {
			// Check if we're update image value.
			if strings.Contains(key, "value_update_") {

			parts := strings.Split(strings.ReplaceAll(key, "value_update_", ""), "-")
				parts := strings.Split(strings.ReplaceAll(key, "value_update_", ""), "-")
				if len(parts) < 2 {
					c.Error(w, r, http.StatusInternalServerError, "invalid name field for value")
					return
				}

				_, id := parts[0], parts[1]

				updateParams = append(updateParams, db.ContentUpdateParam{
					ID:    id,
					Type:  valuetype.File,
					Value: url,
				})

				continue
			}

			parts := strings.Split(key, "-")
			if len(parts) < 2 {
				c.Error(w, r, http.StatusInternalServerError, "invalid name field for value")
				return
			}

			_, id := parts[0], parts[1]
			typ, name := parts[0], parts[1]

			updateParams = append(updateParams, db.ContentUpdateParam{
				ID:    id,
				Type:  valuetype.File,
			newParams = append(newParams, db.ContentNewParam{
				Type:  typ,
				Name:  name,
				Value: url,
			})

			continue
		}

		parts := strings.Split(key, "-")
		if len(parts) < 2 {
			c.Error(w, r, http.StatusInternalServerError, "invalid name field for value")
			return
		}

		typ, name := parts[0], parts[1]

		newParams = append(newParams, db.ContentNewParam{
			Type:  typ,
			Name:  name,
			Value: url,
		})
	}

	content, err = c.db.ContentUpdate(space, ct, content, newParams, updateParams)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, "failed to update content")
		return
	}


@@ 370,7 370,6 @@ func (c *Content) update(w http.ResponseWriter, r *http.Request) {
	go c.hook.Do(space, content, webhook.HookUpdate)

	url := fmt.Sprintf("/content/%s/%s/%s", space.ID(), ct.ID(), content.ID())
	c.log.Println("successfully updated content for user", user.Name(), "in space", space.Name(), "for contenttype", ct.Name(), "redirecting to", url)
	c.Redirect(w, r, url)
}



@@ 379,15 378,13 @@ func (c *Content) delete(w http.ResponseWriter, r *http.Request) {
	contenttypeID := r.FormValue("contenttype")
	contentID := r.FormValue("content")

	user, space, ct, content, err := c.tree(w, r, spaceID, contenttypeID, contentID)
	_, space, ct, content, err := c.tree(w, r, spaceID, contenttypeID, contentID)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, err.Error())
		c.Error2(w, r, http.StatusBadRequest, err)
		return
	}

	if err := c.db.ContentDelete(space, ct, content); err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, "failed to delete content")
		return
	}


@@ 395,7 392,6 @@ func (c *Content) delete(w http.ResponseWriter, r *http.Request) {
	go c.hook.Do(space, content, webhook.HookDelete)

	url := fmt.Sprintf("/contenttype/%s/%s", space.ID(), ct.ID())
	c.log.Println("successfully deleted content for user", user.Name(), "in space", space.Name(), "for contenttype", ct.Name(), "redirecting to", url)
	c.Redirect(w, r, url)
}



@@ 411,30 407,25 @@ func (c *Content) search(w http.ResponseWriter, r *http.Request) {

	user, err := c.GetCookieUser(w, r)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, ErrNoLogin.Error())
		c.Error2(w, r, http.StatusBadRequest, ErrNoLogin)
		return
	}

	space, err := c.db.SpaceGet(user, spaceID)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, "failed to find required space")
		c.Error2(w, r, http.StatusBadRequest, ErrNoSpace)
		return
	}

	ct, err := c.db.ContentTypeGet(space, contenttypeID)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, "failed to find required contenttype")
		c.Error2(w, r, http.StatusBadRequest, ErrNoCT)
		return
	}

	before, _ := strconv.Atoi(r.URL.Query().Get("before"))
	list, err := c.db.ContentSearch(space, ct, field, query, before)
	if err != nil {
		c.log.Println(err)
		c.Error(w, r, http.StatusInternalServerError, "failed to find desired content")
		return
	}


M internal/c/content/content_test.go => internal/c/content/content_test.go +729 -0
@@ 1,1 1,730 @@
package content_test

import (
	"bytes"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"log"
	"mime/multipart"
	"net/http"
	"net/http/httptest"
	"net/url"
	"os"
	"strings"
	"testing"

	"git.sr.ht/~evanj/cms/internal/c/content"
	contentmodel "git.sr.ht/~evanj/cms/internal/m/content"
	"git.sr.ht/~evanj/cms/internal/m/value"
	"git.sr.ht/~evanj/cms/internal/m/valuetype"
	"git.sr.ht/~evanj/cms/internal/s/db"
	webhook "git.sr.ht/~evanj/cms/internal/s/hook"
	"github.com/bmizerany/assert"
	gomock "github.com/golang/mock/gomock"
	"github.com/google/uuid"
)

var file, _ = os.Open("testfiles/zoidberg.png")

func bodyWithFile(name string, form url.Values) (io.Reader, string) {
	var requestBody bytes.Buffer
	mpwriter := multipart.NewWriter(&requestBody)
	defer mpwriter.Close()

	filewriter, _ := mpwriter.CreateFormFile(name, "user-upload")
	_, _ = io.Copy(filewriter, file)

	for key, items := range form {
		item := items[0]
		fieldwriter, _ := mpwriter.CreateFormField(key)
		_, _ = fieldwriter.Write([]byte(item))
	}

	return &requestBody, mpwriter.FormDataContentType()
}

type nmatcher struct {
	to []db.ContentNewParam
}

func (pm nmatcher) Matches(from interface{}) bool {
	val, ok := from.([]db.ContentNewParam)
	if !ok {
		return false
	}

	if len(pm.to) != len(val) {
		return false
	}

	for _, in := range pm.to {
		exists := false
		for _, out := range val {
			if in.Name == out.Name && in.Type == out.Type && in.Value == out.Value {
				exists = true
			}
		}
		if !exists {
		}
	}

	return true
}

func (pm nmatcher) String() string {
	return fmt.Sprintf("%v", pm.to)
}

type umatcher struct {
	to []db.ContentUpdateParam
}

func (pm umatcher) Matches(from interface{}) bool {
	val, ok := from.([]db.ContentUpdateParam)
	if !ok {
		return false
	}

	if len(pm.to) != len(val) {
		return false
	}

	for _, in := range pm.to {
		exists := false
		for _, out := range val {
			if in.ID == out.ID && in.Type == out.Type && in.Value == out.Value {
				exists = true
			}
		}
		if !exists {
			return false
		}
	}

	return true
}

func (pm umatcher) String() string {
	return fmt.Sprintf("%v", pm.to)
}

type fakeuser struct{ u, p string }

func (u fakeuser) ID() string    { return fmt.Sprintf("id-%s-%s", u.u, u.p) }
func (u fakeuser) Name() string  { return u.u }
func (u fakeuser) Token() string { return fmt.Sprintf("token-%s-%s", u.u, u.p) }

type fakespace struct{ n, d string }

func (u fakespace) ID() string   { return fmt.Sprintf("id-%s-%s", u.n, u.d) }
func (u fakespace) Name() string { return u.n }
func (u fakespace) Desc() string { return u.d }

type fakecontenttype struct {
	id, name string
	fields   int
}

func (u fakecontenttype) ID() string   { return u.id }
func (u fakecontenttype) Name() string { return u.name }
func (u fakecontenttype) Fields() (r []valuetype.ValueType) {
	for i := 0; i < u.fields; i++ {
		var val valuetype.ValueType
		r = append(r, val)
	}
	return
}
func (u fakecontenttype) FieldsWithRefCount() (r int) { return }

type fakecontent struct {
	id, typ string
	values  []value.Value
}

func (fc fakecontent) ID() string                                  { return fc.id }
func (fc fakecontent) Type() string                                { return fc.typ }
func (fc fakecontent) Values() []value.Value                       { return fc.values }
func (fc fakecontent) ValueByName(name string) (value.Value, bool) { return nil, false }
func (fc fakecontent) MustValueByName(name string) value.Value     { return nil }

type fakecontentlist struct{}

func (cl fakecontentlist) List() (r []contentmodel.Content) { return }
func (cl fakecontentlist) More() (r bool)                   { return }
func (cl fakecontentlist) Before() (r int)                  { return }

func TestCreateGood(t *testing.T) {
	t.Parallel()

	var (
		fileURL = "https://e3.evanjon.es/zoidberg.png"

		newParams = []db.ContentNewParam{
			{"StringSmall", "name", "content name"},
			{"StringBig", "short", "content short desc"},
			{"InputHTML", "desc", "content desc"},
			{"File", "image", fileURL},
		}

		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}

		form = url.Values{
			"space":            {s.ID()},
			"contenttype":      {ct.ID()},
			"StringSmall-name": {"content name"},
			"StringBig-short":  {"content short desc"},
			"InputHTML-desc":   {"content desc"},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()
	e3.EXPECT().Upload(gomock.Any(), false, gomock.Any(), gomock.Any()).Return(fileURL, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookNew).Return().AnyTimes()

	bod, headerCT := bodyWithFile("File-image", form)
	req, _ := http.NewRequest("POST", ts.URL, bod)
	req.Header.Set("Content-Type", headerCT)
	req.SetBasicAuth(u.Name(), u.p)

	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestUpdateGood(t *testing.T) {
	t.Parallel()

	var (
		fileURL = "https://e3.evanjon.es/zoidberg.png"

		newParams = []db.ContentNewParam{
			{"StringBig", "meta", "some meta description"},
			{"File", "image", fileURL},
		}

		updateParams = []db.ContentUpdateParam{
			{"1", "StringSmall", "content name"},
			{"2", "StringBig", "content short desc"},
			{"3", "InputHTML", "content desc"},
		}

		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}

		form = url.Values{
			"space":                      {s.ID()},
			"contenttype":                {ct.ID()},
			"content":                    {c.ID()},
			"method":                     {"PATCH"},
			"StringBig-meta":             {"some meta description"},
			"value_update_StringSmall-1": {"content name"},
			"value_update_StringBig-2":   {"content short desc"},
			"value_update_InputHTML-3":   {"content desc"},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()
	e3.EXPECT().Upload(gomock.Any(), false, gomock.Any(), gomock.Any()).Return(fileURL, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookUpdate).Return().AnyTimes()

	bod, headerCT := bodyWithFile("File-image", form)
	req, _ := http.NewRequest("POST", ts.URL, bod)
	req.Header.Set("Content-Type", headerCT)
	req.SetBasicAuth(u.Name(), u.p)

	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestUpdateGood2(t *testing.T) {
	t.Parallel()

	var (
		fileURL = "/zoidberg.png"

		newParams = []db.ContentNewParam{
			{"StringBig", "meta", "some meta description"},
		}

		updateParams = []db.ContentUpdateParam{
			{"1", "StringSmall", "content name"},
			{"2", "StringBig", "content short desc"},
			{"3", "InputHTML", "content desc"},
			{"4", "File", "/file" + fileURL},
		}

		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}

		form = url.Values{
			"space":                      {s.ID()},
			"contenttype":                {ct.ID()},
			"content":                    {c.ID()},
			"method":                     {"PATCH"},
			"StringBig-meta":             {"some meta description"},
			"value_update_StringSmall-1": {"content name"},
			"value_update_StringBig-2":   {"content short desc"},
			"value_update_InputHTML-3":   {"content desc"},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()
	e3.EXPECT().Upload(gomock.Any(), false, gomock.Any(), gomock.Any()).Return(fileURL, nil).AnyTimes()
	h.EXPECT().Do(s, c, webhook.HookUpdate).Return().AnyTimes()

	bod, headerCT := bodyWithFile("value_update_File-4-image", form)
	req, _ := http.NewRequest("POST", ts.URL, bod)
	req.Header.Set("Content-Type", headerCT)
	req.SetBasicAuth(u.Name(), u.p)

	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestServeGood(t *testing.T) {
	t.Parallel()

	var (
		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}

		form = url.Values{
			"space":       {s.ID()},
			"contenttype": {ct.ID()},
			"content":     {c.ID()},
			"method":      {"GET"},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()

	req, _ := http.NewRequest("POST", ts.URL,
		strings.NewReader(form.Encode()))
	req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
	req.SetBasicAuth(u.Name(), u.p)

	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestServeGood2(t *testing.T) {
	t.Parallel()

	var (
		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}

		form = url.Values{
			"space":       {s.ID()},
			"contenttype": {ct.ID()},
			"content":     {c.ID()},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()

	req, _ := http.NewRequest("GET", ts.URL+"?"+form.Encode(), nil)
	req.SetBasicAuth(u.Name(), u.p)
	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestServeGood3(t *testing.T) {
	t.Parallel()

	var (
		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()

	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.p)
	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestDeleteGood(t *testing.T) {
	t.Parallel()

	var (
		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		c  = fakecontent{uuid.New().String(), ct.ID(), nil}

		form = url.Values{
			"space":       {s.ID()},
			"contenttype": {ct.ID()},
			"content":     {c.ID()},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()

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

	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestSearchGood(t *testing.T) {
	t.Parallel()

	var (
		ctrl = gomock.NewController(t)
		db   = content.NewMockDBer(ctrl)
		e3   = content.NewMockE3er(ctrl)
		h    = content.NewMockHooker(ctrl)
		l    = log.New(os.Stdout, "", 0)
		ts   = httptest.NewServer(content.New(l, db, e3, h, ""))

		u  = fakeuser{uuid.New().String(), uuid.New().String()}
		s  = fakespace{uuid.New().String(), uuid.New().String()}
		ct = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
		cl = fakecontentlist{}

		form = url.Values{
			"space":       {s.ID()},
			"contenttype": {ct.ID()},
			"field":       {"name"},
			"query":       {"garfield"},
		}
	)

	db.EXPECT().UserGet(u.Name(), u.p).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()

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

	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

func TestBad(t *testing.T) {
	t.Parallel()

	var (
		err = errors.New("placeholder")
		u   = fakeuser{uuid.New().String(), uuid.New().String()}
		s   = fakespace{uuid.New().String(), uuid.New().String()}
		ct  = fakecontenttype{uuid.New().String(), uuid.New().String(), 3}
	// cl = fakecontentlist{}
	)

	type spec struct {
		sc   int
		err  error
		form url.Values
		mock func(*content.MockDBer, *content.MockE3er, *content.MockHooker)
	}

	tests := []spec{
		// No user.
		{
			http.StatusBadRequest,
			content.ErrNoLogin,
			url.Values{
				"method": {"GET"},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoLogin,
			url.Values{
				"method": {"GET"},
				"query":  {"placeholder"},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoLogin,
			url.Values{
				"method": {"PATCH"},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoLogin,
			url.Values{
				"method": {"DELETE"},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoLogin,
			url.Values{
				"method": {"POST"},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(nil, err).AnyTimes()
			},
		},
		// No space.
		{
			http.StatusBadRequest,
			content.ErrNoSpace,
			url.Values{
				"method": {"GET"},
				"space":  {s.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoSpace,
			url.Values{
				"method": {"GET"},
				"query":  {"placeholder"},
				"space":  {s.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoSpace,
			url.Values{
				"method": {"PATCH"},
				"space":  {s.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoSpace,
			url.Values{
				"method": {"DELETE"},
				"space":  {s.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoSpace,
			url.Values{
				"method": {"POST"},
				"space":  {s.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(nil, err).AnyTimes()
			},
		},
		// No content type
		{
			http.StatusBadRequest,
			content.ErrNoCT,
			url.Values{
				"method":      {"GET"},
				"space":       {s.ID()},
				"contenttype": {ct.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoCT,
			url.Values{
				"method":      {"GET"},
				"query":       {"placeholder"},
				"space":       {s.ID()},
				"contenttype": {ct.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoCT,
			url.Values{
				"method":      {"PATCH"},
				"space":       {s.ID()},
				"contenttype": {ct.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoCT,
			url.Values{
				"method":      {"DELETE"},
				"space":       {s.ID()},
				"contenttype": {ct.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
		{
			http.StatusBadRequest,
			content.ErrNoCT,
			url.Values{
				"method":      {"POST"},
				"space":       {s.ID()},
				"contenttype": {ct.ID()},
			},
			func(db *content.MockDBer, e3 *content.MockE3er, h *content.MockHooker) {
				db.EXPECT().UserGet(u.Name(), u.p).Return(u, nil).AnyTimes()
				db.EXPECT().SpaceGet(u, s.ID()).Return(s, nil).AnyTimes()
				db.EXPECT().ContentTypeGet(s, ct.ID()).Return(nil, err).AnyTimes()
			},
		},
	}

	for _, test := range tests {
		var (
			ctrl = gomock.NewController(t)
			db   = content.NewMockDBer(ctrl)
			e3   = content.NewMockE3er(ctrl)
			h    = content.NewMockHooker(ctrl)
			l    = log.New(os.Stdout, "", 0)
			ts   = httptest.NewServer(content.New(l, db, e3, h, ""))
		)

		test.mock(db, e3, h)

		req, _ := http.NewRequest("POST", ts.URL,
			strings.NewReader(test.form.Encode()))
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		req.SetBasicAuth(u.Name(), u.p)

		res, _ := http.DefaultClient.Do(req)
		byt, _ := ioutil.ReadAll(res.Body)
		assert.Equal(t, true, strings.Contains(test.err.Error(), string(byt)))
		assert.Equal(t, res.StatusCode, test.sc)
	}
}

R internal/c/content/content_mock.go => internal/c/content/mock.go +88 -103
@@ 1,5 1,5 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: git.sr.ht/~evanj/cms/internal/c/content (interfaces: DBer,E3er,Hooker)
// Source: content.go

// Package content is a generated GoMock package.
package content


@@ 18,247 18,232 @@ import (
	gomock "github.com/golang/mock/gomock"
)

// MockDBer is a mock of DBer interface.
// MockDBer is a mock of DBer interface
type MockDBer struct {
	ctrl     *gomock.Controller
	recorder *MockDBerMockRecorder
}

// MockDBerMockRecorder is the mock recorder for MockDBer.
// MockDBerMockRecorder is the mock recorder for MockDBer
type MockDBerMockRecorder struct {
	mock *MockDBer
}

// NewMockDBer creates a new mock instance.
// NewMockDBer creates a new mock instance
func NewMockDBer(ctrl *gomock.Controller) *MockDBer {
	mock := &MockDBer{ctrl: ctrl}
	mock.recorder = &MockDBerMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockDBer) EXPECT() *MockDBerMockRecorder {
	return m.recorder
}

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

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

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

// ContentGet indicates an expected call of ContentGet.
func (mr *MockDBerMockRecorder) ContentGet(arg0, arg1, arg2 interface{}) *gomock.Call {
// UserGet indicates an expected call of UserGet
func (mr *MockDBerMockRecorder) UserGet(username, password interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentGet", reflect.TypeOf((*MockDBer)(nil).ContentGet), arg0, arg1, arg2)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserGet", reflect.TypeOf((*MockDBer)(nil).UserGet), username, password)
}

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

// ContentNew indicates an expected call of ContentNew.
func (mr *MockDBerMockRecorder) ContentNew(arg0, arg1, arg2 interface{}) *gomock.Call {
// UserGetFromToken indicates an expected call of UserGetFromToken
func (mr *MockDBerMockRecorder) UserGetFromToken(token interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentNew", reflect.TypeOf((*MockDBer)(nil).ContentNew), arg0, arg1, arg2)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "UserGetFromToken", reflect.TypeOf((*MockDBer)(nil).UserGetFromToken), token)
}

// ContentPerContentType mocks base method.
func (m *MockDBer) ContentPerContentType(arg0 space.Space, arg1 contenttype.ContentType, arg2 int, arg3 db.OrderType, arg4 string) ([]content.Content, error) {
// SpaceGet mocks base method
func (m *MockDBer) SpaceGet(user user.User, spaceID string) (space.Space, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentPerContentType", arg0, arg1, arg2, arg3, arg4)
	ret0, _ := ret[0].([]content.Content)
	ret := m.ctrl.Call(m, "SpaceGet", user, spaceID)
	ret0, _ := ret[0].(space.Space)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// ContentPerContentType indicates an expected call of ContentPerContentType.
func (mr *MockDBerMockRecorder) ContentPerContentType(arg0, arg1, arg2, arg3, arg4 interface{}) *gomock.Call {
// SpaceGet indicates an expected call of SpaceGet
func (mr *MockDBerMockRecorder) SpaceGet(user, spaceID interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "ContentPerContentType", reflect.TypeOf((*MockDBer)(nil).ContentPerContentType), arg0, arg1, arg2, arg3, arg4)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "SpaceGet", reflect.TypeOf((*MockDBer)(nil).SpaceGet), user, spaceID)
}

// ContentSearch mocks base method.
func (m *MockDBer) ContentSearch(arg0 space.Space, arg1 contenttype.ContentType, arg2, arg3 string, arg4 int) ([]content.Content, error) {
// ContentTypeGet mocks base method
func (m *MockDBer) ContentTypeGet(space space.Space, contenttypeID string) (contenttype.ContentType, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentSearch", arg0, arg1, arg2, arg3, arg4)
	ret0, _ := ret[0].([]content.Content)
	ret := m.ctrl.Call(m, "ContentTypeGet", space, contenttypeID)
	ret0, _ := ret[0].(contenttype.ContentType)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

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

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

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

// ContentUpdate mocks base method.
func (m *MockDBer) ContentUpdate(arg0 space.Space, arg1 contenttype.ContentType, arg2 content.Content, arg3 []db.ContentNewParam, arg4 []db.ContentUpdateParam) (content.Content, error) {
// ContentGet mocks base method
func (m *MockDBer) ContentGet(space space.Space, ct contenttype.ContentType, contentID string) (content.Content, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "ContentUpdate", arg0, arg1, arg2, arg3, arg4)
	ret := m.ctrl.Call(m, "ContentGet", space, ct, contentID)
	ret0, _ := ret[0].(content.Content)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

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

// SpaceGet mocks base method.
func (m *MockDBer) SpaceGet(arg0 user.User, arg1 string) (space.Space, error) {
// 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) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "SpaceGet", arg0, arg1)
	ret0, _ := ret[0].(space.Space)
	ret := m.ctrl.Call(m, "ContentUpdate", space, ct, c, newParams, updateParams)
	ret0, _ := ret[0].(content.Content)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

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

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

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

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

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

// MockE3er is a mock of E3er interface.
// MockE3er is a mock of E3er interface
type MockE3er struct {
	ctrl     *gomock.Controller
	recorder *MockE3erMockRecorder
}

// MockE3erMockRecorder is the mock recorder for MockE3er.
// MockE3erMockRecorder is the mock recorder for MockE3er
type MockE3erMockRecorder struct {
	mock *MockE3er
}

// NewMockE3er creates a new mock instance.
// NewMockE3er creates a new mock instance
func NewMockE3er(ctrl *gomock.Controller) *MockE3er {
	mock := &MockE3er{ctrl: ctrl}
	mock.recorder = &MockE3erMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockE3er) EXPECT() *MockE3erMockRecorder {
	return m.recorder
}

// Upload mocks base method.
func (m *MockE3er) Upload(arg0 context.Context, arg1 bool, arg2 string, arg3 io.Reader) (string, error) {
// Upload mocks base method
func (m *MockE3er) Upload(ctx context.Context, public bool, filename string, file io.Reader) (string, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Upload", arg0, arg1, arg2, arg3)
	ret := m.ctrl.Call(m, "Upload", ctx, public, filename, file)
	ret0, _ := ret[0].(string)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// Upload indicates an expected call of Upload.
func (mr *MockE3erMockRecorder) Upload(arg0, arg1, arg2, arg3 interface{}) *gomock.Call {
// Upload indicates an expected call of Upload
func (mr *MockE3erMockRecorder) Upload(ctx, public, filename, file interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockE3er)(nil).Upload), arg0, arg1, arg2, arg3)
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Upload", reflect.TypeOf((*MockE3er)(nil).Upload), ctx, public, filename, file)
}

// MockHooker is a mock of Hooker interface.
// MockHooker is a mock of Hooker interface
type MockHooker struct {
	ctrl     *gomock.Controller
	recorder *MockHookerMockRecorder
}

// MockHookerMockRecorder is the mock recorder for MockHooker.
// MockHookerMockRecorder is the mock recorder for MockHooker
type MockHookerMockRecorder struct {
	mock *MockHooker
}

// NewMockHooker creates a new mock instance.
// NewMockHooker creates a new mock instance
func NewMockHooker(ctrl *gomock.Controller) *MockHooker {
	mock := &MockHooker{ctrl: ctrl}
	mock.recorder = &MockHookerMockRecorder{mock}
	return mock
}

// EXPECT returns an object that allows the caller to indicate expected use.
// EXPECT returns an object that allows the caller to indicate expected use
func (m *MockHooker) EXPECT() *MockHookerMockRecorder {
	return m.recorder
}

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

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

A internal/c/content/testfiles/zoidberg.png => internal/c/content/testfiles/zoidberg.png +0 -0

M internal/c/contenttype/contenttype_test.go => internal/c/contenttype/contenttype_test.go +0 -3
@@ 385,9 385,6 @@ func TestContentTypeBadPath(t *testing.T) {
		req.SetBasicAuth(uItem.Name(), uItem.p)
		res, _ := http.DefaultClient.Do(req)
		byt, _ := ioutil.ReadAll(res.Body)
		if !strings.Contains(test.err.Error(), string(byt)) {
			panic(string(byt))
		}
		assert.Equal(t, true, strings.Contains(test.err.Error(), string(byt)))
		assert.Equal(t, test.expect, res.StatusCode)
	}