password reset
This commit is contained in:
293
backend/tests/unit/models/User.passwordReset.test.js
Normal file
293
backend/tests/unit/models/User.passwordReset.test.js
Normal file
@@ -0,0 +1,293 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Mock crypto module
|
||||
jest.mock('crypto');
|
||||
|
||||
// Mock the entire models module
|
||||
jest.mock('../../../models', () => {
|
||||
const mockUser = {
|
||||
update: jest.fn(),
|
||||
passwordResetToken: null,
|
||||
passwordResetTokenExpiry: null,
|
||||
};
|
||||
|
||||
return {
|
||||
User: mockUser,
|
||||
sequelize: {
|
||||
models: {
|
||||
User: mockUser
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Import User model methods - we'll test them directly
|
||||
const User = require('../../../models/User');
|
||||
|
||||
describe('User Model - Password Reset', () => {
|
||||
let mockUser;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Create a fresh mock user for each test
|
||||
mockUser = {
|
||||
id: 1,
|
||||
email: 'test@example.com',
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
password: 'hashedPassword123',
|
||||
passwordResetToken: null,
|
||||
passwordResetTokenExpiry: null,
|
||||
update: jest.fn().mockImplementation(function(updates) {
|
||||
Object.assign(this, updates);
|
||||
return Promise.resolve(this);
|
||||
})
|
||||
};
|
||||
|
||||
// Add the prototype methods to mockUser
|
||||
Object.setPrototypeOf(mockUser, User.prototype);
|
||||
});
|
||||
|
||||
describe('generatePasswordResetToken', () => {
|
||||
it('should generate a random token and set 1-hour expiry', async () => {
|
||||
const mockRandomBytes = Buffer.from('a'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex');
|
||||
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
|
||||
expect(crypto.randomBytes).toHaveBeenCalledWith(32);
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
passwordResetToken: mockToken
|
||||
})
|
||||
);
|
||||
|
||||
// Check that expiry is approximately 1 hour from now
|
||||
const updateCall = mockUser.update.mock.calls[0][0];
|
||||
const expiryTime = updateCall.passwordResetTokenExpiry.getTime();
|
||||
const expectedExpiry = Date.now() + 60 * 60 * 1000;
|
||||
|
||||
expect(expiryTime).toBeGreaterThan(expectedExpiry - 1000);
|
||||
expect(expiryTime).toBeLessThan(expectedExpiry + 1000);
|
||||
});
|
||||
|
||||
it('should update the user with token and expiry', async () => {
|
||||
const mockRandomBytes = Buffer.from('b'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex');
|
||||
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
const result = await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledTimes(1);
|
||||
expect(result.passwordResetToken).toBe(mockToken);
|
||||
expect(result.passwordResetTokenExpiry).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should generate unique tokens on multiple calls', async () => {
|
||||
const mockRandomBytes1 = Buffer.from('a'.repeat(32));
|
||||
const mockRandomBytes2 = Buffer.from('b'.repeat(32));
|
||||
|
||||
crypto.randomBytes
|
||||
.mockReturnValueOnce(mockRandomBytes1)
|
||||
.mockReturnValueOnce(mockRandomBytes2);
|
||||
|
||||
await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
const firstToken = mockUser.update.mock.calls[0][0].passwordResetToken;
|
||||
|
||||
await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
const secondToken = mockUser.update.mock.calls[1][0].passwordResetToken;
|
||||
|
||||
expect(firstToken).not.toBe(secondToken);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isPasswordResetTokenValid', () => {
|
||||
it('should return true for valid token and non-expired time', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const futureExpiry = new Date(Date.now() + 30 * 60 * 1000); // 30 minutes from now
|
||||
|
||||
mockUser.passwordResetToken = validToken;
|
||||
mockUser.passwordResetTokenExpiry = futureExpiry;
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for missing token', () => {
|
||||
mockUser.passwordResetToken = null;
|
||||
mockUser.passwordResetTokenExpiry = new Date(Date.now() + 30 * 60 * 1000);
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, 'any-token');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for missing expiry', () => {
|
||||
mockUser.passwordResetToken = 'valid-token';
|
||||
mockUser.passwordResetTokenExpiry = null;
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, 'valid-token');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for mismatched token', () => {
|
||||
mockUser.passwordResetToken = 'correct-token';
|
||||
mockUser.passwordResetTokenExpiry = new Date(Date.now() + 30 * 60 * 1000);
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, 'wrong-token');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for expired token', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const pastExpiry = new Date(Date.now() - 10 * 60 * 1000); // 10 minutes ago
|
||||
|
||||
mockUser.passwordResetToken = validToken;
|
||||
mockUser.passwordResetTokenExpiry = pastExpiry;
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for token expiring in the past by 1 second', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const pastExpiry = new Date(Date.now() - 1000); // 1 second ago
|
||||
|
||||
mockUser.passwordResetToken = validToken;
|
||||
mockUser.passwordResetTokenExpiry = pastExpiry;
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle edge case of token expiring exactly now', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
// Set expiry 1ms in the future to handle timing precision
|
||||
const nowExpiry = new Date(Date.now() + 1);
|
||||
|
||||
mockUser.passwordResetToken = validToken;
|
||||
mockUser.passwordResetTokenExpiry = nowExpiry;
|
||||
|
||||
// This should be true because expiry is slightly in the future
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle string dates correctly', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const futureExpiry = new Date(Date.now() + 30 * 60 * 1000).toISOString(); // String date
|
||||
|
||||
mockUser.passwordResetToken = validToken;
|
||||
mockUser.passwordResetTokenExpiry = futureExpiry;
|
||||
|
||||
const result = User.prototype.isPasswordResetTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('resetPassword', () => {
|
||||
it('should update password and clear token fields', async () => {
|
||||
mockUser.passwordResetToken = 'some-token';
|
||||
mockUser.passwordResetTokenExpiry = new Date();
|
||||
|
||||
await User.prototype.resetPassword.call(mockUser, 'newSecurePassword123!');
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledWith({
|
||||
password: 'newSecurePassword123!',
|
||||
passwordResetToken: null,
|
||||
passwordResetTokenExpiry: null
|
||||
});
|
||||
});
|
||||
|
||||
it('should return updated user object', async () => {
|
||||
const result = await User.prototype.resetPassword.call(mockUser, 'newPassword123!');
|
||||
|
||||
expect(result.password).toBe('newPassword123!');
|
||||
expect(result.passwordResetToken).toBe(null);
|
||||
expect(result.passwordResetTokenExpiry).toBe(null);
|
||||
});
|
||||
|
||||
it('should call update only once', async () => {
|
||||
await User.prototype.resetPassword.call(mockUser, 'newPassword123!');
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Complete password reset flow', () => {
|
||||
it('should complete full password reset flow successfully', async () => {
|
||||
// Step 1: Generate password reset token
|
||||
const mockRandomBytes = Buffer.from('c'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex');
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
|
||||
expect(mockUser.passwordResetToken).toBe(mockToken);
|
||||
expect(mockUser.passwordResetTokenExpiry).toBeInstanceOf(Date);
|
||||
|
||||
// Step 2: Validate token
|
||||
const isValid = User.prototype.isPasswordResetTokenValid.call(mockUser, mockToken);
|
||||
expect(isValid).toBe(true);
|
||||
|
||||
// Step 3: Reset password
|
||||
await User.prototype.resetPassword.call(mockUser, 'newPassword123!');
|
||||
|
||||
expect(mockUser.password).toBe('newPassword123!');
|
||||
expect(mockUser.passwordResetToken).toBe(null);
|
||||
expect(mockUser.passwordResetTokenExpiry).toBe(null);
|
||||
});
|
||||
|
||||
it('should fail password reset with wrong token', async () => {
|
||||
// Generate token
|
||||
const mockToken = 'd'.repeat(64);
|
||||
const mockRandomBytes = Buffer.from('d'.repeat(32));
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
|
||||
// Try to validate with wrong token
|
||||
const isValid = User.prototype.isPasswordResetTokenValid.call(mockUser, 'wrong-token');
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('should fail password reset with expired token', async () => {
|
||||
// Manually set an expired token
|
||||
mockUser.passwordResetToken = 'expired-token';
|
||||
mockUser.passwordResetTokenExpiry = new Date(Date.now() - 2 * 60 * 60 * 1000); // 2 hours ago
|
||||
|
||||
const isValid = User.prototype.isPasswordResetTokenValid.call(mockUser, 'expired-token');
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('should not allow password reset after token has been used', async () => {
|
||||
// Generate token
|
||||
const mockRandomBytes = Buffer.from('e'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex');
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generatePasswordResetToken.call(mockUser);
|
||||
|
||||
// Reset password (clears token)
|
||||
await User.prototype.resetPassword.call(mockUser, 'newPassword123!');
|
||||
|
||||
// Try to validate same token again (should fail because it's cleared)
|
||||
const isValid = User.prototype.isPasswordResetTokenValid.call(mockUser, mockToken);
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
296
backend/tests/unit/models/User.verification.test.js
Normal file
296
backend/tests/unit/models/User.verification.test.js
Normal file
@@ -0,0 +1,296 @@
|
||||
const crypto = require('crypto');
|
||||
|
||||
// Mock crypto module
|
||||
jest.mock('crypto');
|
||||
|
||||
// Mock the entire models module
|
||||
jest.mock('../../../models', () => {
|
||||
const mockUser = {
|
||||
update: jest.fn(),
|
||||
verificationToken: null,
|
||||
verificationTokenExpiry: null,
|
||||
isVerified: false,
|
||||
verifiedAt: null
|
||||
};
|
||||
|
||||
return {
|
||||
User: mockUser,
|
||||
sequelize: {
|
||||
models: {
|
||||
User: mockUser
|
||||
}
|
||||
}
|
||||
};
|
||||
});
|
||||
|
||||
// Import User model methods - we'll test them directly
|
||||
const User = require('../../../models/User');
|
||||
|
||||
describe('User Model - Email Verification', () => {
|
||||
let mockUser;
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
|
||||
// Create a fresh mock user for each test
|
||||
mockUser = {
|
||||
id: 1,
|
||||
email: 'test@example.com',
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
verificationToken: null,
|
||||
verificationTokenExpiry: null,
|
||||
isVerified: false,
|
||||
verifiedAt: null,
|
||||
update: jest.fn().mockImplementation(function(updates) {
|
||||
Object.assign(this, updates);
|
||||
return Promise.resolve(this);
|
||||
})
|
||||
};
|
||||
|
||||
// Add the prototype methods to mockUser
|
||||
Object.setPrototypeOf(mockUser, User.prototype);
|
||||
});
|
||||
|
||||
describe('generateVerificationToken', () => {
|
||||
it('should generate a random token and set 24-hour expiry', async () => {
|
||||
const mockRandomBytes = Buffer.from('a'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex'); // This will be "61" repeated 32 times
|
||||
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generateVerificationToken.call(mockUser);
|
||||
|
||||
expect(crypto.randomBytes).toHaveBeenCalledWith(32);
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
verificationToken: mockToken
|
||||
})
|
||||
);
|
||||
|
||||
// Check that expiry is approximately 24 hours from now
|
||||
const updateCall = mockUser.update.mock.calls[0][0];
|
||||
const expiryTime = updateCall.verificationTokenExpiry.getTime();
|
||||
const expectedExpiry = Date.now() + 24 * 60 * 60 * 1000;
|
||||
|
||||
expect(expiryTime).toBeGreaterThan(expectedExpiry - 1000);
|
||||
expect(expiryTime).toBeLessThan(expectedExpiry + 1000);
|
||||
});
|
||||
|
||||
it('should update the user with token and expiry', async () => {
|
||||
const mockRandomBytes = Buffer.from('b'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex');
|
||||
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
const result = await User.prototype.generateVerificationToken.call(mockUser);
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledTimes(1);
|
||||
expect(result.verificationToken).toBe(mockToken);
|
||||
expect(result.verificationTokenExpiry).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should generate unique tokens on multiple calls', async () => {
|
||||
const mockRandomBytes1 = Buffer.from('a'.repeat(32));
|
||||
const mockRandomBytes2 = Buffer.from('b'.repeat(32));
|
||||
|
||||
crypto.randomBytes
|
||||
.mockReturnValueOnce(mockRandomBytes1)
|
||||
.mockReturnValueOnce(mockRandomBytes2);
|
||||
|
||||
await User.prototype.generateVerificationToken.call(mockUser);
|
||||
const firstToken = mockUser.update.mock.calls[0][0].verificationToken;
|
||||
|
||||
await User.prototype.generateVerificationToken.call(mockUser);
|
||||
const secondToken = mockUser.update.mock.calls[1][0].verificationToken;
|
||||
|
||||
expect(firstToken).not.toBe(secondToken);
|
||||
});
|
||||
});
|
||||
|
||||
describe('isVerificationTokenValid', () => {
|
||||
it('should return true for valid token and non-expired time', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const futureExpiry = new Date(Date.now() + 60 * 60 * 1000); // 1 hour from now
|
||||
|
||||
mockUser.verificationToken = validToken;
|
||||
mockUser.verificationTokenExpiry = futureExpiry;
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should return false for missing token', () => {
|
||||
mockUser.verificationToken = null;
|
||||
mockUser.verificationTokenExpiry = new Date(Date.now() + 60 * 60 * 1000);
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, 'any-token');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for missing expiry', () => {
|
||||
mockUser.verificationToken = 'valid-token';
|
||||
mockUser.verificationTokenExpiry = null;
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, 'valid-token');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for mismatched token', () => {
|
||||
mockUser.verificationToken = 'correct-token';
|
||||
mockUser.verificationTokenExpiry = new Date(Date.now() + 60 * 60 * 1000);
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, 'wrong-token');
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for expired token', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const pastExpiry = new Date(Date.now() - 60 * 60 * 1000); // 1 hour ago
|
||||
|
||||
mockUser.verificationToken = validToken;
|
||||
mockUser.verificationTokenExpiry = pastExpiry;
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should return false for token expiring in the past by 1 second', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const pastExpiry = new Date(Date.now() - 1000); // 1 second ago
|
||||
|
||||
mockUser.verificationToken = validToken;
|
||||
mockUser.verificationTokenExpiry = pastExpiry;
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(false);
|
||||
});
|
||||
|
||||
it('should handle edge case of token expiring exactly now', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
// Set expiry 1ms in the future to handle timing precision
|
||||
const nowExpiry = new Date(Date.now() + 1);
|
||||
|
||||
mockUser.verificationToken = validToken;
|
||||
mockUser.verificationTokenExpiry = nowExpiry;
|
||||
|
||||
// This should be true because expiry is slightly in the future
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
|
||||
it('should handle string dates correctly', () => {
|
||||
const validToken = 'valid-token-123';
|
||||
const futureExpiry = new Date(Date.now() + 60 * 60 * 1000).toISOString(); // String date
|
||||
|
||||
mockUser.verificationToken = validToken;
|
||||
mockUser.verificationTokenExpiry = futureExpiry;
|
||||
|
||||
const result = User.prototype.isVerificationTokenValid.call(mockUser, validToken);
|
||||
|
||||
expect(result).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('verifyEmail', () => {
|
||||
it('should mark user as verified and clear token fields', async () => {
|
||||
mockUser.verificationToken = 'some-token';
|
||||
mockUser.verificationTokenExpiry = new Date();
|
||||
|
||||
await User.prototype.verifyEmail.call(mockUser);
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledWith(
|
||||
expect.objectContaining({
|
||||
isVerified: true,
|
||||
verificationToken: null,
|
||||
verificationTokenExpiry: null
|
||||
})
|
||||
);
|
||||
});
|
||||
|
||||
it('should set verifiedAt timestamp', async () => {
|
||||
const beforeTime = Date.now();
|
||||
|
||||
await User.prototype.verifyEmail.call(mockUser);
|
||||
|
||||
const updateCall = mockUser.update.mock.calls[0][0];
|
||||
const verifiedAtTime = updateCall.verifiedAt.getTime();
|
||||
const afterTime = Date.now();
|
||||
|
||||
expect(verifiedAtTime).toBeGreaterThanOrEqual(beforeTime);
|
||||
expect(verifiedAtTime).toBeLessThanOrEqual(afterTime);
|
||||
});
|
||||
|
||||
it('should return updated user object', async () => {
|
||||
const result = await User.prototype.verifyEmail.call(mockUser);
|
||||
|
||||
expect(result.isVerified).toBe(true);
|
||||
expect(result.verificationToken).toBe(null);
|
||||
expect(result.verificationTokenExpiry).toBe(null);
|
||||
expect(result.verifiedAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should call update only once', async () => {
|
||||
await User.prototype.verifyEmail.call(mockUser);
|
||||
|
||||
expect(mockUser.update).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe('Complete verification flow', () => {
|
||||
it('should complete full verification flow successfully', async () => {
|
||||
// Step 1: Generate verification token
|
||||
const mockRandomBytes = Buffer.from('c'.repeat(32));
|
||||
const mockToken = mockRandomBytes.toString('hex');
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generateVerificationToken.call(mockUser);
|
||||
|
||||
expect(mockUser.verificationToken).toBe(mockToken);
|
||||
expect(mockUser.verificationTokenExpiry).toBeInstanceOf(Date);
|
||||
|
||||
// Step 2: Validate token
|
||||
const isValid = User.prototype.isVerificationTokenValid.call(mockUser, mockToken);
|
||||
expect(isValid).toBe(true);
|
||||
|
||||
// Step 3: Verify email
|
||||
await User.prototype.verifyEmail.call(mockUser);
|
||||
|
||||
expect(mockUser.isVerified).toBe(true);
|
||||
expect(mockUser.verificationToken).toBe(null);
|
||||
expect(mockUser.verificationTokenExpiry).toBe(null);
|
||||
expect(mockUser.verifiedAt).toBeInstanceOf(Date);
|
||||
});
|
||||
|
||||
it('should fail verification with wrong token', async () => {
|
||||
// Generate token
|
||||
const mockToken = 'd'.repeat(64);
|
||||
const mockRandomBytes = Buffer.from('d'.repeat(32));
|
||||
crypto.randomBytes.mockReturnValue(mockRandomBytes);
|
||||
|
||||
await User.prototype.generateVerificationToken.call(mockUser);
|
||||
|
||||
// Try to validate with wrong token
|
||||
const isValid = User.prototype.isVerificationTokenValid.call(mockUser, 'wrong-token');
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('should fail verification with expired token', async () => {
|
||||
// Manually set an expired token
|
||||
mockUser.verificationToken = 'expired-token';
|
||||
mockUser.verificationTokenExpiry = new Date(Date.now() - 25 * 60 * 60 * 1000); // 25 hours ago
|
||||
|
||||
const isValid = User.prototype.isVerificationTokenValid.call(mockUser, 'expired-token');
|
||||
|
||||
expect(isValid).toBe(false);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user