Skip to content
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
87 changes: 81 additions & 6 deletions backend/__tests__/auth.test.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
process.env.NODE_ENV = 'test';
const request = require('supertest');
const mongoose = require('mongoose');
const { MongoMemoryServer } = require('mongodb-memory-server');
Expand All @@ -7,18 +8,25 @@ const User = require('../models/User');
let mongoServer;

beforeAll(async () => {
mongoServer = await MongoMemoryServer.create();
const mongoUri = mongoServer.getUri();
process.env.MONGO_URI = mongoUri;
mongoServer = await MongoMemoryServer.create();
const mongoUri = mongoServer.getUri();
process.env.MONGO_URI = mongoUri;
await mongoose.connect(mongoUri, {
useNewUrlParser: true,
useUnifiedTopology: true,
});
});

afterAll(async () => {
await mongoose.disconnect();
await mongoServer.stop();
server.close();
await mongoose.disconnect();
await mongoServer.stop();
server.close();
});

describe('Auth API', () => {
beforeEach(async () => {
await User.deleteMany({});
});
Comment thread
Avdhesh-Varshney marked this conversation as resolved.
Outdated

beforeEach(async () => {
await User.deleteMany({});
Expand Down Expand Up @@ -101,4 +109,71 @@ describe('Auth API', () => {
expect(setupResponse.body.message).toBe('Default currency is required');
});

it('should allow a new user to sign up', async () => {
const newUser = {
email: 'testuser@gmail.com',
password: 'Password123!',
};

const response = await request(app).post('/api/auth/signup').send(newUser);

expect(response.statusCode).toBe(201);
expect(response.body).toHaveProperty('token');

const savedUser = await User.findOne({ email: 'testuser@gmail.com' });
expect(savedUser).not.toBeNull();
});

it('should reject signup with an existing email', async () => {
const testUser = {
email: 'duplicate@gmail.com',
password: 'Password123!',
};

await request(app).post('/api/auth/signup').send(testUser).expect(201);

const response = await request(app)
.post('/api/auth/signup')
.send(testUser)
.expect(400);

expect(response.body.message).toBe('User already exists');

const users = await User.find({ email: testUser.email });
expect(users.length).toBe(1);
});

it('should reject signup when email is missing', async () => {
const missingEmailUser = {
email: "",
password: 'Password123!',
};

const response = await request(app)
.post('/api/auth/signup')
.send(missingEmailUser)
.expect(400);

expect(response.body.message).toBe('Please enter all fields');

const users = await User.find({});
expect(users.length).toBe(0);
});

it('should reject signup when password is missing', async () => {
const missingPasswordUser = {
email: 'user@example.com',
password: "",
};

const response = await request(app)
.post('/api/auth/signup')
.send(missingPasswordUser)
.expect(400);

expect(response.body.message).toBe('Please enter all fields');

const users = await User.find({});
expect(users.length).toBe(0);
});
});
16 changes: 9 additions & 7 deletions backend/config/db.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
const mongoose = require('mongoose');

const connectDB = async () => {
try {
const conn = await mongoose.connect(process.env.MONGO_URI);
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
console.error(`Error: ${error.message}`);
process.exit(1);
}
try {
const conn = await mongoose.connect(process.env.MONGO_URI);
console.log(`MongoDB Connected: ${conn.connection.host}`);
} catch (error) {
console.error(`Error: ${error.message}`);
if (process.env.NODE_ENV !== "test") {
process.exit(1);
}
}
};

module.exports = connectDB;
88 changes: 50 additions & 38 deletions backend/middleware/validationMiddleware.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,49 +2,61 @@ const { body, validationResult } = require('express-validator');
const dns = require('dns');

const validateRegistration = [
// Validate email
body('email')
.isEmail()
.withMessage('Please enter a valid email address.')
.bail() // Stop running validators if the previous one failed
.custom(async (email) => {
const domain = email.split('@')[1];
(req, res, next) => {
if (!req.body.email || !req.body.password) {
return res.status(400).json({ message: "Please enter all fields" });
}
next();
},
// Validate email
body('email')
.isEmail()
.withMessage('Please enter a valid email address.')
.bail() // Stop running validators if the previous one failed
.custom(async (email) => {
const domain = email.split("@")[1];

// Quick blacklist for common invalid domains
const blockedDomains = ['example.com', 'test.com', 'invalid.com'];
if (blockedDomains.includes(domain)) {
return Promise.reject('This email domain is not allowed.');
}
// Quick blacklist for common invalid domains
const blockedDomains = ['example.com', 'test.com', 'invalid.com'];
if (blockedDomains.includes(domain)) {
return Promise.reject('This email domain is not allowed.');
}

// Check for valid MX records
try {
const addresses = await dns.promises.resolveMx(domain);
if (!addresses || addresses.length === 0) {
return Promise.reject('Email domain does not exist or cannot receive mail.');
}
} catch (error) {
// If DNS resolution fails
return Promise.reject('Email domain does not exist or cannot receive mail.');
}
}),
// Check for valid MX records
try {
const addresses = await dns.promises.resolveMx(domain);
if (!addresses || addresses.length === 0) {
return Promise.reject(
'Email domain does not exist or cannot receive mail.'
);
}
} catch (error) {
// If DNS resolution fails
return Promise.reject(
'Email domain does not exist or cannot receive mail.'
);
}
}),

// Validate password
body('password')
.isLength({ min: 8, max: 16 })
.withMessage('Password must be between 8 and 16 characters long.')
.matches(/^(?=.*\d)(?=.*[a-zA-Z])(?=.*[\W_])/)
.withMessage('Password must contain at least one alphabet, one digit, and one symbol.'),
// Validate password
body('password')
.isLength({ min: 8, max: 16 })
.withMessage('Password must be between 8 and 16 characters long.')
.matches(/^(?=.*\d)(?=.*[a-zA-Z])(?=.*[\W_])/)
.withMessage(
'Password must contain at least one alphabet, one digit, and one symbol.'
),

// Middleware to handle the validation result
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ message: errors.array()[0].msg });
}
next();
},
// Middleware to handle the validation result
(req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ message: errors.array()[0].msg });
}
next();
},
];

module.exports = {
validateRegistration,
validateRegistration,
};
15 changes: 0 additions & 15 deletions backend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

31 changes: 18 additions & 13 deletions backend/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,18 @@ const allowedOrigins = [
"https://paisable.netlify.app",
];

app.use(cors({
origin: function(origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error("Not allowed by CORS"));
}
},
credentials: true
}));
app.use(
cors({
origin: function (origin, callback) {
if (!origin || allowedOrigins.includes(origin)) {
callback(null, true);
} else {
callback(new Error('Not allowed by CORS'));
}
},
credentials: true,
})
);
app.use(express.json());

// sanitizeMiddleware
Expand All @@ -50,12 +52,14 @@ app.use('/api/recurring', require('./routes/recurringTransactionRoutes'));
app.use('/uploads', express.static(path.join(__dirname, 'uploads')));

app.get('/', (req, res) => {
res.send('API is Running');
res.send('API is Running');
});

const PORT = process.env.PORT || 5000;

const server = app.listen(PORT, () => console.log(`Server started on port ${PORT}`));
const server = app.listen(PORT, () =>
console.log(`Server started on port ${PORT}`)
);

cron.schedule("*/10 * * * *", async() => {
const keepAliveUrl = process.env.KEEP_ALIVE_URL;
Expand All @@ -74,4 +78,5 @@ cron.schedule("*/10 * * * *", async() => {
}
});

module.exports = { app, server };
module.exports = { app, server };
module.exports = { app, server };