Tell us what’s happening:
Quality Assurance Projects - Issue Tracker is failing put and delete requests, when manually testing the app works fine and all functional tests pass.
Under both functions i have commented the request bodys i’m getting from freecodecamp tests and the responses i’m sending.
Your code so far
server.js
'use strict';
const express = require('express');
const bodyParser = require('body-parser');
const expect = require('chai').expect;
const cors = require('cors');
require('dotenv').config();
const apiRoutes = require('./routes/api.js');
const fccTestingRoutes = require('./routes/fcctesting.js');
const runner = require('./test-runner');
let app = express();
app.use('/public', express.static(process.cwd() + '/public'));
app.use(cors({origin: '*'})); //For FCC testing purposes only
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));
//-------------------------------------
// prevent MIME sniffing
app.use((req, res, next) => {
req.header('X-Content-Type-Options', 'nosniff');
next();
});
// connect to database
const MongoClient = require('mongodb').MongoClient;
const MongoServer = require('mongodb').Server;
async function connectDB(callback) {
const URI = process.env.DB;
const client = new MongoClient(URI, {useNewUrlParser: true, useUnifiedTopology: true});
try{
await client.connect();
await callback(client);
} catch(e) {
//console.error(e);
throw new Error('Unable to Connect to Database');
}
}
// routing
connectDB(async function(client) {
const dataBase = await client.db('database');
//Sample front-end
app.route('/:project/')
.get(function (req, res) {
res.sendFile(process.cwd() + '/views/issue.html');
}
);
//Index page (static HTML)
app.route('/')
.get(function (req, res) {
res.sendFile(process.cwd() + '/views/index.html');
}
);
//For FCC testing purposes
fccTestingRoutes(app);
//Routing for API
apiRoutes(app, dataBase);
//404 Not Found Middleware
app.use(function(req, res, next) {
res.status(404).type('text').send('NOT FOUND');
});
}).catch((e) => {
app.route('/')
.get(function (req, res) {
res.status(500).type('text').send('INTERNAL SERVER ERROR');
}
);
});
//-------------------------------------
//Start our server and tests!
app.listen(process.env.PORT || 3000, function () {
console.log("Listening on port " + process.env.PORT);
if(process.env.NODE_ENV==='test') {
console.log('Running Tests...');
setTimeout(function () {
try {
runner.run();
} catch(e) {
let error = e;
console.log('Tests are not valid:');
console.log('error: '+error);
}
}, 3500);
}
});
module.exports = app; //for testing
api.js
/*
*
*
* Complete the API routing below
*
*
*/
'use strict';
const ObjectID = require('mongodb').ObjectID;
module.exports = function (app, dataBase) {
app.route('/api/issues/:project')
.get(function (req, res){
// get issue
let projectCollection = dataBase.collection(req.params.project);
// TODO: implement filters
let query = {};
if(req.query._id) {query._id = new ObjectID(req.query._id);}
if(req.query.issue_title) {query.issue_title = req.query.issue_title;}
if(req.query.issue_text) {query.issue_text = req.query.issue_text;}
if(req.query.created_on) {query.created_on = new Date(req.query.created_on);}
if(req.query.updated_on) {query.updated_on = new Date(req.query.updated_on);}
if(req.query.created_by) {query.created_by = req.query.created_by;}
if(req.query.assigned_to) {query.assigned_to = req.query.assigned_to;}
if(req.query.open) {query.open = req.query.open;}
if(req.query.status_text) {query.status_text = req.query.status_text;}
projectCollection
.find(query)
.toArray()
.then(result => {
return res.status(200).json(result);
})
.catch(error => {
return res.status(200).json({error: error});
});
//
})
.post(function (req, res){
// create issue
if(!req.body.issue_title || !req.body.issue_text || !req.body.created_by) {
return res.status(200).json({error: 'required field(s) missing'})
}
let projectCollection = dataBase.collection(req.params.project);
projectCollection
.insertOne({
issue_title: req.body.issue_title,
issue_text: req.body.issue_text,
created_on: new Date(),
updated_on: new Date(),
created_by: req.body.created_by,
assigned_to: req.body.assigned_to ? req.body.assigned_to : '',
open: req.body.open ? req.body.open : true,
status_text: req.body.status_text ? req.body.status_text : ''
})
.then(result => {
projectCollection
.findOne({_id: result.insertedId})
.then(doc => {
return res.status(200).json(doc);
})
.catch(error => {
return res.status(200).json({error: error});
});
//
})
.catch(error => {
return res.status(200).json({error: error});
});
//
})
.put(function (req, res){
console.log('UPDATE: req.body is: ');
console.log(req.body);
// update issue
if(!req.body._id) {
console.log('UPDATE: missing _id');
return res.status(200).json({error: 'missing _id'});
}
if(req.body._id == '') {
console.log('UPDATE: missing _id');
return res.status(200).json({error: 'missing _id'});
}
if(!req.body.issue_title && !req.body.issue_text && !req.body.created_on && !req.body.updated_on && !req.body.created_by && !req.body.assigned_to && !req.body.open && !req.body.status_text) {
console.log('UPDATE: no update field(s) sent');
return res.status(200).json({error: 'no update field(s) sent', _id: req.body._id});
}
let objID;
try{
objID = new ObjectID(req.body._id);
} catch(e) {
console.log('UPDATE: could not update');
return res.status(200).json({error: 'could not update', _id: req.body._id});
}
let query = {_id: new ObjectID(req.body._id)};
if(req.body.issue_title) {query.issue_title = req.body.issue_title;}
if(req.body.issue_text) {query.issue_text = req.body.issue_text;}
if(req.body.created_on) {query.created_on = new Date(req.body.created_on);}
query.updated_on = new Date();
if(req.body.created_by) {query.created_by = req.body.created_by;}
if(req.body.assigned_to) {query.assigned_to = req.body.assigned_to;}
if(req.body.open) {query.open = false;}
if(req.body.status_text) {query.status_text = req.body.status_text;}
let projectCollection = dataBase.collection(req.params.project);
projectCollection
.findOneAndUpdate({
_id: objID
}, {
$set: query
}, {
upsert: true,
returnOriginal: false
})
.then(result => {
if(!result.value) {
console.log('UPDATE: could not update');
return res.status(200).json({error: 'could not update', _id: req.body._id});
} else {
console.log('UPDATED');
return res.status(200).json({result: "successfully updated", _id: result.value._id});
}
})
.catch(error => {
console.log('UPDATE: could not update');
return res.status(200).json({error: 'could not update', _id: req.body._id});
});
//
})
/*
req.body: { _id: '5fc27f391d7b2403937ef1a0', issue_text: 'New Issue Text' }
RESPONSE: res.status(200).json({result: "successfully updated", _id: result.value._id});
req.body: {}
RESPONSE: res.status(200).json({error: 'missing _id'});
req.body: { _id: '5f665eb46e296f6b9b6a504d' }
RESPONSE: res.status(200).json({error: 'no update field(s) sent', _id: req.body._id});
req.body: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
RESPONSE: res.status(200).json({result: "successfully updated", _id: result.value._id});
*/
.delete(function (req, res){
try{
console.log('DELETED: req.body is: ');
console.log(req.body);
} catch(e) {
console.log('DELETED: req.body is UNDEFINED');
}
// delete issue
if(!req.body._id) {
console.log('DELETED: missing _id');
return res.status(200).json({error: 'missing _id'});
}
let objID;
try{
objID = new ObjectID(req.body._id);
} catch(e) {
console.log('DELETED: could not delete');
return res.status(200).json({error: 'could not delete', _id: req.body._id});
}
// TODO: filters
let query = {_id: new ObjectID(req.body._id)};
if(req.body.issue_title) {query.issue_title = req.body.issue_title;}
if(req.body.issue_text) {query.issue_text = req.body.issue_text;}
if(req.body.created_on) {query.created_on = new Date(req.body.created_on);}
if(req.body.updated_on) {query.updated_on = new Date(req.body.updated_on);}
if(req.body.created_by) {query.created_by = req.body.created_by;}
if(req.body.assigned_to) {query.assigned_to = req.body.assigned_to;}
if(req.body.open) {query.open = req.body.open;}
if(req.body.status_text) {query.status_text = req.body.status_text;}
let projectCollection = dataBase.collection(req.params.project);
projectCollection
.findOneAndDelete(query, {sort: {_id: -1} } )
.then(result => {
if(!result.value) {
console.log('DELETED: could not delete');
return res.status(200).json({error: 'could not delete', _id: req.body._id});
} else {
console.log('DELETED');
return res.status(200).json({result: 'successfully deleted', _id: req.body._id});
}
})
.catch(error => {
console.log('DELETED: could not delete');
return res.status(200).json({error: 'could not delete', _id: req.body._id});
});
//
});
/*
req.body: { _id: '5fc27532eb23b8023f4ae282' }
RESPONSE: res.status(200).json({result: 'successfully deleted', _id: req.body._id});
req.body: {}
RESPONSE: res.status(200).json({error: 'missing _id'});
req.body: { _id: '5f665eb46e296f6b9b6a504d', issue_text: 'New Issue Text' }
RESPONSE: res.status(200).json({result: 'successfully deleted', _id: req.body._id});
*/
};
2_functional-tests.js
/*
*
*
* FILL IN EACH FUNCTIONAL TEST BELOW COMPLETELY
* -----[Keep the tests in the same order!]-----
* (if additional are added, keep them at the very end!)
*/
const chaiHttp = require('chai-http');
const chai = require('chai');
const assert = chai.assert;
const server = require('../server');
chai.use(chaiHttp);
suite('Functional Tests', function() {
let id;
suite('POST /api/issues/{project}', function() {
test('Every field filled in', function(done) {
chai.request(server)
.post('/api/issues/test')
.send({
issue_title: 'Title',
issue_text: 'text',
created_by: 'Functional Test - Every field filled in',
assigned_to: 'Chai and Mocha',
status_text: 'In QA'
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.issue_title, 'Title');
assert.equal(res.body.issue_text, 'text');
assert.equal(res.body.created_by, 'Functional Test - Every field filled in');
assert.equal(res.body.assigned_to, 'Chai and Mocha');
assert.equal(res.body.status_text, 'In QA');
id = res.body._id; // for testing put updates
done();
});
});
test('Required fields filled in, Optional Fields Blank', function(done) {
chai.request(server)
.post('/api/issues/test')
.send({
issue_title: 'Title',
issue_text: 'text',
created_by: 'Functional Test - Every field filled in'
//assigned_to: 'Chai and Mocha',
//status_text: 'In QA'
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.issue_title, 'Title');
assert.equal(res.body.issue_text, 'text');
assert.equal(res.body.created_by, 'Functional Test - Every field filled in');
done();
});
});
test('Missing required fields => { error: "required field(s) missing" }', function(done) {
chai.request(server)
.post('/api/issues/test')
.send({
issue_title: 'Title',
issue_text: 'text',
created_by: ''
//assigned_to: 'Chai and Mocha',
//status_text: 'In QA'
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.error, 'required field(s) missing');
done();
});
});
});
suite('GET /api/issues/{project}', function() {
test('No filter', function(done) {
chai.request(server)
.get('/api/issues/test')
.query({})
.end(function(err, res){
assert.equal(res.status, 200);
assert.isArray(res.body);
assert.property(res.body[0], 'issue_title');
assert.property(res.body[0], 'issue_text');
assert.property(res.body[0], 'created_on');
assert.property(res.body[0], 'updated_on');
assert.property(res.body[0], 'created_by');
assert.property(res.body[0], 'assigned_to');
assert.property(res.body[0], 'open');
assert.property(res.body[0], 'status_text');
assert.property(res.body[0], '_id');
done();
});
});
test('One filter', function(done) {
chai.request(server)
.get('/api/issues/test')
.query({issue_title: 'Title'})
.end(function(err, res){
assert.equal(res.status, 200);
assert.isArray(res.body);
for(let i in res.body) {
assert.equal(res.body[i].issue_title, 'Title');
}
done();
});
});
test('Multiple filters (test for multiple fields you know will be in the db for a return)', function(done) {
chai.request(server)
.get('/api/issues/test')
.query({
issue_title: 'Title',
issue_text: 'text',
created_by: 'Functional Test - Every field filled in',
assigned_to: 'Chai and Mocha',
status_text: 'In QA'
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.isArray(res.body);
for(let i in res.body) {
assert.equal(res.body[i].issue_title, 'Title');
assert.equal(res.body[i].issue_text, 'text');
assert.equal(res.body[i].created_by, 'Functional Test - Every field filled in');
assert.equal(res.body[i].assigned_to, 'Chai and Mocha');
assert.equal(res.body[i].status_text, 'In QA');
}
done();
});
});
});
suite('PUT /api/issues/{project}', function() {
test('One field to update => {result: "successfully updated", _id: _id}', function(done) {
chai.request(server)
.put('/api/issues/test')
.send({
_id: id,
issue_title: 'Title Change'
//issue_text: 'text',
//created_by: 'Functional Test - Every field filled in',
//assigned_to: 'Chai and Mocha',
//status_text: 'In QA'
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.result, 'successfully updated');
assert.equal(res.body._id, id);
done();
});
});
test('Multiple fields to update => {result: "successfully updated", _id: _id}', function(done) {
chai.request(server)
.put('/api/issues/test')
.send({
_id: id,
issue_title: 'Title Change',
issue_text: 'text',
created_by: 'Functional Test - Every field filled in',
assigned_to: 'Chai and Mocha',
status_text: 'In QA',
open: true
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.result, 'successfully updated');
assert.equal(res.body._id, id);
done();
});
});
test('No _id submitted => { error: "missing _id" }', function(done) {
chai.request(server)
.put('/api/issues/test')
.send({
//_id: id,
issue_title: 'Title Change',
issue_text: 'text',
created_by: 'Functional Test - Every field filled in',
assigned_to: 'Chai and Mocha',
status_text: 'In QA',
open: true
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.error, 'missing _id');
done();
});
});
test('No fields to update => { error: "no update field(s) sent", _id: _id }', function(done) {
chai.request(server)
.put('/api/issues/test')
.send({
_id: id
//issue_title: 'Title Change',
//issue_text: 'text',
//created_by: 'Functional Test - Every field filled in',
//assigned_to: 'Chai and Mocha',
//status_text: 'In QA',
//open: true
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.error, 'no update field(s) sent');
assert.equal(res.body._id, id);
done();
});
});
test('Invalid _id => { error: "missing _id" }', function(done) {
chai.request(server)
.put('/api/issues/test')
.send({
_id: '',
issue_title: 'Title Change',
issue_text: 'text',
created_by: 'Functional Test - Every field filled in',
assigned_to: 'Chai and Mocha',
status_text: 'In QA',
open: true
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.error, 'missing _id');
done();
});
});
});
suite('DELETE /api/issues/{project}', function() {
test('Valid _id', function(done) {
chai.request(server)
.delete('/api/issues/test')
.send({
_id: id
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.result, 'successfully deleted');
assert.equal(res.body._id, id);
done();
});
});
test('Invalid _id => { error: "could not delete", "_id": _id }', function(done) {
const badId = "5f665eb46e296f6b9b6a504d";
chai.request(server)
.delete('/api/issues/test')
.send({
_id: badId
})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.error, 'could not delete');
assert.equal(res.body._id, badId);
done();
});
});
test('No _id => { error: "missing _id" }', function(done) {
chai.request(server)
.delete('/api/issues/test')
.send({})
.end(function(err, res){
assert.equal(res.status, 200);
assert.equal(res.body.error, 'missing _id');
done();
});
});
});
});
Your browser information:
User Agent is: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/87.0.4280.66 Safari/537.36
.
Challenge: Issue Tracker
Link to the challenge: