Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
42 changes: 21 additions & 21 deletions backend/models/User.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,33 +2,33 @@ const mongoose = require("mongoose");
const bcrypt = require("bcryptjs");

const UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
username: {
type: String,
required: true,
unique: true,
},
email: {
type: String,
required: true,
unique: true,
},
password: {
type: String,
required: true,
},
});

// βœ… FIXED: no next()
UserSchema.pre('save', async function () {
if (!this.isModified('password'))
return;
if (!this.isModified('password')) return;

const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
});

// Compare passwords during login
// βœ… password comparison
UserSchema.methods.comparePassword = async function (enteredPassword) {
return await bcrypt.compare(enteredPassword, this.password);
return bcrypt.compare(enteredPassword, this.password);
};

module.exports = mongoose.model("User", UserSchema);
module.exports = mongoose.model("User", UserSchema);
7 changes: 5 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"test:backend": "jasmine spec/**/*.spec.cjs",
"preview": "vite preview",
"docker:dev": "docker compose --profile dev up --build",
"docker:prod": "docker compose --profile prod up -d --build"
"docker:prod": "docker compose --profile prod up -d --build",
"test": "jasmine"
Comment thread
JayRathore10 marked this conversation as resolved.
},
"dependencies": {
"@emotion/react": "^11.11.3",
Expand All @@ -21,8 +22,10 @@
"@primer/octicons-react": "^19.25.0",
"@vitejs/plugin-react": "^4.3.3",
"axios": "^1.7.7",
"express": "^5.2.1",
"framer-motion": "^12.23.12",
"lucide-react": "^0.525.0",
"mongoose": "^9.6.2",
"octokit": "^4.0.2",
"postcss": "^8.4.47",
"react": "^18.3.1",
Expand All @@ -45,7 +48,7 @@
"@types/react-router-dom": "^5.3.3",
"@vitejs/plugin-react-swc": "^3.5.0",
"autoprefixer": "^10.4.20",
"bcryptjs": "^3.0.2",
"bcryptjs": "^3.0.3",
Comment thread
JayRathore10 marked this conversation as resolved.
"eslint": "^9.13.0",
"eslint-plugin-react": "^7.37.2",
"eslint-plugin-react-hooks": "^5.0.0",
Expand Down
113 changes: 79 additions & 34 deletions spec/auth.routes.spec.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,32 @@ const express = require('express');
const request = require('supertest');
const session = require('express-session');
const passport = require('passport');

const User = require('../backend/models/User');
const authRoutes = require('../backend/routes/auth');

// Setup Express app for testing
// Create test app
function createTestApp() {
const app = express();

app.use(express.json());
app.use(session({ secret: 'test', resave: false, saveUninitialized: false }));

app.use(
session({
secret: 'test-secret',
resave: false,
saveUninitialized: false,
})
);

app.use(passport.initialize());
app.use(passport.session());

// Load passport config AFTER initializing passport
require('../backend/config/passportConfig');

app.use('/auth', authRoutes);

return app;
}

Expand All @@ -27,76 +41,107 @@ describe('Auth Routes', () => {
});

afterAll(async () => {
await mongoose.connection.db.dropDatabase();
await mongoose.disconnect();
if (mongoose.connection.readyState === 1) {
await mongoose.connection.db.dropDatabase();
await mongoose.disconnect();
}
});

afterEach(async () => {
await User.deleteMany({});
});

// ---------------- SIGNUP ----------------
it('should sign up a new user', async () => {
const res = await request(app)
.post('/auth/signup')
.send({ username: 'testuser', email: 'test@example.com', password: 'password123' });
.send({
username: 'testuser',
email: 'test@example.com',
password: 'password123',
});

expect(res.status).toBe(201);
expect(res.body.message).toBe('User created successfully');

const user = await User.findOne({ email: 'test@example.com' });
expect(user).toBeTruthy();
});

it('should not sign up a user with existing email', async () => {
await new User({ username: 'testuser', email: 'test@example.com', password: 'password123' }).save();
const res = await request(app)
.post('/auth/signup')
.send({ username: 'testuser2', email: 'test@example.com', password: 'password456' });
expect(res.status).toBe(400);
expect(res.body.message).toBe('User already exists');
});
await User.create({
username: 'testuser',
email: 'test@example.com',
password: 'password123',
});

it('should not sign up a user with existing username', async () => {
await new User({ username: 'testuser', email: 'test@example.com', password: 'password123' }).save();
const res = await request(app)
.post('/auth/signup')
.send({ username: 'testuser', email: 'test2@example.com', password: 'password456' });
.send({
username: 'testuser2',
email: 'test@example.com',
password: 'password456',
});

expect(res.status).toBe(400);
expect(res.body.message).toBe('User already exists');
});

// ---------------- LOGIN ----------------
it('should login a user with correct credentials', async () => {
await request(app)
.post('/auth/signup')
.send({ username: 'testuser', email: 'test@example.com', password: 'password123' });
await User.create({
username: 'testuser',
email: 'test@example.com',
password: 'password123',
});

const agent = request.agent(app);
const res = await agent
.post('/auth/login')
.send({ email: 'test@example.com', password: 'password123' });

const res = await agent.post('/auth/login').send({
email: 'test@example.com',
password: 'password123',
});

expect(res.status).toBe(200);
expect(res.body.message).toBe('Login successful');
expect(res.body.user.email).toBe('test@example.com');
});

it('should not login a user with wrong password', async () => {
await request(app)
.post('/auth/signup')
.send({ username: 'testuser', email: 'test@example.com', password: 'password123' });
await User.create({
username: 'testuser',
email: 'test@example.com',
password: 'password123',
});

const agent = request.agent(app);
const res = await agent
.post('/auth/login')
.send({ email: 'test@example.com', password: 'wrongpassword' });

const res = await agent.post('/auth/login').send({
email: 'test@example.com',
password: 'wrongpassword',
});

expect(res.status).toBe(401);
});

// ---------------- LOGOUT ----------------
it('should logout a logged-in user', async () => {
await request(app)
.post('/auth/signup')
.send({ username: 'testuser', email: 'test@example.com', password: 'password123' });
const agent = request.agent(app);
await agent
.post('/auth/login')
.send({ email: 'test@example.com', password: 'password123' });

await agent.post('/auth/signup').send({
username: 'testuser',
email: 'test@example.com',
password: 'password123',
});

await agent.post('/auth/login').send({
email: 'test@example.com',
password: 'password123',
});

const res = await agent.get('/auth/logout');

expect(res.status).toBe(200);
expect(res.body.message).toBe('Logged out successfully');
});
});
});
57 changes: 39 additions & 18 deletions spec/user.model.spec.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -8,40 +8,61 @@ describe('User Model', () => {
});

afterAll(async () => {
await mongoose.connection.db.dropDatabase();
await mongoose.disconnect();
if (mongoose.connection.readyState === 1) {
await mongoose.connection.db.dropDatabase();
await mongoose.disconnect();
}
});

afterEach(async () => {
await User.deleteMany({});
});

it('should create a user with hashed password', async () => {
const userData = { username: 'testuser', email: 'test@example.com', password: 'password123' };
const user = new User(userData);
// -------- CREATE USER --------
it('should hash password before saving user', async () => {
const user = new User({
username: 'testuser',
email: 'test@example.com',
password: 'password123',
});

await user.save();
expect(user.password).not.toBe(userData.password);

// password should not be plain text
expect(user.password).not.toBe('password123');

const isMatch = await bcrypt.compare('password123', user.password);
expect(isMatch).toBeTrue();
expect(isMatch).toBe(true);
});

it('should not hash password again if not modified', async () => {
const userData = { username: 'testuser2', email: 'test2@example.com', password: 'password123' };
const user = new User(userData);
await user.save();
// -------- PASSWORD NOT RE-HASHED --------
it('should not re-hash password if not modified', async () => {
const user = await User.create({
username: 'testuser2',
email: 'test2@example.com',
password: 'password123',
});

const originalHash = user.password;

user.username = 'updateduser';
await user.save();

expect(user.password).toBe(originalHash);
});

it('should compare passwords correctly', async () => {
const userData = { username: 'testuser3', email: 'test3@example.com', password: 'password123' };
const user = new User(userData);
await user.save();
// -------- COMPARE PASSWORD --------
it('should correctly compare passwords', async () => {
const user = await User.create({
username: 'testuser3',
email: 'test3@example.com',
password: 'password123',
});

const isMatch = await user.comparePassword('password123');
expect(isMatch).toBeTrue();
const isNotMatch = await user.comparePassword('wrongpassword');
expect(isNotMatch).toBeFalse();

expect(isMatch).toBe(true);
expect(isNotMatch).toBe(false);
});
});
});