segunolalive/helloBooks

View on GitHub
server/test/controllers/bookController.spec.js

Summary

Maintainability
A
0 mins
Test Coverage
import supertest from 'supertest';
import { assert } from 'chai';
import sinon from 'sinon';
import app from '../../app';
import mock from '../mock';
import { Book, BorrowedBook } from '../../models';


const server = supertest.agent(app);
let jwtToken;

describe('Book Controller', () => {
  before((done) => {
    server
      .post('/api/v1/users/signin')
      .send(mock.adminUser)
      .end((err, res) => {
        jwtToken = res.body.token;
        done();
      });
  });
  describe('#getBooks', () => {
    it('should be allow users to view all books', (done) => {
      server
        .get('/api/v1/books')
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert(Array.isArray(res.body.books));
          assert(res.body.books[0].title, 'eloquent javascript');
          assert(res.body.books[0].authors, 'marijn haverbeke');
          assert(res.body.books[0].total, 20);
          assert(res.body.books[0].categoryId, 1);
          assert(res.body.books.length, 4);
          done();
        });
    });
    it('should be allow users to view all books under a category', (done) => {
      server
        .get('/api/v1/books?categoryId=1')
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert(Array.isArray(res.body.books));
          assert(res.body.books[0].title, 'eloquent javascript');
          assert(res.body.books[0].authors, 'marijn haverbeke');
          assert(res.body.books[0].total, 20);
          assert(res.body.books[0].categoryId, 1);
          assert(res.body.books[1].title, 'eloquent ruby');
          assert(res.body.books[1].categoryId, 1);
          assert(res.body.books[1].title, 'eloquent fish');
          assert(res.body.books[2].categoryId, 1);
          assert(res.body.books.length, 3);
          done();
        });
    });
    it('should be allow users search for books', (done) => {
      server
        .get('/api/v1/books?search=fish')
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert(Array.isArray(res.body.books));
          assert.include(res.body.books[0].title, 'fish');
          assert.include(res.body.books[0].authors, 'flo harrison, dan fish');
          assert.include(res.body.books[0].description, 'feel the fish');
          done();
        });
    });
    it('should return a message when search for books returns no match',
      (done) => {
        server
          .get('/api/v1/books?search=gibberish')
          .expect(200)
          .end((err, res) => {
            assert.equal(res.status, 200);
            assert(Array.isArray(res.body.books));
            assert.equal(res.body.books.length, 0);
            assert.equal(res.body.message, 'No book matched your request');
            done();
          });
      });
  });

  describe('#getBook', () => {
    it('should allow users to view a book\'s information', (done) => {
      server
        .get('/api/v1/books/1')
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert(res.body.book.title, 'eloquent javascript');
          assert(res.body.book.authors, 'marijn haverbeke');
          assert(res.body.book.total, 'a modern introduction to programming');
          assert(res.body.book.description, 20);
          assert(res.body.book.categoryId, 1);
          done();
        });
    });
    it('should send a 404 status if book is not found', (done) => {
      server
        .get('/api/v1/books/200')
        .expect(404)
        .end((err, res) => {
          assert.equal(res.status, 404);
          assert.equal(res.body.message, 'Book not found');
          done();
        });
    });
  });
  describe('#borrowBook', () => {
    it('should allow users borrow book', (done) => {
      server
        .post('/api/v1/users/1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ id: 4 })
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert.equal(res.body.message,
            'You have successfully borrowed eloquent fish again. Check your dashboard to read it'
          );
          done();
        });
    });

    it('should allow users borrow book again after returning', (done) => {
      server
        .post('/api/v1/users/1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ id: 2 })
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert.equal(res.body.message,
            'You have successfully borrowed eloquent ruby again. Check your dashboard to read it'
          );
          done();
        });
    });
    it('should send a 404 status code if book does not exist', (done) => {
      server
        .post('/api/v1/users/1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ id: 200 })
        .expect(404)
        .end((err, res) => {
          assert.equal(res.status, 404);
          assert.equal(res.body.message, 'Book not found');
          done();
        });
    });
    it('should send message if there are no available copies', (done) => {
      server
        .post('/api/v1/users/1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ id: 3 })
        .expect(404)
        .end((err, res) => {
          assert.equal(res.status, 404);
          assert.equal(
            res.body.message,
            'There are no available copies of this book at this time');
          done();
        });
    });
    it('should prevent user from borrowing book multiple times unless previously returned',
      (done) => {
        server
          .post('/api/v1/users/1/books')
          .set('X-ACCESS-TOKEN', jwtToken)
          .send({ id: 1 })
          .expect(200)
          .end((err, res) => {
            assert.equal(res.status, 403);
            assert.equal(res.body.message,
              'You currently have this book. Return it before trying to borrow it again',
            );
            done();
          });
      });
  });
  describe('#returnBook', () => {
    it('should allow logged in users return borrowed book', (done) => {
      const id = 1;
      server
        .put('/api/v1/users/1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ id })
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert.equal(res.body.message,
            'You have successfully returned eloquent javascript');
          server
            .get('/api/v1/users/1/books?returned=false')
            .set('X-ACCESS-TOKEN', jwtToken)
            .end((err, res) => {
              const ids = res.body.books.map(book => book.id);
              assert.notInclude(ids, id);
              done();
            });
        });
    });
    it('should send an error message if user tries to return a book they don\'t currently have', (done) => {
      server
        .put('/api/v1/users/1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ id: 1 })
        .expect(403)
        .end((err, res) => {
          assert.equal(res.status, 403);
          assert.equal(res.body.message, 'This book is currently not on your list. You have either returned it or never borrowed it');
          done();
        });
    });
  });
  describe('#editBookInfo', () => {
    it('should allow admin user modify book info', (done) => {
      server
        .put('/api/v1/books/1')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ title: 'Learn Rust' })
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert.equal(res.body.book.title, 'Learn Rust');
          assert.equal(res.body.book.authors, 'marijn haverbeke');
          done();
        });
    });
    it('should send a message if no update data is sent', (done) => {
      server
        .put('/api/v1/books/1')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({})
        .expect(400)
        .end((err, res) => {
          assert.equal(res.status, 400);
          assert.equal(res.body.book, undefined);
          assert.equal(res.body.message, 'Nothing to update');
          done();
        });
    });
    it('should send a 404 when attrempting to update a non-existent book book info',
      (done) => {
        server
          .put('/api/v1/books/1900000')
          .set('X-ACCESS-TOKEN', jwtToken)
          .send({ title: 'Learn Rust' })
          .end((err, res) => {
            assert.equal(res.status, 404);
            assert.equal(res.body.message, 'Book not found');
            done();
          });
      });
  });

  describe('#createBook', () => {
    it('should allow user add book to library', (done) => {
      server.post('/api/v1/books')
        .set('X-ACCESS-TOKEN', jwtToken)
        .send({ ...mock.addBook })
        .expect(201)
        .end((err, res) => {
          assert.equal(res.status, 201);
          assert.equal(res.body.message,
            'Successfully added eloquent testing to Library');
          assert(res.body.book.title, 'eloquent testing');
          done();
        });
    });
  });

  describe('#deleteBook', () => {
    it('should allow user delete book', (done) => {
      server
        .delete('/api/v1/books/3')
        .set('X-ACCESS-TOKEN', jwtToken)
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert.equal(res.body.message,
            'Successfully deleted book from database');
          server.get('/api/v1/books/3')
            .end((err, res) => {
              assert.equal(res.status, 404);
              assert.equal(res.body.message, 'Book not found');
            });
          done();
        });
    });
  });

  describe('#suggestedBooks', () => {
    it('should send an array of book suggestions', (done) => {
      server
        .get('/api/v1/books/suggestions')
        .set('X-ACCESS-TOKEN', jwtToken)
        .expect(200)
        .end((err, res) => {
          assert.equal(res.status, 200);
          assert(Array.isArray(res.body.suggestions));
          assert.containsAllKeys(res.body.suggestions[0],
            ['id', 'title', 'cover']);
          done();
        });
    });
  });

  describe('Book Category', () => {
    describe('#getBookCategories', () => {
      it('should return a list of book categories', (done) => {
        server
          .get('/api/v1/books/category')
          .expect(200)
          .end((err, res) => {
            const { categories } = res.body;
            assert.equal(res.status, 200);
            assert.equal(Array.isArray(categories), true);
            assert.equal(categories[0].id, 1);
            assert.equal(categories[0].category, 'javascript');
            assert.equal(categories[1].id, 2);
            assert.equal(categories[1].category, 'ruby');
            done();
          });
      });
    });
    describe('#addBookCategory', () => {
      before((done) => {
        server
          .post('/api/v1/users/signin')
          .send(mock.adminUser)
          .end((err, res) => {
            jwtToken = res.body.token;
            done();
          });
      });
      it('should allow adding of book category', (done) => {
        server
          .post('/api/v1/books/category')
          .set('X-ACCESS-TOKEN', jwtToken)
          .send({ category: 'Rust' })
          .expect(201)
          .end((err, res) => {
            assert.equal(res.status, 201);
            assert.equal(res.body.message,
              'Successfully added new category, Rust, to Library');
            assert.equal(res.body.category.category, 'Rust');
            server
              .get('/api/v1/books/category')
              .expect(200)
              .end((err, res) => {
                const { categories } = res.body;
                const newestCategory = categories[categories.length - 1];
                assert.equal(newestCategory.category, 'Rust');
                done();
              });
          });
      });
    });
  });
});


describe('Book Server Errors', () => {
  before((done) => {
    server
      .post('/api/v1/users/signin')
      .send(mock.adminUser)
      .end((err, res) => {
        jwtToken = res.body.token;
        done();
      });
  });
  let sandbox;
  beforeEach(() => {
    sandbox = sinon.sandbox.create();
  });
  afterEach(() => {
    sandbox = sandbox.restore();
  });
  describe('#borrowBook', () => {
    it('should send a message if an error occurs while looking up the book',
      (done) => {
        const stub = sandbox.stub(BorrowedBook, 'findOne');
        stub.rejects();
        server
          .post('/api/v1/users/1/books')
          .set('X-ACCESS-TOKEN', jwtToken)
          .send({ id: 1 })
          .expect(500)
          .end((err, res) => {
            assert.equal(res.status, 500);
            assert.equal(res.body.message,
              'Something went wrong. Internal server error');
            done();
          });
      });
  });
  describe('#deleteBook', () => {
    it('should send a message if an error occurs while deleting book',
      (done) => {
        const stub = sandbox.stub(Book, 'destroy');
        stub.rejects();
        server
          .delete('/api/v1/books/1')
          .set('X-ACCESS-TOKEN', jwtToken)
          .send({ id: 4 })
          .expect(500)
          .end((err, res) => {
            assert.equal(res.status, 500);
            assert.equal(res.body.message,
              'Something went wrong. Internal server error');
            done();
          });
      });
  });
  describe('createBook', () => {
    it('should send a message if an error occurs while adding book',
      (done) => {
        const stub = sandbox.stub(Book, 'create');
        stub.rejects();
        server.post('/api/v1/books')
          .set('X-ACCESS-TOKEN', jwtToken)
          .send({ ...mock.addBook, title: 'failing' })
          .expect(500)
          .end((err, res) => {
            assert.equal(res.status, 500);
            assert.equal(res.body.message,
              'Something went wrong. Internal server error');
            done();
          });
      });
  });
  describe('getBook', () => {
    it('should send a message if an error occurs while fetching book',
      (done) => {
        const stub = sandbox.stub(Book, 'findById');
        stub.rejects();
        server.get('/api/v1/books/1')
          .expect(500)
          .end((err, res) => {
            assert.equal(res.status, 500);
            assert.equal(res.body.message,
              'Something went wrong. Internal server error');
            done();
          });
      });
  });
});