321 lines
11 KiB
JavaScript
321 lines
11 KiB
JavaScript
const { execSync, spawn } = require('child_process');
|
|
const path = require('path');
|
|
const { User } = require('../../models');
|
|
|
|
describe('User Create Script', () => {
|
|
const scriptPath = path.join(__dirname, '../../scripts/user-create.js');
|
|
|
|
// Helper function to run the script and capture output
|
|
const runUserCreateScript = (args = []) => {
|
|
return new Promise((resolve, reject) => {
|
|
const child = spawn('node', [scriptPath, ...args], {
|
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
env: { ...process.env, NODE_ENV: 'test' },
|
|
});
|
|
|
|
let stdout = '';
|
|
let stderr = '';
|
|
|
|
child.stdout.on('data', (data) => {
|
|
stdout += data.toString();
|
|
});
|
|
|
|
child.stderr.on('data', (data) => {
|
|
stderr += data.toString();
|
|
});
|
|
|
|
child.on('close', (code) => {
|
|
resolve({
|
|
code,
|
|
stdout: stdout.trim(),
|
|
stderr: stderr.trim(),
|
|
});
|
|
});
|
|
|
|
child.on('error', (error) => {
|
|
reject(error);
|
|
});
|
|
});
|
|
};
|
|
|
|
afterEach(async () => {
|
|
// Clean up any test users created during tests
|
|
await User.destroy({
|
|
where: {
|
|
email: [
|
|
'testuser@example.com',
|
|
'admin@example.com',
|
|
'invalid-email',
|
|
'existing@example.com',
|
|
],
|
|
},
|
|
});
|
|
});
|
|
|
|
describe('Success Cases', () => {
|
|
it('should create a new user with valid email and password', async () => {
|
|
const email = 'testuser@example.com';
|
|
const password = 'securepassword123';
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Verify user was actually created in database
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
expect(createdUser.email).toBe(email);
|
|
expect(createdUser.password_digest).toBeTruthy();
|
|
expect(createdUser.password_digest).not.toBe(password); // Should be hashed
|
|
});
|
|
|
|
it('should create user with minimum password length', async () => {
|
|
const email = 'testuser2@example.com';
|
|
const password = '123456'; // Exactly 6 characters
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Verify user was created
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
});
|
|
|
|
it('should create user with complex email format', async () => {
|
|
const email = 'user.name+tag@sub.domain.com';
|
|
const password = 'password123';
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Verify user was created
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
});
|
|
});
|
|
|
|
describe('Error Cases', () => {
|
|
it('should show usage when no arguments provided', async () => {
|
|
const result = await runUserCreateScript([]);
|
|
|
|
expect(result.code).toBe(1);
|
|
});
|
|
|
|
it('should show usage when only email provided', async () => {
|
|
const result = await runUserCreateScript(['test@example.com']);
|
|
|
|
expect(result.code).toBe(1);
|
|
});
|
|
|
|
it('should show usage when only password provided', async () => {
|
|
const result = await runUserCreateScript(['', 'password123']);
|
|
|
|
expect(result.code).toBe(1);
|
|
});
|
|
|
|
it('should reject invalid email format', async () => {
|
|
const invalidEmails = [
|
|
'invalid-email',
|
|
'missing@domain',
|
|
'@missing-local.com',
|
|
'spaces in@email.com',
|
|
'double@@domain.com',
|
|
'trailing.dot.@domain.com',
|
|
];
|
|
|
|
for (const email of invalidEmails) {
|
|
const result = await runUserCreateScript([
|
|
email,
|
|
'password123',
|
|
]);
|
|
|
|
expect(result.code).toBe(1);
|
|
}
|
|
});
|
|
|
|
it('should reject password shorter than 6 characters', async () => {
|
|
const shortPasswords = ['', '1', '12', '123', '1234', '12345'];
|
|
|
|
for (const password of shortPasswords) {
|
|
const result = await runUserCreateScript([
|
|
'test@example.com',
|
|
password,
|
|
]);
|
|
|
|
expect(result.code).toBe(1);
|
|
}
|
|
}, 15000);
|
|
|
|
it('should update password for existing user', async () => {
|
|
const email = 'existing@example.com';
|
|
const password = 'password123';
|
|
const newPassword = 'newpassword456';
|
|
|
|
// Create user first
|
|
await User.create({
|
|
email,
|
|
password_digest: await require('bcrypt').hash(password, 10),
|
|
});
|
|
|
|
// Try to create same user again (should update password)
|
|
const result = await runUserCreateScript([email, newPassword]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Verify user still exists and password was updated
|
|
const updatedUser = await User.findOne({ where: { email } });
|
|
expect(updatedUser).toBeTruthy();
|
|
|
|
// Verify the password was updated
|
|
const bcrypt = require('bcrypt');
|
|
const isValid = await bcrypt.compare(
|
|
newPassword,
|
|
updatedUser.password_digest
|
|
);
|
|
expect(isValid).toBe(true);
|
|
});
|
|
});
|
|
|
|
describe('Integration with npm script', () => {
|
|
it('should work when called via npm run command', async () => {
|
|
const email = 'npmtest@example.com';
|
|
const password = 'testpassword123';
|
|
|
|
// Clean up any existing user first
|
|
await User.destroy({ where: { email } });
|
|
|
|
try {
|
|
// This simulates running: npm run user:create npmtest@example.com testpassword123
|
|
const output = execSync(
|
|
`npm run user:create ${email} ${password}`,
|
|
{
|
|
cwd: path.join(__dirname, '../..'),
|
|
env: { ...process.env, NODE_ENV: 'test' },
|
|
encoding: 'utf8',
|
|
timeout: 10000,
|
|
}
|
|
);
|
|
|
|
expect(output).toBeTruthy();
|
|
|
|
// Verify user was created
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
expect(createdUser.email).toBe(email);
|
|
} finally {
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
}
|
|
});
|
|
});
|
|
|
|
describe('Database Validation', () => {
|
|
it('should hash password properly', async () => {
|
|
const email = 'hashtest@example.com';
|
|
const password = 'plaintextpassword';
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
|
|
// Password should be hashed (bcrypt hashes start with $2b$)
|
|
expect(createdUser.password_digest).toMatch(/^\$2b\$10\$/);
|
|
expect(createdUser.password_digest).not.toBe(password);
|
|
expect(createdUser.password_digest.length).toBeGreaterThan(50);
|
|
|
|
// Verify the hash is valid
|
|
const bcrypt = require('bcrypt');
|
|
const isValid = await bcrypt.compare(
|
|
password,
|
|
createdUser.password_digest
|
|
);
|
|
expect(isValid).toBe(true);
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
});
|
|
|
|
it('should set correct default values', async () => {
|
|
const email = 'defaultstest@example.com';
|
|
const password = 'password123';
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
|
|
// Check that created_at and updated_at are set
|
|
expect(createdUser.created_at).toBeTruthy();
|
|
expect(createdUser.updated_at).toBeTruthy();
|
|
|
|
// Check that it's a valid date
|
|
expect(createdUser.created_at instanceof Date).toBe(true);
|
|
expect(createdUser.updated_at instanceof Date).toBe(true);
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
});
|
|
});
|
|
|
|
describe('Edge Cases', () => {
|
|
it('should handle special characters in password', async () => {
|
|
const email = 'specialchars@example.com';
|
|
const password = 'p@ssw0rd!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Verify user was created and password works
|
|
const createdUser = await User.findOne({ where: { email } });
|
|
expect(createdUser).toBeTruthy();
|
|
|
|
const bcrypt = require('bcrypt');
|
|
const isValid = await bcrypt.compare(
|
|
password,
|
|
createdUser.password_digest
|
|
);
|
|
expect(isValid).toBe(true);
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
});
|
|
|
|
it('should handle very long email', async () => {
|
|
const longEmail = 'a'.repeat(50) + '@' + 'b'.repeat(50) + '.com';
|
|
const password = 'password123';
|
|
|
|
const result = await runUserCreateScript([longEmail, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email: longEmail } });
|
|
});
|
|
|
|
it('should handle very long password', async () => {
|
|
const email = 'longpassword@example.com';
|
|
const password = 'a'.repeat(200); // Very long password
|
|
|
|
const result = await runUserCreateScript([email, password]);
|
|
|
|
expect(result.code).toBe(0);
|
|
|
|
// Clean up
|
|
await User.destroy({ where: { email } });
|
|
});
|
|
});
|
|
});
|