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) // create input 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) }) } }