add_thing_test.go 9.2 KB

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