chore: sync infra docs and coverage
This commit is contained in:
79
backend/src/tests/applications.test.js
Normal file
79
backend/src/tests/applications.test.js
Normal file
@@ -0,0 +1,79 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const { registerUser, createEmployerProfile } = require('./utils');
|
||||
|
||||
async function createJob(token) {
|
||||
const response = await request(app)
|
||||
.post('/api/jobs')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({
|
||||
title: 'QA Engineer',
|
||||
description: 'Ensure software quality',
|
||||
requirements: ['Attention to detail'],
|
||||
responsibilities: ['Write test plans'],
|
||||
location: 'Remote',
|
||||
employmentType: 'full-time',
|
||||
remoteAllowed: true
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
return response.body.job;
|
||||
}
|
||||
|
||||
describe('Applications API', () => {
|
||||
it('supports full application lifecycle', async () => {
|
||||
const { token: employerToken } = await registerUser('employer');
|
||||
await createEmployerProfile(employerToken);
|
||||
const job = await createJob(employerToken);
|
||||
|
||||
const { token: candidateToken } = await registerUser('candidate');
|
||||
|
||||
await request(app)
|
||||
.post('/api/candidates')
|
||||
.set('Authorization', `Bearer ${candidateToken}`)
|
||||
.send({
|
||||
location: 'Remote',
|
||||
skills: ['Automation'],
|
||||
experienceLevel: 'mid'
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const applicationResponse = await request(app)
|
||||
.post('/api/applications')
|
||||
.set('Authorization', `Bearer ${candidateToken}`)
|
||||
.send({
|
||||
jobId: job.id,
|
||||
coverLetter: 'Excited to apply'
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const applicationId = applicationResponse.body.application.id;
|
||||
|
||||
const listForCandidate = await request(app)
|
||||
.get('/api/applications')
|
||||
.set('Authorization', `Bearer ${candidateToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(listForCandidate.body.applications).toHaveLength(1);
|
||||
|
||||
await request(app)
|
||||
.put(`/api/applications/${applicationId}/status`)
|
||||
.set('Authorization', `Bearer ${employerToken}`)
|
||||
.send({ status: 'reviewed' })
|
||||
.expect(200);
|
||||
|
||||
await request(app)
|
||||
.put(`/api/applications/${applicationId}/notes`)
|
||||
.set('Authorization', `Bearer ${employerToken}`)
|
||||
.send({ notes: 'Strong automation background' })
|
||||
.expect(200);
|
||||
|
||||
const detailResponse = await request(app)
|
||||
.get(`/api/applications/${applicationId}`)
|
||||
.set('Authorization', `Bearer ${employerToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(detailResponse.body.status).toBe('reviewed');
|
||||
expect(detailResponse.body.notes).toBe('Strong automation background');
|
||||
});
|
||||
});
|
||||
@@ -1,23 +1,13 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const pool = require('../database/connection');
|
||||
|
||||
describe('Authentication', () => {
|
||||
beforeEach(async () => {
|
||||
// Clean up database before each test
|
||||
await pool.query('DELETE FROM users WHERE email LIKE $1', ['test%']);
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await pool.end();
|
||||
});
|
||||
|
||||
describe('POST /api/auth/register', () => {
|
||||
it('should register a new user successfully', async () => {
|
||||
const email = `test-${Date.now()}@example.com`;
|
||||
const userData = {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test@example.com',
|
||||
email,
|
||||
password: 'password123',
|
||||
role: 'candidate'
|
||||
};
|
||||
@@ -34,10 +24,11 @@ describe('Authentication', () => {
|
||||
});
|
||||
|
||||
it('should return error for duplicate email', async () => {
|
||||
const email = `test-${Date.now()}@example.com`;
|
||||
const userData = {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test@example.com',
|
||||
email,
|
||||
password: 'password123',
|
||||
role: 'candidate'
|
||||
};
|
||||
@@ -57,10 +48,11 @@ describe('Authentication', () => {
|
||||
});
|
||||
|
||||
it('should return error for invalid role', async () => {
|
||||
const email = `test-${Date.now()}@example.com`;
|
||||
const userData = {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test@example.com',
|
||||
email,
|
||||
password: 'password123',
|
||||
role: 'invalid_role'
|
||||
};
|
||||
@@ -75,12 +67,15 @@ describe('Authentication', () => {
|
||||
});
|
||||
|
||||
describe('POST /api/auth/login', () => {
|
||||
let loginEmail;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a test user
|
||||
loginEmail = `test-${Date.now()}@example.com`;
|
||||
const userData = {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test@example.com',
|
||||
email: loginEmail,
|
||||
password: 'password123',
|
||||
role: 'candidate'
|
||||
};
|
||||
@@ -92,7 +87,7 @@ describe('Authentication', () => {
|
||||
|
||||
it('should login with valid credentials', async () => {
|
||||
const loginData = {
|
||||
email: 'test@example.com',
|
||||
email: loginEmail,
|
||||
password: 'password123'
|
||||
};
|
||||
|
||||
@@ -108,7 +103,7 @@ describe('Authentication', () => {
|
||||
|
||||
it('should return error for invalid credentials', async () => {
|
||||
const loginData = {
|
||||
email: 'test@example.com',
|
||||
email: loginEmail,
|
||||
password: 'wrongpassword'
|
||||
};
|
||||
|
||||
@@ -137,13 +132,15 @@ describe('Authentication', () => {
|
||||
|
||||
describe('GET /api/auth/me', () => {
|
||||
let token;
|
||||
let email;
|
||||
|
||||
beforeEach(async () => {
|
||||
// Create a test user and get token
|
||||
email = `test-${Date.now()}@example.com`;
|
||||
const userData = {
|
||||
firstName: 'Test',
|
||||
lastName: 'User',
|
||||
email: 'test@example.com',
|
||||
email,
|
||||
password: 'password123',
|
||||
role: 'candidate'
|
||||
};
|
||||
@@ -162,7 +159,7 @@ describe('Authentication', () => {
|
||||
.expect(200);
|
||||
|
||||
expect(response.body).toHaveProperty('user');
|
||||
expect(response.body.user.email).toBe('test@example.com');
|
||||
expect(response.body.user.email).toBe(email);
|
||||
});
|
||||
|
||||
it('should return error without token', async () => {
|
||||
|
||||
44
backend/src/tests/candidates.test.js
Normal file
44
backend/src/tests/candidates.test.js
Normal file
@@ -0,0 +1,44 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const { registerUser } = require('./utils');
|
||||
|
||||
describe('Candidates API', () => {
|
||||
it('creates and retrieves candidate profiles with filters', async () => {
|
||||
const { token } = await registerUser('candidate');
|
||||
|
||||
const createResponse = await request(app)
|
||||
.post('/api/candidates')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({
|
||||
phone: '+1-555-5555',
|
||||
location: 'Austin, TX',
|
||||
skills: ['JavaScript', 'React'],
|
||||
experienceLevel: 'mid'
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const candidateId = createResponse.body.candidate.id;
|
||||
|
||||
const listResponse = await request(app)
|
||||
.get('/api/candidates?skills=React&location=Austin')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
expect(listResponse.body.candidates).toHaveLength(1);
|
||||
|
||||
const detailResponse = await request(app)
|
||||
.get(`/api/candidates/${candidateId}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
expect(detailResponse.body.skills).toContain('React');
|
||||
|
||||
const updateResponse = await request(app)
|
||||
.put(`/api/candidates/${candidateId}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ availability: 'Immediate' })
|
||||
.expect(200);
|
||||
|
||||
expect(updateResponse.body.candidate.availability).toBe('Immediate');
|
||||
});
|
||||
});
|
||||
34
backend/src/tests/employers.test.js
Normal file
34
backend/src/tests/employers.test.js
Normal file
@@ -0,0 +1,34 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const { registerUser, createEmployerProfile } = require('./utils');
|
||||
|
||||
describe('Employers API', () => {
|
||||
it('allows an employer to create and update their profile', async () => {
|
||||
const { token } = await registerUser('employer');
|
||||
|
||||
const employer = await createEmployerProfile(token, {
|
||||
companyName: 'Acme Corp',
|
||||
industry: 'Manufacturing'
|
||||
});
|
||||
|
||||
expect(employer).toMatchObject({
|
||||
company_name: 'Acme Corp',
|
||||
industry: 'Manufacturing'
|
||||
});
|
||||
|
||||
const listResponse = await request(app)
|
||||
.get('/api/employers')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
expect(listResponse.body.length).toBeGreaterThan(0);
|
||||
|
||||
const updateResponse = await request(app)
|
||||
.put(`/api/employers/${employer.id}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ description: 'Updated description' })
|
||||
.expect(200);
|
||||
|
||||
expect(updateResponse.body.employer.description).toBe('Updated description');
|
||||
});
|
||||
});
|
||||
1
backend/src/tests/fixtures/resume.txt
vendored
Normal file
1
backend/src/tests/fixtures/resume.txt
vendored
Normal file
@@ -0,0 +1 @@
|
||||
Sample resume content for testing.
|
||||
65
backend/src/tests/globalSetup.js
Normal file
65
backend/src/tests/globalSetup.js
Normal file
@@ -0,0 +1,65 @@
|
||||
const { spawnSync } = require('child_process');
|
||||
const path = require('path');
|
||||
|
||||
const sleep = (ms) => Atomics.wait(new Int32Array(new SharedArrayBuffer(4)), 0, 0, ms);
|
||||
|
||||
module.exports = async () => {
|
||||
process.env.NODE_ENV = process.env.NODE_ENV || 'test';
|
||||
process.env.JWT_SECRET = process.env.JWT_SECRET || 'merchantsofhope_test_secret';
|
||||
process.env.POSTGRES_DB = process.env.POSTGRES_DB || 'merchantsofhope_test';
|
||||
process.env.POSTGRES_USER = process.env.POSTGRES_USER || 'postgres';
|
||||
process.env.POSTGRES_PASSWORD = process.env.POSTGRES_PASSWORD || 'postgres';
|
||||
process.env.POSTGRES_HOST = process.env.POSTGRES_HOST || '127.0.0.1';
|
||||
process.env.POSTGRES_PORT = process.env.POSTGRES_PORT || '55432';
|
||||
|
||||
const composeFile = path.join(__dirname, '..', '..', '..', 'docker-compose.test.yml');
|
||||
|
||||
const upResult = spawnSync(
|
||||
'docker',
|
||||
['compose', '-f', composeFile, 'up', '-d', 'merchantsofhope-supplyanddemandportal-test-database'],
|
||||
{
|
||||
cwd: path.join(__dirname, '..', '..'),
|
||||
stdio: 'inherit',
|
||||
env: process.env
|
||||
}
|
||||
);
|
||||
|
||||
if (upResult.status !== 0) {
|
||||
throw new Error('Failed to start test database container');
|
||||
}
|
||||
|
||||
let ready = false;
|
||||
for (let attempt = 0; attempt < 30; attempt += 1) {
|
||||
const health = spawnSync(
|
||||
'docker',
|
||||
['compose', '-f', composeFile, 'exec', '-T', 'merchantsofhope-supplyanddemandportal-test-database', 'pg_isready', '-U', process.env.POSTGRES_USER],
|
||||
{
|
||||
cwd: path.join(__dirname, '..', '..'),
|
||||
env: process.env,
|
||||
stdio: 'ignore'
|
||||
}
|
||||
);
|
||||
|
||||
if (health.status === 0) {
|
||||
ready = true;
|
||||
break;
|
||||
}
|
||||
|
||||
sleep(1000);
|
||||
}
|
||||
|
||||
if (!ready) {
|
||||
throw new Error('Database did not become ready in time');
|
||||
}
|
||||
|
||||
const migratePath = path.join(__dirname, '..', 'database', 'migrate.js');
|
||||
const result = spawnSync('node', [migratePath], {
|
||||
cwd: path.join(__dirname, '..', '..'),
|
||||
stdio: 'inherit',
|
||||
env: process.env
|
||||
});
|
||||
|
||||
if (result.status !== 0) {
|
||||
throw new Error('Database migration failed before running tests');
|
||||
}
|
||||
};
|
||||
20
backend/src/tests/globalTeardown.js
Normal file
20
backend/src/tests/globalTeardown.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const { spawnSync } = require('child_process');
|
||||
const path = require('path');
|
||||
const pool = require('../database/connection');
|
||||
const { cleanupUploads } = require('./utils');
|
||||
|
||||
module.exports = async () => {
|
||||
await cleanupUploads();
|
||||
await pool.end();
|
||||
|
||||
const composeFile = path.join(__dirname, '..', '..', '..', 'docker-compose.test.yml');
|
||||
spawnSync(
|
||||
'docker',
|
||||
['compose', '-f', composeFile, 'down', '--volumes'],
|
||||
{
|
||||
cwd: path.join(__dirname, '..', '..'),
|
||||
stdio: 'inherit',
|
||||
env: process.env
|
||||
}
|
||||
);
|
||||
};
|
||||
@@ -1,48 +1,14 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const pool = require('../database/connection');
|
||||
const { registerUser, createEmployerProfile } = require('./utils');
|
||||
|
||||
describe('Jobs API', () => {
|
||||
let authToken;
|
||||
let employerId;
|
||||
|
||||
beforeAll(async () => {
|
||||
// Create test user and get auth token
|
||||
const userData = {
|
||||
firstName: 'Test',
|
||||
lastName: 'Employer',
|
||||
email: 'employer@test.com',
|
||||
password: 'password123',
|
||||
role: 'employer'
|
||||
};
|
||||
|
||||
const registerResponse = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send(userData);
|
||||
|
||||
authToken = registerResponse.body.token;
|
||||
|
||||
// Create employer profile
|
||||
const employerData = {
|
||||
companyName: 'Test Company',
|
||||
industry: 'Technology',
|
||||
companySize: '50-200',
|
||||
website: 'https://testcompany.com',
|
||||
description: 'A test company',
|
||||
address: '123 Test St',
|
||||
phone: '+1-555-0123'
|
||||
};
|
||||
|
||||
const employerResponse = await request(app)
|
||||
.post('/api/employers')
|
||||
.set('Authorization', `Bearer ${authToken}`)
|
||||
.send(employerData);
|
||||
|
||||
employerId = employerResponse.body.employer.id;
|
||||
});
|
||||
|
||||
afterAll(async () => {
|
||||
await pool.end();
|
||||
beforeEach(async () => {
|
||||
const { token } = await registerUser('employer');
|
||||
authToken = token;
|
||||
await createEmployerProfile(token);
|
||||
});
|
||||
|
||||
describe('POST /api/jobs', () => {
|
||||
|
||||
55
backend/src/tests/resumes.test.js
Normal file
55
backend/src/tests/resumes.test.js
Normal file
@@ -0,0 +1,55 @@
|
||||
const path = require('path');
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const { registerUser, ensureUploadDir } = require('./utils');
|
||||
|
||||
describe('Resumes API', () => {
|
||||
it('handles resume upload lifecycle for candidates', async () => {
|
||||
const { token } = await registerUser('candidate');
|
||||
|
||||
const candidateResponse = await request(app)
|
||||
.post('/api/candidates')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({
|
||||
location: 'Houston, TX',
|
||||
skills: ['Node.js']
|
||||
})
|
||||
.expect(201);
|
||||
|
||||
const candidateId = candidateResponse.body.candidate.id;
|
||||
const uploadDir = await ensureUploadDir();
|
||||
expect(uploadDir).toBeTruthy();
|
||||
|
||||
const uploadResponse = await request(app)
|
||||
.post('/api/resumes/upload')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.field('isPrimary', 'true')
|
||||
.attach('resume', path.join(__dirname, 'fixtures', 'resume.txt'))
|
||||
.expect(201);
|
||||
|
||||
const resumeId = uploadResponse.body.resume.id;
|
||||
|
||||
const listResponse = await request(app)
|
||||
.get(`/api/resumes/candidate/${candidateId}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
expect(listResponse.body).toHaveLength(1);
|
||||
expect(listResponse.body[0].is_primary).toBe(true);
|
||||
|
||||
await request(app)
|
||||
.put(`/api/resumes/${resumeId}/primary`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
await request(app)
|
||||
.get(`/api/resumes/${resumeId}/download`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
|
||||
await request(app)
|
||||
.delete(`/api/resumes/${resumeId}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.expect(200);
|
||||
});
|
||||
});
|
||||
17
backend/src/tests/setup.js
Normal file
17
backend/src/tests/setup.js
Normal file
@@ -0,0 +1,17 @@
|
||||
const pool = require('../database/connection');
|
||||
const { cleanupUploads } = require('./utils');
|
||||
|
||||
afterEach(async () => {
|
||||
await pool.query(`
|
||||
TRUNCATE TABLE
|
||||
applications,
|
||||
resumes,
|
||||
interviews,
|
||||
jobs,
|
||||
candidates,
|
||||
employers,
|
||||
users
|
||||
RESTART IDENTITY CASCADE
|
||||
`);
|
||||
await cleanupUploads();
|
||||
});
|
||||
35
backend/src/tests/users.test.js
Normal file
35
backend/src/tests/users.test.js
Normal file
@@ -0,0 +1,35 @@
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const { registerUser, createAdminUser } = require('./utils');
|
||||
|
||||
describe('Users API', () => {
|
||||
it('allows admin to list users and manage activation', async () => {
|
||||
const { token: adminToken } = await createAdminUser();
|
||||
const { user, token } = await registerUser('candidate');
|
||||
|
||||
const listResponse = await request(app)
|
||||
.get('/api/users')
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(Array.isArray(listResponse.body)).toBe(true);
|
||||
|
||||
await request(app)
|
||||
.put(`/api/users/${user.id}`)
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send({ firstName: 'Updated' })
|
||||
.expect(200);
|
||||
|
||||
await request(app)
|
||||
.put(`/api/users/${user.id}/deactivate`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
const reactivate = await request(app)
|
||||
.put(`/api/users/${user.id}/activate`)
|
||||
.set('Authorization', `Bearer ${adminToken}`)
|
||||
.expect(200);
|
||||
|
||||
expect(reactivate.body.user.is_active).toBe(true);
|
||||
});
|
||||
});
|
||||
112
backend/src/tests/utils.js
Normal file
112
backend/src/tests/utils.js
Normal file
@@ -0,0 +1,112 @@
|
||||
const { randomUUID } = require('crypto');
|
||||
const path = require('path');
|
||||
const fs = require('fs');
|
||||
const bcrypt = require('bcryptjs');
|
||||
const request = require('supertest');
|
||||
const app = require('../server');
|
||||
const config = require('../config');
|
||||
const pool = require('../database/connection');
|
||||
|
||||
const uniqueEmail = (prefix = 'user') => `${prefix}-${randomUUID()}@example.com`;
|
||||
|
||||
const defaultPassword = 'Password123!';
|
||||
|
||||
async function registerUser(role = 'candidate', overrides = {}) {
|
||||
const userData = {
|
||||
firstName: overrides.firstName || 'Test',
|
||||
lastName: overrides.lastName || 'User',
|
||||
email: overrides.email || uniqueEmail(role),
|
||||
password: overrides.password || defaultPassword,
|
||||
role,
|
||||
...overrides
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/auth/register')
|
||||
.send(userData);
|
||||
|
||||
return {
|
||||
token: response.body.token,
|
||||
user: response.body.user,
|
||||
credentials: {
|
||||
email: userData.email,
|
||||
password: userData.password
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
async function createEmployerProfile(token, overrides = {}) {
|
||||
const employerData = {
|
||||
companyName: overrides.companyName || `Company ${randomUUID().slice(0, 8)}`,
|
||||
industry: overrides.industry || 'Technology',
|
||||
companySize: overrides.companySize || '11-50',
|
||||
website: overrides.website || 'https://example.com',
|
||||
description: overrides.description || 'Test company description',
|
||||
address: overrides.address || '123 Test St',
|
||||
phone: overrides.phone || '+1-555-0000',
|
||||
...overrides
|
||||
};
|
||||
|
||||
const response = await request(app)
|
||||
.post('/api/employers')
|
||||
.set('Authorization', `Bearer ${token}`)
|
||||
.send(employerData);
|
||||
|
||||
return response.body.employer;
|
||||
}
|
||||
|
||||
async function createAdminUser() {
|
||||
const email = uniqueEmail('admin');
|
||||
const password = defaultPassword;
|
||||
const passwordHash = await bcrypt.hash(password, 10);
|
||||
|
||||
const result = await pool.query(
|
||||
'INSERT INTO users (email, password_hash, first_name, last_name, role) VALUES ($1, $2, $3, $4, $5) RETURNING id',
|
||||
[email, passwordHash, 'Admin', 'User', 'admin']
|
||||
);
|
||||
|
||||
const loginResponse = await request(app)
|
||||
.post('/api/auth/login')
|
||||
.send({ email, password });
|
||||
|
||||
return {
|
||||
token: loginResponse.body.token,
|
||||
user: loginResponse.body.user,
|
||||
userId: result.rows[0].id,
|
||||
credentials: { email, password }
|
||||
};
|
||||
}
|
||||
|
||||
async function ensureUploadDir() {
|
||||
const uploadDir = path.join(__dirname, '..', '..', config.uploadDir);
|
||||
if (!fs.existsSync(uploadDir)) {
|
||||
fs.mkdirSync(uploadDir, { recursive: true });
|
||||
}
|
||||
return uploadDir;
|
||||
}
|
||||
|
||||
async function cleanupUploads() {
|
||||
const uploadDir = await ensureUploadDir();
|
||||
if (!fs.existsSync(uploadDir)) {
|
||||
return;
|
||||
}
|
||||
const entries = fs.readdirSync(uploadDir);
|
||||
for (const entry of entries) {
|
||||
const filePath = path.join(uploadDir, entry);
|
||||
try {
|
||||
fs.unlinkSync(filePath);
|
||||
} catch (error) {
|
||||
// Ignore deletion errors in tests
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
registerUser,
|
||||
createEmployerProfile,
|
||||
createAdminUser,
|
||||
uniqueEmail,
|
||||
defaultPassword,
|
||||
ensureUploadDir,
|
||||
cleanupUploads
|
||||
};
|
||||
Reference in New Issue
Block a user