Prechádzať zdrojové kódy

Merge branch 'refactoring' of git.dmitriygnatenko.ru:dima/homethings

# Conflicts:
#	build/app/app
#	internal/api/v1/place/add_place_test.go
#	internal/api/v1/place/delete_place.go
#	internal/api/v1/place/delete_place_test.go
#	internal/api/v1/place/get_place_test.go
#	internal/api/v1/user/add_user_test.go
#	internal/api/v1/user/update_user_test.go
Dmitriy Gnatenko 3 týždňov pred
rodič
commit
c882d489b3

BIN
build/app/app


+ 1 - 1
internal/api/v1/place/add_place.go

@@ -2,7 +2,7 @@ package place
 
 //go:generate mkdir -p mocks
 //go:generate rm -rf ./mocks/*_minimock.go
-//go:generate minimock -i PlaceRepository,ThingRepository,PlaceImageRepository,ThingImageRepository,PlaceThingRepository,ThingTagRepository,ThingNotificationRepository,FileRepository -o ./mocks/ -s "_minimock.go"
+//go:generate minimock -i TransactionManager,PlaceRepository,ThingRepository,PlaceImageRepository,ThingImageRepository,PlaceThingRepository,ThingTagRepository,ThingNotificationRepository,FileRepository -o ./mocks/ -s "_minimock.go"
 
 import (
 	"context"

+ 175 - 0
internal/api/v1/place/add_place_test.go

@@ -0,0 +1,175 @@
+package place
+
+import (
+	"context"
+	"database/sql"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/brianvoe/gofakeit/v6"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gojuno/minimock/v3"
+	"github.com/stretchr/testify/assert"
+
+	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/place/mocks"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
+)
+
+func TestAddPlaceHandler(t *testing.T) {
+	t.Parallel()
+
+	type req struct {
+		method      string
+		route       string
+		contentType string
+		body        *dto.AddPlaceRequest
+	}
+
+	var (
+		placeID   = uint64(gofakeit.Number(1, 1000))
+		parentID  = uint64(gofakeit.Number(1, 1000))
+		title     = gofakeit.Phrase()
+		testError = gofakeit.Error()
+		layout    = "2006-01-02 15:04:05"
+
+		correctReq = req{
+			method: fiber.MethodPost,
+			route:  "/v1/places",
+			body: &dto.AddPlaceRequest{
+				Title:    title,
+				ParentID: &parentID,
+			},
+			contentType: fiber.MIMEApplicationJSON,
+		}
+
+		repoRes = models.Place{
+			ID:        placeID,
+			Title:     title,
+			ParentID:  sql.NullInt64{Int64: int64(parentID), Valid: true},
+			CreatedAt: gofakeit.Date(),
+			UpdatedAt: gofakeit.Date(),
+		}
+
+		expectedRes = dto.PlaceResponse{
+			ID:        placeID,
+			ParentID:  &parentID,
+			Title:     repoRes.Title,
+			CreatedAt: repoRes.CreatedAt.Format(layout),
+			UpdatedAt: repoRes.UpdatedAt.Format(layout),
+		}
+	)
+
+	tests := []struct {
+		name          string
+		req           req
+		resCode       int
+		resBody       interface{}
+		placeRepoMock func(mc *minimock.Controller) PlaceRepository
+	}{
+		{
+			name:    "positive case",
+			req:     correctReq,
+			resCode: fiber.StatusOK,
+			resBody: expectedRes,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceRequest) {
+					assert.Equal(mc, title, req.Title)
+					assert.Equal(mc, parentID, uint64(req.ParentID.Int64))
+				}).Return(placeID, nil)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(&repoRes, nil)
+
+				return mock
+			},
+		},
+		{
+			name: "negative case - body parse error",
+			req: req{
+				method: fiber.MethodPost,
+				route:  "/v1/places",
+			},
+			resCode: fiber.StatusBadRequest,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				return mocks.NewPlaceRepositoryMock(mc)
+			},
+		},
+		{
+			name: "negative case - request without title",
+			req: req{
+				method:      fiber.MethodPost,
+				route:       "/v1/places",
+				contentType: fiber.MIMEApplicationJSON,
+				body:        &dto.AddPlaceRequest{},
+			},
+			resCode: fiber.StatusBadRequest,
+			resBody: []*dto.ValidateErrorResponse{
+				{
+					Field: "AddPlaceRequest.Title",
+					Tag:   "required",
+				},
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				return mocks.NewPlaceRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (add place)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceRequest) {
+					assert.Equal(mc, title, req.Title)
+					assert.Equal(mc, parentID, uint64(req.ParentID.Int64))
+				}).Return(0, testError)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - repository error (get place)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceRequest) {
+					assert.Equal(mc, title, req.Title)
+					assert.Equal(mc, parentID, uint64(req.ParentID.Int64))
+				}).Return(placeID, nil)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			mc := minimock.NewController(t)
+			fiberApp := fiber.New()
+			fiberApp.Post("/v1/places", AddPlaceHandler(tt.placeRepoMock(mc)))
+
+			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, test.ConvertDataToIOReader(tt.req.body))
+			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
+
+			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
+			if tt.resBody != nil {
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
+			}
+		})
+	}
+}

+ 7 - 6
internal/api/v1/place/delete_place.go

@@ -9,6 +9,7 @@ import (
 	"github.com/gofiber/fiber/v2"
 
 	"git.dmitriygnatenko.ru/dima/homethings/internal/factory"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/request"
 	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
 )
 
@@ -35,13 +36,13 @@ func DeletePlaceHandler(
 ) fiber.Handler {
 	return func(fctx *fiber.Ctx) error {
 		ctx := fctx.Context()
-		id, err := fctx.ParamsInt("placeId")
+		id, err := request.ConvertToUint64(fctx, "placeId")
 		if err != nil {
 			logger.Info(ctx, err.Error())
 			return fiber.NewError(fiber.StatusBadRequest, err.Error())
 		}
 
-		_, err = placeRepository.Get(ctx, uint64(id))
+		_, err = placeRepository.Get(ctx, id)
 		if err != nil {
 			if errors.Is(err, sql.ErrNoRows) {
 				logger.Info(ctx, err.Error())
@@ -52,7 +53,7 @@ func DeletePlaceHandler(
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
 		}
 
-		nestedRes, err := placeRepository.GetNestedPlaces(ctx, uint64(id))
+		nestedRes, err := placeRepository.GetNestedPlaces(ctx, id)
 		if err != nil {
 			logger.Error(ctx, err.Error())
 			return fiber.NewError(fiber.StatusInternalServerError, err.Error())
@@ -63,12 +64,12 @@ func DeletePlaceHandler(
 		}
 
 		err = tm.ReadCommitted(ctx, func(ctx context.Context) error {
-			placeImages, txErr := placeImageRepository.GetByPlaceID(ctx, uint64(id))
+			placeImages, txErr := placeImageRepository.GetByPlaceID(ctx, id)
 			if txErr != nil {
 				return txErr
 			}
 
-			things, txErr := thingRepository.GetByPlaceID(ctx, uint64(id))
+			things, txErr := thingRepository.GetByPlaceID(ctx, id)
 			if txErr != nil {
 				return txErr
 			}
@@ -119,7 +120,7 @@ func DeletePlaceHandler(
 				}
 			}
 
-			if txErr = placeRepository.Delete(ctx, uint64(id)); txErr != nil {
+			if txErr = placeRepository.Delete(ctx, id); txErr != nil {
 				return txErr
 			}
 

+ 1357 - 0
internal/api/v1/place/delete_place_test.go

@@ -0,0 +1,1357 @@
+package place
+
+import (
+	"context"
+	"database/sql"
+	"net/http/httptest"
+	"strconv"
+	"testing"
+
+	"github.com/brianvoe/gofakeit/v6"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gojuno/minimock/v3"
+	"github.com/stretchr/testify/assert"
+
+	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/place/mocks"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
+)
+
+func TestDeletePlaceHandler(t *testing.T) {
+	t.Parallel()
+
+	type req struct {
+		method string
+		route  string
+	}
+
+	var (
+		placeID       = uint64(gofakeit.Number(1, 1000))
+		thingID       = uint64(gofakeit.Number(1, 1000))
+		placeImageID  = uint64(gofakeit.Number(1, 1000))
+		thingImageID  = uint64(gofakeit.Number(1, 1000))
+		placeImageURL = gofakeit.URL()
+		thingImageURL = gofakeit.URL()
+		testError     = gofakeit.Error()
+
+		txMockFunc = func(ctx context.Context, f func(ctx context.Context) error) error {
+			return f(ctx)
+		}
+
+		correctReq = req{
+			method: fiber.MethodDelete,
+			route:  "/v1/places/" + strconv.FormatUint(placeID, 10),
+		}
+
+		things = []models.Thing{
+			{
+				ID:          thingID,
+				PlaceID:     placeID,
+				Title:       gofakeit.Word(),
+				Description: gofakeit.Phrase(),
+				CreatedAt:   gofakeit.Date(),
+				UpdatedAt:   gofakeit.Date(),
+			},
+		}
+
+		placeImages = []models.Image{
+			{
+				ID:    placeImageID,
+				Image: placeImageURL,
+			},
+		}
+
+		thingImages = []models.Image{
+			{
+				ID:    thingImageID,
+				Image: thingImageURL,
+			},
+		}
+	)
+
+	tests := []struct {
+		name                      string
+		req                       req
+		resCode                   int
+		resBody                   interface{}
+		tmMock                    func(mc *minimock.Controller) TransactionManager
+		placeRepoMock             func(mc *minimock.Controller) PlaceRepository
+		thingRepoMock             func(mc *minimock.Controller) ThingRepository
+		placeImageRepoMock        func(mc *minimock.Controller) PlaceImageRepository
+		thingImageRepoMock        func(mc *minimock.Controller) ThingImageRepository
+		placeThingRepoMock        func(mc *minimock.Controller) PlaceThingRepository
+		thingTagRepoMock          func(mc *minimock.Controller) ThingTagRepository
+		thingNotificationRepoMock func(mc *minimock.Controller) ThingNotificationRepository
+		fileRepoMock              func(mc *minimock.Controller) FileRepository
+	}{
+		{
+			name: "negative case - bad request",
+			req: req{
+				method: fiber.MethodDelete,
+				route:  "/v1/places/" + gofakeit.Word(),
+			},
+			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				return mocks.NewPlaceRepositoryMock(mc)
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				return mocks.NewThingRepositoryMock(mc)
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				return mocks.NewPlaceImageRepositoryMock(mc)
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - bad request (place not found)",
+			req:     correctReq,
+			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, sql.ErrNoRows)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				return mocks.NewThingRepositoryMock(mc)
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				return mocks.NewPlaceImageRepositoryMock(mc)
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (get place)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				return mocks.NewThingRepositoryMock(mc)
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				return mocks.NewPlaceImageRepositoryMock(mc)
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (get nested places)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				return mocks.NewThingRepositoryMock(mc)
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				return mocks.NewPlaceImageRepositoryMock(mc)
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - not empty nested places",
+			req:     correctReq,
+			resCode: fiber.StatusBadRequest,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				return mocks.NewTransactionManagerMock(mc)
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return([]models.Place{{ID: gofakeit.Uint64()}}, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				return mocks.NewThingRepositoryMock(mc)
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				return mocks.NewPlaceImageRepositoryMock(mc)
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (get place images)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				return mocks.NewThingRepositoryMock(mc)
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (get thing)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				return mocks.NewThingImageRepositoryMock(mc)
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (get thing images)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete place images)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete thing images)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				return mocks.NewPlaceThingRepositoryMock(mc)
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete place thing)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				return mocks.NewThingTagRepositoryMock(mc)
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete thing tag)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				return mocks.NewThingNotificationRepositoryMock(mc)
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete thing notifications)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				mock := mocks.NewThingNotificationRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete thing)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				mock := mocks.NewThingNotificationRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete place)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(testError)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				mock := mocks.NewThingNotificationRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				return mocks.NewFileRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error (delete place image files)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				mock := mocks.NewThingNotificationRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				mock := mocks.NewFileRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(path string) {
+					assert.Equal(mc, placeImageURL, path)
+				}).Return(testError)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - repository error (delete thing image files)",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				mock := mocks.NewThingNotificationRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				mock := mocks.NewFileRepositoryMock(mc)
+
+				mock.DeleteMock.Set(func(path string) (err error) {
+					if path == placeImageURL {
+						return nil
+					}
+
+					return testError
+				})
+
+				return mock
+			},
+		},
+		{
+			name:    "positive case",
+			req:     correctReq,
+			resCode: fiber.StatusOK,
+			tmMock: func(mc *minimock.Controller) TransactionManager {
+				mock := mocks.NewTransactionManagerMock(mc)
+				mock.ReadCommittedMock.Set(txMockFunc)
+				return mock
+			},
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.GetNestedPlacesMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingRepoMock: func(mc *minimock.Controller) ThingRepository {
+				mock := mocks.NewThingRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(things, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
+				mock := mocks.NewPlaceThingRepositoryMock(mc)
+
+				mock.DeleteThingMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
+				mock := mocks.NewPlaceImageRepositoryMock(mc)
+
+				mock.GetByPlaceIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(placeImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
+				mock := mocks.NewThingImageRepositoryMock(mc)
+
+				mock.GetByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(thingImages, nil)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingImageID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingTagRepoMock: func(mc *minimock.Controller) ThingTagRepository {
+				mock := mocks.NewThingTagRepositoryMock(mc)
+
+				mock.DeleteByThingIDMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			thingNotificationRepoMock: func(mc *minimock.Controller) ThingNotificationRepository {
+				mock := mocks.NewThingNotificationRepositoryMock(mc)
+
+				mock.DeleteMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, thingID, id)
+				}).Return(nil)
+
+				return mock
+			},
+			fileRepoMock: func(mc *minimock.Controller) FileRepository {
+				mock := mocks.NewFileRepositoryMock(mc)
+				mock.DeleteMock.Return(nil)
+				return mock
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			mc := minimock.NewController(t)
+			fiberApp := fiber.New()
+
+			fiberApp.Delete("/v1/places/:placeId", DeletePlaceHandler(
+				tt.tmMock(mc),
+				tt.placeRepoMock(mc),
+				tt.thingRepoMock(mc),
+				tt.placeImageRepoMock(mc),
+				tt.thingImageRepoMock(mc),
+				tt.placeThingRepoMock(mc),
+				tt.thingTagRepoMock(mc),
+				tt.thingNotificationRepoMock(mc),
+				tt.fileRepoMock(mc),
+			))
+
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
+			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
+			if tt.resBody != nil {
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
+			}
+		})
+	}
+}

+ 138 - 0
internal/api/v1/place/get_place_test.go

@@ -0,0 +1,138 @@
+package place
+
+import (
+	"context"
+	"database/sql"
+	"net/http/httptest"
+	"strconv"
+	"testing"
+
+	"github.com/brianvoe/gofakeit/v6"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gojuno/minimock/v3"
+	"github.com/stretchr/testify/assert"
+
+	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/place/mocks"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
+)
+
+func TestGetPlaceHandler(t *testing.T) {
+	t.Parallel()
+
+	type req struct {
+		method string
+		route  string
+	}
+
+	var (
+		placeID   = uint64(gofakeit.Number(1, 1000))
+		testError = gofakeit.Error()
+		layout    = "2006-01-02 15:04:05"
+
+		correctReq = req{
+			method: fiber.MethodGet,
+			route:  "/v1/places/" + strconv.FormatUint(placeID, 10),
+		}
+
+		repoRes = models.Place{
+			ID:        placeID,
+			ParentID:  sql.NullInt64{Int64: int64(placeID), Valid: true},
+			Title:     gofakeit.Phrase(),
+			CreatedAt: gofakeit.Date(),
+			UpdatedAt: gofakeit.Date(),
+		}
+
+		expectedRes = dto.PlaceResponse{
+			ID:        repoRes.ID,
+			ParentID:  &placeID,
+			Title:     repoRes.Title,
+			CreatedAt: repoRes.CreatedAt.Format(layout),
+			UpdatedAt: repoRes.UpdatedAt.Format(layout),
+		}
+	)
+
+	tests := []struct {
+		name          string
+		req           req
+		resCode       int
+		resBody       interface{}
+		placeRepoMock func(mc *minimock.Controller) PlaceRepository
+	}{
+		{
+			name:    "positive case",
+			req:     correctReq,
+			resCode: fiber.StatusOK,
+			resBody: expectedRes,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(&repoRes, nil)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - repository error",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, testError)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - not found",
+			req:     correctReq,
+			resCode: fiber.StatusNotFound,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				mock := mocks.NewPlaceRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, id uint64) {
+					assert.Equal(mc, placeID, id)
+				}).Return(nil, sql.ErrNoRows)
+
+				return mock
+			},
+		},
+		{
+			name: "negative case - bad request",
+			req: req{
+				method: fiber.MethodGet,
+				route:  "/v1/places/" + gofakeit.Word(),
+			},
+			resCode: fiber.StatusBadRequest,
+			resBody: nil,
+			placeRepoMock: func(mc *minimock.Controller) PlaceRepository {
+				return mocks.NewPlaceRepositoryMock(mc)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			mc := minimock.NewController(t)
+			fiberApp := fiber.New()
+			fiberApp.Get("/v1/places/:placeId", GetPlaceHandler(tt.placeRepoMock(mc)))
+
+			fiberRes, _ := fiberApp.Test(
+				httptest.NewRequest(tt.req.method, tt.req.route, nil),
+				test.TestTimeout,
+			)
+			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
+			if tt.resBody != nil {
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
+			}
+		})
+	}
+}

+ 393 - 0
internal/api/v1/place/mocks/transaction_manager_minimock.go

@@ -0,0 +1,393 @@
+// Code generated by http://github.com/gojuno/minimock (v3.3.14). DO NOT EDIT.
+
+package mocks
+
+//go:generate minimock -i git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/place.TransactionManager -o transaction_manager_minimock.go -n TransactionManagerMock -p mocks
+
+import (
+	"context"
+	"sync"
+	mm_atomic "sync/atomic"
+	mm_time "time"
+
+	"github.com/gojuno/minimock/v3"
+)
+
+// TransactionManagerMock implements place.TransactionManager
+type TransactionManagerMock struct {
+	t          minimock.Tester
+	finishOnce sync.Once
+
+	funcReadCommitted          func(ctx context.Context, f1 func(ctx context.Context) error) (err error)
+	inspectFuncReadCommitted   func(ctx context.Context, f1 func(ctx context.Context) error)
+	afterReadCommittedCounter  uint64
+	beforeReadCommittedCounter uint64
+	ReadCommittedMock          mTransactionManagerMockReadCommitted
+}
+
+// NewTransactionManagerMock returns a mock for place.TransactionManager
+func NewTransactionManagerMock(t minimock.Tester) *TransactionManagerMock {
+	m := &TransactionManagerMock{t: t}
+
+	if controller, ok := t.(minimock.MockController); ok {
+		controller.RegisterMocker(m)
+	}
+
+	m.ReadCommittedMock = mTransactionManagerMockReadCommitted{mock: m}
+	m.ReadCommittedMock.callArgs = []*TransactionManagerMockReadCommittedParams{}
+
+	t.Cleanup(m.MinimockFinish)
+
+	return m
+}
+
+type mTransactionManagerMockReadCommitted struct {
+	optional           bool
+	mock               *TransactionManagerMock
+	defaultExpectation *TransactionManagerMockReadCommittedExpectation
+	expectations       []*TransactionManagerMockReadCommittedExpectation
+
+	callArgs []*TransactionManagerMockReadCommittedParams
+	mutex    sync.RWMutex
+
+	expectedInvocations uint64
+}
+
+// TransactionManagerMockReadCommittedExpectation specifies expectation struct of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedExpectation struct {
+	mock      *TransactionManagerMock
+	params    *TransactionManagerMockReadCommittedParams
+	paramPtrs *TransactionManagerMockReadCommittedParamPtrs
+	results   *TransactionManagerMockReadCommittedResults
+	Counter   uint64
+}
+
+// TransactionManagerMockReadCommittedParams contains parameters of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedParams struct {
+	ctx context.Context
+	f1  func(ctx context.Context) error
+}
+
+// TransactionManagerMockReadCommittedParamPtrs contains pointers to parameters of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedParamPtrs struct {
+	ctx *context.Context
+	f1  *func(ctx context.Context) error
+}
+
+// TransactionManagerMockReadCommittedResults contains results of the TransactionManager.ReadCommitted
+type TransactionManagerMockReadCommittedResults struct {
+	err error
+}
+
+// Marks this method to be optional. The default behavior of any method with Return() is '1 or more', meaning
+// the test will fail minimock's automatic final call check if the mocked method was not called at least once.
+// Optional() makes method check to work in '0 or more' mode.
+// It is NOT RECOMMENDED to use this option unless you really need it, as default behaviour helps to
+// catch the problems when the expected method call is totally skipped during test run.
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Optional() *mTransactionManagerMockReadCommitted {
+	mmReadCommitted.optional = true
+	return mmReadCommitted
+}
+
+// Expect sets up expected params for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Expect(ctx context.Context, f1 func(ctx context.Context) error) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by ExpectParams functions")
+	}
+
+	mmReadCommitted.defaultExpectation.params = &TransactionManagerMockReadCommittedParams{ctx, f1}
+	for _, e := range mmReadCommitted.expectations {
+		if minimock.Equal(e.params, mmReadCommitted.defaultExpectation.params) {
+			mmReadCommitted.mock.t.Fatalf("Expectation set by When has same params: %#v", *mmReadCommitted.defaultExpectation.params)
+		}
+	}
+
+	return mmReadCommitted
+}
+
+// ExpectCtxParam1 sets up expected param ctx for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) ExpectCtxParam1(ctx context.Context) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.params != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Expect")
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs == nil {
+		mmReadCommitted.defaultExpectation.paramPtrs = &TransactionManagerMockReadCommittedParamPtrs{}
+	}
+	mmReadCommitted.defaultExpectation.paramPtrs.ctx = &ctx
+
+	return mmReadCommitted
+}
+
+// ExpectF1Param2 sets up expected param f1 for TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) ExpectF1Param2(f1 func(ctx context.Context) error) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{}
+	}
+
+	if mmReadCommitted.defaultExpectation.params != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Expect")
+	}
+
+	if mmReadCommitted.defaultExpectation.paramPtrs == nil {
+		mmReadCommitted.defaultExpectation.paramPtrs = &TransactionManagerMockReadCommittedParamPtrs{}
+	}
+	mmReadCommitted.defaultExpectation.paramPtrs.f1 = &f1
+
+	return mmReadCommitted
+}
+
+// Inspect accepts an inspector function that has same arguments as the TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Inspect(f func(ctx context.Context, f1 func(ctx context.Context) error)) *mTransactionManagerMockReadCommitted {
+	if mmReadCommitted.mock.inspectFuncReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("Inspect function is already set for TransactionManagerMock.ReadCommitted")
+	}
+
+	mmReadCommitted.mock.inspectFuncReadCommitted = f
+
+	return mmReadCommitted
+}
+
+// Return sets up results that will be returned by TransactionManager.ReadCommitted
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Return(err error) *TransactionManagerMock {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	if mmReadCommitted.defaultExpectation == nil {
+		mmReadCommitted.defaultExpectation = &TransactionManagerMockReadCommittedExpectation{mock: mmReadCommitted.mock}
+	}
+	mmReadCommitted.defaultExpectation.results = &TransactionManagerMockReadCommittedResults{err}
+	return mmReadCommitted.mock
+}
+
+// Set uses given function f to mock the TransactionManager.ReadCommitted method
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Set(f func(ctx context.Context, f1 func(ctx context.Context) error) (err error)) *TransactionManagerMock {
+	if mmReadCommitted.defaultExpectation != nil {
+		mmReadCommitted.mock.t.Fatalf("Default expectation is already set for the TransactionManager.ReadCommitted method")
+	}
+
+	if len(mmReadCommitted.expectations) > 0 {
+		mmReadCommitted.mock.t.Fatalf("Some expectations are already set for the TransactionManager.ReadCommitted method")
+	}
+
+	mmReadCommitted.mock.funcReadCommitted = f
+	return mmReadCommitted.mock
+}
+
+// When sets expectation for the TransactionManager.ReadCommitted which will trigger the result defined by the following
+// Then helper
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) When(ctx context.Context, f1 func(ctx context.Context) error) *TransactionManagerMockReadCommittedExpectation {
+	if mmReadCommitted.mock.funcReadCommitted != nil {
+		mmReadCommitted.mock.t.Fatalf("TransactionManagerMock.ReadCommitted mock is already set by Set")
+	}
+
+	expectation := &TransactionManagerMockReadCommittedExpectation{
+		mock:   mmReadCommitted.mock,
+		params: &TransactionManagerMockReadCommittedParams{ctx, f1},
+	}
+	mmReadCommitted.expectations = append(mmReadCommitted.expectations, expectation)
+	return expectation
+}
+
+// Then sets up TransactionManager.ReadCommitted return parameters for the expectation previously defined by the When method
+func (e *TransactionManagerMockReadCommittedExpectation) Then(err error) *TransactionManagerMock {
+	e.results = &TransactionManagerMockReadCommittedResults{err}
+	return e.mock
+}
+
+// Times sets number of times TransactionManager.ReadCommitted should be invoked
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Times(n uint64) *mTransactionManagerMockReadCommitted {
+	if n == 0 {
+		mmReadCommitted.mock.t.Fatalf("Times of TransactionManagerMock.ReadCommitted mock can not be zero")
+	}
+	mm_atomic.StoreUint64(&mmReadCommitted.expectedInvocations, n)
+	return mmReadCommitted
+}
+
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) invocationsDone() bool {
+	if len(mmReadCommitted.expectations) == 0 && mmReadCommitted.defaultExpectation == nil && mmReadCommitted.mock.funcReadCommitted == nil {
+		return true
+	}
+
+	totalInvocations := mm_atomic.LoadUint64(&mmReadCommitted.mock.afterReadCommittedCounter)
+	expectedInvocations := mm_atomic.LoadUint64(&mmReadCommitted.expectedInvocations)
+
+	return totalInvocations > 0 && (expectedInvocations == 0 || expectedInvocations == totalInvocations)
+}
+
+// ReadCommitted implements place.TransactionManager
+func (mmReadCommitted *TransactionManagerMock) ReadCommitted(ctx context.Context, f1 func(ctx context.Context) error) (err error) {
+	mm_atomic.AddUint64(&mmReadCommitted.beforeReadCommittedCounter, 1)
+	defer mm_atomic.AddUint64(&mmReadCommitted.afterReadCommittedCounter, 1)
+
+	if mmReadCommitted.inspectFuncReadCommitted != nil {
+		mmReadCommitted.inspectFuncReadCommitted(ctx, f1)
+	}
+
+	mm_params := TransactionManagerMockReadCommittedParams{ctx, f1}
+
+	// Record call args
+	mmReadCommitted.ReadCommittedMock.mutex.Lock()
+	mmReadCommitted.ReadCommittedMock.callArgs = append(mmReadCommitted.ReadCommittedMock.callArgs, &mm_params)
+	mmReadCommitted.ReadCommittedMock.mutex.Unlock()
+
+	for _, e := range mmReadCommitted.ReadCommittedMock.expectations {
+		if minimock.Equal(*e.params, mm_params) {
+			mm_atomic.AddUint64(&e.Counter, 1)
+			return e.results.err
+		}
+	}
+
+	if mmReadCommitted.ReadCommittedMock.defaultExpectation != nil {
+		mm_atomic.AddUint64(&mmReadCommitted.ReadCommittedMock.defaultExpectation.Counter, 1)
+		mm_want := mmReadCommitted.ReadCommittedMock.defaultExpectation.params
+		mm_want_ptrs := mmReadCommitted.ReadCommittedMock.defaultExpectation.paramPtrs
+
+		mm_got := TransactionManagerMockReadCommittedParams{ctx, f1}
+
+		if mm_want_ptrs != nil {
+
+			if mm_want_ptrs.ctx != nil && !minimock.Equal(*mm_want_ptrs.ctx, mm_got.ctx) {
+				mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameter ctx, want: %#v, got: %#v%s\n", *mm_want_ptrs.ctx, mm_got.ctx, minimock.Diff(*mm_want_ptrs.ctx, mm_got.ctx))
+			}
+
+			if mm_want_ptrs.f1 != nil && !minimock.Equal(*mm_want_ptrs.f1, mm_got.f1) {
+				mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameter f1, want: %#v, got: %#v%s\n", *mm_want_ptrs.f1, mm_got.f1, minimock.Diff(*mm_want_ptrs.f1, mm_got.f1))
+			}
+
+		} else if mm_want != nil && !minimock.Equal(*mm_want, mm_got) {
+			mmReadCommitted.t.Errorf("TransactionManagerMock.ReadCommitted got unexpected parameters, want: %#v, got: %#v%s\n", *mm_want, mm_got, minimock.Diff(*mm_want, mm_got))
+		}
+
+		mm_results := mmReadCommitted.ReadCommittedMock.defaultExpectation.results
+		if mm_results == nil {
+			mmReadCommitted.t.Fatal("No results are set for the TransactionManagerMock.ReadCommitted")
+		}
+		return (*mm_results).err
+	}
+	if mmReadCommitted.funcReadCommitted != nil {
+		return mmReadCommitted.funcReadCommitted(ctx, f1)
+	}
+	mmReadCommitted.t.Fatalf("Unexpected call to TransactionManagerMock.ReadCommitted. %v %v", ctx, f1)
+	return
+}
+
+// ReadCommittedAfterCounter returns a count of finished TransactionManagerMock.ReadCommitted invocations
+func (mmReadCommitted *TransactionManagerMock) ReadCommittedAfterCounter() uint64 {
+	return mm_atomic.LoadUint64(&mmReadCommitted.afterReadCommittedCounter)
+}
+
+// ReadCommittedBeforeCounter returns a count of TransactionManagerMock.ReadCommitted invocations
+func (mmReadCommitted *TransactionManagerMock) ReadCommittedBeforeCounter() uint64 {
+	return mm_atomic.LoadUint64(&mmReadCommitted.beforeReadCommittedCounter)
+}
+
+// Calls returns a list of arguments used in each call to TransactionManagerMock.ReadCommitted.
+// The list is in the same order as the calls were made (i.e. recent calls have a higher index)
+func (mmReadCommitted *mTransactionManagerMockReadCommitted) Calls() []*TransactionManagerMockReadCommittedParams {
+	mmReadCommitted.mutex.RLock()
+
+	argCopy := make([]*TransactionManagerMockReadCommittedParams, len(mmReadCommitted.callArgs))
+	copy(argCopy, mmReadCommitted.callArgs)
+
+	mmReadCommitted.mutex.RUnlock()
+
+	return argCopy
+}
+
+// MinimockReadCommittedDone returns true if the count of the ReadCommitted invocations corresponds
+// the number of defined expectations
+func (m *TransactionManagerMock) MinimockReadCommittedDone() bool {
+	if m.ReadCommittedMock.optional {
+		// Optional methods provide '0 or more' call count restriction.
+		return true
+	}
+
+	for _, e := range m.ReadCommittedMock.expectations {
+		if mm_atomic.LoadUint64(&e.Counter) < 1 {
+			return false
+		}
+	}
+
+	return m.ReadCommittedMock.invocationsDone()
+}
+
+// MinimockReadCommittedInspect logs each unmet expectation
+func (m *TransactionManagerMock) MinimockReadCommittedInspect() {
+	for _, e := range m.ReadCommittedMock.expectations {
+		if mm_atomic.LoadUint64(&e.Counter) < 1 {
+			m.t.Errorf("Expected call to TransactionManagerMock.ReadCommitted with params: %#v", *e.params)
+		}
+	}
+
+	afterReadCommittedCounter := mm_atomic.LoadUint64(&m.afterReadCommittedCounter)
+	// if default expectation was set then invocations count should be greater than zero
+	if m.ReadCommittedMock.defaultExpectation != nil && afterReadCommittedCounter < 1 {
+		if m.ReadCommittedMock.defaultExpectation.params == nil {
+			m.t.Error("Expected call to TransactionManagerMock.ReadCommitted")
+		} else {
+			m.t.Errorf("Expected call to TransactionManagerMock.ReadCommitted with params: %#v", *m.ReadCommittedMock.defaultExpectation.params)
+		}
+	}
+	// if func was set then invocations count should be greater than zero
+	if m.funcReadCommitted != nil && afterReadCommittedCounter < 1 {
+		m.t.Error("Expected call to TransactionManagerMock.ReadCommitted")
+	}
+
+	if !m.ReadCommittedMock.invocationsDone() && afterReadCommittedCounter > 0 {
+		m.t.Errorf("Expected %d calls to TransactionManagerMock.ReadCommitted but found %d calls",
+			mm_atomic.LoadUint64(&m.ReadCommittedMock.expectedInvocations), afterReadCommittedCounter)
+	}
+}
+
+// MinimockFinish checks that all mocked methods have been called the expected number of times
+func (m *TransactionManagerMock) MinimockFinish() {
+	m.finishOnce.Do(func() {
+		if !m.minimockDone() {
+			m.MinimockReadCommittedInspect()
+		}
+	})
+}
+
+// MinimockWait waits for all mocked methods to be called the expected number of times
+func (m *TransactionManagerMock) MinimockWait(timeout mm_time.Duration) {
+	timeoutCh := mm_time.After(timeout)
+	for {
+		if m.minimockDone() {
+			return
+		}
+		select {
+		case <-timeoutCh:
+			m.MinimockFinish()
+			return
+		case <-mm_time.After(10 * mm_time.Millisecond):
+		}
+	}
+}
+
+func (m *TransactionManagerMock) minimockDone() bool {
+	done := true
+	return done &&
+		m.MinimockReadCommittedDone()
+}

+ 170 - 0
internal/api/v1/user/add_user_test.go

@@ -0,0 +1,170 @@
+package user
+
+import (
+	"context"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/brianvoe/gofakeit/v6"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gojuno/minimock/v3"
+	"github.com/stretchr/testify/assert"
+
+	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/user/mocks"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
+)
+
+func TestAddUserHandler(t *testing.T) {
+	t.Parallel()
+
+	type req struct {
+		method      string
+		route       string
+		contentType string
+		body        *dto.AddUserRequest
+	}
+
+	var (
+		username  = gofakeit.Username()
+		password  = gofakeit.Word()
+		hash      = gofakeit.Word()
+		testError = gofakeit.Error()
+
+		correctReq = req{
+			method: fiber.MethodPost,
+			route:  "/v1/users/",
+			body: &dto.AddUserRequest{
+				Username: username,
+				Password: password,
+			},
+			contentType: fiber.MIMEApplicationJSON,
+		}
+	)
+
+	tests := []struct {
+		name         string
+		req          req
+		resCode      int
+		resBody      interface{}
+		authService  func(mc *minimock.Controller) AuthService
+		userRepoMock func(mc *minimock.Controller) UserRepository
+	}{
+		{
+			name:    "positive case",
+			req:     correctReq,
+			resCode: fiber.StatusOK,
+			resBody: dto.EmptyResponse{},
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return(hash, nil)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				mock := mocks.NewUserRepositoryMock(mc)
+
+				mock.AddMock.Inspect(func(ctx context.Context, u string, p string) {
+					assert.Equal(mc, hash, p)
+					assert.Equal(mc, username, u)
+				}).Return(gofakeit.Uint64(), nil)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - user repository error",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return(hash, nil)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				mock := mocks.NewUserRepositoryMock(mc)
+
+				mock.AddMock.Inspect(func(ctx context.Context, u string, p string) {
+					assert.Equal(mc, hash, p)
+					assert.Equal(mc, username, u)
+				}).Return(0, testError)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - auth service error",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return("", testError)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				return mocks.NewUserRepositoryMock(mc)
+			},
+		},
+		{
+			name: "negative case - bad request",
+			req: req{
+				method:      fiber.MethodPost,
+				route:       "/v1/users/",
+				body:        &dto.AddUserRequest{},
+				contentType: fiber.MIMEApplicationJSON,
+			},
+			resCode: fiber.StatusBadRequest,
+			authService: func(mc *minimock.Controller) AuthService {
+				return mocks.NewAuthServiceMock(mc)
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				return mocks.NewUserRepositoryMock(mc)
+			},
+		},
+		{
+			name: "negative case - body parse error",
+			req: req{
+				method: fiber.MethodPost,
+				route:  "/v1/users/",
+				body:   &dto.AddUserRequest{},
+			},
+			resCode: fiber.StatusBadRequest,
+			authService: func(mc *minimock.Controller) AuthService {
+				return mocks.NewAuthServiceMock(mc)
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				return mocks.NewUserRepositoryMock(mc)
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			mc := minimock.NewController(t)
+			fiberApp := fiber.New()
+			fiberApp.Post("/v1/users", AddUserHandler(tt.authService(mc), tt.userRepoMock(mc)))
+
+			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, test.ConvertDataToIOReader(tt.req.body))
+			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
+
+			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
+			if tt.resBody != nil {
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
+			}
+		})
+	}
+}

+ 248 - 0
internal/api/v1/user/update_user_test.go

@@ -0,0 +1,248 @@
+package user
+
+import (
+	"context"
+	"database/sql"
+	"net/http/httptest"
+	"testing"
+
+	"github.com/brianvoe/gofakeit/v6"
+	"github.com/gofiber/fiber/v2"
+	"github.com/gojuno/minimock/v3"
+	"github.com/golang-jwt/jwt/v4"
+	"github.com/stretchr/testify/assert"
+
+	"git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/user/mocks"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/dto"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/helpers/test"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/models"
+	"git.dmitriygnatenko.ru/dima/homethings/internal/services/auth"
+)
+
+func TestUpdateUserHandler(t *testing.T) {
+	t.Parallel()
+
+	type req struct {
+		method      string
+		route       string
+		contentType string
+		body        *dto.UpdateUserRequest
+	}
+
+	var (
+		username  = gofakeit.Username()
+		password  = gofakeit.Word()
+		hash      = gofakeit.Word()
+		testError = gofakeit.Error()
+
+		claims = jwt.MapClaims{
+			auth.ClaimsKeyName: username,
+		}
+
+		user = models.User{
+			ID:        gofakeit.Uint64(),
+			Username:  username,
+			Password:  password,
+			CreatedAt: gofakeit.Date(),
+			UpdatedAt: gofakeit.Date(),
+		}
+
+		correctReq = req{
+			method: fiber.MethodPut,
+			route:  "/v1/users/",
+			body: &dto.UpdateUserRequest{
+				Username: &username,
+				Password: &password,
+			},
+			contentType: fiber.MIMEApplicationJSON,
+		}
+	)
+
+	tests := []struct {
+		name         string
+		req          req
+		resCode      int
+		resBody      interface{}
+		authService  func(mc *minimock.Controller) AuthService
+		userRepoMock func(mc *minimock.Controller) UserRepository
+	}{
+		{
+			name:    "positive case",
+			req:     correctReq,
+			resCode: fiber.StatusOK,
+			resBody: dto.EmptyResponse{},
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return(hash, nil)
+
+				mock.GetClaimsMock.Return(claims)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				mock := mocks.NewUserRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, u string) {
+					assert.Equal(mc, username, u)
+				}).Return(&user, nil)
+
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateUserRequest) {
+					assert.Equal(mc, hash, req.Password.String)
+					assert.Equal(mc, username, req.Username.String)
+				}).Return(nil)
+
+				return mock
+			},
+		},
+		{
+			name: "negative case - bad request",
+			req: req{
+				method:      fiber.MethodPut,
+				route:       "/v1/users/",
+				body:        &dto.UpdateUserRequest{},
+				contentType: fiber.MIMEApplicationJSON,
+			},
+			resCode: fiber.StatusBadRequest,
+			authService: func(mc *minimock.Controller) AuthService {
+				return mocks.NewAuthServiceMock(mc)
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				return mocks.NewUserRepositoryMock(mc)
+			},
+		},
+		{
+			name: "negative case - body parse error",
+			req: req{
+				method: fiber.MethodPut,
+				route:  "/v1/users/",
+				body:   &dto.UpdateUserRequest{},
+			},
+			resCode: fiber.StatusBadRequest,
+			authService: func(mc *minimock.Controller) AuthService {
+				return mocks.NewAuthServiceMock(mc)
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				return mocks.NewUserRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - auth service error",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return("", testError)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				return mocks.NewUserRepositoryMock(mc)
+			},
+		},
+		{
+			name:    "negative case - repository error - get user",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return(hash, nil)
+
+				mock.GetClaimsMock.Return(claims)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				mock := mocks.NewUserRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, u string) {
+					assert.Equal(mc, username, u)
+				}).Return(nil, testError)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - repository error - update user",
+			req:     correctReq,
+			resCode: fiber.StatusInternalServerError,
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return(hash, nil)
+
+				mock.GetClaimsMock.Return(claims)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				mock := mocks.NewUserRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, u string) {
+					assert.Equal(mc, username, u)
+				}).Return(&user, nil)
+
+				mock.UpdateMock.Inspect(func(ctx context.Context, req models.UpdateUserRequest) {
+					assert.Equal(mc, hash, req.Password.String)
+					assert.Equal(mc, username, req.Username.String)
+				}).Return(testError)
+
+				return mock
+			},
+		},
+		{
+			name:    "negative case - user not found",
+			req:     correctReq,
+			resCode: fiber.StatusBadRequest,
+			authService: func(mc *minimock.Controller) AuthService {
+				mock := mocks.NewAuthServiceMock(mc)
+
+				mock.GeneratePasswordHashMock.Inspect(func(p string) {
+					assert.Equal(mc, password, p)
+				}).Return(hash, nil)
+
+				mock.GetClaimsMock.Return(claims)
+
+				return mock
+			},
+			userRepoMock: func(mc *minimock.Controller) UserRepository {
+				mock := mocks.NewUserRepositoryMock(mc)
+
+				mock.GetMock.Inspect(func(ctx context.Context, u string) {
+					assert.Equal(mc, username, u)
+				}).Return(nil, sql.ErrNoRows)
+
+				return mock
+			},
+		},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			t.Parallel()
+
+			mc := minimock.NewController(t)
+			fiberApp := fiber.New()
+			fiberApp.Put("/v1/users", UpdateUserHandler(tt.authService(mc), tt.userRepoMock(mc)))
+
+			fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, test.ConvertDataToIOReader(tt.req.body))
+			fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
+			fiberRes, _ := fiberApp.Test(fiberReq, test.TestTimeout)
+
+			assert.Equal(t, tt.resCode, fiberRes.StatusCode)
+			if tt.resBody != nil {
+				assert.Equal(t, test.MarshalResponse(tt.resBody), test.ConvertBodyToString(fiberRes.Body))
+			}
+		})
+	}
+}