~evanj/cms

8509c418f9d5781a981ecd1490bfaf4e3ff7bd3d — Evan M Jones 4 months ago eb76754
Feat(c/*): Adding testing to small controllers. Removing ping
controller.
M cms.go => cms.go +0 -5
@@ 12,7 12,6 @@ import (
	"git.sr.ht/~evanj/cms/internal/c/doc"
	"git.sr.ht/~evanj/cms/internal/c/file"
	"git.sr.ht/~evanj/cms/internal/c/hook"
	"git.sr.ht/~evanj/cms/internal/c/ping"
	"git.sr.ht/~evanj/cms/internal/c/redirect"
	"git.sr.ht/~evanj/cms/internal/c/space"
	"git.sr.ht/~evanj/cms/internal/c/user"


@@ 126,10 125,6 @@ func init() {
				log.New(w, "[cms:hook] ", 0),
				cacher,
			),
			"ping": ping.New(
				log.New(w, "[cms:ping] ", 0),
				cacher,
			),
			"file": file.New(
				log.New(w, "[cms:file] ", 0),
				cacher,

M internal/c/doc/doc_test.go => internal/c/doc/doc_test.go +77 -0
@@ 1,1 1,78 @@
package doc_test

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"testing"

	"git.sr.ht/~evanj/cms/internal/c"
	"git.sr.ht/~evanj/cms/internal/c/doc"
	"github.com/bmizerany/assert"
	gomock "github.com/golang/mock/gomock"
	"github.com/google/uuid"
)

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

	var (
		ctrl = gomock.NewController(t)
		db   = doc.NewMockdber(ctrl)
		s    = doc.New(nil, db)
		ts   = httptest.NewServer(s)
	)

	res, err := http.Get(ts.URL)
	assert.Equal(t, nil, err)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

type FakeUser struct{}

func (u FakeUser) ID() string    { return "98" }
func (u FakeUser) Name() string  { return "Spike" }
func (u FakeUser) Token() string { return uuid.New().String() }

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

	var (
		ctrl = gomock.NewController(t)
		db   = doc.NewMockdber(ctrl)
		s    = doc.New(nil, db)
		ts   = httptest.NewServer(s)
	)

	db.EXPECT().UserGet("user", "pass").Return(FakeUser{}, nil).AnyTimes()

	req, _ := http.NewRequest("GET", ts.URL, nil)
	req.SetBasicAuth("user", "pass")
	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

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

	var (
		ctrl = gomock.NewController(t)
		db   = doc.NewMockdber(ctrl)
		s    = doc.New(nil, db)
		ts   = httptest.NewServer(s)
		u    = FakeUser{}
		tok  = u.Token()
		err  = errors.New("placeholder")
	)

	db.EXPECT().UserGetFromToken(tok).Return(nil, err).AnyTimes()

	// Shouldn't error.
	req, _ := http.NewRequest("GET", ts.URL, nil)
	req.AddCookie(&http.Cookie{
		Name:  c.KeyUserLogin,
		Value: tok,
	})
	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

A internal/c/doc/mock.go => internal/c/doc/mock.go +64 -0
@@ 0,0 1,64 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: doc.go

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

import (
	user "git.sr.ht/~evanj/cms/internal/m/user"
	gomock "github.com/golang/mock/gomock"
	reflect "reflect"
)

// Mockdber is a mock of dber interface
type Mockdber struct {
	ctrl     *gomock.Controller
	recorder *MockdberMockRecorder
}

// MockdberMockRecorder is the mock recorder for Mockdber
type MockdberMockRecorder struct {
	mock *Mockdber
}

// 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
func (m *Mockdber) EXPECT() *MockdberMockRecorder {
	return m.recorder
}

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

// 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, "UserGet", reflect.TypeOf((*Mockdber)(nil).UserGet), username, password)
}

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

// 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, "UserGetFromToken", reflect.TypeOf((*Mockdber)(nil).UserGetFromToken), token)
}

M internal/c/file/file.go => internal/c/file/file.go +1 -3
@@ 36,15 36,13 @@ func New(log *log.Logger, db DBer, e3 E3er, baseURL string) *File {
func (f *File) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	ok, err := f.db.FileExists(f.baseURL + r.URL.Path)
	if !ok || err != nil {
		f.log.Println(err)
		f.Error(w, r, http.StatusInternalServerError, "file does not exist")
		f.Error(w, r, http.StatusNotFound, "file does not exist")
		return
	}

	full := strings.TrimRight(f.e3.URL(), "api") + strings.TrimLeft(r.URL.Path, "/file") // TODO: Cleanup, this is hacky.
	bytes, err := f.e3.Proxy(r.Context(), full)
	if err != nil {
		f.log.Println(err)
		f.Error(w, r, http.StatusInternalServerError, "failed to serve file")
		return
	}

M internal/c/file/file_test.go => internal/c/file/file_test.go +79 -0
@@ 1,1 1,80 @@
package file_test

import (
	"errors"
	"net/http"
	"net/http/httptest"
	"strings"
	"testing"

	"git.sr.ht/~evanj/cms/internal/c/file"
	E3 "git.sr.ht/~evanj/cms/pkg/e3"
	"github.com/bmizerany/assert"
	gomock "github.com/golang/mock/gomock"
)

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

	var (
		ctrl  = gomock.NewController(t)
		db    = file.NewMockDBer(ctrl)
		e3    = file.NewMockE3er(ctrl)
		s     = file.New(nil, db, e3, E3.DefaultURL)
		ts    = httptest.NewServer(s)
		fp    = "/file/some-file.jpg"
		url   = strings.TrimRight(E3.DefaultURL, "api") + "some-file.jpg"
		bytes []byte
	)

	db.EXPECT().FileExists(E3.DefaultURL+fp).Return(true, nil).AnyTimes()
	e3.EXPECT().URL().Return(E3.DefaultURL).AnyTimes()
	e3.EXPECT().Proxy(gomock.Any(), url).Return(bytes, nil).AnyTimes()

	res, err := http.Get(ts.URL + fp)
	assert.Equal(t, nil, err)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

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

	var (
		ctrl = gomock.NewController(t)
		db   = file.NewMockDBer(ctrl)
		e3   = file.NewMockE3er(ctrl)
		s    = file.New(nil, db, e3, E3.DefaultURL)
		ts   = httptest.NewServer(s)
		fp   = "/file/some-file.jpg"
		url  = strings.TrimRight(E3.DefaultURL, "api") + "some-file.jpg"
		err  = errors.New("placeholder")
	)

	db.EXPECT().FileExists(E3.DefaultURL+fp).Return(true, nil).AnyTimes()
	e3.EXPECT().URL().Return(E3.DefaultURL).AnyTimes()
	e3.EXPECT().Proxy(gomock.Any(), url).Return(nil, err).AnyTimes()

	res, err := http.Get(ts.URL + fp)
	assert.Equal(t, nil, err)
	assert.Equal(t, res.StatusCode, http.StatusInternalServerError)
}

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

	var (
		ctrl = gomock.NewController(t)
		db   = file.NewMockDBer(ctrl)
		e3   = file.NewMockE3er(ctrl)
		s    = file.New(nil, db, e3, E3.DefaultURL)
		ts   = httptest.NewServer(s)
		fp   = "/file/some-file.jpg"
		err  = errors.New("placeholder")
	)

	db.EXPECT().FileExists(E3.DefaultURL+fp).Return(false, err).AnyTimes()

	res, err := http.Get(ts.URL + fp)
	assert.Equal(t, nil, err)
	assert.Equal(t, res.StatusCode, http.StatusNotFound)
}

A internal/c/file/mock.go => internal/c/file/mock.go +132 -0
@@ 0,0 1,132 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: file.go

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

import (
	context "context"
	user "git.sr.ht/~evanj/cms/internal/m/user"
	gomock "github.com/golang/mock/gomock"
	reflect "reflect"
)

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

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

// 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
func (m *MockDBer) EXPECT() *MockDBerMockRecorder {
	return m.recorder
}

// UserGet mocks base method
func (m *MockDBer) UserGet(username, password string) (user.User, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "UserGet", username, password)
	ret0, _ := ret[0].(user.User)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// 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, "UserGet", reflect.TypeOf((*MockDBer)(nil).UserGet), username, password)
}

// UserGetFromToken mocks base method
func (m *MockDBer) UserGetFromToken(token string) (user.User, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "UserGetFromToken", token)
	ret0, _ := ret[0].(user.User)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// 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, "UserGetFromToken", reflect.TypeOf((*MockDBer)(nil).UserGetFromToken), token)
}

// FileExists mocks base method
func (m *MockDBer) FileExists(URL string) (bool, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "FileExists", URL)
	ret0, _ := ret[0].(bool)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

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

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

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

// 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
func (m *MockE3er) EXPECT() *MockE3erMockRecorder {
	return m.recorder
}

// Proxy mocks base method
func (m *MockE3er) Proxy(ctx context.Context, objectURL string) ([]byte, error) {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "Proxy", ctx, objectURL)
	ret0, _ := ret[0].([]byte)
	ret1, _ := ret[1].(error)
	return ret0, ret1
}

// Proxy indicates an expected call of Proxy
func (mr *MockE3erMockRecorder) Proxy(ctx, objectURL interface{}) *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "Proxy", reflect.TypeOf((*MockE3er)(nil).Proxy), ctx, objectURL)
}

// URL mocks base method
func (m *MockE3er) URL() string {
	m.ctrl.T.Helper()
	ret := m.ctrl.Call(m, "URL")
	ret0, _ := ret[0].(string)
	return ret0
}

// URL indicates an expected call of URL
func (mr *MockE3erMockRecorder) URL() *gomock.Call {
	mr.mock.ctrl.T.Helper()
	return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "URL", reflect.TypeOf((*MockE3er)(nil).URL))
}

D internal/c/ping/ping.go => internal/c/ping/ping.go +0 -30
@@ 1,30 0,0 @@
package ping

import (
	"log"
	"net/http"

	"git.sr.ht/~evanj/cms/internal/c"
	"git.sr.ht/~evanj/cms/internal/m/user"
)

type Ping struct {
	*c.Controller
	log *log.Logger
}

type dber interface {
	UserGet(username, password string) (user.User, error)
	UserGetFromToken(token string) (user.User, error)
}

func New(log *log.Logger, db dber) *Ping {
	return &Ping{
		c.New(log, db),
		log,
	}
}

func (p *Ping) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	p.String(w, r, "pong")
}

D internal/c/ping/ping_test.go => internal/c/ping/ping_test.go +0 -1
@@ 1,1 0,0 @@
package ping_test

A internal/c/redirect/mock.go => internal/c/redirect/mock.go +64 -0
@@ 0,0 1,64 @@
// Code generated by MockGen. DO NOT EDIT.
// Source: redirect.go

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

import (
	user "git.sr.ht/~evanj/cms/internal/m/user"
	gomock "github.com/golang/mock/gomock"
	reflect "reflect"
)

// Mockdber is a mock of dber interface
type Mockdber struct {
	ctrl     *gomock.Controller
	recorder *MockdberMockRecorder
}

// MockdberMockRecorder is the mock recorder for Mockdber
type MockdberMockRecorder struct {
	mock *Mockdber
}

// 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
func (m *Mockdber) EXPECT() *MockdberMockRecorder {
	return m.recorder
}

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

// 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, "UserGet", reflect.TypeOf((*Mockdber)(nil).UserGet), username, password)
}

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

// 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, "UserGetFromToken", reflect.TypeOf((*Mockdber)(nil).UserGetFromToken), token)
}

M internal/c/redirect/redirect.go => internal/c/redirect/redirect.go +2 -3
@@ 9,9 9,7 @@ import (
	"git.sr.ht/~evanj/cms/internal/s/tmpl"
)

var (
	redirectHTML = tmpl.MustParse("html/redirect.html")
)
var redirectHTML = tmpl.MustParse("html/redirect.html")

type Redirect struct {
	*c.Controller


@@ 38,6 36,7 @@ func (rd *Redirect) ServeHTTP(w http.ResponseWriter, r *http.Request) {
		return
	}

	w.WriteHeader(http.StatusOK)
	rd.HTML(w, r, redirectHTML, map[string]interface{}{
		"URL": to,
	})

M internal/c/redirect/redirect_test.go => internal/c/redirect/redirect_test.go +33 -0
@@ 1,1 1,34 @@
package redirect_test

import (
	"log"
	"net/http"
	"net/http/httptest"
	"testing"

	"git.sr.ht/~evanj/cms/internal/c/redirect"
	"github.com/bmizerany/assert"
	gomock "github.com/golang/mock/gomock"
)

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

	var (
		ctrl = gomock.NewController(t)
		db   = redirect.NewMockdber(ctrl)
		l    *log.Logger
		s    = redirect.New(l, db)
		ts   = httptest.NewServer(s)
	)

	t.Run("good", func(t *testing.T) {
		res, _ := http.Get(ts.URL + "?url=/space/21")
		assert.Equal(t, res.StatusCode, http.StatusOK)
	})

	t.Run("empty", func(t *testing.T) {
		res, _ := http.Get(ts.URL)
		assert.Equal(t, res.StatusCode, http.StatusBadRequest)
	})
}