feat: Nambahin feature notifikasi
This commit is contained in:
@@ -0,0 +1,209 @@
|
||||
const axios = require('axios');
|
||||
|
||||
const BASE_URL = 'http://localhost:9000';
|
||||
|
||||
async function runTests() {
|
||||
console.log('=== STARTING INTEGRATION TESTS FOR MENTOR TASK MANAGEMENT ===\n');
|
||||
|
||||
let adminToken = '';
|
||||
let mentorToken = '';
|
||||
let otherMentorToken = '';
|
||||
let createdProjectId = null;
|
||||
let otherProjectId = null;
|
||||
let taskId = null;
|
||||
|
||||
const mentorEmail = `mentor_task_${Date.now()}@internify.com`;
|
||||
const otherMentorEmail = `other_mentor_task_${Date.now()}@internify.com`;
|
||||
const mentorPassword = 'Password123';
|
||||
|
||||
// Helper config generator
|
||||
const getHeader = (token) => ({
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
try {
|
||||
// 1. Login as Admin
|
||||
console.log('1. Logging in as Admin (admin1@internify.com)...');
|
||||
const loginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
|
||||
email: 'admin1@internify.com',
|
||||
password: 'password123'
|
||||
});
|
||||
adminToken = loginRes.data.data.token;
|
||||
console.log(' Admin logged in successfully!\n');
|
||||
|
||||
// 2. Create Mentor Accounts via Admin API
|
||||
console.log(`2. Creating mentor accounts...`);
|
||||
await axios.post(`${BASE_URL}/admin-api/add`, {
|
||||
nama_depan: 'John',
|
||||
nama_belakang: 'MentorTask',
|
||||
email: mentorEmail,
|
||||
password: mentorPassword,
|
||||
role: 'mentor'
|
||||
}, getHeader(adminToken));
|
||||
|
||||
await axios.post(`${BASE_URL}/admin-api/add`, {
|
||||
nama_depan: 'Jane',
|
||||
nama_belakang: 'OtherMentorTask',
|
||||
email: otherMentorEmail,
|
||||
password: mentorPassword,
|
||||
role: 'mentor'
|
||||
}, getHeader(adminToken));
|
||||
console.log(' Mentor accounts created successfully!\n');
|
||||
|
||||
// 3. Login as Mentors
|
||||
console.log('3. Logging in as mentors...');
|
||||
const mentorLoginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
|
||||
email: mentorEmail,
|
||||
password: mentorPassword
|
||||
});
|
||||
mentorToken = mentorLoginRes.data.data.token;
|
||||
|
||||
const otherMentorLoginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
|
||||
email: otherMentorEmail,
|
||||
password: mentorPassword
|
||||
});
|
||||
otherMentorToken = otherMentorLoginRes.data.data.token;
|
||||
console.log(' Mentors logged in successfully!\n');
|
||||
|
||||
// 4. Mentors create projects
|
||||
console.log('4. Creating projects for mentors...');
|
||||
const createProjectRes = await axios.post(`${BASE_URL}/project-api/add`, {
|
||||
project_icon: 'code',
|
||||
project_name: `Mentor's Project - ${Date.now()}`,
|
||||
description: 'Project to test mentor task permissions.',
|
||||
start_date: '2026-07-01',
|
||||
end_date: '2026-10-01',
|
||||
member_emails: []
|
||||
}, getHeader(mentorToken));
|
||||
createdProjectId = createProjectRes.data.data.id;
|
||||
|
||||
const otherProjectRes = await axios.post(`${BASE_URL}/project-api/add`, {
|
||||
project_icon: 'shield',
|
||||
project_name: `Other Mentor's Project - ${Date.now()}`,
|
||||
description: 'Project owned by another mentor.',
|
||||
start_date: '2026-07-01',
|
||||
end_date: '2026-10-01',
|
||||
member_emails: []
|
||||
}, getHeader(otherMentorToken));
|
||||
otherProjectId = otherProjectRes.data.data.id;
|
||||
console.log(` Projects created: Mentor Project ID = ${createdProjectId}, Other Project ID = ${otherProjectId}\n`);
|
||||
|
||||
// 5. Mentor creates a task in their project
|
||||
console.log(`5. Mentor creating task in project ID ${createdProjectId}...`);
|
||||
const createTaskRes = await axios.post(`${BASE_URL}/task-api/projects/${createdProjectId}/tasks`, {
|
||||
title: 'Task 1',
|
||||
description: 'First task created by mentor.',
|
||||
deadline_date: '2026-08-01',
|
||||
specific_time: '23:59',
|
||||
submission_type: 'url_link'
|
||||
}, getHeader(mentorToken));
|
||||
taskId = createTaskRes.data.data.id;
|
||||
console.log(` Task created successfully! ID: ${taskId}\n`);
|
||||
|
||||
// 6. Mentor tries to create task in project they do not own
|
||||
console.log(`6. Testing isolation: Mentor trying to create task in project ID ${otherProjectId} (owned by other mentor)...`);
|
||||
try {
|
||||
await axios.post(`${BASE_URL}/task-api/projects/${otherProjectId}/tasks`, {
|
||||
title: 'Task in other project',
|
||||
description: 'Should fail.',
|
||||
deadline_date: '2026-08-01',
|
||||
specific_time: '23:59',
|
||||
submission_type: 'url_link'
|
||||
}, getHeader(mentorToken));
|
||||
throw new Error('TEST FAILED: Mentor created a task in a project they do not own!');
|
||||
} catch (err) {
|
||||
if (err.response && err.response.status === 403) {
|
||||
console.log(' PASSED: Access denied with 403 Forbidden as expected.\n');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// 7. Mentor retrieves task list of their project
|
||||
console.log(`7. Mentor retrieving task list for project ID ${createdProjectId}...`);
|
||||
const getTasksRes = await axios.get(`${BASE_URL}/task-api/projects/${createdProjectId}/tasks`, getHeader(mentorToken));
|
||||
const tasks = getTasksRes.data.data;
|
||||
console.log(` Tasks found: ${tasks.length}`);
|
||||
if (tasks.length !== 1 || tasks[0].id !== taskId) {
|
||||
throw new Error('TEST FAILED: Task list not retrieved correctly by mentor!');
|
||||
}
|
||||
console.log(' PASSED: Task list retrieved successfully.\n');
|
||||
|
||||
// 8. Mentor retrieves details of their task
|
||||
console.log(`8. Mentor retrieving details of task ID ${taskId}...`);
|
||||
const getTaskDetailRes = await axios.get(`${BASE_URL}/task-api/tasks/${taskId}`, getHeader(mentorToken));
|
||||
const taskDetail = getTaskDetailRes.data.data;
|
||||
|
||||
// Mentor should get admin-like details including the submission_summary and submissions list.
|
||||
// Let's verify if submission_summary exists in response.
|
||||
console.log(' Retrieved detail data fields:', Object.keys(taskDetail));
|
||||
if (!taskDetail.submission_summary) {
|
||||
throw new Error('TEST FAILED: Mentor retrieved detail as an intern (missing submission_summary)!');
|
||||
}
|
||||
console.log(' PASSED: Mentor successfully retrieved detailed view of the task with submission summary.\n');
|
||||
|
||||
// 9. Mentor updates their task
|
||||
console.log(`9. Mentor updating task ID ${taskId}...`);
|
||||
await axios.patch(`${BASE_URL}/task-api/tasks/${taskId}`, {
|
||||
title: 'Task 1 Updated',
|
||||
description: 'Updated description by mentor.'
|
||||
}, getHeader(mentorToken));
|
||||
console.log(' PASSED: Task updated successfully.\n');
|
||||
|
||||
// 10. Mentor tries to update task they do not own
|
||||
// Let's create a task in the other project first
|
||||
const otherTaskRes = await axios.post(`${BASE_URL}/task-api/projects/${otherProjectId}/tasks`, {
|
||||
title: 'Other Task',
|
||||
description: 'Task by other mentor.',
|
||||
deadline_date: '2026-08-01',
|
||||
specific_time: '23:59',
|
||||
submission_type: 'url_link'
|
||||
}, getHeader(otherMentorToken));
|
||||
const otherTaskId = otherTaskRes.data.data.id;
|
||||
|
||||
console.log(`10. Testing isolation: Mentor trying to update task ID ${otherTaskId}...`);
|
||||
try {
|
||||
await axios.patch(`${BASE_URL}/task-api/tasks/${otherTaskId}`, {
|
||||
title: 'Unauthorized Update'
|
||||
}, getHeader(mentorToken));
|
||||
throw new Error('TEST FAILED: Mentor updated a task they do not own!');
|
||||
} catch (err) {
|
||||
if (err.response && err.response.status === 403) {
|
||||
console.log(' PASSED: Access denied with 403 Forbidden as expected.\n');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
// 11. Mentor deletes their task
|
||||
console.log(`11. Mentor deleting task ID ${taskId}...`);
|
||||
await axios.delete(`${BASE_URL}/task-api/tasks/${taskId}`, getHeader(mentorToken));
|
||||
console.log(' PASSED: Task deleted successfully.\n');
|
||||
|
||||
// 12. Mentor tries to delete task they do not own
|
||||
console.log(`12. Testing isolation: Mentor trying to delete task ID ${otherTaskId}...`);
|
||||
try {
|
||||
await axios.delete(`${BASE_URL}/task-api/tasks/${otherTaskId}`, getHeader(mentorToken));
|
||||
throw new Error('TEST FAILED: Mentor deleted a task they do not own!');
|
||||
} catch (err) {
|
||||
if (err.response && err.response.status === 403) {
|
||||
console.log(' PASSED: Access denied with 403 Forbidden as expected.\n');
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
console.log('=== ALL INTEGRATION TESTS PASSED SUCCESSFULLY! ===');
|
||||
} catch (err) {
|
||||
console.error('=== 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);
|
||||
}
|
||||
process.exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
runTests();
|
||||
@@ -0,0 +1,227 @@
|
||||
const axios = require('axios');
|
||||
const ioClient = require('socket.io-client');
|
||||
const prisma = require('../src/helpers/db/db_connection');
|
||||
|
||||
const BASE_URL = 'http://localhost:9000';
|
||||
|
||||
async function runTests() {
|
||||
console.log('=== STARTING INTEGRATION TESTS FOR NOTIFICATION SYSTEM ===\n');
|
||||
|
||||
// 0. Clean up existing memberships for rafi@student.com (user ID 1) to allow project assignment
|
||||
console.log('0. Cleaning up existing project memberships for rafi@student.com...');
|
||||
await prisma.projectMember.deleteMany({
|
||||
where: { id_user: 1 }
|
||||
});
|
||||
console.log(' Cleanup successful!\n');
|
||||
|
||||
let adminToken = '';
|
||||
let mentorToken = '';
|
||||
let internToken = '';
|
||||
let mentorId = null;
|
||||
let internId = null;
|
||||
let createdProjectId = null;
|
||||
let taskId = null;
|
||||
|
||||
const mentorEmail = `mentor_notif_${Date.now()}@internify.com`;
|
||||
const mentorPassword = 'Password123';
|
||||
const internEmail = 'rafi@student.com';
|
||||
const internPassword = 'password123';
|
||||
|
||||
// Helper config generator
|
||||
const getHeader = (token) => ({
|
||||
headers: { Authorization: `Bearer ${token}` }
|
||||
});
|
||||
|
||||
let internSocket = null;
|
||||
let mentorSocket = null;
|
||||
|
||||
const internReceivedNotifs = [];
|
||||
const mentorReceivedNotifs = [];
|
||||
|
||||
try {
|
||||
// 1. Login as Admin
|
||||
console.log('1. Logging in as Admin (admin1@internify.com)...');
|
||||
const loginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
|
||||
email: 'admin1@internify.com',
|
||||
password: 'password123'
|
||||
});
|
||||
adminToken = loginRes.data.data.token;
|
||||
console.log(' Admin logged in successfully!\n');
|
||||
|
||||
// 2. Create Mentor Account via Admin API
|
||||
console.log(`2. Creating a new Mentor account (${mentorEmail})...`);
|
||||
const createMentorRes = await axios.post(`${BASE_URL}/admin-api/add`, {
|
||||
nama_depan: 'Jane',
|
||||
nama_belakang: 'MentorNotif',
|
||||
email: mentorEmail,
|
||||
password: mentorPassword,
|
||||
role: 'mentor'
|
||||
}, getHeader(adminToken));
|
||||
console.log(' Mentor account created successfully!\n');
|
||||
|
||||
// 3. Login as the new Mentor
|
||||
console.log('3. Logging in as the new Mentor...');
|
||||
const mentorLoginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
|
||||
email: mentorEmail,
|
||||
password: mentorPassword
|
||||
});
|
||||
mentorToken = mentorLoginRes.data.data.token;
|
||||
mentorId = mentorLoginRes.data.data.user.id;
|
||||
console.log(` Mentor logged in successfully! ID: ${mentorId}\n`);
|
||||
|
||||
// 4. Login as Intern
|
||||
console.log(`4. Logging in as Intern (${internEmail})...`);
|
||||
const internLoginRes = await axios.post(`${BASE_URL}/auth-api/login`, {
|
||||
email: internEmail,
|
||||
password: internPassword
|
||||
});
|
||||
internToken = internLoginRes.data.data.token;
|
||||
internId = internLoginRes.data.data.user.id;
|
||||
console.log(` Intern logged in successfully! ID: ${internId}\n`);
|
||||
|
||||
// 5. Establish WebSocket Connections for Intern and Mentor
|
||||
console.log('5. Connecting to WebSocket (Socket.io) server...');
|
||||
|
||||
internSocket = ioClient(BASE_URL, {
|
||||
auth: { token: internToken },
|
||||
transports: ['websocket']
|
||||
});
|
||||
|
||||
mentorSocket = ioClient(BASE_URL, {
|
||||
auth: { token: mentorToken },
|
||||
transports: ['websocket']
|
||||
});
|
||||
|
||||
// Setup socket listeners
|
||||
internSocket.on('notification', (notif) => {
|
||||
console.log(` [WS Event] Intern received real-time notification: "${notif.title}" - ${notif.message}`);
|
||||
internReceivedNotifs.push(notif);
|
||||
});
|
||||
|
||||
mentorSocket.on('notification', (notif) => {
|
||||
console.log(` [WS Event] Mentor received real-time notification: "${notif.title}" - ${notif.message}`);
|
||||
mentorReceivedNotifs.push(notif);
|
||||
});
|
||||
|
||||
// Wait a brief moment to ensure sockets are connected
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
console.log(' Sockets connected!\n');
|
||||
|
||||
// 6. Mentor creates project and assigns Intern
|
||||
console.log(`6. Mentor creating project and assigning intern (ID: ${internId})...`);
|
||||
const createProjectRes = await axios.post(`${BASE_URL}/project-api/add`, {
|
||||
project_icon: 'code',
|
||||
project_name: `Notification Test Project - ${Date.now()}`,
|
||||
description: 'Project to test WebSocket notifications.',
|
||||
start_date: '2026-07-01',
|
||||
end_date: '2026-10-01',
|
||||
member_emails: [internEmail]
|
||||
}, getHeader(mentorToken));
|
||||
createdProjectId = createProjectRes.data.data.id;
|
||||
console.log(` Project created successfully! ID: ${createdProjectId}`);
|
||||
|
||||
// Wait for the WS notification to arrive
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Check if intern received "Diterima ke Proyek" notification
|
||||
const assignmentNotif = internReceivedNotifs.find(n => n.title === 'Diterima ke Proyek');
|
||||
if (!assignmentNotif) {
|
||||
throw new Error('TEST FAILED: Intern did not receive real-time project assignment notification!');
|
||||
}
|
||||
console.log(' PASSED: Intern received project assignment notification over WebSocket.\n');
|
||||
|
||||
// 7. Mentor creates a task in the project
|
||||
console.log(`7. Mentor creating a task in project ID ${createdProjectId}...`);
|
||||
const createTaskRes = await axios.post(`${BASE_URL}/task-api/projects/${createdProjectId}/tasks`, {
|
||||
title: 'Real-time Task',
|
||||
description: 'Submit this task to test mentor notifications.',
|
||||
deadline_date: '2026-08-01',
|
||||
specific_time: '23:59',
|
||||
submission_type: 'url_link'
|
||||
}, getHeader(mentorToken));
|
||||
taskId = createTaskRes.data.data.id;
|
||||
console.log(` Task created successfully! ID: ${taskId}`);
|
||||
|
||||
// Wait for the WS notification to arrive
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Check if intern received "Tugas Baru Ditambahkan" notification
|
||||
const taskNotif = internReceivedNotifs.find(n => n.title === 'Tugas Baru Ditambahkan');
|
||||
if (!taskNotif) {
|
||||
throw new Error('TEST FAILED: Intern did not receive real-time task creation notification!');
|
||||
}
|
||||
console.log(' PASSED: Intern received task creation notification over WebSocket.\n');
|
||||
|
||||
// 8. Intern submits the task
|
||||
console.log(`8. Intern submitting task ID ${taskId}...`);
|
||||
await axios.post(`${BASE_URL}/task-api/tasks/${taskId}/submissions`, {
|
||||
url_link: 'https://github.com/rafiathallah/notification-system'
|
||||
}, getHeader(internToken));
|
||||
console.log(' Task submitted successfully!');
|
||||
|
||||
// Wait for the WS notification to arrive
|
||||
await new Promise(resolve => setTimeout(resolve, 1000));
|
||||
|
||||
// Check if mentor received "Pengiriman Tugas Baru" notification
|
||||
const submissionNotif = mentorReceivedNotifs.find(n => n.title === 'Pengiriman Tugas Baru');
|
||||
if (!submissionNotif) {
|
||||
throw new Error('TEST FAILED: Mentor did not receive real-time task submission notification!');
|
||||
}
|
||||
console.log(' PASSED: Mentor received task submission notification over WebSocket.\n');
|
||||
|
||||
// 9. Intern retrieves their notifications via HTTP
|
||||
console.log('9. Intern fetching notifications via GET /notification-api...');
|
||||
const getNotifsRes = await axios.get(`${BASE_URL}/notification-api`, getHeader(internToken));
|
||||
const dbNotifications = getNotifsRes.data.data;
|
||||
console.log(` Notifications found: ${dbNotifications.length}`);
|
||||
dbNotifications.forEach(n => console.log(` - [ID: ${n.id}] [Read: ${n.is_read}] ${n.title}: ${n.message}`));
|
||||
|
||||
if (dbNotifications.length < 2) {
|
||||
throw new Error('TEST FAILED: Intern notifications list in database is incomplete!');
|
||||
}
|
||||
const hasUnread = dbNotifications.every(n => n.is_read === false);
|
||||
if (!hasUnread) {
|
||||
throw new Error('TEST FAILED: Notifications should be unread by default!');
|
||||
}
|
||||
console.log(' PASSED: HTTP notification retrieval correct.\n');
|
||||
|
||||
// 10. Intern marks a single notification as read
|
||||
const targetNotifId = dbNotifications[0].id;
|
||||
console.log(`10. Intern marking notification ID ${targetNotifId} as read...`);
|
||||
const markReadRes = await axios.patch(`${BASE_URL}/notification-api/${targetNotifId}/read`, {}, getHeader(internToken));
|
||||
|
||||
if (!markReadRes.data.data.is_read) {
|
||||
throw new Error('TEST FAILED: Notification status did not update to read!');
|
||||
}
|
||||
console.log(' PASSED: Single notification marked as read.\n');
|
||||
|
||||
// 11. Intern marks all notifications as read
|
||||
console.log('11. Intern marking all remaining notifications as read...');
|
||||
await axios.patch(`${BASE_URL}/notification-api/read-all`, {}, getHeader(internToken));
|
||||
|
||||
const recheckNotifsRes = await axios.get(`${BASE_URL}/notification-api`, getHeader(internToken));
|
||||
const updatedNotifications = recheckNotifsRes.data.data;
|
||||
const allRead = updatedNotifications.every(n => n.is_read === true);
|
||||
if (!allRead) {
|
||||
throw new Error('TEST FAILED: Not all notifications were marked as read!');
|
||||
}
|
||||
console.log(' PASSED: All notifications marked as read successfully.\n');
|
||||
|
||||
console.log('=== ALL INTEGRATION TESTS PASSED SUCCESSFULLY! ===');
|
||||
} catch (err) {
|
||||
console.error('=== 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);
|
||||
}
|
||||
process.exit(1);
|
||||
} finally {
|
||||
// Cleanup WebSocket connections
|
||||
if (internSocket) internSocket.close();
|
||||
if (mentorSocket) mentorSocket.close();
|
||||
}
|
||||
}
|
||||
|
||||
runTests();
|
||||
Reference in New Issue
Block a user