article_test.go 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. package handler
  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. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/helpers/test"
  12. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/models"
  13. "git.dmitriygnatenko.ru/dima/dmitriygnatenko-v2/internal/services/handler/mocks"
  14. )
  15. func TestArticleHandler(t *testing.T) {
  16. t.Parallel()
  17. type req struct {
  18. method string
  19. route string
  20. }
  21. var (
  22. articleID = gofakeit.Uint64()
  23. articleURL = gofakeit.Word()
  24. publishTime = gofakeit.Date()
  25. internalErr = gofakeit.Error()
  26. article = models.Article{
  27. ID: articleID,
  28. URL: articleURL,
  29. Title: gofakeit.Phrase(),
  30. Text: gofakeit.Phrase(),
  31. PublishTime: publishTime,
  32. PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
  33. Image: sql.NullString{Valid: true, String: gofakeit.URL()},
  34. IsActive: true,
  35. }
  36. notActiveArticle = models.Article{
  37. ID: articleID,
  38. URL: articleURL,
  39. Title: gofakeit.Phrase(),
  40. Text: gofakeit.Phrase(),
  41. PublishTime: publishTime,
  42. PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
  43. Image: sql.NullString{Valid: true, String: gofakeit.URL()},
  44. }
  45. tags = []models.Tag{
  46. {
  47. ID: gofakeit.Uint64(),
  48. Tag: gofakeit.Word(),
  49. URL: gofakeit.Word(),
  50. },
  51. {
  52. ID: gofakeit.Uint64(),
  53. Tag: gofakeit.Word(),
  54. URL: gofakeit.Word(),
  55. },
  56. }
  57. previewArticles = []models.ArticlePreview{
  58. {
  59. ID: gofakeit.Uint64(),
  60. URL: gofakeit.URL(),
  61. Title: gofakeit.Phrase(),
  62. PublishTime: publishTime,
  63. PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
  64. Image: sql.NullString{Valid: true, String: gofakeit.URL()},
  65. },
  66. {
  67. ID: gofakeit.Uint64(),
  68. URL: gofakeit.URL(),
  69. Title: gofakeit.Phrase(),
  70. PublishTime: publishTime,
  71. PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
  72. Image: sql.NullString{Valid: true, String: gofakeit.URL()},
  73. },
  74. {
  75. ID: gofakeit.Uint64(),
  76. URL: gofakeit.URL(),
  77. Title: gofakeit.Phrase(),
  78. PublishTime: publishTime,
  79. PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
  80. Image: sql.NullString{Valid: true, String: gofakeit.URL()},
  81. },
  82. {
  83. ID: gofakeit.Uint64(),
  84. URL: gofakeit.URL(),
  85. Title: gofakeit.Phrase(),
  86. PublishTime: publishTime,
  87. PreviewText: sql.NullString{Valid: true, String: gofakeit.Phrase()},
  88. Image: sql.NullString{Valid: true, String: gofakeit.URL()},
  89. },
  90. }
  91. )
  92. tests := []struct {
  93. name string
  94. req req
  95. res int
  96. err error
  97. cacheMock func(mc *minimock.Controller) CacheService
  98. tagMock func(mc *minimock.Controller) TagRepository
  99. articleMock func(mc *minimock.Controller) ArticleRepository
  100. }{
  101. {
  102. name: "positive case",
  103. req: req{
  104. method: fiber.MethodGet,
  105. route: "/article/" + articleURL,
  106. },
  107. res: fiber.StatusOK,
  108. err: nil,
  109. cacheMock: func(mc *minimock.Controller) CacheService {
  110. mock := mocks.NewCacheServiceMock(mc)
  111. mock.GetMock.Return(nil, false)
  112. mock.SetMock.Return()
  113. return mock
  114. },
  115. tagMock: func(mc *minimock.Controller) TagRepository {
  116. mock := mocks.NewTagRepositoryMock(mc)
  117. mock.GetAllUsedMock.Return(tags, nil)
  118. return mock
  119. },
  120. articleMock: func(mc *minimock.Controller) ArticleRepository {
  121. mock := mocks.NewArticleRepositoryMock(mc)
  122. mock.GetByURLMock.Inspect(func(ctx context.Context, url string) {
  123. assert.Equal(mc, articleURL, url)
  124. }).Return(&article, nil)
  125. mock.GetAllPreviewMock.Return(previewArticles, nil)
  126. return mock
  127. },
  128. },
  129. {
  130. name: "negative case - article not found",
  131. req: req{
  132. method: fiber.MethodGet,
  133. route: "/article/" + articleURL,
  134. },
  135. res: fiber.StatusNotFound,
  136. err: nil,
  137. cacheMock: func(mc *minimock.Controller) CacheService {
  138. mock := mocks.NewCacheServiceMock(mc)
  139. mock.GetMock.Return(nil, false)
  140. return mock
  141. },
  142. tagMock: func(mc *minimock.Controller) TagRepository {
  143. mock := mocks.NewTagRepositoryMock(mc)
  144. return mock
  145. },
  146. articleMock: func(mc *minimock.Controller) ArticleRepository {
  147. mock := mocks.NewArticleRepositoryMock(mc)
  148. mock.GetByURLMock.Inspect(func(ctx context.Context, url string) {
  149. assert.Equal(mc, articleURL, url)
  150. }).Return(nil, sql.ErrNoRows)
  151. return mock
  152. },
  153. },
  154. {
  155. name: "negative case - article repository error",
  156. req: req{
  157. method: fiber.MethodGet,
  158. route: "/article/" + articleURL,
  159. },
  160. res: fiber.StatusInternalServerError,
  161. err: nil,
  162. cacheMock: func(mc *minimock.Controller) CacheService {
  163. mock := mocks.NewCacheServiceMock(mc)
  164. mock.GetMock.Return(nil, false)
  165. return mock
  166. },
  167. tagMock: func(mc *minimock.Controller) TagRepository {
  168. mock := mocks.NewTagRepositoryMock(mc)
  169. return mock
  170. },
  171. articleMock: func(mc *minimock.Controller) ArticleRepository {
  172. mock := mocks.NewArticleRepositoryMock(mc)
  173. mock.GetByURLMock.Inspect(func(ctx context.Context, url string) {
  174. assert.Equal(mc, articleURL, url)
  175. }).Return(nil, internalErr)
  176. return mock
  177. },
  178. },
  179. {
  180. name: "negative case - article not active",
  181. req: req{
  182. method: fiber.MethodGet,
  183. route: "/article/" + articleURL,
  184. },
  185. res: fiber.StatusNotFound,
  186. err: nil,
  187. cacheMock: func(mc *minimock.Controller) CacheService {
  188. mock := mocks.NewCacheServiceMock(mc)
  189. mock.GetMock.Return(nil, false)
  190. return mock
  191. },
  192. tagMock: func(mc *minimock.Controller) TagRepository {
  193. mock := mocks.NewTagRepositoryMock(mc)
  194. return mock
  195. },
  196. articleMock: func(mc *minimock.Controller) ArticleRepository {
  197. mock := mocks.NewArticleRepositoryMock(mc)
  198. mock.GetByURLMock.Inspect(func(ctx context.Context, url string) {
  199. assert.Equal(mc, articleURL, url)
  200. }).Return(&notActiveArticle, nil)
  201. return mock
  202. },
  203. },
  204. {
  205. name: "negative case - tags repository error",
  206. req: req{
  207. method: fiber.MethodGet,
  208. route: "/article/" + articleURL,
  209. },
  210. res: fiber.StatusInternalServerError,
  211. err: nil,
  212. cacheMock: func(mc *minimock.Controller) CacheService {
  213. mock := mocks.NewCacheServiceMock(mc)
  214. mock.GetMock.Return(nil, false)
  215. return mock
  216. },
  217. tagMock: func(mc *minimock.Controller) TagRepository {
  218. mock := mocks.NewTagRepositoryMock(mc)
  219. mock.GetAllUsedMock.Return(nil, internalErr)
  220. return mock
  221. },
  222. articleMock: func(mc *minimock.Controller) ArticleRepository {
  223. mock := mocks.NewArticleRepositoryMock(mc)
  224. mock.GetByURLMock.Inspect(func(ctx context.Context, url string) {
  225. assert.Equal(mc, articleURL, url)
  226. }).Return(&article, nil)
  227. return mock
  228. },
  229. },
  230. {
  231. name: "negative case - articles repository error",
  232. req: req{
  233. method: fiber.MethodGet,
  234. route: "/article/" + articleURL,
  235. },
  236. res: fiber.StatusInternalServerError,
  237. err: nil,
  238. cacheMock: func(mc *minimock.Controller) CacheService {
  239. mock := mocks.NewCacheServiceMock(mc)
  240. mock.GetMock.Return(nil, false)
  241. return mock
  242. },
  243. tagMock: func(mc *minimock.Controller) TagRepository {
  244. mock := mocks.NewTagRepositoryMock(mc)
  245. mock.GetAllUsedMock.Return(tags, nil)
  246. return mock
  247. },
  248. articleMock: func(mc *minimock.Controller) ArticleRepository {
  249. mock := mocks.NewArticleRepositoryMock(mc)
  250. mock.GetByURLMock.Inspect(func(ctx context.Context, url string) {
  251. assert.Equal(mc, articleURL, url)
  252. }).Return(&article, nil)
  253. mock.GetAllPreviewMock.Return(nil, internalErr)
  254. return mock
  255. },
  256. },
  257. }
  258. for _, tt := range tests {
  259. t.Run(tt.name, func(t *testing.T) {
  260. t.Parallel()
  261. mc := minimock.NewController(t)
  262. fiberApp := fiber.New(test.GetFiberTestConfig())
  263. fiberReq := httptest.NewRequest(tt.req.method, tt.req.route, nil)
  264. fiberApp.Get("/article/:article", ArticleHandler(
  265. tt.cacheMock(mc),
  266. tt.articleMock(mc),
  267. tt.tagMock(mc),
  268. ))
  269. fiberRes, fiberErr := fiberApp.Test(fiberReq)
  270. assert.Equal(t, tt.res, fiberRes.StatusCode)
  271. assert.Equal(t, tt.err, fiberErr)
  272. })
  273. }
  274. }