WIP
This commit is contained in:
442
backend/tests/test-bookmark-endpoints.js
Normal file
442
backend/tests/test-bookmark-endpoints.js
Normal file
@ -0,0 +1,442 @@
|
||||
// Test script for bookmark API endpoints
|
||||
const axios = require('axios');
|
||||
|
||||
const BASE_URL = 'http://localhost:3001';
|
||||
|
||||
// Test data
|
||||
const testUser = {
|
||||
email: 'bookmarktest@example.com',
|
||||
password: 'TestPassword123!'
|
||||
};
|
||||
|
||||
const testBookmarks = [
|
||||
{
|
||||
title: 'Google',
|
||||
url: 'https://www.google.com',
|
||||
folder: 'Search Engines',
|
||||
status: 'valid'
|
||||
},
|
||||
{
|
||||
title: 'GitHub',
|
||||
url: 'https://github.com',
|
||||
folder: 'Development',
|
||||
status: 'valid'
|
||||
},
|
||||
{
|
||||
title: 'Stack Overflow',
|
||||
url: 'https://stackoverflow.com',
|
||||
folder: 'Development',
|
||||
status: 'valid'
|
||||
}
|
||||
];
|
||||
|
||||
let authToken = null;
|
||||
let createdBookmarkIds = [];
|
||||
|
||||
async function testEndpoint(name, testFn) {
|
||||
try {
|
||||
console.log(`\n🧪 Testing: ${name}`);
|
||||
await testFn();
|
||||
console.log(`✅ ${name} - PASSED`);
|
||||
} catch (error) {
|
||||
console.log(`❌ ${name} - FAILED`);
|
||||
if (error.response) {
|
||||
console.log(` Status: ${error.response.status}`);
|
||||
console.log(` Error: ${JSON.stringify(error.response.data, null, 2)}`);
|
||||
} else {
|
||||
console.log(` Error: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function setupTestUser() {
|
||||
try {
|
||||
// Try to register user (might fail if already exists)
|
||||
await axios.post(`${BASE_URL}/api/auth/register`, testUser);
|
||||
} catch (error) {
|
||||
// User might already exist, that's okay
|
||||
}
|
||||
|
||||
// Login to get token
|
||||
const response = await axios.post(`${BASE_URL}/api/auth/login`, testUser);
|
||||
|
||||
const cookies = response.headers['set-cookie'];
|
||||
if (cookies) {
|
||||
const authCookie = cookies.find(cookie => cookie.startsWith('authToken='));
|
||||
if (authCookie) {
|
||||
authToken = authCookie.split('=')[1].split(';')[0];
|
||||
}
|
||||
}
|
||||
|
||||
if (!authToken) {
|
||||
throw new Error('Failed to get auth token');
|
||||
}
|
||||
|
||||
console.log(`✅ Test user logged in successfully`);
|
||||
}
|
||||
|
||||
async function testCreateBookmark() {
|
||||
const response = await axios.post(`${BASE_URL}/api/bookmarks`, testBookmarks[0], {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 201) {
|
||||
throw new Error(`Expected status 201, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.bookmark || !response.data.bookmark.id) {
|
||||
throw new Error('Response should contain bookmark with ID');
|
||||
}
|
||||
|
||||
createdBookmarkIds.push(response.data.bookmark.id);
|
||||
console.log(` Created bookmark: ${response.data.bookmark.title}`);
|
||||
}
|
||||
|
||||
async function testGetBookmarks() {
|
||||
const response = await axios.get(`${BASE_URL}/api/bookmarks`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.bookmarks || !Array.isArray(response.data.bookmarks)) {
|
||||
throw new Error('Response should contain bookmarks array');
|
||||
}
|
||||
|
||||
if (!response.data.pagination) {
|
||||
throw new Error('Response should contain pagination info');
|
||||
}
|
||||
|
||||
console.log(` Retrieved ${response.data.bookmarks.length} bookmarks`);
|
||||
console.log(` Pagination: page ${response.data.pagination.page} of ${response.data.pagination.totalPages}`);
|
||||
}
|
||||
|
||||
async function testGetBookmarkById() {
|
||||
if (createdBookmarkIds.length === 0) {
|
||||
throw new Error('No bookmarks created to test');
|
||||
}
|
||||
|
||||
const bookmarkId = createdBookmarkIds[0];
|
||||
const response = await axios.get(`${BASE_URL}/api/bookmarks/${bookmarkId}`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.bookmark || response.data.bookmark.id !== bookmarkId) {
|
||||
throw new Error('Response should contain correct bookmark');
|
||||
}
|
||||
|
||||
console.log(` Retrieved bookmark: ${response.data.bookmark.title}`);
|
||||
}
|
||||
|
||||
async function testUpdateBookmark() {
|
||||
if (createdBookmarkIds.length === 0) {
|
||||
throw new Error('No bookmarks created to test');
|
||||
}
|
||||
|
||||
const bookmarkId = createdBookmarkIds[0];
|
||||
const updates = {
|
||||
title: 'Updated Google',
|
||||
folder: 'Updated Folder'
|
||||
};
|
||||
|
||||
const response = await axios.put(`${BASE_URL}/api/bookmarks/${bookmarkId}`, updates, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (response.data.bookmark.title !== updates.title) {
|
||||
throw new Error('Bookmark title should be updated');
|
||||
}
|
||||
|
||||
console.log(` Updated bookmark: ${response.data.bookmark.title}`);
|
||||
}
|
||||
|
||||
async function testBulkCreateBookmarks() {
|
||||
const response = await axios.post(`${BASE_URL}/api/bookmarks/bulk`, {
|
||||
bookmarks: testBookmarks.slice(1) // Create remaining test bookmarks
|
||||
}, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 201) {
|
||||
throw new Error(`Expected status 201, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.bookmarks || response.data.bookmarks.length !== 2) {
|
||||
throw new Error('Should create 2 bookmarks');
|
||||
}
|
||||
|
||||
// Store created bookmark IDs
|
||||
response.data.bookmarks.forEach(bookmark => {
|
||||
createdBookmarkIds.push(bookmark.id);
|
||||
});
|
||||
|
||||
console.log(` Bulk created ${response.data.count} bookmarks`);
|
||||
}
|
||||
|
||||
async function testGetFolders() {
|
||||
const response = await axios.get(`${BASE_URL}/api/bookmarks/folders`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.folders || !Array.isArray(response.data.folders)) {
|
||||
throw new Error('Response should contain folders array');
|
||||
}
|
||||
|
||||
console.log(` Retrieved ${response.data.folders.length} folders`);
|
||||
response.data.folders.forEach(folder => {
|
||||
console.log(` - ${folder.folder}: ${folder.count} bookmarks`);
|
||||
});
|
||||
}
|
||||
|
||||
async function testGetStats() {
|
||||
const response = await axios.get(`${BASE_URL}/api/bookmarks/stats`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.stats) {
|
||||
throw new Error('Response should contain stats');
|
||||
}
|
||||
|
||||
console.log(` Stats: ${response.data.stats.totalBookmarks} total, ${response.data.stats.totalFolders} folders`);
|
||||
}
|
||||
|
||||
async function testBookmarkFiltering() {
|
||||
// Test filtering by folder
|
||||
const response = await axios.get(`${BASE_URL}/api/bookmarks?folder=Development`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
const developmentBookmarks = response.data.bookmarks.filter(b => b.folder === 'Development');
|
||||
if (developmentBookmarks.length !== response.data.bookmarks.length) {
|
||||
throw new Error('All returned bookmarks should be in Development folder');
|
||||
}
|
||||
|
||||
console.log(` Filtered ${response.data.bookmarks.length} bookmarks in Development folder`);
|
||||
}
|
||||
|
||||
async function testBookmarkSearch() {
|
||||
// Test search functionality
|
||||
const response = await axios.get(`${BASE_URL}/api/bookmarks?search=GitHub`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
const hasGitHub = response.data.bookmarks.some(b =>
|
||||
b.title.toLowerCase().includes('github') || b.url.toLowerCase().includes('github')
|
||||
);
|
||||
|
||||
if (!hasGitHub) {
|
||||
throw new Error('Search should return bookmarks containing "GitHub"');
|
||||
}
|
||||
|
||||
console.log(` Search returned ${response.data.bookmarks.length} bookmarks`);
|
||||
}
|
||||
|
||||
async function testExportBookmarks() {
|
||||
const response = await axios.post(`${BASE_URL}/api/bookmarks/export`, {
|
||||
format: 'json'
|
||||
}, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
if (!response.data.bookmarks || !Array.isArray(response.data.bookmarks)) {
|
||||
throw new Error('Export should contain bookmarks array');
|
||||
}
|
||||
|
||||
console.log(` Exported ${response.data.count} bookmarks`);
|
||||
}
|
||||
|
||||
async function testDeleteBookmark() {
|
||||
if (createdBookmarkIds.length === 0) {
|
||||
throw new Error('No bookmarks created to test');
|
||||
}
|
||||
|
||||
const bookmarkId = createdBookmarkIds[0];
|
||||
const response = await axios.delete(`${BASE_URL}/api/bookmarks/${bookmarkId}`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
|
||||
if (response.status !== 200) {
|
||||
throw new Error(`Expected status 200, got ${response.status}`);
|
||||
}
|
||||
|
||||
// Remove from our tracking array
|
||||
createdBookmarkIds = createdBookmarkIds.filter(id => id !== bookmarkId);
|
||||
|
||||
console.log(` Deleted bookmark successfully`);
|
||||
}
|
||||
|
||||
async function testUnauthorizedAccess() {
|
||||
try {
|
||||
await axios.get(`${BASE_URL}/api/bookmarks`);
|
||||
throw new Error('Should have failed without authentication');
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 401) {
|
||||
console.log(` Unauthorized access correctly rejected`);
|
||||
} else {
|
||||
throw error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async function testDataIsolation() {
|
||||
// Create a second user to test data isolation
|
||||
const testUser2 = {
|
||||
email: 'isolation@example.com',
|
||||
password: 'TestPassword123!'
|
||||
};
|
||||
|
||||
try {
|
||||
await axios.post(`${BASE_URL}/api/auth/register`, testUser2);
|
||||
} catch (error) {
|
||||
// User might already exist
|
||||
}
|
||||
|
||||
const loginResponse = await axios.post(`${BASE_URL}/api/auth/login`, testUser2);
|
||||
const cookies = loginResponse.headers['set-cookie'];
|
||||
let user2Token = null;
|
||||
|
||||
if (cookies) {
|
||||
const authCookie = cookies.find(cookie => cookie.startsWith('authToken='));
|
||||
if (authCookie) {
|
||||
user2Token = authCookie.split('=')[1].split(';')[0];
|
||||
}
|
||||
}
|
||||
|
||||
// Get bookmarks for user2 (should be empty)
|
||||
const bookmarksResponse = await axios.get(`${BASE_URL}/api/bookmarks`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${user2Token}`
|
||||
}
|
||||
});
|
||||
|
||||
if (bookmarksResponse.data.bookmarks.length > 0) {
|
||||
throw new Error('User2 should not see user1 bookmarks');
|
||||
}
|
||||
|
||||
console.log(` Data isolation verified - user2 sees 0 bookmarks`);
|
||||
}
|
||||
|
||||
async function cleanup() {
|
||||
// Delete remaining test bookmarks
|
||||
for (const bookmarkId of createdBookmarkIds) {
|
||||
try {
|
||||
await axios.delete(`${BASE_URL}/api/bookmarks/${bookmarkId}`, {
|
||||
headers: {
|
||||
'Cookie': `authToken=${authToken}`
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
// Ignore cleanup errors
|
||||
}
|
||||
}
|
||||
console.log(`✅ Cleanup completed`);
|
||||
}
|
||||
|
||||
async function runTests() {
|
||||
console.log('🚀 Starting Bookmark API Tests');
|
||||
console.log('==============================');
|
||||
|
||||
// Setup
|
||||
await testEndpoint('Setup Test User', setupTestUser);
|
||||
|
||||
// Basic CRUD operations
|
||||
await testEndpoint('Create Bookmark', testCreateBookmark);
|
||||
await testEndpoint('Get Bookmarks', testGetBookmarks);
|
||||
await testEndpoint('Get Bookmark by ID', testGetBookmarkById);
|
||||
await testEndpoint('Update Bookmark', testUpdateBookmark);
|
||||
|
||||
// Bulk operations
|
||||
await testEndpoint('Bulk Create Bookmarks', testBulkCreateBookmarks);
|
||||
|
||||
// Additional endpoints
|
||||
await testEndpoint('Get Folders', testGetFolders);
|
||||
await testEndpoint('Get Statistics', testGetStats);
|
||||
await testEndpoint('Export Bookmarks', testExportBookmarks);
|
||||
|
||||
// Filtering and search
|
||||
await testEndpoint('Filter by Folder', testBookmarkFiltering);
|
||||
await testEndpoint('Search Bookmarks', testBookmarkSearch);
|
||||
|
||||
// Security tests
|
||||
await testEndpoint('Unauthorized Access', testUnauthorizedAccess);
|
||||
await testEndpoint('Data Isolation', testDataIsolation);
|
||||
|
||||
// Cleanup
|
||||
await testEndpoint('Delete Bookmark', testDeleteBookmark);
|
||||
await testEndpoint('Cleanup', cleanup);
|
||||
|
||||
console.log('\n🎉 All bookmark API tests completed!');
|
||||
}
|
||||
|
||||
// Check if server is running
|
||||
async function checkServer() {
|
||||
try {
|
||||
await axios.get(`${BASE_URL}/health`);
|
||||
console.log('✅ Server is running');
|
||||
return true;
|
||||
} catch (error) {
|
||||
console.log('❌ Server is not running. Please start the server first with: npm start');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const serverRunning = await checkServer();
|
||||
if (serverRunning) {
|
||||
await runTests();
|
||||
}
|
||||
}
|
||||
|
||||
main().catch(console.error);
|
||||
Reference in New Issue
Block a user