add_image_test.go 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437
  1. package image
  2. import (
  3. "bytes"
  4. "context"
  5. "database/sql"
  6. "errors"
  7. "mime/multipart"
  8. "net/http/httptest"
  9. "strconv"
  10. "testing"
  11. "github.com/brianvoe/gofakeit/v6"
  12. "github.com/gofiber/fiber/v2"
  13. "github.com/gojuno/minimock/v3"
  14. "github.com/stretchr/testify/assert"
  15. API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
  16. "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/image/mocks"
  17. "git.dmitriygnatenko.ru/dima/homethings/internal/dto"
  18. "git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
  19. "git.dmitriygnatenko.ru/dima/homethings/internal/models"
  20. )
  21. // nolint:errcheck
  22. func TestAddImageHandler(t *testing.T) {
  23. t.Parallel()
  24. type req struct {
  25. method string
  26. route string
  27. contentType string
  28. body []byte
  29. }
  30. var (
  31. placeID = gofakeit.Number(1, 1000)
  32. thingID = gofakeit.Number(1, 1000)
  33. testError = errors.New(gofakeit.Phrase())
  34. )
  35. // Correct request for adding place image
  36. addPlaceCorrectBody := &bytes.Buffer{}
  37. addPlaceCorrectWriter := multipart.NewWriter(addPlaceCorrectBody)
  38. addPlaceCorrectWriter.WriteField("place_id", strconv.Itoa(placeID))
  39. addPlaceCorrectWriter.CreateFormFile("files", gofakeit.Word())
  40. addPlaceCorrectContentType := addPlaceCorrectWriter.FormDataContentType()
  41. addPlaceCorrectWriter.Close()
  42. // Correct request for adding thing image
  43. addThingCorrectBody := &bytes.Buffer{}
  44. addThingCorrectWriter := multipart.NewWriter(addThingCorrectBody)
  45. addThingCorrectWriter.WriteField("thing_id", strconv.Itoa(thingID))
  46. addThingCorrectWriter.CreateFormFile("files", gofakeit.Word())
  47. addThingCorrectContentType := addThingCorrectWriter.FormDataContentType()
  48. addThingCorrectWriter.Close()
  49. // Incorrect request for adding place image
  50. addPlaceIncorrectBody := &bytes.Buffer{}
  51. addPlaceIncorrectWriter := multipart.NewWriter(addPlaceIncorrectBody)
  52. addPlaceIncorrectWriter.WriteField("place_id", gofakeit.Word())
  53. addPlaceIncorrectContentType := addPlaceIncorrectWriter.FormDataContentType()
  54. addPlaceIncorrectWriter.Close()
  55. // Incorrect request for adding thing image
  56. addThingIncorrectBody := &bytes.Buffer{}
  57. addThingIncorrectWriter := multipart.NewWriter(addThingIncorrectBody)
  58. addThingIncorrectWriter.WriteField("thing_id", gofakeit.Word())
  59. addThingIncorrectContentType := addThingIncorrectWriter.FormDataContentType()
  60. addThingIncorrectWriter.Close()
  61. // Incorrect empty request
  62. emptyBody := &bytes.Buffer{}
  63. emptyWriter := multipart.NewWriter(emptyBody)
  64. emptyContentType := emptyWriter.FormDataContentType()
  65. emptyWriter.Close()
  66. tests := []struct {
  67. name string
  68. req req
  69. resCode int
  70. resBody interface{}
  71. fileRepoMock func(mc *minimock.Controller) FileRepository
  72. placeImageRepoMock func(mc *minimock.Controller) PlaceImageRepository
  73. thingImageRepoMock func(mc *minimock.Controller) ThingImageRepository
  74. }{
  75. {
  76. name: "negative case - bad request",
  77. req: req{
  78. method: fiber.MethodPost,
  79. route: "/v1/images",
  80. },
  81. resCode: fiber.StatusBadRequest,
  82. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  83. return mocks.NewPlaceImageRepositoryMock(mc)
  84. },
  85. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  86. return mocks.NewThingImageRepositoryMock(mc)
  87. },
  88. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  89. return mocks.NewFileRepositoryMock(mc)
  90. },
  91. },
  92. {
  93. name: "negative case - incorrect request",
  94. req: req{
  95. method: fiber.MethodPost,
  96. route: "/v1/images",
  97. body: addPlaceIncorrectBody.Bytes(),
  98. contentType: addPlaceIncorrectContentType,
  99. },
  100. resCode: fiber.StatusBadRequest,
  101. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  102. return mocks.NewPlaceImageRepositoryMock(mc)
  103. },
  104. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  105. return mocks.NewThingImageRepositoryMock(mc)
  106. },
  107. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  108. return mocks.NewFileRepositoryMock(mc)
  109. },
  110. },
  111. {
  112. name: "negative case - incorrect request",
  113. req: req{
  114. method: fiber.MethodPost,
  115. route: "/v1/images",
  116. body: addThingIncorrectBody.Bytes(),
  117. contentType: addThingIncorrectContentType,
  118. },
  119. resCode: fiber.StatusBadRequest,
  120. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  121. return mocks.NewPlaceImageRepositoryMock(mc)
  122. },
  123. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  124. return mocks.NewThingImageRepositoryMock(mc)
  125. },
  126. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  127. return mocks.NewFileRepositoryMock(mc)
  128. },
  129. },
  130. {
  131. name: "negative case - empty request",
  132. req: req{
  133. method: fiber.MethodPost,
  134. route: "/v1/images",
  135. body: emptyBody.Bytes(),
  136. contentType: emptyContentType,
  137. },
  138. resCode: fiber.StatusBadRequest,
  139. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  140. return mocks.NewPlaceImageRepositoryMock(mc)
  141. },
  142. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  143. return mocks.NewThingImageRepositoryMock(mc)
  144. },
  145. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  146. return mocks.NewFileRepositoryMock(mc)
  147. },
  148. },
  149. {
  150. name: "negative case - repository error (begin tx)",
  151. req: req{
  152. method: fiber.MethodPost,
  153. route: "/v1/images",
  154. body: addPlaceCorrectBody.Bytes(),
  155. contentType: addPlaceCorrectContentType,
  156. },
  157. resCode: fiber.StatusInternalServerError,
  158. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  159. mock := mocks.NewPlaceImageRepositoryMock(mc)
  160. mock.BeginTxMock.Return(nil, testError)
  161. return mock
  162. },
  163. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  164. return mocks.NewThingImageRepositoryMock(mc)
  165. },
  166. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  167. mock := mocks.NewFileRepositoryMock(mc)
  168. mock.SaveMock.Return(nil)
  169. return mock
  170. },
  171. },
  172. {
  173. name: "negative case - repository error (commit tx)",
  174. req: req{
  175. method: fiber.MethodPost,
  176. route: "/v1/images",
  177. body: addPlaceCorrectBody.Bytes(),
  178. contentType: addPlaceCorrectContentType,
  179. },
  180. resCode: fiber.StatusInternalServerError,
  181. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  182. mock := mocks.NewPlaceImageRepositoryMock(mc)
  183. mock.BeginTxMock.Return(nil, nil)
  184. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceImageRequest, tx *sql.Tx) {
  185. assert.Equal(mc, placeID, req.PlaceID)
  186. }).Return(nil)
  187. mock.CommitTxMock.Return(testError)
  188. return mock
  189. },
  190. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  191. return mocks.NewThingImageRepositoryMock(mc)
  192. },
  193. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  194. mock := mocks.NewFileRepositoryMock(mc)
  195. mock.SaveMock.Return(nil)
  196. return mock
  197. },
  198. },
  199. {
  200. name: "negative case - repository error (add image)",
  201. req: req{
  202. method: fiber.MethodPost,
  203. route: "/v1/images",
  204. body: addPlaceCorrectBody.Bytes(),
  205. contentType: addPlaceCorrectContentType,
  206. },
  207. resCode: fiber.StatusInternalServerError,
  208. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  209. mock := mocks.NewPlaceImageRepositoryMock(mc)
  210. mock.BeginTxMock.Return(nil, nil)
  211. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceImageRequest, tx *sql.Tx) {
  212. assert.Equal(mc, placeID, req.PlaceID)
  213. }).Return(testError)
  214. return mock
  215. },
  216. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  217. return mocks.NewThingImageRepositoryMock(mc)
  218. },
  219. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  220. mock := mocks.NewFileRepositoryMock(mc)
  221. mock.SaveMock.Return(nil)
  222. return mock
  223. },
  224. },
  225. {
  226. name: "positive case - add place image",
  227. req: req{
  228. method: fiber.MethodPost,
  229. route: "/v1/images",
  230. body: addPlaceCorrectBody.Bytes(),
  231. contentType: addPlaceCorrectContentType,
  232. },
  233. resCode: fiber.StatusOK,
  234. resBody: dto.EmptyResponse{},
  235. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  236. mock := mocks.NewPlaceImageRepositoryMock(mc)
  237. mock.BeginTxMock.Return(nil, nil)
  238. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceImageRequest, tx *sql.Tx) {
  239. assert.Equal(mc, placeID, req.PlaceID)
  240. }).Return(nil)
  241. mock.CommitTxMock.Return(nil)
  242. return mock
  243. },
  244. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  245. return mocks.NewThingImageRepositoryMock(mc)
  246. },
  247. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  248. mock := mocks.NewFileRepositoryMock(mc)
  249. mock.SaveMock.Return(nil)
  250. return mock
  251. },
  252. },
  253. {
  254. name: "negative case - repository error (begin tx)",
  255. req: req{
  256. method: fiber.MethodPost,
  257. route: "/v1/images",
  258. body: addThingCorrectBody.Bytes(),
  259. contentType: addThingCorrectContentType,
  260. },
  261. resCode: fiber.StatusInternalServerError,
  262. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  263. return mocks.NewPlaceImageRepositoryMock(mc)
  264. },
  265. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  266. mock := mocks.NewThingImageRepositoryMock(mc)
  267. mock.BeginTxMock.Return(nil, testError)
  268. return mock
  269. },
  270. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  271. mock := mocks.NewFileRepositoryMock(mc)
  272. mock.SaveMock.Return(nil)
  273. return mock
  274. },
  275. },
  276. {
  277. name: "negative case - repository error (commit tx)",
  278. req: req{
  279. method: fiber.MethodPost,
  280. route: "/v1/images",
  281. body: addThingCorrectBody.Bytes(),
  282. contentType: addThingCorrectContentType,
  283. },
  284. resCode: fiber.StatusInternalServerError,
  285. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  286. return mocks.NewPlaceImageRepositoryMock(mc)
  287. },
  288. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  289. mock := mocks.NewThingImageRepositoryMock(mc)
  290. mock.BeginTxMock.Return(nil, nil)
  291. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingImageRequest, tx *sql.Tx) {
  292. assert.Equal(mc, thingID, req.ThingID)
  293. }).Return(nil)
  294. mock.CommitTxMock.Return(testError)
  295. return mock
  296. },
  297. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  298. mock := mocks.NewFileRepositoryMock(mc)
  299. mock.SaveMock.Return(nil)
  300. return mock
  301. },
  302. },
  303. {
  304. name: "negative case - repository error (add image)",
  305. req: req{
  306. method: fiber.MethodPost,
  307. route: "/v1/images",
  308. body: addThingCorrectBody.Bytes(),
  309. contentType: addThingCorrectContentType,
  310. },
  311. resCode: fiber.StatusInternalServerError,
  312. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  313. return mocks.NewPlaceImageRepositoryMock(mc)
  314. },
  315. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  316. mock := mocks.NewThingImageRepositoryMock(mc)
  317. mock.BeginTxMock.Return(nil, nil)
  318. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingImageRequest, tx *sql.Tx) {
  319. assert.Equal(mc, thingID, req.ThingID)
  320. }).Return(testError)
  321. return mock
  322. },
  323. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  324. mock := mocks.NewFileRepositoryMock(mc)
  325. mock.SaveMock.Return(nil)
  326. return mock
  327. },
  328. },
  329. {
  330. name: "positive case - add thing image",
  331. req: req{
  332. method: fiber.MethodPost,
  333. route: "/v1/images",
  334. body: addThingCorrectBody.Bytes(),
  335. contentType: addThingCorrectContentType,
  336. },
  337. resCode: fiber.StatusOK,
  338. resBody: dto.EmptyResponse{},
  339. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  340. return mocks.NewPlaceImageRepositoryMock(mc)
  341. },
  342. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  343. mock := mocks.NewThingImageRepositoryMock(mc)
  344. mock.BeginTxMock.Return(nil, nil)
  345. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingImageRequest, tx *sql.Tx) {
  346. assert.Equal(mc, thingID, req.ThingID)
  347. }).Return(nil)
  348. mock.CommitTxMock.Return(nil)
  349. return mock
  350. },
  351. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  352. mock := mocks.NewFileRepositoryMock(mc)
  353. mock.SaveMock.Return(nil)
  354. return mock
  355. },
  356. },
  357. {
  358. name: "negative case - save file error",
  359. req: req{
  360. method: fiber.MethodPost,
  361. route: "/v1/images",
  362. body: addThingCorrectBody.Bytes(),
  363. contentType: addThingCorrectContentType,
  364. },
  365. resCode: fiber.StatusInternalServerError,
  366. placeImageRepoMock: func(mc *minimock.Controller) PlaceImageRepository {
  367. return mocks.NewPlaceImageRepositoryMock(mc)
  368. },
  369. thingImageRepoMock: func(mc *minimock.Controller) ThingImageRepository {
  370. return mocks.NewThingImageRepositoryMock(mc)
  371. },
  372. fileRepoMock: func(mc *minimock.Controller) FileRepository {
  373. mock := mocks.NewFileRepositoryMock(mc)
  374. mock.SaveMock.Return(testError)
  375. return mock
  376. },
  377. },
  378. }
  379. for _, tt := range tests {
  380. t.Run(tt.name, func(t *testing.T) {
  381. t.Parallel()
  382. mc := minimock.NewController(t)
  383. fiberApp := fiber.New()
  384. fiberApp.Post("/v1/images", AddImageHandler(
  385. tt.fileRepoMock(mc),
  386. tt.thingImageRepoMock(mc),
  387. tt.placeImageRepoMock(mc),
  388. ))
  389. fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, bytes.NewReader(tt.req.body))
  390. fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
  391. fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
  392. assert.Equal(t, tt.resCode, fiberRes.StatusCode)
  393. if tt.resBody != nil {
  394. assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
  395. }
  396. })
  397. }
  398. }