add changes

This commit is contained in:
AD2025
2025-12-25 00:24:11 +02:00
parent 079c10e843
commit efb4f69e20
64 changed files with 576 additions and 568 deletions

View File

@@ -0,0 +1,571 @@
const axios = require('axios');
const API_URL = 'http://localhost:3000/api';
// Admin credentials (from seeder)
const adminUser = {
email: 'admin@quiz.com',
password: 'Admin@123'
};
// Regular user (we'll create one for testing - with timestamp to avoid conflicts)
const timestamp = Date.now();
const regularUser = {
username: `testuser${timestamp}`,
email: `testuser${timestamp}@example.com`,
password: 'Test@123'
};
// ANSI color codes
const colors = {
reset: '\x1b[0m',
green: '\x1b[32m',
red: '\x1b[31m',
yellow: '\x1b[33m',
blue: '\x1b[34m',
cyan: '\x1b[36m'
};
let adminToken = null;
let regularUserToken = null;
let testCategoryId = null;
/**
* Login as admin
*/
async function loginAdmin() {
try {
const response = await axios.post(`${API_URL}/auth/login`, adminUser);
adminToken = response.data.data.token;
console.log(`${colors.cyan}✓ Logged in as admin${colors.reset}`);
return adminToken;
} catch (error) {
console.error(`${colors.red}✗ Failed to login as admin:${colors.reset}`, error.response?.data || error.message);
throw error;
}
}
/**
* Create and login regular user
*/
async function createRegularUser() {
try {
// Register
const registerResponse = await axios.post(`${API_URL}/auth/register`, regularUser);
regularUserToken = registerResponse.data.data.token;
console.log(`${colors.cyan}✓ Created and logged in as regular user${colors.reset}`);
return regularUserToken;
} catch (error) {
console.error(`${colors.red}✗ Failed to create regular user:${colors.reset}`, error.response?.data || error.message);
throw error;
}
}
/**
* Test 1: Create category as admin
*/
async function testCreateCategoryAsAdmin() {
console.log(`\n${colors.blue}Test 1: Create category as admin${colors.reset}`);
try {
const newCategory = {
name: 'Test Category',
description: 'A test category for admin operations',
icon: 'test-icon',
color: '#FF5733',
guestAccessible: false,
displayOrder: 10
};
const response = await axios.post(`${API_URL}/categories`, newCategory, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
const { success, data, message } = response.data;
if (!success) throw new Error('success should be true');
if (!data.id) throw new Error('Missing category ID');
if (data.name !== newCategory.name) throw new Error('Name mismatch');
if (data.slug !== 'test-category') throw new Error('Slug should be auto-generated');
if (data.color !== newCategory.color) throw new Error('Color mismatch');
if (data.guestAccessible !== false) throw new Error('guestAccessible mismatch');
if (data.questionCount !== 0) throw new Error('questionCount should be 0');
if (data.isActive !== true) throw new Error('isActive should be true');
// Save for later tests
testCategoryId = data.id;
console.log(`${colors.green}✓ Test 1 Passed${colors.reset}`);
console.log(` Category ID: ${data.id}`);
console.log(` Name: ${data.name}`);
console.log(` Slug: ${data.slug}`);
return true;
} catch (error) {
console.error(`${colors.red}✗ Test 1 Failed:${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 2: Create category without authentication
*/
async function testCreateCategoryNoAuth() {
console.log(`\n${colors.blue}Test 2: Create category without authentication${colors.reset}`);
try {
const newCategory = {
name: 'Unauthorized Category',
description: 'Should not be created'
};
await axios.post(`${API_URL}/categories`, newCategory);
console.error(`${colors.red}✗ Test 2 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 401) {
console.log(`${colors.green}✓ Test 2 Passed${colors.reset}`);
console.log(` Status: 401 Unauthorized`);
return true;
} else {
console.error(`${colors.red}✗ Test 2 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
}
/**
* Test 3: Create category as regular user
*/
async function testCreateCategoryAsRegularUser() {
console.log(`\n${colors.blue}Test 3: Create category as regular user (non-admin)${colors.reset}`);
try {
const newCategory = {
name: 'Regular User Category',
description: 'Should not be created'
};
await axios.post(`${API_URL}/categories`, newCategory, {
headers: { 'Authorization': `Bearer ${regularUserToken}` }
});
console.error(`${colors.red}✗ Test 3 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 403) {
console.log(`${colors.green}✓ Test 3 Passed${colors.reset}`);
console.log(` Status: 403 Forbidden`);
return true;
} else {
console.error(`${colors.red}✗ Test 3 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
}
/**
* Test 4: Create category with duplicate name
*/
async function testCreateCategoryDuplicateName() {
console.log(`\n${colors.blue}Test 4: Create category with duplicate name${colors.reset}`);
try {
const duplicateCategory = {
name: 'Test Category', // Same as test 1
description: 'Duplicate name'
};
await axios.post(`${API_URL}/categories`, duplicateCategory, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
console.error(`${colors.red}✗ Test 4 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 400) {
const { message } = error.response.data;
if (message.includes('already exists')) {
console.log(`${colors.green}✓ Test 4 Passed${colors.reset}`);
console.log(` Status: 400 Bad Request`);
console.log(` Message: ${message}`);
return true;
}
}
console.error(`${colors.red}✗ Test 4 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 5: Create category without required name
*/
async function testCreateCategoryMissingName() {
console.log(`\n${colors.blue}Test 5: Create category without required name${colors.reset}`);
try {
const invalidCategory = {
description: 'No name provided'
};
await axios.post(`${API_URL}/categories`, invalidCategory, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
console.error(`${colors.red}✗ Test 5 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 400) {
const { message } = error.response.data;
if (message.includes('required')) {
console.log(`${colors.green}✓ Test 5 Passed${colors.reset}`);
console.log(` Status: 400 Bad Request`);
console.log(` Message: ${message}`);
return true;
}
}
console.error(`${colors.red}✗ Test 5 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 6: Update category as admin
*/
async function testUpdateCategoryAsAdmin() {
console.log(`\n${colors.blue}Test 6: Update category as admin${colors.reset}`);
try {
const updates = {
description: 'Updated description',
guestAccessible: true,
displayOrder: 20
};
const response = await axios.put(`${API_URL}/categories/${testCategoryId}`, updates, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
const { success, data } = response.data;
if (!success) throw new Error('success should be true');
if (data.description !== updates.description) throw new Error('Description not updated');
if (data.guestAccessible !== updates.guestAccessible) throw new Error('guestAccessible not updated');
if (data.displayOrder !== updates.displayOrder) throw new Error('displayOrder not updated');
console.log(`${colors.green}✓ Test 6 Passed${colors.reset}`);
console.log(` Updated description: ${data.description}`);
console.log(` Updated guestAccessible: ${data.guestAccessible}`);
console.log(` Updated displayOrder: ${data.displayOrder}`);
return true;
} catch (error) {
console.error(`${colors.red}✗ Test 6 Failed:${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 7: Update category as regular user
*/
async function testUpdateCategoryAsRegularUser() {
console.log(`\n${colors.blue}Test 7: Update category as regular user (non-admin)${colors.reset}`);
try {
const updates = {
description: 'Should not update'
};
await axios.put(`${API_URL}/categories/${testCategoryId}`, updates, {
headers: { 'Authorization': `Bearer ${regularUserToken}` }
});
console.error(`${colors.red}✗ Test 7 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 403) {
console.log(`${colors.green}✓ Test 7 Passed${colors.reset}`);
console.log(` Status: 403 Forbidden`);
return true;
} else {
console.error(`${colors.red}✗ Test 7 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
}
/**
* Test 8: Update non-existent category
*/
async function testUpdateNonExistentCategory() {
console.log(`\n${colors.blue}Test 8: Update non-existent category${colors.reset}`);
try {
const fakeId = '00000000-0000-0000-0000-000000000000';
const updates = {
description: 'Should not work'
};
await axios.put(`${API_URL}/categories/${fakeId}`, updates, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
console.error(`${colors.red}✗ Test 8 Failed: Should have returned 404${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 404) {
console.log(`${colors.green}✓ Test 8 Passed${colors.reset}`);
console.log(` Status: 404 Not Found`);
return true;
} else {
console.error(`${colors.red}✗ Test 8 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
}
/**
* Test 9: Update category with duplicate name
*/
async function testUpdateCategoryDuplicateName() {
console.log(`\n${colors.blue}Test 9: Update category with duplicate name${colors.reset}`);
try {
const updates = {
name: 'JavaScript' // Existing category from seed data
};
await axios.put(`${API_URL}/categories/${testCategoryId}`, updates, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
console.error(`${colors.red}✗ Test 9 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 400) {
const { message } = error.response.data;
if (message.includes('already exists')) {
console.log(`${colors.green}✓ Test 9 Passed${colors.reset}`);
console.log(` Status: 400 Bad Request`);
console.log(` Message: ${message}`);
return true;
}
}
console.error(`${colors.red}✗ Test 9 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 10: Delete category as admin
*/
async function testDeleteCategoryAsAdmin() {
console.log(`\n${colors.blue}Test 10: Delete category as admin (soft delete)${colors.reset}`);
try {
const response = await axios.delete(`${API_URL}/categories/${testCategoryId}`, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
const { success, data, message } = response.data;
if (!success) throw new Error('success should be true');
if (data.id !== testCategoryId) throw new Error('ID mismatch');
if (!message.includes('successfully')) throw new Error('Success message expected');
console.log(`${colors.green}✓ Test 10 Passed${colors.reset}`);
console.log(` Category: ${data.name}`);
console.log(` Message: ${message}`);
return true;
} catch (error) {
console.error(`${colors.red}✗ Test 10 Failed:${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 11: Verify deleted category is not in active list
*/
async function testDeletedCategoryNotInList() {
console.log(`\n${colors.blue}Test 11: Verify deleted category not in active list${colors.reset}`);
try {
const response = await axios.get(`${API_URL}/categories`, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
const { data } = response.data;
const deletedCategory = data.find(cat => cat.id === testCategoryId);
if (deletedCategory) {
throw new Error('Deleted category should not appear in active list');
}
console.log(`${colors.green}✓ Test 11 Passed${colors.reset}`);
console.log(` Deleted category not in active list`);
return true;
} catch (error) {
console.error(`${colors.red}✗ Test 11 Failed:${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 12: Delete already deleted category
*/
async function testDeleteAlreadyDeletedCategory() {
console.log(`\n${colors.blue}Test 12: Delete already deleted category${colors.reset}`);
try {
await axios.delete(`${API_URL}/categories/${testCategoryId}`, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
console.error(`${colors.red}✗ Test 12 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 400) {
const { message } = error.response.data;
if (message.includes('already deleted')) {
console.log(`${colors.green}✓ Test 12 Passed${colors.reset}`);
console.log(` Status: 400 Bad Request`);
console.log(` Message: ${message}`);
return true;
}
}
console.error(`${colors.red}✗ Test 12 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Test 13: Delete category as regular user
*/
async function testDeleteCategoryAsRegularUser() {
console.log(`\n${colors.blue}Test 13: Delete category as regular user (non-admin)${colors.reset}`);
try {
// Create a new category for this test
const newCategory = {
name: 'Delete Test Category',
description: 'For delete permissions test'
};
const createResponse = await axios.post(`${API_URL}/categories`, newCategory, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
const categoryId = createResponse.data.data.id;
// Try to delete as regular user
await axios.delete(`${API_URL}/categories/${categoryId}`, {
headers: { 'Authorization': `Bearer ${regularUserToken}` }
});
console.error(`${colors.red}✗ Test 13 Failed: Should have been rejected${colors.reset}`);
return false;
} catch (error) {
if (error.response && error.response.status === 403) {
console.log(`${colors.green}✓ Test 13 Passed${colors.reset}`);
console.log(` Status: 403 Forbidden`);
return true;
} else {
console.error(`${colors.red}✗ Test 13 Failed: Wrong error${colors.reset}`, error.response?.data || error.message);
return false;
}
}
}
/**
* Test 14: Create category with custom slug
*/
async function testCreateCategoryWithCustomSlug() {
console.log(`\n${colors.blue}Test 14: Create category with custom slug${colors.reset}`);
try {
const newCategory = {
name: 'Custom Slug Category',
slug: 'my-custom-slug',
description: 'Testing custom slug'
};
const response = await axios.post(`${API_URL}/categories`, newCategory, {
headers: { 'Authorization': `Bearer ${adminToken}` }
});
const { success, data } = response.data;
if (!success) throw new Error('success should be true');
if (data.slug !== 'my-custom-slug') throw new Error('Custom slug not applied');
console.log(`${colors.green}✓ Test 14 Passed${colors.reset}`);
console.log(` Custom slug: ${data.slug}`);
return true;
} catch (error) {
console.error(`${colors.red}✗ Test 14 Failed:${colors.reset}`, error.response?.data || error.message);
return false;
}
}
/**
* Run all tests
*/
async function runAllTests() {
console.log(`${colors.cyan}========================================${colors.reset}`);
console.log(`${colors.cyan}Testing Category Admin API${colors.reset}`);
console.log(`${colors.cyan}========================================${colors.reset}`);
const results = [];
try {
// Setup
await loginAdmin();
await createRegularUser();
// Run tests
results.push(await testCreateCategoryAsAdmin());
results.push(await testCreateCategoryNoAuth());
results.push(await testCreateCategoryAsRegularUser());
results.push(await testCreateCategoryDuplicateName());
results.push(await testCreateCategoryMissingName());
results.push(await testUpdateCategoryAsAdmin());
results.push(await testUpdateCategoryAsRegularUser());
results.push(await testUpdateNonExistentCategory());
results.push(await testUpdateCategoryDuplicateName());
results.push(await testDeleteCategoryAsAdmin());
results.push(await testDeletedCategoryNotInList());
results.push(await testDeleteAlreadyDeletedCategory());
results.push(await testDeleteCategoryAsRegularUser());
results.push(await testCreateCategoryWithCustomSlug());
// Summary
console.log(`\n${colors.cyan}========================================${colors.reset}`);
console.log(`${colors.cyan}Test Summary${colors.reset}`);
console.log(`${colors.cyan}========================================${colors.reset}`);
const passed = results.filter(r => r === true).length;
const failed = results.filter(r => r === false).length;
console.log(`${colors.green}Passed: ${passed}${colors.reset}`);
console.log(`${colors.red}Failed: ${failed}${colors.reset}`);
console.log(`Total: ${results.length}`);
if (failed === 0) {
console.log(`\n${colors.green}✓ All tests passed!${colors.reset}`);
} else {
console.log(`\n${colors.red}✗ Some tests failed${colors.reset}`);
process.exit(1);
}
} catch (error) {
console.error(`${colors.red}Test execution error:${colors.reset}`, error);
process.exit(1);
}
}
// Run tests
runAllTests();