Quality Assurance Projects - Personal Library

Tell us what’s happening:

I am trying to set the connection string to the MongoDB database, but I have problems with that so far.

The connection string in the description looks like this

DB=mongodb://admin:pass@1234.mlab.com:1234/fccpersonallib

However, from mongodb.com I get a connection string that looks like this

mongodb+srv://<db_username>:<db_password>@cluster0.<something>.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0

I tried with the latter but I receive in all cases the error message

[Error: Error saving book]

Any suggestions?

Your code so far

If it helps I can also share the code I tried to produce so far, but I figured that this is probably an issue that is before the actual code is executed.

Thank you very much!

Your browser information:

User Agent is: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/128.0.0.0 Safari/537.36

Challenge Information:

Quality Assurance Projects - Personal Library

Post your code. A repo would be a good option.

I would assume you would get a connection error before the save error if that was the issue.


The connection string in the description is just an old example from before Atlas. All the other challenges in this section doesn’t even have connection instructions, as that is by now something you should know how to do.

Thank you for your help.

I surprisingly did not have an issue earlier to connect to the database.

I edited the following files for this project:

.env

PORT=3000
NODE_ENV=test
DB='mongodb+srv://<DB_USER>:<DB_PW>@cluster0.<CLUSTER_ID>.mongodb.net/?retryWrites=true&w=majority&appName=Cluster0'

routes/api.js

/*
*
*
*       Complete the API routing below
*       
*       
*/

'use strict';

module.exports = function (app) {

  app.route('/api/books')
    .get(async function (req, res) {
      try {
        const books = await Book.find({}, '_id title comments');
        const formattedBooks = books.map(book => ({
          _id: book._id,
          title: book.title,
          commentcount: book.comments.length
        }));
        res.json(formattedBooks);
      } catch (err) {
        res.status(500).send('Error fetching books');
      }
    })

    
    .post(async function (req, res) {
      const title = req.body.title;
    
      if (!title) {
        return res.status(200).send('missing required field title');
      }
    
      try {
        const newBook = new Book({ title, comments: [] });
        await newBook.save();
        res.json({ _id: newBook._id, title: newBook.title });
      } catch (err) {
        res.status(500).send('Error saving book');
      }
    }) 
  
    
    .delete(async function (req, res) {
      try {
        await Book.deleteMany({});
        res.send('complete delete successful');
      } catch (err) {
        res.status(500).send('Error deleting books');
      }
    });
    



    app.route('/api/books/:id')
      .get(async function (req, res) {
        const bookid = req.params.id;
        try {
          const book = await Book.findById(bookid);
          if (!book) {
            return res.send('no book exists');
          }
          res.json({ _id: book._id, title: book.title, comments: book.comments });
        } catch (err) {
          res.send('no book exists');
        }
      })
  
    
      .post(async function (req, res) {
        const bookid = req.params.id;
        const comment = req.body.comment;
        if (!comment) {
          return res.send('missing required field comment');
        }
        try {
          const book = await Book.findById(bookid);
          if (!book) {
            return res.send('no book exists');
          }
          book.comments.push(comment);
          await book.save();
          res.json({ _id: book._id, title: book.title, comments: book.comments });
        } catch (err) {
          res.send('no book exists');
        }
      })
      
    
      .delete(async function (req, res) {
        const bookid = req.params.id;
        try {
          const book = await Book.findByIdAndDelete(bookid);
          if (!book) {
            return res.send('no book exists');
          }
          res.send('delete successful');
        } catch (err) {
          res.send('no book exists');
        }
      });
      
  
};

tests/2_functional-tests.js

/*
*
*
*       FILL IN EACH FUNCTIONAL TEST BELOW COMPLETELY
*       -----[Keep the tests in the same order!]-----
*       
*/

const chaiHttp = require('chai-http');
const chai = require('chai');
const assert = chai.assert;
const server = require('../server');

chai.use(chaiHttp);

suite('Functional Tests', function() {

  /*
  * ----[EXAMPLE TEST]----
  * Each test should completely test the response of the API end-point including response status code!
  */
  test('#example Test GET /api/books', function(done){
     chai.request(server)
      .get('/api/books')
      .end(function(err, res){
        assert.equal(res.status, 200);
        assert.isArray(res.body, 'response should be an array');
        assert.property(res.body[0], 'commentcount', 'Books in array should contain commentcount');
        assert.property(res.body[0], 'title', 'Books in array should contain title');
        assert.property(res.body[0], '_id', 'Books in array should contain _id');
        done();
      });
  });
  /*
  * ----[END of EXAMPLE TEST]----
  */

  suite('Routing tests', function() {


    suite('POST /api/books with title => create book object/expect book object', function() {
      
      test('Test POST /api/books with title', function(done) {
        chai.request(server)
          .post('/api/books')
          .send({ title: 'Book Title' })
          .end(function(err, res) {
            assert.equal(res.status, 200);
            assert.equal(res.body.title, 'Book Title');
            assert.property(res.body, '_id');
            done();
          });
      });
      
      
      test('Test POST /api/books with no title given', function(done) {
        chai.request(server)
          .post('/api/books')
          .send({})
          .end(function(err, res) {
            assert.equal(res.status, 200);
            assert.equal(res.text, 'missing required field title');
            done();
          });
      });
      
      
    });


    suite('GET /api/books => array of books', function(){
      
      test('Test GET /api/books', function(done) {
        chai.request(server)
          .get('/api/books')
          .end(function(err, res) {
            assert.equal(res.status, 200);
            assert.isArray(res.body);
            done();
          });
      });
        
      
    });


    suite('GET /api/books/[id] => book object with [id]', function(){
      
      test('Test GET /api/books/[id] with id not in db', function(done) {
        chai.request(server)
          .get('/api/books/invalidId')
          .end(function(err, res) {
            assert.equal(res.status, 200);
            assert.equal(res.text, 'no book exists');
            done();
          });
      });
      
      
      test('Test POST /api/books/[id] with comment', function(done) {
        chai.request(server)
          .post('/api/books/validBookId')
          .send({ comment: 'New Comment' })
          .end(function(err, res) {
            assert.equal(res.status, 200);
            assert.isArray(res.body.comments);
            assert.include(res.body.comments, 'New Comment');
            done();
          });
      });
      
      
    });


    suite('POST /api/books/[id] => add comment/expect book object with id', function(){
      
      test('Test POST /api/books/[id] with comment', function(done){
        chai.request(server)
          .post('/api/books/validBookId')
          .send({ comment: 'New Comment' })
          .end(function(err, res) {
            assert.equal(res.status, 200);
            assert.property(res.body, '_id', 'Book should contain _id');
            assert.property(res.body, 'title', 'Book should contain title');
            assert.isArray(res.body.comments, 'Comments should be an array');
            assert.include(res.body.comments, 'New Comment', 'Comments array should include the new comment');
            done();
          });
      });

      test('Test POST /api/books/[id] without comment field', function(done){
        chai.request(server)
          .post('/api/books/validBookId')
          .send({})
          .end(function(err, res){
            assert.equal(res.status, 200);
            assert.equal(res.text, 'missing required field comment');
            done();
          });
      });

      test('Test POST /api/books/[id] with comment, id not in db', function(done){
        chai.request(server)
          .post('/api/books/invalidId')
          .send({ comment: 'New Comment' })
          .end(function(err, res){
            assert.equal(res.status, 200);
            assert.equal(res.text, 'no book exists');
            done();
          });
      });
      
    });

    suite('DELETE /api/books/[id] => delete book object id', function() {

      test('Test DELETE /api/books/[id] with valid id in db', function(done){
        chai.request(server)
          .delete('/api/books/validBookId')
          .end(function(err, res){
            assert.equal(res.status, 200);
            assert.equal(res.text, 'delete successful');
            done();
          });
      });

      test('Test DELETE /api/books/[id] with id not in db', function(done){
        chai.request(server)
          .delete('/api/books/invalidId')
          .end(function(err, res){
            assert.equal(res.status, 200);
            assert.equal(res.text, 'no book exists');
            done();
          });
      });

    });

  });

});

and here are my test results

Running Tests...


  Functional Tests
    1) #example Test GET /api/books
    Routing tests
      POST /api/books with title => create book object/expect book object
        2) Test POST /api/books with title
        ✓ Test POST /api/books with no title given
      GET /api/books => array of books
        3) Test GET /api/books
      GET /api/books/[id] => book object with [id]
        ✓ Test GET /api/books/[id] with id not in db
        4) Test POST /api/books/[id] with comment
      POST /api/books/[id] => add comment/expect book object with id
        5) Test POST /api/books/[id] with comment
        ✓ Test POST /api/books/[id] without comment field
        ✓ Test POST /api/books/[id] with comment, id not in db
      DELETE /api/books/[id] => delete book object id
        6) Test DELETE /api/books/[id] with valid id in db
        ✓ Test DELETE /api/books/[id] with id not in db


  5 passing (70ms)
  6 failing

  1) Functional Tests #example Test GET /api/books:

      Uncaught AssertionError: expected 500 to equal 200
      + expected - actual

      -500
      +200
      
      at tests/2_functional-tests.js:26:16
      at Request.callback (node_modules/superagent/lib/node/index.js:716:12)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:18)
      at IncomingMessage.emit (node:events:531:35)
      at endReadableNT (node:internal/streams/readable:1696:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

  2) Functional Tests Routing tests POST /api/books with title => create book object/expect book object Test POST /api/books with title:

      Uncaught AssertionError: expected 500 to equal 200
      + expected - actual

      -500
      +200
      
      at tests/2_functional-tests.js:48:20
      at Request.callback (node_modules/superagent/lib/node/index.js:716:12)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:18)
      at IncomingMessage.emit (node:events:531:35)
      at endReadableNT (node:internal/streams/readable:1696:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

  3) Functional Tests Routing tests GET /api/books => array of books Test GET /api/books:

      Uncaught AssertionError: expected 500 to equal 200
      + expected - actual

      -500
      +200
      
      at tests/2_functional-tests.js:77:20
      at Request.callback (node_modules/superagent/lib/node/index.js:716:12)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:18)
      at IncomingMessage.emit (node:events:531:35)
      at endReadableNT (node:internal/streams/readable:1696:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

  4) Functional Tests Routing tests GET /api/books/[id] => book object with [id] Test POST /api/books/[id] with comment:
     Uncaught AssertionError: expected undefined to be an array
      at tests/2_functional-tests.js:106:20
      at Request.callback (node_modules/superagent/lib/node/index.js:716:12)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:18)
      at IncomingMessage.emit (node:events:531:35)
      at endReadableNT (node:internal/streams/readable:1696:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

  5) Functional Tests Routing tests POST /api/books/[id] => add comment/expect book object with id Test POST /api/books/[id] with comment:
     Uncaught AssertionError: Book should contain _id: expected {} to have property '_id'
      at tests/2_functional-tests.js:124:20
      at Request.callback (node_modules/superagent/lib/node/index.js:716:12)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:18)
      at IncomingMessage.emit (node:events:531:35)
      at endReadableNT (node:internal/streams/readable:1696:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

  6) Functional Tests Routing tests DELETE /api/books/[id] => delete book object id Test DELETE /api/books/[id] with valid id in db:

      Uncaught AssertionError: expected 'no book exists' to equal 'delete successful'
      + expected - actual

      -no book exists
      +delete successful
      
      at tests/2_functional-tests.js:163:20
      at Request.callback (node_modules/superagent/lib/node/index.js:716:12)
      at IncomingMessage.<anonymous> (node_modules/superagent/lib/node/index.js:916:18)
      at IncomingMessage.emit (node:events:531:35)
      at endReadableNT (node:internal/streams/readable:1696:12)
      at process.processTicksAndRejections (node:internal/process/task_queues:82:21)

Thank you very much for your help!

Please provide your connection code and Mongoose schema. It would be easier if you made a repo for it.

I don’t see you importing the schema in the API file?

Not sure why I forgot it. I restarted the program fresh and could solve it in the second attempt.