diff --git a/backend/__tests__/auth.test.js b/backend/__tests__/auth.test.js index a122d16..32a7d2a 100644 --- a/backend/__tests__/auth.test.js +++ b/backend/__tests__/auth.test.js @@ -1,3 +1,4 @@ +process.env.NODE_ENV = 'test'; const request = require('supertest'); const mongoose = require('mongoose'); const { MongoMemoryServer } = require('mongodb-memory-server'); @@ -9,7 +10,11 @@ let mongoServer; beforeAll(async () => { mongoServer = await MongoMemoryServer.create(); const mongoUri = mongoServer.getUri(); - process.env.MONGO_URI = mongoUri; + process.env.MONGO_URI = mongoUri; + await mongoose.connect(mongoUri, { + useNewUrlParser: true, + useUnifiedTopology: true, + }); }); afterAll(async () => { @@ -19,7 +24,6 @@ afterAll(async () => { }); describe('Auth API', () => { - beforeEach(async () => { await User.deleteMany({}); }); @@ -36,7 +40,7 @@ describe('Auth API', () => { expect(response.statusCode).toBe(201); expect(response.body).toHaveProperty('token'); - + const savedUser = await User.findOne({ email: 'testuser@gmail.com' }); expect(savedUser).not.toBeNull(); expect(savedUser.defaultCurrency).toBe('USD'); @@ -101,4 +105,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); + }); }); \ No newline at end of file diff --git a/backend/config/db.js b/backend/config/db.js index 7cdfefa..c274c60 100644 --- a/backend/config/db.js +++ b/backend/config/db.js @@ -6,8 +6,10 @@ const connectDB = async () => { console.log(`MongoDB Connected: ${conn.connection.host}`); } catch (error) { console.error(`Error: ${error.message}`); - process.exit(1); + if (process.env.NODE_ENV !== "test") { + process.exit(1); + } } }; -module.exports = connectDB; \ No newline at end of file +module.exports = connectDB; diff --git a/backend/middleware/validationMiddleware.js b/backend/middleware/validationMiddleware.js index 9a1caab..fc11eec 100644 --- a/backend/middleware/validationMiddleware.js +++ b/backend/middleware/validationMiddleware.js @@ -2,13 +2,19 @@ const { body, validationResult } = require('express-validator'); const dns = require('dns'); const validateRegistration = [ + (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]; + const domain = email.split("@")[1]; // Quick blacklist for common invalid domains const blockedDomains = ['example.com', 'test.com', 'invalid.com']; @@ -20,11 +26,15 @@ const validateRegistration = [ 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.'); + 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.'); + return Promise.reject( + 'Email domain does not exist or cannot receive mail.' + ); } }), @@ -33,7 +43,9 @@ const validateRegistration = [ .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.'), + .withMessage( + 'Password must contain at least one alphabet, one digit, and one symbol.' + ), // Middleware to handle the validation result (req, res, next) => { diff --git a/backend/package-lock.json b/backend/package-lock.json index 30137a9..3d0ee83 100644 --- a/backend/package-lock.json +++ b/backend/package-lock.json @@ -2603,21 +2603,6 @@ "dev": true, "license": "ISC" }, - "node_modules/fsevents": { - "version": "2.3.3", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", - "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", - "dev": true, - "hasInstallScript": true, - "license": "MIT", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": "^8.16.0 || ^10.6.0 || >=11.0.0" - } - }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", diff --git a/backend/server.js b/backend/server.js index 70012a9..b083d9c 100644 --- a/backend/server.js +++ b/backend/server.js @@ -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 @@ -55,9 +57,11 @@ app.get('/', (req, res) => { 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() => { +cron.schedule("*/10 * * * *", async () => { const keepAliveUrl = process.env.KEEP_ALIVE_URL; if (!keepAliveUrl) { console.error( @@ -74,4 +78,4 @@ cron.schedule("*/10 * * * *", async() => { } }); -module.exports = { app, server }; \ No newline at end of file +module.exports = { app, server };