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,520 @@
const axios = require('axios');
const BASE_URL = 'http://localhost:3000/api';
// Test user credentials
const testUser = {
username: 'bookmarklist1',
email: 'bookmarklist1@example.com',
password: 'Test123!@#'
};
const testUser2 = {
username: 'bookmarklist2',
email: 'bookmarklist2@example.com',
password: 'Test123!@#'
};
let userToken = '';
let userId = '';
let user2Token = '';
let user2Id = '';
let categoryId = '';
let questionIds = [];
let bookmarkIds = [];
// Test results tracking
let passedTests = 0;
let failedTests = 0;
const testResults = [];
// Helper function to log test results
function logTest(testName, passed, error = null) {
if (passed) {
console.log(`${testName}`);
passedTests++;
} else {
console.log(`${testName}`);
if (error) {
console.log(` Error: ${error}`);
}
failedTests++;
}
testResults.push({ testName, passed, error });
}
// Helper function to delay between tests
const delay = (ms) => new Promise(resolve => setTimeout(resolve, ms));
async function setup() {
try {
console.log('Setting up test data...');
// Register and login first user
try {
const regRes = await axios.post(`${BASE_URL}/auth/register`, testUser);
console.log('✓ Test user registered');
} catch (err) {
// User might already exist, continue to login
if (err.response?.status !== 409) {
console.log('Registration error:', err.response?.data?.message || err.message);
}
}
const loginRes = await axios.post(`${BASE_URL}/auth/login`, {
email: testUser.email,
password: testUser.password
});
userToken = loginRes.data.data.token;
userId = loginRes.data.data.user.id;
console.log('✓ Test user logged in');
// Register and login second user
try {
const regRes = await axios.post(`${BASE_URL}/auth/register`, testUser2);
console.log('✓ Second user registered');
} catch (err) {
// User might already exist, continue to login
if (err.response?.status !== 409) {
console.log('Registration error:', err.response?.data?.message || err.message);
}
}
const login2Res = await axios.post(`${BASE_URL}/auth/login`, {
email: testUser2.email,
password: testUser2.password
});
user2Token = login2Res.data.data.token;
user2Id = login2Res.data.data.user.id;
console.log('✓ Second user logged in');
// Get categories
const categoriesRes = await axios.get(`${BASE_URL}/categories`);
const categories = categoriesRes.data.data;
// Find a category with questions
let testCategory = null;
for (const cat of categories) {
if (cat.questionCount > 0) {
testCategory = cat;
break;
}
}
if (!testCategory) {
throw new Error('No categories with questions found');
}
categoryId = testCategory.id;
console.log(`✓ Found test category: ${testCategory.name} (${testCategory.questionCount} questions)`);
// Get questions from this category
const questionsRes = await axios.get(`${BASE_URL}/questions/category/${categoryId}?limit=10`);
const questions = questionsRes.data.data;
if (questions.length === 0) {
throw new Error('No questions available in category for testing');
}
questionIds = questions.slice(0, Math.min(5, questions.length)).map(q => q.id);
console.log(`✓ Found ${questionIds.length} test questions`);
// Delete any existing bookmarks first (cleanup from previous test runs)
for (const questionId of questionIds) {
try {
await axios.delete(
`${BASE_URL}/users/${userId}/bookmarks/${questionId}`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
} catch (err) {
// Ignore if bookmark doesn't exist
}
}
// Create bookmarks for testing
for (const questionId of questionIds) {
const bookmarkRes = await axios.post(
`${BASE_URL}/users/${userId}/bookmarks`,
{ questionId },
{ headers: { Authorization: `Bearer ${userToken}` } }
);
bookmarkIds.push(bookmarkRes.data.data.id);
await delay(100);
}
console.log(`✓ Created ${bookmarkIds.length} bookmarks for testing`);
} catch (error) {
console.error('Setup failed:', error.response?.data || error.message);
throw error;
}
}
async function runTests() {
console.log('\n============================================================');
console.log('USER BOOKMARKS API TESTS');
console.log('============================================================\n');
await setup();
console.log('\nRunning tests...');
// Test 1: Get bookmarks with default pagination
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.success === true &&
Array.isArray(response.data.data.bookmarks) &&
response.data.data.bookmarks.length > 0 &&
response.data.data.pagination.currentPage === 1 &&
response.data.data.pagination.itemsPerPage === 10;
logTest('Get bookmarks with default pagination', passed);
} catch (error) {
logTest('Get bookmarks with default pagination', false, error.response?.data?.message || error.message);
}
// Test 2: Pagination structure validation
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?page=1&limit=5`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const pagination = response.data.data.pagination;
const passed = response.status === 200 &&
pagination.currentPage === 1 &&
pagination.itemsPerPage === 5 &&
typeof pagination.totalPages === 'number' &&
typeof pagination.totalItems === 'number' &&
typeof pagination.hasNextPage === 'boolean' &&
typeof pagination.hasPreviousPage === 'boolean';
logTest('Pagination structure validation', passed);
} catch (error) {
logTest('Pagination structure validation', false, error.response?.data?.message || error.message);
}
// Test 3: Bookmark fields validation
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const bookmark = response.data.data.bookmarks[0];
const passed = response.status === 200 &&
bookmark.bookmarkId &&
bookmark.bookmarkedAt &&
bookmark.question &&
bookmark.question.id &&
bookmark.question.questionText &&
bookmark.question.difficulty &&
bookmark.question.category &&
bookmark.question.statistics;
logTest('Bookmark fields validation', passed);
} catch (error) {
logTest('Bookmark fields validation', false, error.response?.data?.message || error.message);
}
// Test 4: Custom limit
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?limit=2`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.data.bookmarks.length <= 2 &&
response.data.data.pagination.itemsPerPage === 2;
logTest('Custom limit', passed);
} catch (error) {
logTest('Custom limit', false, error.response?.data?.message || error.message);
}
// Test 5: Page 2 navigation
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?page=2&limit=3`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.data.pagination.currentPage === 2 &&
response.data.data.pagination.hasPreviousPage === true;
logTest('Page 2 navigation', passed);
} catch (error) {
logTest('Page 2 navigation', false, error.response?.data?.message || error.message);
}
// Test 6: Category filter
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?category=${categoryId}`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const allMatchCategory = response.data.data.bookmarks.every(
b => b.question.category.id === categoryId
);
const passed = response.status === 200 &&
allMatchCategory &&
response.data.data.filters.category === categoryId;
logTest('Category filter', passed);
} catch (error) {
logTest('Category filter', false, error.response?.data?.message || error.message);
}
// Test 7: Difficulty filter
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?difficulty=medium`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const allMatchDifficulty = response.data.data.bookmarks.every(
b => b.question.difficulty === 'medium'
);
const passed = response.status === 200 &&
(response.data.data.bookmarks.length === 0 || allMatchDifficulty) &&
response.data.data.filters.difficulty === 'medium';
logTest('Difficulty filter', passed);
} catch (error) {
logTest('Difficulty filter', false, error.response?.data?.message || error.message);
}
// Test 8: Sort by difficulty ascending
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?sortBy=difficulty&sortOrder=asc`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.data.sorting.sortBy === 'difficulty' &&
response.data.data.sorting.sortOrder === 'asc';
logTest('Sort by difficulty ascending', passed);
} catch (error) {
logTest('Sort by difficulty ascending', false, error.response?.data?.message || error.message);
}
// Test 9: Sort by date descending (default)
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?sortBy=date&sortOrder=desc`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.data.sorting.sortBy === 'date' &&
response.data.data.sorting.sortOrder === 'desc';
logTest('Sort by date descending', passed);
} catch (error) {
logTest('Sort by date descending', false, error.response?.data?.message || error.message);
}
// Test 10: Max limit enforcement (50)
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?limit=100`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.data.pagination.itemsPerPage === 50;
logTest('Max limit enforcement (50)', passed);
} catch (error) {
logTest('Max limit enforcement (50)', false, error.response?.data?.message || error.message);
}
// Test 11: Cross-user access blocked
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${user2Id}/bookmarks`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
logTest('Cross-user access blocked', false, 'Expected 403 but got 200');
} catch (error) {
const passed = error.response?.status === 403;
logTest('Cross-user access blocked', passed, passed ? null : `Expected 403 but got ${error.response?.status}`);
}
// Test 12: Unauthenticated request blocked
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks`
);
logTest('Unauthenticated request blocked', false, 'Expected 401 but got 200');
} catch (error) {
const passed = error.response?.status === 401;
logTest('Unauthenticated request blocked', passed, passed ? null : `Expected 401 but got ${error.response?.status}`);
}
// Test 13: Invalid UUID format
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/invalid-uuid/bookmarks`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
logTest('Invalid UUID format', false, 'Expected 400 but got 200');
} catch (error) {
const passed = error.response?.status === 400;
logTest('Invalid UUID format', passed, passed ? null : `Expected 400 but got ${error.response?.status}`);
}
// Test 14: Non-existent user
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/11111111-1111-1111-1111-111111111111/bookmarks`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
logTest('Non-existent user', false, 'Expected 404 but got 200');
} catch (error) {
const passed = error.response?.status === 404;
logTest('Non-existent user', passed, passed ? null : `Expected 404 but got ${error.response?.status}`);
}
// Test 15: Invalid category ID format
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?category=invalid-uuid`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
logTest('Invalid category ID format', false, 'Expected 400 but got 200');
} catch (error) {
const passed = error.response?.status === 400;
logTest('Invalid category ID format', passed, passed ? null : `Expected 400 but got ${error.response?.status}`);
}
// Test 16: Invalid difficulty value
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?difficulty=invalid`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
logTest('Invalid difficulty value', false, 'Expected 400 but got 200');
} catch (error) {
const passed = error.response?.status === 400;
logTest('Invalid difficulty value', passed, passed ? null : `Expected 400 but got ${error.response?.status}`);
}
// Test 17: Invalid sort order
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?sortOrder=invalid`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
logTest('Invalid sort order', false, 'Expected 400 but got 200');
} catch (error) {
const passed = error.response?.status === 400;
logTest('Invalid sort order', passed, passed ? null : `Expected 400 but got ${error.response?.status}`);
}
// Test 18: Empty bookmarks list
await delay(100);
try {
// Use second user who has no bookmarks
const response = await axios.get(
`${BASE_URL}/users/${user2Id}/bookmarks`,
{ headers: { Authorization: `Bearer ${user2Token}` } }
);
const passed = response.status === 200 &&
Array.isArray(response.data.data.bookmarks) &&
response.data.data.bookmarks.length === 0 &&
response.data.data.pagination.totalItems === 0;
logTest('Empty bookmarks list', passed);
} catch (error) {
logTest('Empty bookmarks list', false, error.response?.data?.message || error.message);
}
// Test 19: Combined filters (category + difficulty + sorting)
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks?category=${categoryId}&difficulty=easy&sortBy=date&sortOrder=asc`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const passed = response.status === 200 &&
response.data.data.filters.category === categoryId &&
response.data.data.filters.difficulty === 'easy' &&
response.data.data.sorting.sortBy === 'date' &&
response.data.data.sorting.sortOrder === 'asc';
logTest('Combined filters', passed);
} catch (error) {
logTest('Combined filters', false, error.response?.data?.message || error.message);
}
// Test 20: Question statistics included
await delay(100);
try {
const response = await axios.get(
`${BASE_URL}/users/${userId}/bookmarks`,
{ headers: { Authorization: `Bearer ${userToken}` } }
);
const bookmark = response.data.data.bookmarks[0];
const stats = bookmark.question.statistics;
const passed = response.status === 200 &&
stats &&
typeof stats.timesAttempted === 'number' &&
typeof stats.timesCorrect === 'number' &&
typeof stats.accuracy === 'number';
logTest('Question statistics included', passed);
} catch (error) {
logTest('Question statistics included', false, error.response?.data?.message || error.message);
}
// Print results
console.log('\n============================================================');
console.log(`RESULTS: ${passedTests} passed, ${failedTests} failed out of ${passedTests + failedTests} tests`);
console.log('============================================================\n');
process.exit(failedTests > 0 ? 1 : 0);
}
// Run tests
runTests();