~evanj/cms

4f9297f6ef234866f383697077a8eb6a4b044c2f — Evan M Jones 4 months ago 20ecf61
Feat(c/c.go): Testing for core controller.
2 files changed, 400 insertions(+), 0 deletions(-)

M internal/c/c_test.go
A internal/c/mock/mock_c.go
M internal/c/c_test.go => internal/c/c_test.go +336 -0
@@ 1,1 1,337 @@
package c_test

import (
	"encoding/json"
	"errors"
	"fmt"
	"html/template"
	"io/ioutil"
	"log"
	"net/http"
	"net/http/httptest"
	"net/url"
	"strings"
	"testing"

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

var (
	tmpl = template.Must(template.New("c_test").Parse("sup earth"))
)

type server struct{ *c.Controller }

func (s server) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.HTML(w, r, tmpl, nil)
}

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

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

	res, _ := http.Get(ts.URL)
	assert.Equal(t, res.StatusCode, http.StatusOK)
	assert.Equal(t, res.Header.Get("Content-Type"), "application/json")
}

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

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

	req, _ := http.NewRequest("GET", ts.URL, nil)
	req.Header.Set("Accept", "text/html")
	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
	assert.Equal(t, res.Header.Get("Content-Type"), "text/html")
}

type server2 struct {
	*c.Controller
	it func(string)
}

func (s server2) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.it(s.Method(r))
	w.WriteHeader(http.StatusOK)
}

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

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

	type spec struct {
		method    string
		withInput bool
		inputVal  string
		expect    string
	}

	tests := []spec{
		spec{"GET", false, "", "GET"},
		spec{"POST", false, "", "POST"},
		spec{"PUT", false, "", "PUT"},
		spec{"PATCH", false, "", "PATCH"},
		spec{"DELETE", false, "", "DELETE"},
		spec{"POST", true, "GET", "GET"},
		spec{"POST", true, "POST", "POST"},
		spec{"POST", true, "PUT", "PUT"},
		spec{"POST", true, "PATCH", "PATCH"},
		spec{"POST", true, "DELETE", "DELETE"},
	}

	for _, test := range tests {
		var (
			s = server2{
				c.New(l, db),
				func(method string) {
					assert.Equal(t, method, test.expect)
				},
			}
			ts = httptest.NewServer(s)
		)

		form := url.Values{}
		if test.withInput {
			form.Add("method", test.inputVal)
		}

		req, _ := http.NewRequest(test.method, ts.URL,
			strings.NewReader(form.Encode()))
		req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
		res, _ := http.DefaultClient.Do(req)
		assert.Equal(t, res.StatusCode, http.StatusOK)
	}
}

type server3 struct {
	*c.Controller
	to string
}

func (s server3) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	s.Redirect(w, r, s.to)
}

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

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

	tests := []string{
		"/space/2",
		"/contenttype/2/3",
		"/content/2/3/4/",
		"/hook/2/3",
		"/space?space=2",
		"/contenttype?space=2&contenttype=3",
		"/content?space=2&contenttype=3&content=4",
		"/hook/?space=2&hook=3",
	}

	for _, test := range tests {
		var (
			s   = server3{c.New(l, db), test}
			ts  = httptest.NewServer(s)
			val = url.Values{"url": {test}}
		)

		req, _ := http.NewRequest("GET", ts.URL+test, nil)
		req.Header.Set("Accept", "text/html")
		res, _ := http.DefaultClient.Do(req)
		assert.Equal(t, res.StatusCode, http.StatusTemporaryRedirect)
		assert.Equal(t, "/redirect?"+val.Encode(), res.Header.Get("Location"))
	}

	for _, test := range tests {
		var (
			s  = server3{c.New(l, db), test}
			ts = httptest.NewServer(s)
		)
		buf, _ := json.Marshal(map[string]string{"redirectURL": test})
		res, _ := http.Get(ts.URL + test)
		bod, _ := ioutil.ReadAll(res.Body)
		assert.Equal(t, res.StatusCode, http.StatusOK)
		assert.Equal(t, string(buf), string(bod))
	}
}

type server4 struct {
	*c.Controller
}

func (s server4) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	u, err := s.GetCookieUser(w, r)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}
	s.SetCookieUser(w, r, u)
	w.WriteHeader(http.StatusOK)
}

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

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

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

		uname = uuid.New().String()
		upass = uuid.New().String()
		u     = FakeUser{uname, upass}
	)

	db.EXPECT().UserGet(uname, upass).Return(u, nil).AnyTimes()

	req, _ := http.NewRequest("GET", ts.URL, nil)
	req.SetBasicAuth(uname, upass)

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

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

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

		uname  = uuid.New().String()
		upass  = uuid.New().String()
		cookie = uuid.New().String()
		u      = FakeUser{uname, upass}
	)

	db.EXPECT().UserGetFromToken(cookie).Return(u, nil).AnyTimes()

	req, _ := http.NewRequest("GET", ts.URL, nil)
	req.AddCookie(&http.Cookie{
		Name:     string(c.KeyUserLogin),
		Value:    cookie,
		MaxAge:   0,
		Secure:   false,
		HttpOnly: false,
	})
	res, _ := http.DefaultClient.Do(req)
	assert.Equal(t, res.StatusCode, http.StatusOK)
}

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

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

		cookie = uuid.New().String()
	)

	db.EXPECT().UserGetFromToken(cookie).Return(nil, errors.New("no user")).AnyTimes()

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

type server5 struct {
	*c.Controller
	typ int
	str string
}

func (s server5) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	switch s.typ {
	case 0:
		s.String(w, r, s.str)
		break
	case 1:
		s.ErrorString(w, r, 500+s.typ, s.str)
		break
	case 2:
		s.Error(w, r, 500+s.typ, s.str)
		break
	case 3:
		s.Error2(w, r, 500+s.typ, errors.New(s.str))
		break
	}
}

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

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

	type spec struct {
		typ int
		str string
	}

	tests := []spec{
		spec{0, "sup earth"},
		spec{1, "err1"},
		spec{2, "err2"},
		spec{3, "err3"},
	}

	for _, test := range tests {
		var (
			s  = server5{c.New(l, db), test.typ, test.str}
			ts = httptest.NewServer(s)
		)
		res, _ := http.Get(ts.URL)
		if test.typ == 0 {
			assert.Equal(t, res.StatusCode, http.StatusOK)
		} else {
			assert.Equal(t, res.StatusCode, 500+test.typ)
		}
	}

}

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

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

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