category.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505
  1. process.env.NODE_ENV = 'test'
  2. let chai = require('chai')
  3. let server = require('../server')
  4. let should = chai.should()
  5. let expect = chai.expect
  6. let { sequelize, Thread, Category } = require('../models')
  7. const Errors = require('../lib/errors.js')
  8. chai.use(require('chai-http'))
  9. chai.use(require('chai-things'))
  10. describe('Category', () => {
  11. //Wait for app to start before commencing
  12. before((done) => {
  13. if(server.locals.appStarted) done()
  14. server.on('appStarted', () => {
  15. done()
  16. })
  17. })
  18. //Delete all rows in table after
  19. //tests completed
  20. after((done) => {
  21. sequelize.sync({ force: true })
  22. .then(() => {
  23. done(null);
  24. })
  25. .catch((err) => {
  26. done(err)
  27. })
  28. })
  29. describe('POST /category', () => {
  30. let agent = chai.request.agent(server)
  31. it('should add a new category if logged in', async () => {
  32. await agent
  33. .post('/api/v1/user')
  34. .set('content-type', 'application/json')
  35. .send({
  36. username: 'adminaccount',
  37. password: 'password',
  38. admin: true
  39. })
  40. let res = await agent
  41. .post('/api/v1/category')
  42. .set('content-type', 'application/json')
  43. .send({ name: 'category' })
  44. res.should.be.json
  45. res.should.have.status(200)
  46. res.body.should.have.property('name', 'category')
  47. res.body.should.have.property('color')
  48. })
  49. it('should have an "underscored" value field', async () => {
  50. let res = await agent
  51. .post('/api/v1/category')
  52. .set('content-type', 'application/json')
  53. .send({ name: ' another category here ' })
  54. res.should.be.json
  55. res.should.have.status(200)
  56. res.body.should.have.property('name', ' another category here ')
  57. res.body.should.have.property('value', 'ANOTHER_CATEGORY_HERE')
  58. })
  59. it('should return an error if category already exists', async () => {
  60. try {
  61. let res = await agent
  62. .post('/api/v1/category')
  63. .set('content-type', 'application/json')
  64. .send({ name: 'category' })
  65. res.should.be.json
  66. res.should.have.status(400)
  67. res.body.errors.should.contain.something.that.deep.equals(Errors.categoryAlreadyExists)
  68. } catch (res) {
  69. res.should.have.status(400)
  70. res.response.body.errors.should.contain.something.that.deep.equals(Errors.categoryAlreadyExists)
  71. }
  72. })
  73. it('should return an error if missing category parameter', done => {
  74. agent
  75. .post('/api/v1/category')
  76. .set('content-type', 'application/json')
  77. .send({})
  78. .end((err, res) => {
  79. res.should.be.json
  80. res.should.have.status(400)
  81. res.body.errors.should.contain.something.that.has.property('message', 'name cannot be null')
  82. done()
  83. })
  84. })
  85. it('should return an error if category parameter has no length', done => {
  86. agent
  87. .post('/api/v1/category')
  88. .set('content-type', 'application/json')
  89. .send({ name: '' })
  90. .end((err, res) => {
  91. res.should.be.json
  92. res.should.have.status(400)
  93. res.body.errors.should.contain.something.that.has.property('message', 'The category name can\'t be empty')
  94. done()
  95. })
  96. })
  97. it('should return an error if not an admin account', async () => {
  98. let agent = chai.request.agent(server)
  99. await agent
  100. .post('/api/v1/user')
  101. .set('content-type', 'application/json')
  102. .send({
  103. username: 'username',
  104. password: 'password',
  105. })
  106. try {
  107. let res = await agent
  108. .post('/api/v1/category')
  109. .set('content-type', 'application/json')
  110. .send({ name: 'category1' })
  111. res.should.be.json
  112. res.should.have.status(401)
  113. res.body.errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
  114. } catch (res) {
  115. res.should.have.status(401)
  116. JSON.parse(res.response.text).errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
  117. }
  118. })
  119. it('should return an error if not logged', async () => {
  120. try {
  121. await chai.request(server)
  122. .post('/api/v1/category')
  123. .set('content-type', 'application/json')
  124. .send({ name: 'category1' })
  125. res.should.be.json
  126. res.should.have.status(401)
  127. res.body.errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
  128. } catch (res) {
  129. res.should.have.status(401)
  130. JSON.parse(res.response.text).errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
  131. }
  132. })
  133. })
  134. describe('GET /category', () => {
  135. before(async () => {
  136. let agent = chai.request.agent(server)
  137. await agent
  138. .post('/api/v1/user/adminaccount/login')
  139. .set('content-type', 'application/json')
  140. .send({ password: 'password' })
  141. await agent
  142. .post('/api/v1/category')
  143. .set('content-type', 'application/json')
  144. .send({ name: 'another_category' })
  145. await agent
  146. .post('/api/v1/category')
  147. .set('content-type', 'application/json')
  148. .send({ name: 'category with spaces' })
  149. })
  150. it('should return all categories', async () => {
  151. let res = await chai.request(server)
  152. .get('/api/v1/category')
  153. res.should.be.json
  154. res.should.have.status(200)
  155. res.body.should.contain.an.item.with.property('name', 'category')
  156. res.body.should.contain.an.item.with.property('name', 'another_category')
  157. res.body.should.contain.an.item.with.property('name', 'category with spaces')
  158. })
  159. })
  160. describe('GET /category/:category', () => {
  161. it('should return allow pagination for category ALL', async () => {
  162. let agent = chai.request.agent(server)
  163. await agent
  164. .post('/api/v1/user/adminaccount/login')
  165. .set('content-type', 'application/json')
  166. .send({ password: 'password' })
  167. await agent
  168. .post('/api/v1/category')
  169. .set('content-type', 'application/json')
  170. .send({ name: 'pagination1' })
  171. await agent
  172. .post('/api/v1/category')
  173. .set('content-type', 'application/json')
  174. .send({ name: 'pagination2' })
  175. for(var i = 0; i < 30; i++) {
  176. let category = 'pagination1'
  177. if(i % 2) category = 'pagination2'
  178. let thread = await agent
  179. .post('/api/v1/thread')
  180. .set('content-type', 'application/json')
  181. .send({ name: `THREAD ${i}`, category })
  182. await agent
  183. .post('/api/v1/post')
  184. .set('content-type', 'application/json')
  185. .send({ content: `POST ${i}`, threadId: thread.body.id })
  186. }
  187. let pageOne = await agent.get('/api/v1/category/ALL')
  188. let pageTwo = await agent.get(pageOne.body.meta.nextURL)
  189. let pageThree = await agent.get(pageTwo.body.meta.nextURL)
  190. pageOne.body.Threads.should.have.length(10)
  191. pageOne.body.meta.should.have.property('nextThreadsCount', 10)
  192. pageOne.body.Threads[0].Posts[0].should.have.property('content', '<p>POST 29</p>\n')
  193. pageTwo.body.Threads.should.have.length(10)
  194. pageTwo.body.meta.should.have.property('nextThreadsCount', 10)
  195. pageTwo.body.Threads[0].Posts[0].should.have.property('content', '<p>POST 19</p>\n')
  196. pageThree.body.Threads.should.have.length(10)
  197. pageThree.body.meta.should.have.property('nextThreadsCount', 0)
  198. pageThree.body.Threads[0].Posts[0].should.have.property('content', '<p>POST 9</p>\n')
  199. pageThree.body.Threads[9].Posts[0].should.have.property('content', '<p>POST 0</p>\n')
  200. expect(pageThree.body.meta.nextURL).to.be.null
  201. })
  202. it('should return all threads in a category', async () => {
  203. let agent = chai.request.agent(server)
  204. await agent
  205. .post('/api/v1/user/adminaccount/login')
  206. .set('content-type', 'application/json')
  207. .send({ password: 'password' })
  208. for(var i = 0; i < 3; i++) {
  209. let thread = await agent
  210. .post('/api/v1/thread')
  211. .set('content-type', 'application/json')
  212. .send({ name: 'thread ' + i, category: 'category' })
  213. await agent
  214. .post('/api/v1/post')
  215. .set('content-type', 'application/json')
  216. .send({ content: 'content here ' + i, threadId: thread.body.id })
  217. }
  218. let res = await chai.request(server)
  219. .get('/api/v1/category/CATEGORY')
  220. res.should.be.json
  221. res.should.have.status(200)
  222. res.body.should.have.property('name', 'category')
  223. res.body.Threads.should.have.property('length', 3)
  224. res.body.Threads.should.contain.an.item.with.deep.property('User.username', 'adminaccount')
  225. res.body.Threads[0].Posts[0].should.have.property('content', '<p>content here 2</p>\n')
  226. res.body.Threads[1].Posts[0].should.have.property('content', '<p>content here 1</p>\n')
  227. res.body.Threads[2].Posts[0].should.have.property('content', '<p>content here 0</p>\n')
  228. res.body.Threads.should.contain.an.item.with.deep.property('Posts.0.User.username', 'adminaccount')
  229. })
  230. it('should return all threads in a category with spaces', async () => {
  231. let agent = chai.request.agent(server)
  232. await agent
  233. .post('/api/v1/user/adminaccount/login')
  234. .set('content-type', 'application/json')
  235. .send({ password: 'password' })
  236. let thread = await agent
  237. .post('/api/v1/thread')
  238. .set('content-type', 'application/json')
  239. .send({ name: 'thread', category: 'CATEGORY_WITH_SPACES' })
  240. await agent
  241. .post('/api/v1/post')
  242. .set('content-type', 'application/json')
  243. .send({ content: 'content here', threadId: thread.body.id })
  244. let res = await chai.request(server)
  245. .get('/api/v1/category/CATEGORY_WITH_SPACES')
  246. res.should.be.json
  247. res.should.have.status(200)
  248. res.body.should.have.property('name', 'category with spaces')
  249. res.body.Threads.should.have.property('length', 1)
  250. res.body.Threads.should.contain.an.item.with.deep.property('User.username', 'adminaccount')
  251. res.body.Threads[0].Posts[0].should.have.property('content', '<p>content here</p>\n')
  252. res.body.Threads.should.contain.an.item.with.deep.property('Posts.0.User.username', 'adminaccount')
  253. })
  254. it('should return an error if category does not exist', async () => {
  255. try {
  256. let res = await chai.request(server)
  257. .get('/api/v1/category/not_real')
  258. res.should.be.json
  259. res.should.have.status(400)
  260. res.body.errors.should.contain.something.that.deep.equals(Errors.invalidParameter('id', 'category does not exist'))
  261. } catch (res) {
  262. let body = JSON.parse(res.response.text)
  263. res.should.have.status(400)
  264. body.errors.should.contain.something.that.deep.equals(Errors.invalidParameter('id', 'category does not exist'))
  265. }
  266. })
  267. })
  268. describe('PUT /category/:category_id', () => {
  269. let admin = chai.request.agent(server)
  270. before(done => {
  271. admin
  272. .post('/api/v1/user/adminaccount/login')
  273. .set('content-type', 'application/json')
  274. .send({ password: 'password' })
  275. .end((err, res) => {
  276. done()
  277. })
  278. })
  279. it('should update a category', async () => {
  280. let res = await admin
  281. .put('/api/v1/category/1')
  282. .set('content-type', 'application/json')
  283. .send({
  284. name: 'new category name',
  285. color: '#8ae6f2'
  286. })
  287. res.should.have.status(200)
  288. res.should.be.json
  289. res.body.should.have.property('name', 'new category name')
  290. res.body.should.have.property('color', '#8ae6f2')
  291. let categoryUpdated = await Category.findById(1)
  292. categoryUpdated.should.have.property('name', 'new category name')
  293. categoryUpdated.should.have.property('color', '#8ae6f2')
  294. })
  295. it('should update a category with only one param', async () => {
  296. let res = await admin
  297. .put('/api/v1/category/1')
  298. .set('content-type', 'application/json')
  299. .send({
  300. name: 'new category name2',
  301. })
  302. let categoryUpdated = await Category.findById(1)
  303. categoryUpdated.should.have.property('name', 'new category name2')
  304. categoryUpdated.should.have.property('color', '#8ae6f2')
  305. })
  306. it('should return an error if not admin', done => {
  307. chai.request(server)
  308. .put('/api/v1/category/1')
  309. .set('content-type', 'application/json')
  310. .send({
  311. name: 'new name here again',
  312. color: '#fffff'
  313. })
  314. .end((err, res) => {
  315. res.should.be.json
  316. res.should.have.status(401)
  317. res.body.errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
  318. done()
  319. })
  320. })
  321. it('should return an error if not valid id', done => {
  322. admin
  323. .put('/api/v1/category/notavalidid')
  324. .set('content-type', 'application/json')
  325. .send({
  326. name: 'new category name',
  327. color: '#8ae6f2'
  328. })
  329. .end((err, res) => {
  330. res.should.be.json
  331. res.should.have.status(400)
  332. res.body.errors.should.contain.something.with.property('message', 'category id is not valid')
  333. done()
  334. })
  335. })
  336. it('should return an error if invalid types', done => {
  337. admin
  338. .put('/api/v1/category/2')
  339. .set('content-type', 'application/json')
  340. .send({
  341. name: 123,
  342. color: 456
  343. })
  344. .end((err, res) => {
  345. res.should.be.json
  346. res.should.have.status(400)
  347. res.body.errors.should.contain.something.with.property('message', 'The color must be a string')
  348. res.body.errors.should.contain.something.with.property('message', 'The category name must be a string')
  349. done()
  350. })
  351. })
  352. })
  353. describe('DELETE /category/:category_id', () => {
  354. let admin = chai.request.agent(server)
  355. let categoryId, thread1Id, thread2Id
  356. before(done => {
  357. admin
  358. .post('/api/v1/user/adminaccount/login')
  359. .set('content-type', 'application/json')
  360. .send({ password: 'password' })
  361. .then(res => {
  362. return admin
  363. .post('/api/v1/category')
  364. .set('content-type', 'application/json')
  365. .send({ name: 'category_to_delete' })
  366. })
  367. .then(res => {
  368. categoryId = res.body.id
  369. return admin
  370. .post('/api/v1/thread')
  371. .set('content-type', 'application/json')
  372. .send({ name: 'thread1', category: 'category_to_delete' })
  373. })
  374. .then(res => {
  375. thread1Id = res.body.id
  376. return admin
  377. .post('/api/v1/thread')
  378. .set('content-type', 'application/json')
  379. .send({ name: 'thread2', category: 'category_to_delete' })
  380. })
  381. .then(res => {
  382. thread2Id = res.body.id
  383. done()
  384. })
  385. .catch(e => {
  386. console.log(e)
  387. done(e)
  388. })
  389. })
  390. it('should delete a category and place all threads in that category into "Other"', async () => {
  391. let res = await admin.delete('/api/v1/category/' + categoryId)
  392. res.should.be.json
  393. res.should.have.status(200)
  394. let category = await Category.findById(categoryId)
  395. expect(category).to.be.null
  396. let thread1 = await Thread.findById(thread1Id, {
  397. include: [Category]
  398. })
  399. let thread2 = await Thread.findById(thread2Id, {
  400. include: [Category]
  401. })
  402. thread1.Category.should.have.property('name', 'Other')
  403. thread2.Category.should.have.property('name', 'Other')
  404. })
  405. it('should return an error if not an admin', done => {
  406. chai.request(server)
  407. .delete('/api/v1/category/1')
  408. .end((err, res) => {
  409. res.should.be.json
  410. res.should.have.status(401)
  411. res.body.errors.should.contain.something.that.deep.equals(Errors.requestNotAuthorized)
  412. done()
  413. })
  414. })
  415. it('should return an error if invalid id', done => {
  416. admin
  417. .delete('/api/v1/category/notavalidid')
  418. .end((err, res) => {
  419. res.should.be.json
  420. res.should.have.status(400)
  421. res.body.errors.should.contain.something.with.property('message', 'category id does not exist')
  422. done()
  423. })
  424. })
  425. })
  426. })