Files
internify-be/test/test_batch_generate_certificate.js

338 lines
12 KiB
JavaScript

const axios = require('axios');
const bcrypt = require('bcrypt');
const { PrismaClient } = require('../src/generated/prisma');
const BASE_URL = 'http://localhost:9000';
const prisma = new PrismaClient();
function createMultipartPayload(boundary, fields, files) {
const chunks = [];
for (const [key, value] of Object.entries(fields)) {
if (value !== undefined && value !== null) {
chunks.push(Buffer.from(`--${boundary}\r\n` +
`Content-Disposition: form-data; name="${key}"\r\n\r\n` +
`${value}\r\n`));
}
}
for (const [key, file] of Object.entries(files)) {
if (file) {
chunks.push(Buffer.from(`--${boundary}\r\n` +
`Content-Disposition: form-data; name="${key}"; filename="${file.name}"\r\n` +
`Content-Type: ${file.type}\r\n\r\n`));
chunks.push(file.content);
chunks.push(Buffer.from('\r\n'));
}
}
chunks.push(Buffer.from(`--${boundary}--\r\n`));
return Buffer.concat(chunks);
}
async function registerIntern(adminToken, email, firstName, lastName) {
// Create lowongan magang
const lwnBoundary = '----WebKitFormBoundaryLowonganUpload';
const lwnFields = {
posisi: `Web Developer Test - ${Date.now()}`,
kelompok_peminatan: 'Software Engineering',
jobdesk: 'Testing batch certificate generation.',
lokasi: 'Remote',
kualifikasi: 'NodeJS',
benefit: 'Certificate',
durasi_awal: '2026-07-01',
durasi_akhir: '2026-10-01',
paid: 'unpaid'
};
const lwnFiles = {
image: { name: 'poster.png', type: 'image/png', content: Buffer.from('fake-image-data') }
};
const lwnPayload = createMultipartPayload(lwnBoundary, lwnFields, lwnFiles);
const createLwnRes = await axios.post(
`${BASE_URL}/lowongan-magang-api/add`,
lwnPayload,
{
headers: {
Authorization: `Bearer ${adminToken}`,
'Content-Type': `multipart/form-data; boundary=${lwnBoundary}`
}
}
);
const lowonganId = createLwnRes.data.data.id;
// Submit application
const appBoundary = '----WebKitFormBoundaryApplicationSubmission';
const appFields = {
nama_depan: firstName,
nama_belakang: lastName,
email: email,
kontak: '08123456789',
jurusan: 'Computer Science',
universitas: 'Intern University',
negara: 'Indonesia',
motivasi: 'I want to learn.',
relevant_skills: 'NodeJS, React'
};
const appFiles = {
cv: { name: 'cv.pdf', type: 'application/pdf', content: Buffer.from('fake-pdf-cv') },
portofolio: { name: 'portfolio.pdf', type: 'application/pdf', content: Buffer.from('fake-pdf-portfolio') }
};
const appPayload = createMultipartPayload(appBoundary, appFields, appFiles);
const applyRes = await axios.post(
`${BASE_URL}/lamaran-magang-api/add-mobile/${lowonganId}`,
appPayload,
{
headers: {
'Content-Type': `multipart/form-data; boundary=${appBoundary}`
}
}
);
const idMahasiswa = applyRes.data.data.id_mahasiswa;
// Wait a brief moment
await new Promise(resolve => setTimeout(resolve, 500));
// Find lamaran ID
const listLamaranRes = await axios.get(`${BASE_URL}/lamaran-magang-api/get?limit=100`, {
headers: { Authorization: `Bearer ${adminToken}` }
});
const lamaranItem = listLamaranRes.data.data.find(l => l.id_mahasiswa === idMahasiswa);
if (!lamaranItem) {
throw new Error(`Failed to find lamaran for student ID: ${idMahasiswa}`);
}
const lamaranId = lamaranItem.id;
// Accept application
await axios.patch(`${BASE_URL}/lamaran-magang-api/update/${lamaranId}`, {
status: 'diterima'
}, {
headers: { Authorization: `Bearer ${adminToken}` }
});
await new Promise(resolve => setTimeout(resolve, 1000));
// Force override password
const hashedPassword = await bcrypt.hash('password123', 10);
await prisma.user.update({
where: { email },
data: { password: hashedPassword }
});
// Login
const loginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
email,
password: 'password123'
});
const user = await prisma.user.findUnique({
where: { email }
});
return {
userId: user.id,
token: loginRes.data.data.token
};
}
async function runTests() {
console.log('=== STARTING BATCH GENERATE CERTIFICATE TESTS ===\n');
let adminToken = '';
let mentor1Token = '';
let mentor2Token = '';
let project1Id = null;
const password = 'password123';
const intern1Email = `intern1_batch_${Date.now()}@internify.com`;
const intern2Email = `intern2_batch_${Date.now()}@internify.com`;
const getHeader = (token) => ({
headers: { Authorization: `Bearer ${token}` }
});
try {
// 1. Login Admin
console.log('1. Logging in as Admin (admin1@internify.com)...');
const adminLoginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
email: 'admin1@internify.com',
password: password
});
adminToken = adminLoginRes.data.data.token;
// 2. Login Mentors
console.log('2. Logging in as Mentors...');
const mentor1Login = await axios.post(`${BASE_URL}/auth-api/login`, {
email: 'mentor1@internify.com',
password
});
mentor1Token = mentor1Login.data.data.token;
const mentor2Login = await axios.post(`${BASE_URL}/auth-api/login`, {
email: 'mentor2@internify.com',
password
});
mentor2Token = mentor2Login.data.data.token;
// 3. Register Intern 1 and Intern 2
console.log('3. Registering Intern 1 & Intern 2...');
const intern1 = await registerIntern(adminToken, intern1Email, 'InternOne', 'Batch');
console.log(` Registered Intern 1: User ID ${intern1.userId}`);
const intern2 = await registerIntern(adminToken, intern2Email, 'InternTwo', 'Batch');
console.log(` Registered Intern 2: User ID ${intern2.userId}`);
// 4. Create Project owned by Mentor 1 and assign both interns
console.log('4. Creating Project under Mentor 1 with both interns...');
const createProjectRes = await axios.post(`${BASE_URL}/project-api/add`, {
project_icon: 'code',
project_name: `Batch Certificate Project - ${Date.now()}`,
description: 'Project to test batch generation.',
start_date: '2026-07-01',
end_date: '2026-10-01',
member_emails: [intern1Email, intern2Email]
}, getHeader(mentor1Token));
project1Id = createProjectRes.data.data.id;
console.log(` Project created successfully! ID: ${project1Id}`);
// 5. Create a task in the project
console.log('5. Creating a required project task...');
const task = await prisma.task.create({
data: {
id_project: project1Id,
slug: `required-task-${Date.now()}`,
title: 'Project Submission',
description: 'Complete project tasks.',
deadline_at: new Date(Date.now() + 5 * 24 * 60 * 60 * 1000),
submission_type: 'url_link'
}
});
console.log(` Task created! ID: ${task.id}`);
// 6. Submit task for Intern 1 only
console.log('6. Submitting task for Intern 1 only...');
await prisma.taskSubmission.create({
data: {
id_task: task.id,
id_user: intern1.userId,
url_link: 'https://github.com/intern1/solution'
}
});
console.log(' Intern 1 task submitted successfully. Intern 2 has NO submissions.');
// 7. Test Authorization: Call generate endpoint using Intern 1 token (Should fail - 401)
console.log('\n--- API TEST 1: Intern calls generate endpoint (Should fail - 401) ---');
try {
await axios.post(`${BASE_URL}/certificate-api/generate`, {
id_project: project1Id,
id_users: [intern1.userId]
}, getHeader(intern1.token));
throw new Error('TEST FAILED: Intern generated certificate!');
} catch (err) {
if (err.response && err.response.status === 401) {
console.log(' PASSED: Denied access to intern with 401 Unauthorized.');
} else {
throw err;
}
}
// 8. Test Authorization: Call generate endpoint using Mentor 2 token (Should fail - 403)
console.log('\n--- API TEST 2: Mentor 2 (non-owner) calls generate endpoint (Should fail - 403) ---');
try {
await axios.post(`${BASE_URL}/certificate-api/generate`, {
id_project: project1Id,
id_users: [intern1.userId]
}, getHeader(mentor2Token));
throw new Error('TEST FAILED: Unauthorized mentor generated certificate!');
} catch (err) {
if (err.response && err.response.status === 403) {
console.log(' PASSED: Denied access to unauthorized mentor with 403 Forbidden.');
} else {
throw err;
}
}
// 9. Test Eligibility: Request generation for both Intern 1 (completed) and Intern 2 (incomplete) (Should fail - 400)
console.log('\n--- API TEST 3: Generate batch containing ineligible intern (Should fail - 400) ---');
try {
await axios.post(`${BASE_URL}/certificate-api/generate`, {
id_project: project1Id,
id_users: [intern1.userId, intern2.userId]
}, getHeader(mentor1Token));
throw new Error('TEST FAILED: Generated certificate for batch containing ineligible intern!');
} catch (err) {
if (err.response && err.response.status === 400) {
console.log(' PASSED: Rejected batch due to ineligible intern with 400 Bad Request.');
console.log(' Error Message:', err.response.data.message);
} else {
throw err;
}
}
// 10. Success case: Generate certificate for Intern 1 only (Should succeed - 201)
console.log('\n--- API TEST 4: Generate certificate for eligible Intern 1 (Should succeed - 201) ---');
const genRes = await axios.post(`${BASE_URL}/certificate-api/generate`, {
id_project: project1Id,
id_users: [intern1.userId]
}, getHeader(mentor1Token));
console.log(' Response status:', genRes.status);
if (genRes.status !== 201) {
throw new Error(`TEST FAILED: Expected 201 Created, got ${genRes.status}`);
}
console.log(' Generated Certificate No:', genRes.data.data.certificates[0].certificate_no);
console.log(' PASSED: Certificate generated successfully.');
// 11. Conflict check: Try to generate for Intern 1 again (Should fail - 409)
console.log('\n--- API TEST 5: Re-generate certificate for Intern 1 (Should fail - 409) ---');
try {
await axios.post(`${BASE_URL}/certificate-api/generate`, {
id_project: project1Id,
id_users: [intern1.userId]
}, getHeader(mentor1Token));
throw new Error('TEST FAILED: Re-generated certificate for Intern 1!');
} catch (err) {
if (err.response && err.response.status === 409) {
console.log(' PASSED: Blocked duplicate generation with 409 Conflict.');
} else {
throw err;
}
}
// 12. Verify Project Details mapping: Check task submissions and certificates in details response
console.log('\n--- API TEST 6: Fetch project details and verify database mapping ---');
const projectDetailsRes = await axios.get(`${BASE_URL}/project-api/get/${project1Id}`, getHeader(mentor1Token));
const projectData = projectDetailsRes.data.data;
// Verify certificates list exists
if (!projectData.certificates || projectData.certificates.length !== 1) {
throw new Error('TEST FAILED: Certificates list in project details is missing or incorrect!');
}
console.log(' Found Certificates in Project details:', projectData.certificates);
// Verify task submissions exist
const projectTask = projectData.tasks.find(t => t.id === task.id);
if (!projectTask || !projectTask.submissions || projectTask.submissions.length !== 1) {
throw new Error('TEST FAILED: Task submissions list in project details is missing or incorrect!');
}
console.log(' Found Submissions in Project Task:', projectTask.submissions);
console.log(' PASSED: Database mapping checked successfully.');
console.log('\n=== ALL BATCH GENERATE CERTIFICATE TESTS PASSED SUCCESSFULLY! ===');
await prisma.$disconnect();
process.exit(0);
} catch (err) {
console.error('\n=== TEST EXECUTION ENCOUNTERED AN ERROR ===');
if (err.response) {
console.error(`Status: ${err.response.status}`);
console.error('Response Data:', JSON.stringify(err.response.data, null, 2));
} else {
console.error(err);
}
await prisma.$disconnect();
process.exit(1);
}
}
runTests();