feat(tests): add Jest+Supertest infrastructure with jump route suite (14 tests)
- Extract Express app creation into src/createApp.js to enable testing without starting the server
- Add Jest, Supertest, test helpers (createTestApp, auth token generator)
- Write 14 integration tests for skydive/jumps: auth protection, CRUD, ownership checks
Bugs caught and fixed by the test suite:
- MongoDB models: author field typed as Schema.Types.UUID (Mongoose 6 rejects string UUIDs) → changed to String
- Jump.toJSONFor: called toProfileJSONFor on a UUID string → guarded with typeof check
- exceptions.handler: used err.statusCode but express-jwt throws err.status → returns 500 on 401 errors
- Jump.count(query): deprecated Mongoose method was ignoring the filter → replaced with countDocuments
- jumps.routes GET /: $or query included non-schema field "title" stripped by strictQuery, leaving {} (match-all) → replaced with slug/lieu search
- $regex: was passing a JS RegExp object which serializes to {} in MongoDB → now passes raw string
This commit is contained in:
@@ -0,0 +1,35 @@
|
||||
var express = require('express'),
|
||||
session = require('express-session'),
|
||||
cors = require('cors'),
|
||||
errorhandler = require('errorhandler');
|
||||
|
||||
const config = require('./config'),
|
||||
swaggerUi = require('swagger-ui-express'),
|
||||
swaggerSpec = require('./config/swagger');
|
||||
|
||||
function createApp() {
|
||||
const app = express();
|
||||
app.use(cors());
|
||||
app.use(express.json());
|
||||
app.use(express.urlencoded({ extended: true }));
|
||||
app.use(require('method-override')());
|
||||
app.use(express.static(__dirname + '/../public'));
|
||||
app.use(session({
|
||||
secret: config.secret,
|
||||
cookie: { maxAge: config.session_lifetime },
|
||||
resave: false,
|
||||
saveUninitialized: false
|
||||
}));
|
||||
|
||||
if (process.env.APP_ENV !== 'production') {
|
||||
app.use(errorhandler());
|
||||
}
|
||||
|
||||
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerSpec, {
|
||||
swaggerOptions: { persistAuthorization: true },
|
||||
}));
|
||||
|
||||
return app;
|
||||
}
|
||||
|
||||
module.exports = createApp;
|
||||
@@ -5,7 +5,7 @@ var AeronefSchema = new mongoose.Schema({
|
||||
slug: { type: String, lowercase: true, unique: true },
|
||||
aeronef: String,
|
||||
imat: String,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
AeronefSchema.pre('validate', function (next) {
|
||||
|
||||
@@ -8,7 +8,7 @@ var ApplicationSchema = new mongoose.Schema({
|
||||
slug: { type: String, lowercase: true, unique: true },
|
||||
title: String,
|
||||
description: String,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, { timestamps: true, toJSON: { virtuals: true } });
|
||||
|
||||
ApplicationSchema.plugin(uniqueValidator, { message: 'is already taken' });
|
||||
|
||||
@@ -5,7 +5,7 @@ var CanopySchema = new mongoose.Schema({
|
||||
slug: { type: String, lowercase: true, unique: true },
|
||||
voile: String,
|
||||
taille: Number,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
CanopySchema.pre('validate', function (next) {
|
||||
|
||||
@@ -5,7 +5,7 @@ var DropzoneSchema = new mongoose.Schema({
|
||||
slug: { type: String, lowercase: true, unique: true },
|
||||
lieu: String,
|
||||
oaci: String,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
DropzoneSchema.pre('validate', function (next) {
|
||||
|
||||
@@ -25,7 +25,7 @@ var JumpSchema = new mongoose.Schema({
|
||||
video: String,
|
||||
files: [{ type: mongoose.Schema.Types.ObjectId, ref: 'JumpFile' }],
|
||||
x2data: { type: mongoose.Schema.Types.ObjectId, ref: 'X2DataLog' },
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
JumpSchema.plugin(uniqueValidator, { message: 'is already taken' });
|
||||
@@ -79,7 +79,9 @@ JumpSchema.methods.toJSONFor = function(user) {
|
||||
x2data: this.x2data ? this.x2data.toJSONFor() : null,
|
||||
createdAt: this.createdAt,
|
||||
updatedAt: this.updatedAt,
|
||||
author: this.author.toProfileJSONFor(user)
|
||||
author: this.author && typeof this.author.toProfileJSONFor === 'function'
|
||||
? this.author.toProfileJSONFor(user)
|
||||
: this.author ?? null
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ var JumpFileSchema = new mongoose.Schema({
|
||||
enum : ['csv', 'image', 'kml', 'video'],
|
||||
default: 'video'
|
||||
},
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
JumpFileSchema.plugin(uniqueValidator, { message: 'is already taken' });
|
||||
|
||||
@@ -60,7 +60,7 @@ var X2DataLogSchema = new mongoose.Schema({
|
||||
],
|
||||
date: String,
|
||||
createdOn: String,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
X2DataLogSchema.methods.toJSONFor = function() {
|
||||
|
||||
@@ -24,7 +24,7 @@ var HWClanSchema = new mongoose.Schema({
|
||||
title: String,
|
||||
topActivity: String,
|
||||
topDungeon: String,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
HWClanSchema.plugin(uniqueValidator, { message: 'is already taken' });
|
||||
|
||||
@@ -47,7 +47,7 @@ var HWMemberSchemaOld = new mongoose.Schema({
|
||||
scoreGifts: Number,
|
||||
warGifts: Number,
|
||||
rewards: Number,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
*/
|
||||
var HWMemberSchema = new mongoose.Schema({
|
||||
@@ -104,7 +104,7 @@ var HWMemberSchema = new mongoose.Schema({
|
||||
scoreGifts: Number,
|
||||
warGifts: Number,
|
||||
rewards: Number,
|
||||
author: { type: mongoose.Schema.Types.UUID, ref: 'User' }
|
||||
author: { type: String }
|
||||
}, {timestamps: true, toJSON: {virtuals: true}});
|
||||
|
||||
HWMemberSchema.plugin(uniqueValidator, { message: 'is already taken' });
|
||||
|
||||
@@ -27,7 +27,7 @@ const exceptionHandler = {
|
||||
},
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
fianlErrorHandler: (err, req, res, next) => {
|
||||
res.status(err.statusCode || 500);
|
||||
res.status(err.statusCode || err.status || 500);
|
||||
if (!isProduction) {
|
||||
/* development error handler will print stacktrace */
|
||||
res.json({errors: {
|
||||
|
||||
@@ -54,7 +54,7 @@ router.get('/', auth.required, function (req, res, next) {
|
||||
.sort({ createdAt: 'desc' })
|
||||
.populate('author')
|
||||
.exec(),
|
||||
Application.count(query).exec(),
|
||||
Application.countDocuments(query).exec(),
|
||||
req.payload ? UserService.getUserById(req.payload.id) : null
|
||||
]).then(function (results) {
|
||||
let applications = results[0];
|
||||
|
||||
@@ -39,13 +39,12 @@ router.get('/', auth.required, function (req, res, next) {
|
||||
offset = req.query.offset;
|
||||
}
|
||||
if (typeof req.query.title !== 'undefined') {
|
||||
const rgx = new RegExp(`.*${req.query.title}.*`);
|
||||
query = {
|
||||
$or: [
|
||||
{ title: { $regex: rgx, $options: "i" } },
|
||||
{ slug: { $regex: rgx, $options: "i" } },
|
||||
{ slug: { $regex: req.query.title, $options: 'i' } },
|
||||
{ lieu: { $regex: req.query.title, $options: 'i' } },
|
||||
],
|
||||
}
|
||||
};
|
||||
}
|
||||
if (typeof req.query.numeroRangeStart !== 'undefined' && typeof req.query.numeroRangeEnd !== 'undefined') {
|
||||
query.numero = {
|
||||
@@ -103,7 +102,7 @@ router.get('/', auth.required, function (req, res, next) {
|
||||
.populate('files')
|
||||
.populate('x2data')
|
||||
.exec(),
|
||||
Jump.count(query).exec(),
|
||||
Jump.countDocuments(query).exec(),
|
||||
req.payload ? UserService.getUserById(req.payload.id) : null
|
||||
]).then(function (results) {
|
||||
let jumps = results[0];
|
||||
|
||||
Reference in New Issue
Block a user