tests for GetRecipeHandler
This commit is contained in:
parent
2685baa3e7
commit
606cc3763a
|
@ -0,0 +1,141 @@
|
|||
package recipe_test
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"recipes/internal/domain/models"
|
||||
"recipes/internal/http-server/handlers/recipe"
|
||||
"recipes/internal/http-server/handlers/recipe/mocks"
|
||||
"recipes/internal/lib/logger/handlers/slogdiscard"
|
||||
"recipes/internal/storage"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestGetRecipeHandler(t *testing.T) {
|
||||
// cases
|
||||
cases := []struct {
|
||||
name string // name of test
|
||||
inputJsonPattern string // input json
|
||||
recipe_id uint // recipe id to get
|
||||
recipe models.Recipe // recipe data
|
||||
wantRespStatus string // expected status of response
|
||||
wantBody string // expected response body
|
||||
respError string // expected response error
|
||||
storageMockError error // mock error to return
|
||||
storageTimes int // count of storage calls
|
||||
}{
|
||||
{
|
||||
name: "Success",
|
||||
recipe_id: 1,
|
||||
recipe: models.Recipe{
|
||||
ID: 1,
|
||||
Title: "title",
|
||||
Description: "description",
|
||||
Image: "img.jpg",
|
||||
CookingTime: "1 час",
|
||||
ServingsNum: 1,
|
||||
Calories: "1 kCal",
|
||||
Ingredients: []models.RecipeIngredients{
|
||||
{
|
||||
Title: "Ингредиенты",
|
||||
Ingredients: []string{"Ингредиент 1", "Ингредиент 2"},
|
||||
},
|
||||
},
|
||||
Recipe_steps: []string{
|
||||
"Шаг 1",
|
||||
"Шаг 2",
|
||||
},
|
||||
Advices: []string{
|
||||
"1. совет",
|
||||
"2. совет",
|
||||
},
|
||||
Categories: []string{
|
||||
"Категория 1",
|
||||
"Категория 2",
|
||||
},
|
||||
},
|
||||
wantBody: "{\"status\":\"OK\",\"recipe\":{\"id\":1,\"title\":\"title\",\"desc\":\"description\",\"img\":\"img.jpg\",\"ctime\":\"1 час\",\"snum\":1,\"cal\":\"1 kCal\",\"ingredients\":[{\"title\":\"Ингредиенты\",\"ingredients\":[\"Ингредиент 1\",\"Ингредиент 2\"]}],\"recipe_steps\":[\"Шаг 1\",\"Шаг 2\"],\"advices\":[\"1. совет\",\"2. совет\"],\"categories\":[\"Категория 1\",\"Категория 2\"]}}\n",
|
||||
storageTimes: 1,
|
||||
wantRespStatus: "OK",
|
||||
inputJsonPattern: `{"recipe_id": %d}`,
|
||||
},
|
||||
{
|
||||
name: "Broken JSON",
|
||||
respError: "failed to decode request",
|
||||
inputJsonPattern: `{"recipe_id: %d}`,
|
||||
wantRespStatus: "Error",
|
||||
wantBody: "{\"status\":\"Error\",\"error\":\"failed to decode request\"}\n",
|
||||
storageTimes: 0,
|
||||
},
|
||||
{
|
||||
name: "Recipe not exists",
|
||||
respError: "recipe not found",
|
||||
inputJsonPattern: `{"recipe_id": %d}`,
|
||||
recipe_id: 1,
|
||||
wantRespStatus: "Error",
|
||||
storageMockError: storage.ErrRecipeNotFound,
|
||||
storageTimes: 1,
|
||||
wantBody: "{\"status\":\"Error\",\"error\":\"recipe not found\"}\n",
|
||||
},
|
||||
{
|
||||
name: "Storage error",
|
||||
respError: "failed to get recipe",
|
||||
inputJsonPattern: `{"recipe_id": %d}`,
|
||||
recipe_id: 1,
|
||||
wantRespStatus: "Error",
|
||||
storageMockError: errors.New("SOME ERROR"),
|
||||
storageTimes: 1,
|
||||
wantBody: "{\"status\":\"Error\",\"error\":\"failed to get recipe\"}\n",
|
||||
},
|
||||
{
|
||||
name: "Miss recipe_id",
|
||||
inputJsonPattern: "{}",
|
||||
respError: "field RecipeId is a required field",
|
||||
wantRespStatus: "Error",
|
||||
wantBody: "{\"status\":\"Error\",\"error\":\"field RecipeId is a required field\"}\n",
|
||||
storageTimes: 0,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tc := range cases {
|
||||
tc := tc
|
||||
|
||||
t.Run(tc.name, func(t *testing.T) {
|
||||
t.Parallel()
|
||||
// create storage mock
|
||||
recipeProviderMock := mocks.NewRecipeProvider(t)
|
||||
if tc.storageTimes > 0 {
|
||||
recipeProviderMock.On("GetRecipe", context.Background(), tc.recipe_id).Return(tc.recipe, tc.storageMockError).Times(tc.storageTimes)
|
||||
}
|
||||
// create handler
|
||||
handler := recipe.New(slogdiscard.NewDiscardLogger(), recipeProviderMock)
|
||||
//
|
||||
input := fmt.Sprintf(tc.inputJsonPattern, 1)
|
||||
// http request
|
||||
req, err := http.NewRequest(http.MethodGet, "/recipe", bytes.NewReader([]byte(input)))
|
||||
require.NoError(t, err)
|
||||
// create request
|
||||
rr := httptest.NewRecorder()
|
||||
handler.ServeHTTP(rr, req)
|
||||
// compare expected and actual
|
||||
assert.Equal(t, http.StatusOK, rr.Code)
|
||||
|
||||
body := rr.Body.String()
|
||||
assert.Equal(t, tc.wantBody, body)
|
||||
|
||||
var resp recipe.Response
|
||||
require.NoError(t, json.Unmarshal([]byte(body), &resp))
|
||||
assert.Equal(t, tc.respError, resp.Error)
|
||||
assert.Equal(t, tc.wantRespStatus, resp.Status)
|
||||
assert.Equal(t, tc.recipe, resp.Recipe)
|
||||
})
|
||||
}
|
||||
}
|
|
@ -0,0 +1,40 @@
|
|||
package slogdiscard
|
||||
|
||||
/*
|
||||
Logger for tests with ignoring input
|
||||
*/
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log/slog"
|
||||
)
|
||||
|
||||
func NewDiscardLogger() *slog.Logger {
|
||||
return slog.New(NewDiscardHandler())
|
||||
}
|
||||
|
||||
type DiscardHandler struct{}
|
||||
|
||||
func NewDiscardHandler() *DiscardHandler {
|
||||
return &DiscardHandler{}
|
||||
}
|
||||
|
||||
func (h *DiscardHandler) Handle(_ context.Context, _ slog.Record) error {
|
||||
// Просто игнорируем запись журнала
|
||||
return nil
|
||||
}
|
||||
|
||||
func (h *DiscardHandler) WithAttrs(_ []slog.Attr) slog.Handler {
|
||||
// Возвращает тот же обработчик, так как нет атрибутов для сохранения
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *DiscardHandler) WithGroup(_ string) slog.Handler {
|
||||
// Возвращает тот же обработчик, так как нет группы для сохранения
|
||||
return h
|
||||
}
|
||||
|
||||
func (h *DiscardHandler) Enabled(_ context.Context, _ slog.Level) bool {
|
||||
// Всегда возвращает false, так как запись журнала игнорируется
|
||||
return false
|
||||
}
|
Loading…
Reference in New Issue