test: note middleware & rest handler unit tests
This commit is contained in:
parent
a32bdef092
commit
18e650c898
@ -232,9 +232,21 @@ func TestUserCtxMiddleware(t *testing.T) {
|
||||
urlID string
|
||||
statusCode int
|
||||
}{
|
||||
{"valid ID", validUserID.String(), http.StatusOK},
|
||||
{"invalid ID", invalidUserID, http.StatusNotFound},
|
||||
{"non existent ID", uuid.New().String(), http.StatusNotFound},
|
||||
{
|
||||
"valid ID",
|
||||
validUserID.String(),
|
||||
http.StatusOK,
|
||||
},
|
||||
{
|
||||
"invalid ID",
|
||||
invalidUserID,
|
||||
http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
"non existent ID",
|
||||
uuid.New().String(),
|
||||
http.StatusNotFound,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
@ -257,6 +269,97 @@ func TestUserCtxMiddleware(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestNoteCtxMiddleware(t *testing.T) {
|
||||
userID := uuid.New()
|
||||
noteID := uuid.New()
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
noteID string
|
||||
user any
|
||||
mock func(*mockNoteStore)
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
"invalid note ID",
|
||||
"invalid",
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
func(m *mockNoteStore) {},
|
||||
http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
"unauthorized user",
|
||||
noteID.String(),
|
||||
nil,
|
||||
func(m *mockNoteStore) {},
|
||||
http.StatusUnauthorized,
|
||||
},
|
||||
{
|
||||
"note not found",
|
||||
noteID.String(),
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
func(m *mockNoteStore) {
|
||||
m.GetNoteFunc = func(ctx context.Context, arg data.GetNoteParams) (data.Note, error) {
|
||||
return data.Note{}, errors.New("not found")
|
||||
}
|
||||
},
|
||||
http.StatusNotFound,
|
||||
},
|
||||
{
|
||||
"success",
|
||||
noteID.String(),
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
func(m *mockNoteStore) {
|
||||
m.GetNoteFunc = func(ctx context.Context, arg data.GetNoteParams) (data.Note, error) {
|
||||
assert.Equal(t, noteID, arg.ID)
|
||||
assert.Equal(t, userID, arg.UserID)
|
||||
return data.Note{ID: noteID}, nil
|
||||
}
|
||||
},
|
||||
http.StatusOK,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockStore := &mockNoteStore{}
|
||||
tc.mock(mockStore)
|
||||
|
||||
handler := noteCtx(mockStore)(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
_, ok := r.Context().Value(noteCtxKey{}).(data.Note)
|
||||
assert.True(t, ok)
|
||||
w.WriteHeader(http.StatusOK)
|
||||
}))
|
||||
|
||||
req := httptest.NewRequest("GET", fmt.Sprintf("/notes/%s", tc.noteID), nil)
|
||||
|
||||
// Chi router context mocks ID passed in a URL parameter
|
||||
rctx := chi.NewRouteContext()
|
||||
rctx.URLParams.Add("id", tc.noteID)
|
||||
req = req.WithContext(context.WithValue(req.Context(), chi.RouteCtxKey, rctx))
|
||||
|
||||
if tc.user != nil {
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
userCtxKey{},
|
||||
tc.user,
|
||||
))
|
||||
}
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
handler.ServeHTTP(w, req)
|
||||
|
||||
assert.Equal(t, tc.statusCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func generateTestToken(t *testing.T, secret, tokenType, userID string, isAdmin bool, opts ...func(*userClaims)) string {
|
||||
t.Helper()
|
||||
|
||||
|
364
server/pkg/service/notes_test.go
Normal file
364
server/pkg/service/notes_test.go
Normal file
@ -0,0 +1,364 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.umbrella.haus/ae/notatest/pkg/data"
|
||||
"github.com/golang-jwt/jwt/v5"
|
||||
"github.com/google/uuid"
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
type mockNoteStore struct {
|
||||
CreateNoteFunc func(context.Context, uuid.UUID) (data.Note, error)
|
||||
DeleteNoteFunc func(context.Context, data.DeleteNoteParams) error
|
||||
GetNoteFunc func(context.Context, data.GetNoteParams) (data.Note, error)
|
||||
ListNotesFunc func(context.Context, data.ListNotesParams) ([]data.Note, error)
|
||||
CreateNoteVersionFunc func(context.Context, data.CreateNoteVersionParams) (data.NoteVersion, error)
|
||||
FindDuplicateContentFunc func(context.Context, data.FindDuplicateContentParams) (bool, error)
|
||||
GetNoteVersionFunc func(context.Context, data.GetNoteVersionParams) (data.NoteVersion, error)
|
||||
GetNoteVersionsFunc func(context.Context, data.GetNoteVersionsParams) ([]data.NoteVersion, error)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) CreateNote(ctx context.Context, userID uuid.UUID) (data.Note, error) {
|
||||
return m.CreateNoteFunc(ctx, userID)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) DeleteNote(ctx context.Context, arg data.DeleteNoteParams) error {
|
||||
return m.DeleteNoteFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) GetNote(ctx context.Context, arg data.GetNoteParams) (data.Note, error) {
|
||||
return m.GetNoteFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) ListNotes(ctx context.Context, arg data.ListNotesParams) ([]data.Note, error) {
|
||||
return m.ListNotesFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) CreateNoteVersion(ctx context.Context, arg data.CreateNoteVersionParams) (data.NoteVersion, error) {
|
||||
return m.CreateNoteVersionFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) FindDuplicateContent(ctx context.Context, arg data.FindDuplicateContentParams) (bool, error) {
|
||||
return m.FindDuplicateContentFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) GetNoteVersion(ctx context.Context, arg data.GetNoteVersionParams) (data.NoteVersion, error) {
|
||||
return m.GetNoteVersionFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func (m *mockNoteStore) GetNoteVersions(ctx context.Context, arg data.GetNoteVersionsParams) ([]data.NoteVersion, error) {
|
||||
return m.GetNoteVersionsFunc(ctx, arg)
|
||||
}
|
||||
|
||||
func TestNotes_CreateNote(t *testing.T) {
|
||||
userID := uuid.New()
|
||||
testNote := data.Note{ID: uuid.New(), UserID: userID}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
mock func(*mockNoteStore)
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
"success",
|
||||
func(m *mockNoteStore) {
|
||||
m.CreateNoteFunc = func(_ context.Context, uid uuid.UUID) (data.Note, error) {
|
||||
assert.Equal(t, userID, uid)
|
||||
return testNote, nil
|
||||
}
|
||||
},
|
||||
http.StatusCreated,
|
||||
},
|
||||
{
|
||||
"database error",
|
||||
func(m *mockNoteStore) {
|
||||
m.CreateNoteFunc = func(context.Context, uuid.UUID) (data.Note, error) {
|
||||
return data.Note{}, errors.New("db error")
|
||||
}
|
||||
},
|
||||
http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockStore := &mockNoteStore{}
|
||||
tc.mock(mockStore)
|
||||
|
||||
rs := notesResource{Notes: mockStore}
|
||||
req := httptest.NewRequest("POST", "/", nil)
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
userCtxKey{},
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
rs.CreateNote(w, req)
|
||||
|
||||
assert.Equal(t, tc.statusCode, w.Code)
|
||||
if tc.statusCode == http.StatusCreated {
|
||||
var note data.Note
|
||||
json.Unmarshal(w.Body.Bytes(), ¬e)
|
||||
assert.Equal(t, testNote.ID, note.ID)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotes_ListNotes(t *testing.T) {
|
||||
userID := uuid.New()
|
||||
notes := []data.Note{
|
||||
{ID: uuid.New(), UserID: userID},
|
||||
{ID: uuid.New(), UserID: userID},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
query string
|
||||
mock func(*mockNoteStore)
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
"success",
|
||||
"",
|
||||
func(m *mockNoteStore) {
|
||||
m.ListNotesFunc = func(_ context.Context, arg data.ListNotesParams) ([]data.Note, error) {
|
||||
assert.Equal(t, userID, arg.UserID)
|
||||
return notes, nil
|
||||
}
|
||||
},
|
||||
http.StatusOK,
|
||||
},
|
||||
{
|
||||
"with pagination",
|
||||
"?limit=10&offset=20",
|
||||
func(m *mockNoteStore) {
|
||||
m.ListNotesFunc = func(_ context.Context, arg data.ListNotesParams) ([]data.Note, error) {
|
||||
assert.EqualValues(t, 10, arg.Limit)
|
||||
assert.EqualValues(t, 20, arg.Offset)
|
||||
return notes, nil
|
||||
}
|
||||
},
|
||||
http.StatusOK,
|
||||
},
|
||||
{
|
||||
"database error",
|
||||
"",
|
||||
func(m *mockNoteStore) {
|
||||
m.ListNotesFunc = func(context.Context, data.ListNotesParams) ([]data.Note, error) {
|
||||
return nil, errors.New("db error")
|
||||
}
|
||||
},
|
||||
http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockStore := &mockNoteStore{}
|
||||
tc.mock(mockStore)
|
||||
|
||||
rs := notesResource{Notes: mockStore}
|
||||
req := httptest.NewRequest("GET", fmt.Sprintf("/%s", tc.query), nil)
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
userCtxKey{},
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
rs.ListNotes(w, req)
|
||||
|
||||
assert.Equal(t, tc.statusCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestNotes_GetNote(t *testing.T) {
|
||||
noteID := uuid.New()
|
||||
userID := uuid.New()
|
||||
validNote := data.Note{ID: noteID, UserID: userID}
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
rs := notesResource{}
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
noteCtxKey{},
|
||||
validNote,
|
||||
))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
rs.GetNote(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusOK, w.Code)
|
||||
var note data.Note
|
||||
json.Unmarshal(w.Body.Bytes(), ¬e)
|
||||
assert.Equal(t, validNote.ID, note.ID)
|
||||
})
|
||||
|
||||
t.Run("not found", func(t *testing.T) {
|
||||
rs := notesResource{}
|
||||
req := httptest.NewRequest("GET", "/", nil)
|
||||
w := httptest.NewRecorder()
|
||||
rs.GetNote(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusNotFound, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotes_DeleteNote(t *testing.T) {
|
||||
noteID := uuid.New()
|
||||
userID := uuid.New()
|
||||
validNote := data.Note{ID: noteID, UserID: userID}
|
||||
|
||||
t.Run("success", func(t *testing.T) {
|
||||
mockStore := &mockNoteStore{
|
||||
DeleteNoteFunc: func(_ context.Context, arg data.DeleteNoteParams) error {
|
||||
assert.Equal(t, noteID, arg.ID)
|
||||
assert.Equal(t, userID, arg.UserID)
|
||||
return nil
|
||||
},
|
||||
}
|
||||
|
||||
rs := notesResource{Notes: mockStore}
|
||||
req := httptest.NewRequest("DELETE", "/", nil)
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
noteCtxKey{},
|
||||
validNote,
|
||||
))
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
userCtxKey{},
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
rs.DeleteNote(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusNoContent, w.Code)
|
||||
})
|
||||
|
||||
t.Run("database error", func(t *testing.T) {
|
||||
mockStore := &mockNoteStore{
|
||||
DeleteNoteFunc: func(context.Context, data.DeleteNoteParams) error {
|
||||
return errors.New("db error")
|
||||
},
|
||||
}
|
||||
|
||||
rs := notesResource{Notes: mockStore}
|
||||
req := httptest.NewRequest("DELETE", "/", nil)
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
noteCtxKey{},
|
||||
validNote,
|
||||
))
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
userCtxKey{},
|
||||
&userClaims{RegisteredClaims: jwt.RegisteredClaims{
|
||||
Subject: userID.String(),
|
||||
}},
|
||||
))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
rs.DeleteNote(w, req)
|
||||
|
||||
assert.Equal(t, http.StatusInternalServerError, w.Code)
|
||||
})
|
||||
}
|
||||
|
||||
func TestNotes_CreateNoteVersion(t *testing.T) {
|
||||
noteID := uuid.New()
|
||||
validRequest := `{"title": "Test", "content": "Content"}`
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
body string
|
||||
mock func(*mockNoteStore)
|
||||
statusCode int
|
||||
}{
|
||||
{
|
||||
"success",
|
||||
validRequest,
|
||||
func(m *mockNoteStore) {
|
||||
m.FindDuplicateContentFunc = func(context.Context, data.FindDuplicateContentParams) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
m.CreateNoteVersionFunc = func(_ context.Context, arg data.CreateNoteVersionParams) (data.NoteVersion, error) {
|
||||
assert.Equal(t, noteID, arg.NoteID)
|
||||
return data.NoteVersion{}, nil
|
||||
}
|
||||
},
|
||||
http.StatusCreated,
|
||||
},
|
||||
{
|
||||
"duplicate content",
|
||||
validRequest,
|
||||
func(m *mockNoteStore) {
|
||||
m.FindDuplicateContentFunc = func(context.Context, data.FindDuplicateContentParams) (bool, error) {
|
||||
return true, nil
|
||||
}
|
||||
},
|
||||
http.StatusConflict,
|
||||
},
|
||||
{
|
||||
"invalid request",
|
||||
"{invalid}",
|
||||
func(m *mockNoteStore) {},
|
||||
http.StatusBadRequest,
|
||||
},
|
||||
{
|
||||
"database error",
|
||||
validRequest,
|
||||
func(m *mockNoteStore) {
|
||||
m.FindDuplicateContentFunc = func(context.Context, data.FindDuplicateContentParams) (bool, error) {
|
||||
return false, nil
|
||||
}
|
||||
m.CreateNoteVersionFunc = func(context.Context, data.CreateNoteVersionParams) (data.NoteVersion, error) {
|
||||
return data.NoteVersion{}, errors.New("db error")
|
||||
}
|
||||
},
|
||||
http.StatusInternalServerError,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range tests {
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
mockStore := &mockNoteStore{}
|
||||
tc.mock(mockStore)
|
||||
|
||||
rs := notesResource{Notes: mockStore}
|
||||
req := httptest.NewRequest("POST", "/", strings.NewReader(tc.body))
|
||||
req = req.WithContext(context.WithValue(
|
||||
req.Context(),
|
||||
noteCtxKey{},
|
||||
data.Note{ID: noteID},
|
||||
))
|
||||
|
||||
w := httptest.NewRecorder()
|
||||
rs.CreateNoteVersion(w, req)
|
||||
|
||||
assert.Equal(t, tc.statusCode, w.Code)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: add similar tests for `ListNoteVersions` and `GetNoteVersion`
|
Loading…
x
Reference in New Issue
Block a user