Skip to content

Commit f92e53b

Browse files
authored
Add route tests using supertest and node test runner (#327)
* Create mongodb outside of app creation * Controller - return/if fixes * Add route tests * Update all dependencies to latest * Update nodemon version * Revert the creation of a separate app setup * Remove the unnecessary async handler (in Expr5) * Remove the unnecessary async handler (in Expr5) * Convert to using node:test runner * remove a bit of cruft * Fixes when doing content changes * Update app.js - remove jquery from CSP
1 parent 5907338 commit f92e53b

16 files changed

+2889
-2171
lines changed

app.js

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ const express = require("express");
33
const path = require("path");
44
const cookieParser = require("cookie-parser");
55
const logger = require("morgan");
6+
const RateLimit = require("express-rate-limit");
67

78
const indexRouter = require("./routes/index");
89
const usersRouter = require("./routes/users");
@@ -14,28 +15,13 @@ const helmet = require("helmet");
1415
const app = express();
1516

1617
// Set up rate limiter: maximum of twenty requests per minute
17-
const RateLimit = require("express-rate-limit");
1818
const limiter = RateLimit({
19-
windowMs: 1 * 10 * 1000, // 10 seconds
20-
max: 10,
19+
windowMs: 1 * 60 * 1000, // 10 seconds
20+
max: 60,
2121
});
2222
// Apply rate limiter to all requests
2323
app.use(limiter);
2424

25-
// Set up mongoose connection
26-
const mongoose = require("mongoose");
27-
28-
mongoose.set("strictQuery", false);
29-
30-
const dev_db_url =
31-
"mongodb+srv://cooluser:coolpassword@cluster0.cojoign.mongodb.net/local_library?retryWrites=true&w=majority&appName=Cluster0";
32-
const mongoDB = process.env.MONGODB_URI || dev_db_url;
33-
34-
main().catch((err) => console.log(err));
35-
async function main() {
36-
await mongoose.connect(mongoDB);
37-
}
38-
3925
// view engine setup
4026
app.set("views", path.join(__dirname, "views"));
4127
app.set("view engine", "pug");
@@ -48,7 +34,7 @@ app.use(cookieParser());
4834
app.use(
4935
helmet.contentSecurityPolicy({
5036
directives: {
51-
"script-src": ["'self'", "code.jquery.com", "cdn.jsdelivr.net"],
37+
"script-src": ["'self'", "cdn.jsdelivr.net"],
5238
},
5339
})
5440
);

bin/www

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -4,72 +4,70 @@
44
* Module dependencies.
55
*/
66

7-
var app = require('../app');
8-
var debug = require('debug')('express-locallibrary-tutorial:server');
9-
var http = require('http');
7+
var app = require("../app");
8+
var debug = require("debug")("express-locallibrary-tutorial:server");
9+
var http = require("http");
1010

1111
/**
1212
* Get port from environment and store in Express.
1313
*/
1414

15-
var port = normalizePort(process.env.PORT || '3000');
16-
app.set('port', port);
15+
let port = normalizePort(process.env.PORT || "3000");
16+
app.set("port", port);
1717

1818
/**
19-
* Create HTTP server.
19+
* Set up mongoose connection
2020
*/
21+
const mongoose = require("mongoose");
22+
mongoose.set("strictQuery", false);
23+
const dev_db_url =
24+
"mongodb+srv://cooluser:coolpassword@cluster0.cojoign.mongodb.net/local_library?retryWrites=true&w=majority&appName=Cluster0";
25+
const mongoDB = process.env.MONGODB_URI || dev_db_url;
26+
27+
try {
28+
connectMongoose();
29+
} catch (err) {
30+
console.error("Failed to connect to MongoDB:", err);
31+
process.exit(1);
32+
}
2133

22-
var server = http.createServer(app);
34+
async function connectMongoose() {
35+
await mongoose.connect(mongoDB);
36+
}
2337

2438
/**
25-
* Listen on provided port, on all network interfaces.
39+
* Create HTTP server.
2640
*/
2741

28-
server.listen(port);
29-
server.on('error', onError);
30-
server.on('listening', onListening);
42+
var server = http.createServer(app);
3143

3244
/**
33-
* Normalize a port into a number, string, or false.
45+
* Listen on provided port, on all network interfaces.
3446
*/
3547

36-
function normalizePort(val) {
37-
var port = parseInt(val, 10);
38-
39-
if (isNaN(port)) {
40-
// named pipe
41-
return val;
42-
}
43-
44-
if (port >= 0) {
45-
// port number
46-
return port;
47-
}
48-
49-
return false;
50-
}
48+
server.listen(port);
49+
server.on("error", onError);
50+
server.on("listening", onListening);
5151

5252
/**
5353
* Event listener for HTTP server "error" event.
5454
*/
5555

5656
function onError(error) {
57-
if (error.syscall !== 'listen') {
57+
if (error.syscall !== "listen") {
5858
throw error;
5959
}
6060

61-
var bind = typeof port === 'string'
62-
? 'Pipe ' + port
63-
: 'Port ' + port;
61+
var bind = typeof port === "string" ? "Pipe " + port : "Port " + port;
6462

6563
// handle specific listen errors with friendly messages
6664
switch (error.code) {
67-
case 'EACCES':
68-
console.error(bind + ' requires elevated privileges');
65+
case "EACCES":
66+
console.error(bind + " requires elevated privileges");
6967
process.exit(1);
7068
break;
71-
case 'EADDRINUSE':
72-
console.error(bind + ' is already in use');
69+
case "EADDRINUSE":
70+
console.error(bind + " is already in use");
7371
process.exit(1);
7472
break;
7573
default:
@@ -83,8 +81,26 @@ function onError(error) {
8381

8482
function onListening() {
8583
var addr = server.address();
86-
var bind = typeof addr === 'string'
87-
? 'pipe ' + addr
88-
: 'port ' + addr.port;
89-
debug('Listening on ' + bind);
84+
var bind = typeof addr === "string" ? "pipe " + addr : "port " + addr.port;
85+
debug("Listening on " + bind);
86+
}
87+
88+
/**
89+
* Normalize a port into a number, string, or false.
90+
*/
91+
92+
function normalizePort(val) {
93+
var port = parseInt(val, 10);
94+
95+
if (isNaN(port)) {
96+
// named pipe
97+
return val;
98+
}
99+
100+
if (port >= 0) {
101+
// port number
102+
return port;
103+
}
104+
105+
return false;
90106
}

controllers/authorController.js

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,19 +2,18 @@ const Author = require("../models/author");
22
const Book = require("../models/book");
33

44
const { body, validationResult } = require("express-validator");
5-
const asyncHandler = require("express-async-handler");
65

76
// Display list of all Authors.
8-
exports.author_list = asyncHandler(async (req, res, next) => {
7+
exports.author_list = async (req, res, next) => {
98
const allAuthors = await Author.find().sort({ family_name: 1 }).exec();
109
res.render("author_list", {
1110
title: "Author List",
1211
author_list: allAuthors,
1312
});
14-
});
13+
};
1514

1615
// Display detail page for a specific Author.
17-
exports.author_detail = asyncHandler(async (req, res, next) => {
16+
exports.author_detail = async (req, res, next) => {
1817
// Get details of author and all their books (in parallel)
1918
const [author, allBooksByAuthor] = await Promise.all([
2019
Author.findById(req.params.id).exec(),
@@ -33,7 +32,7 @@ exports.author_detail = asyncHandler(async (req, res, next) => {
3332
author,
3433
author_books: allBooksByAuthor,
3534
});
36-
});
35+
};
3736

3837
// Display Author create form on GET.
3938
exports.author_create_get = (req, res, next) => {
@@ -67,7 +66,7 @@ exports.author_create_post = [
6766
.toDate(),
6867

6968
// Process request after validation and sanitization.
70-
asyncHandler(async (req, res, next) => {
69+
async (req, res, next) => {
7170
// Extract the validation errors from a request.
7271
const errors = validationResult(req);
7372

@@ -90,14 +89,14 @@ exports.author_create_post = [
9089
}
9190

9291
// Data from form is valid.
92+
// Save and redirect to new author record.
9393
await author.save();
94-
// Redirect to new author record.
9594
res.redirect(author.url);
96-
}),
95+
},
9796
];
9897

9998
// Display Author delete form on GET.
100-
exports.author_delete_get = asyncHandler(async (req, res, next) => {
99+
exports.author_delete_get = async (req, res, next) => {
101100
// Get details of author and all their books (in parallel)
102101
const [author, allBooksByAuthor] = await Promise.all([
103102
Author.findById(req.params.id).exec(),
@@ -107,17 +106,18 @@ exports.author_delete_get = asyncHandler(async (req, res, next) => {
107106
if (author === null) {
108107
// No results.
109108
res.redirect("/catalog/authors");
109+
return;
110110
}
111111

112112
res.render("author_delete", {
113113
title: "Delete Author",
114114
author,
115115
author_books: allBooksByAuthor,
116116
});
117-
});
117+
};
118118

119119
// Handle Author delete on POST.
120-
exports.author_delete_post = asyncHandler(async (req, res, next) => {
120+
exports.author_delete_post = async (req, res, next) => {
121121
// Get details of author and all their books (in parallel)
122122
const [author, allBooksByAuthor] = await Promise.all([
123123
Author.findById(req.params.id).exec(),
@@ -137,10 +137,10 @@ exports.author_delete_post = asyncHandler(async (req, res, next) => {
137137
// Author has no books. Delete object and redirect to the list of authors.
138138
await Author.findByIdAndDelete(req.body.authorid);
139139
res.redirect("/catalog/authors");
140-
});
140+
};
141141

142142
// Display Author update form on GET.
143-
exports.author_update_get = asyncHandler(async (req, res, next) => {
143+
exports.author_update_get = async (req, res, next) => {
144144
const author = await Author.findById(req.params.id).exec();
145145
if (author === null) {
146146
// No results.
@@ -150,7 +150,7 @@ exports.author_update_get = asyncHandler(async (req, res, next) => {
150150
}
151151

152152
res.render("author_form", { title: "Update Author", author });
153-
});
153+
};
154154

155155
// Handle Author update on POST.
156156
exports.author_update_post = [
@@ -179,7 +179,7 @@ exports.author_update_post = [
179179
.toDate(),
180180

181181
// Process request after validation and sanitization.
182-
asyncHandler(async (req, res, next) => {
182+
async (req, res, next) => {
183183
// Extract the validation errors from a request.
184184
const errors = validationResult(req);
185185

@@ -205,5 +205,5 @@ exports.author_update_post = [
205205
// Data from form is valid. Update the record.
206206
await Author.findByIdAndUpdate(req.params.id, author);
207207
res.redirect(author.url);
208-
}),
208+
},
209209
];

0 commit comments

Comments
 (0)