add_thing_test.go 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. package thing
  2. import (
  3. "context"
  4. "database/sql"
  5. "net/http/httptest"
  6. "testing"
  7. "github.com/brianvoe/gofakeit/v6"
  8. "github.com/gofiber/fiber/v2"
  9. "github.com/gojuno/minimock/v3"
  10. "github.com/stretchr/testify/assert"
  11. API "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1"
  12. "git.dmitriygnatenko.ru/dima/homethings/internal/api/v1/thing/mocks"
  13. "git.dmitriygnatenko.ru/dima/homethings/internal/dto"
  14. "git.dmitriygnatenko.ru/dima/homethings/internal/helpers"
  15. "git.dmitriygnatenko.ru/dima/homethings/internal/models"
  16. )
  17. func TestAddThingHandler(t *testing.T) {
  18. t.Parallel()
  19. type req struct {
  20. method string
  21. route string
  22. contentType string
  23. body *dto.AddThingRequest
  24. }
  25. var (
  26. placeID = gofakeit.Number(1, 1000)
  27. thingID = gofakeit.Number(1, 1000)
  28. title = gofakeit.Phrase()
  29. description = gofakeit.Phrase()
  30. testError = gofakeit.Error()
  31. layout = "2006-01-02 15:04:05"
  32. correctReq = req{
  33. method: fiber.MethodPost,
  34. route: "/v1/things",
  35. body: &dto.AddThingRequest{
  36. PlaceID: placeID,
  37. Title: title,
  38. Description: description,
  39. },
  40. contentType: fiber.MIMEApplicationJSON,
  41. }
  42. repoRes = models.Thing{
  43. ID: thingID,
  44. PlaceID: placeID,
  45. Title: title,
  46. Description: description,
  47. CreatedAt: gofakeit.Date(),
  48. UpdatedAt: gofakeit.Date(),
  49. }
  50. expectedRes = dto.ThingResponse{
  51. ID: thingID,
  52. PlaceID: placeID,
  53. Title: title,
  54. Description: description,
  55. CreatedAt: repoRes.CreatedAt.Format(layout),
  56. UpdatedAt: repoRes.UpdatedAt.Format(layout),
  57. }
  58. )
  59. tests := []struct {
  60. name string
  61. req req
  62. resCode int
  63. resBody interface{}
  64. thingRepoMock func(mc *minimock.Controller) ThingRepository
  65. placeThingRepoMock func(mc *minimock.Controller) PlaceThingRepository
  66. }{
  67. {
  68. name: "positive case",
  69. req: correctReq,
  70. resCode: fiber.StatusOK,
  71. resBody: expectedRes,
  72. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  73. mock := mocks.NewThingRepositoryMock(mc)
  74. mock.BeginTxMock.Return(nil, nil)
  75. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingRequest, tx *sql.Tx) {
  76. assert.Equal(mc, title, req.Title)
  77. assert.Equal(mc, description, req.Description)
  78. }).Return(thingID, nil)
  79. mock.CommitTxMock.Return(nil)
  80. mock.GetMock.Inspect(func(ctx context.Context, id int) {
  81. assert.Equal(mc, thingID, id)
  82. }).Return(&repoRes, nil)
  83. return mock
  84. },
  85. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  86. mock := mocks.NewPlaceThingRepositoryMock(mc)
  87. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceThingRequest, tx *sql.Tx) {
  88. assert.Equal(mc, thingID, req.ThingID)
  89. assert.Equal(mc, placeID, req.PlaceID)
  90. }).Return(nil)
  91. return mock
  92. },
  93. },
  94. {
  95. name: "negative case - body parse error",
  96. req: req{
  97. method: fiber.MethodPost,
  98. route: "/v1/things",
  99. },
  100. resCode: fiber.StatusBadRequest,
  101. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  102. return mocks.NewThingRepositoryMock(mc)
  103. },
  104. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  105. return mocks.NewPlaceThingRepositoryMock(mc)
  106. },
  107. },
  108. {
  109. name: "negative case - request without place_id",
  110. req: req{
  111. method: fiber.MethodPost,
  112. route: "/v1/things",
  113. contentType: fiber.MIMEApplicationJSON,
  114. body: &dto.AddThingRequest{
  115. Title: title,
  116. Description: description,
  117. },
  118. },
  119. resCode: fiber.StatusBadRequest,
  120. resBody: []*dto.ValidateErrorResponse{
  121. {
  122. Field: "AddThingRequest.PlaceID",
  123. Tag: "required",
  124. },
  125. },
  126. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  127. return mocks.NewThingRepositoryMock(mc)
  128. },
  129. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  130. return mocks.NewPlaceThingRepositoryMock(mc)
  131. },
  132. },
  133. {
  134. name: "negative case - request without title",
  135. req: req{
  136. method: fiber.MethodPost,
  137. route: "/v1/things",
  138. contentType: fiber.MIMEApplicationJSON,
  139. body: &dto.AddThingRequest{
  140. PlaceID: placeID,
  141. Description: description,
  142. },
  143. },
  144. resCode: fiber.StatusBadRequest,
  145. resBody: []*dto.ValidateErrorResponse{
  146. {
  147. Field: "AddThingRequest.Title",
  148. Tag: "required",
  149. },
  150. },
  151. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  152. return mocks.NewThingRepositoryMock(mc)
  153. },
  154. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  155. return mocks.NewPlaceThingRepositoryMock(mc)
  156. },
  157. },
  158. {
  159. name: "negative case - repository error (begin tx)",
  160. req: correctReq,
  161. resCode: fiber.StatusInternalServerError,
  162. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  163. mock := mocks.NewThingRepositoryMock(mc)
  164. mock.BeginTxMock.Return(nil, testError)
  165. return mock
  166. },
  167. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  168. return mocks.NewPlaceThingRepositoryMock(mc)
  169. },
  170. },
  171. {
  172. name: "negative case - repository error (add thing)",
  173. req: correctReq,
  174. resCode: fiber.StatusInternalServerError,
  175. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  176. mock := mocks.NewThingRepositoryMock(mc)
  177. mock.BeginTxMock.Return(nil, nil)
  178. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingRequest, tx *sql.Tx) {
  179. assert.Equal(mc, title, req.Title)
  180. assert.Equal(mc, description, req.Description)
  181. }).Return(0, testError)
  182. return mock
  183. },
  184. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  185. return mocks.NewPlaceThingRepositoryMock(mc)
  186. },
  187. },
  188. {
  189. name: "negative case - repository error (add place thing)",
  190. req: correctReq,
  191. resCode: fiber.StatusInternalServerError,
  192. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  193. mock := mocks.NewThingRepositoryMock(mc)
  194. mock.BeginTxMock.Return(nil, nil)
  195. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingRequest, tx *sql.Tx) {
  196. assert.Equal(mc, title, req.Title)
  197. assert.Equal(mc, description, req.Description)
  198. }).Return(thingID, nil)
  199. return mock
  200. },
  201. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  202. mock := mocks.NewPlaceThingRepositoryMock(mc)
  203. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceThingRequest, tx *sql.Tx) {
  204. assert.Equal(mc, thingID, req.ThingID)
  205. assert.Equal(mc, placeID, req.PlaceID)
  206. }).Return(testError)
  207. return mock
  208. },
  209. },
  210. {
  211. name: "negative case - repository error (commit tx)",
  212. req: correctReq,
  213. resCode: fiber.StatusInternalServerError,
  214. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  215. mock := mocks.NewThingRepositoryMock(mc)
  216. mock.BeginTxMock.Return(nil, nil)
  217. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingRequest, tx *sql.Tx) {
  218. assert.Equal(mc, title, req.Title)
  219. assert.Equal(mc, description, req.Description)
  220. }).Return(thingID, nil)
  221. mock.CommitTxMock.Return(testError)
  222. return mock
  223. },
  224. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  225. mock := mocks.NewPlaceThingRepositoryMock(mc)
  226. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceThingRequest, tx *sql.Tx) {
  227. assert.Equal(mc, thingID, req.ThingID)
  228. assert.Equal(mc, placeID, req.PlaceID)
  229. }).Return(nil)
  230. return mock
  231. },
  232. },
  233. {
  234. name: "negative case - repository error (get thing)",
  235. req: correctReq,
  236. resCode: fiber.StatusInternalServerError,
  237. thingRepoMock: func(mc *minimock.Controller) ThingRepository {
  238. mock := mocks.NewThingRepositoryMock(mc)
  239. mock.BeginTxMock.Return(nil, nil)
  240. mock.AddMock.Inspect(func(ctx context.Context, req models.AddThingRequest, tx *sql.Tx) {
  241. assert.Equal(mc, title, req.Title)
  242. assert.Equal(mc, description, req.Description)
  243. }).Return(thingID, nil)
  244. mock.CommitTxMock.Return(nil)
  245. mock.GetMock.Inspect(func(ctx context.Context, id int) {
  246. assert.Equal(mc, thingID, id)
  247. }).Return(nil, sql.ErrNoRows)
  248. return mock
  249. },
  250. placeThingRepoMock: func(mc *minimock.Controller) PlaceThingRepository {
  251. mock := mocks.NewPlaceThingRepositoryMock(mc)
  252. mock.AddMock.Inspect(func(ctx context.Context, req models.AddPlaceThingRequest, tx *sql.Tx) {
  253. assert.Equal(mc, thingID, req.ThingID)
  254. assert.Equal(mc, placeID, req.PlaceID)
  255. }).Return(nil)
  256. return mock
  257. },
  258. },
  259. }
  260. for _, tt := range tests {
  261. t.Run(tt.name, func(t *testing.T) {
  262. t.Parallel()
  263. mc := minimock.NewController(t)
  264. fiberApp := fiber.New()
  265. fiberApp.Post("/v1/things", AddThingHandler(tt.thingRepoMock(mc), tt.placeThingRepoMock(mc)))
  266. fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, helpers.ConvertDataToIOReader(tt.req.body))
  267. fiberReq.Header.Add(fiber.HeaderContentType, tt.req.contentType)
  268. fiberRes, _ := fiberApp.Test(fiberReq, API.DefaultTestTimeOut)
  269. assert.Equal(t, tt.resCode, fiberRes.StatusCode)
  270. if tt.resBody != nil {
  271. assert.Equal(t, helpers.MarshalResponse(tt.resBody), helpers.ConvertBodyToString(fiberRes.Body))
  272. }
  273. })
  274. }
  275. }