Add comprehensive database setup and user management system
- Implement PostgreSQL database schema with users and bookmarks tables - Add database connection pooling with retry logic and error handling - Create migration system with automatic schema initialization - Add database CLI tools for management (init, status, validate, etc.) - Include comprehensive error handling and diagnostics - Add development seed data and testing utilities - Implement health monitoring and connection pool statistics - Create detailed documentation and troubleshooting guide Database features: - Users table with authentication fields and email verification - Bookmarks table with user association and metadata - Proper indexes for performance optimization - Automatic timestamp triggers - Transaction support with rollback handling - Connection pooling (20 max connections, 30s idle timeout) - Graceful shutdown handling CLI commands available: - npm run db:init - Initialize database - npm run db:status - Check database status - npm run db:validate - Validate schema - npm run db:test - Run database tests - npm run db:diagnostics - Full diagnostics
This commit is contained in:
262
tests/test_enhanced_link_testing.html
Normal file
262
tests/test_enhanced_link_testing.html
Normal file
@ -0,0 +1,262 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Enhanced Link Testing Test</title>
|
||||
<style>
|
||||
body { font-family: Arial, sans-serif; margin: 20px; }
|
||||
.test-section { margin: 20px 0; padding: 15px; border: 1px solid #ddd; }
|
||||
.test-result { margin: 10px 0; padding: 10px; background: #f5f5f5; }
|
||||
.success { background: #d4edda; color: #155724; }
|
||||
.error { background: #f8d7da; color: #721c24; }
|
||||
button { margin: 5px; padding: 10px 15px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Enhanced Link Testing Test</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Test Configuration</h2>
|
||||
<div id="configDisplay"></div>
|
||||
<button onclick="showCurrentConfig()">Show Current Config</button>
|
||||
<button onclick="testConfigMethods()">Test Config Methods</button>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Error Categorization Test</h2>
|
||||
<button onclick="testErrorCategorization()">Test Error Categories</button>
|
||||
<div id="errorCategoryResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Link Testing Test</h2>
|
||||
<button onclick="testSingleLink()">Test Single Link</button>
|
||||
<button onclick="testMultipleLinks()">Test Multiple Links</button>
|
||||
<div id="linkTestResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Utility Functions Test</h2>
|
||||
<button onclick="testUtilityFunctions()">Test Utility Functions</button>
|
||||
<div id="utilityResults"></div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script>
|
||||
// Initialize bookmark manager for testing
|
||||
const testManager = new BookmarkManager();
|
||||
|
||||
function showCurrentConfig() {
|
||||
const configDiv = document.getElementById('configDisplay');
|
||||
configDiv.innerHTML = `
|
||||
<div class="test-result">
|
||||
<strong>Current Configuration:</strong><br>
|
||||
Timeout: ${testManager.linkTestConfig.timeout}ms<br>
|
||||
Max Retries: ${testManager.linkTestConfig.maxRetries}<br>
|
||||
Retry Delay: ${testManager.linkTestConfig.retryDelay}ms<br>
|
||||
User Agent: ${testManager.linkTestConfig.userAgent}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function testConfigMethods() {
|
||||
const configDiv = document.getElementById('configDisplay');
|
||||
try {
|
||||
// Test saving and loading config
|
||||
const originalConfig = {...testManager.linkTestConfig};
|
||||
|
||||
// Modify config
|
||||
testManager.linkTestConfig.timeout = 15000;
|
||||
testManager.linkTestConfig.maxRetries = 3;
|
||||
testManager.saveLinkTestConfigToStorage();
|
||||
|
||||
// Reset and reload
|
||||
testManager.linkTestConfig = {timeout: 10000, maxRetries: 2, retryDelay: 1000, userAgent: 'Test'};
|
||||
testManager.loadLinkTestConfigFromStorage();
|
||||
|
||||
const success = testManager.linkTestConfig.timeout === 15000 && testManager.linkTestConfig.maxRetries === 3;
|
||||
|
||||
configDiv.innerHTML += `
|
||||
<div class="test-result ${success ? 'success' : 'error'}">
|
||||
Config save/load test: ${success ? 'PASSED' : 'FAILED'}
|
||||
</div>
|
||||
`;
|
||||
|
||||
// Restore original config
|
||||
testManager.linkTestConfig = originalConfig;
|
||||
testManager.saveLinkTestConfigToStorage();
|
||||
|
||||
} catch (error) {
|
||||
configDiv.innerHTML += `
|
||||
<div class="test-result error">
|
||||
Config test error: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
function testErrorCategorization() {
|
||||
const resultsDiv = document.getElementById('errorCategoryResults');
|
||||
const testCases = [
|
||||
{ error: new Error('fetch failed'), expected: 'network_error' },
|
||||
{ error: { name: 'AbortError', message: 'aborted' }, expected: 'timeout' },
|
||||
{ error: new Error('Invalid URL'), expected: 'invalid_url' },
|
||||
{ error: new Error('DNS resolution failed'), expected: 'dns_error' },
|
||||
{ error: new Error('SSL certificate error'), expected: 'ssl_error' },
|
||||
{ error: new Error('connection refused'), expected: 'connection_refused' }
|
||||
];
|
||||
|
||||
let results = '<h3>Error Categorization Results:</h3>';
|
||||
|
||||
testCases.forEach((testCase, index) => {
|
||||
try {
|
||||
const category = testManager.categorizeError(testCase.error);
|
||||
const success = category === testCase.expected;
|
||||
results += `
|
||||
<div class="test-result ${success ? 'success' : 'error'}">
|
||||
Test ${index + 1}: ${testCase.error.message} → ${category}
|
||||
${success ? '✓' : `✗ (expected ${testCase.expected})`}
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
results += `
|
||||
<div class="test-result error">
|
||||
Test ${index + 1} failed: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
});
|
||||
|
||||
resultsDiv.innerHTML = results;
|
||||
}
|
||||
|
||||
async function testSingleLink() {
|
||||
const resultsDiv = document.getElementById('linkTestResults');
|
||||
resultsDiv.innerHTML = '<div class="test-result">Testing single link...</div>';
|
||||
|
||||
try {
|
||||
// Test with a reliable URL
|
||||
const result = await testManager.performLinkTest('https://httpbin.org/status/200', 'Test Link');
|
||||
|
||||
resultsDiv.innerHTML = `
|
||||
<div class="test-result ${result.status === 'valid' ? 'success' : 'error'}">
|
||||
Single link test result:<br>
|
||||
Status: ${result.status}<br>
|
||||
Error Category: ${result.errorCategory || 'None'}<br>
|
||||
Attempts: ${result.errorDetails?.attempts || 'N/A'}
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
resultsDiv.innerHTML = `
|
||||
<div class="test-result error">
|
||||
Single link test failed: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
async function testMultipleLinks() {
|
||||
const resultsDiv = document.getElementById('linkTestResults');
|
||||
resultsDiv.innerHTML = '<div class="test-result">Testing multiple links...</div>';
|
||||
|
||||
const testUrls = [
|
||||
'https://httpbin.org/status/200',
|
||||
'https://httpbin.org/status/404',
|
||||
'https://invalid-url-that-does-not-exist.com',
|
||||
'not-a-valid-url'
|
||||
];
|
||||
|
||||
let results = '<h3>Multiple Link Test Results:</h3>';
|
||||
|
||||
for (let i = 0; i < testUrls.length; i++) {
|
||||
try {
|
||||
const result = await testManager.performLinkTest(testUrls[i], `Test Link ${i + 1}`);
|
||||
results += `
|
||||
<div class="test-result">
|
||||
${testUrls[i]}<br>
|
||||
Status: ${result.status}<br>
|
||||
Category: ${result.errorCategory || 'None'}<br>
|
||||
Attempts: ${result.errorDetails?.attempts || 'N/A'}
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
results += `
|
||||
<div class="test-result error">
|
||||
${testUrls[i]}: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
resultsDiv.innerHTML = results;
|
||||
}
|
||||
|
||||
function testUtilityFunctions() {
|
||||
const resultsDiv = document.getElementById('utilityResults');
|
||||
let results = '<h3>Utility Function Test Results:</h3>';
|
||||
|
||||
// Test formatErrorCategory
|
||||
try {
|
||||
const formatted = testManager.formatErrorCategory('network_error');
|
||||
const success = formatted === 'Network Error';
|
||||
results += `
|
||||
<div class="test-result ${success ? 'success' : 'error'}">
|
||||
formatErrorCategory test: ${success ? 'PASSED' : 'FAILED'} (${formatted})
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
results += `
|
||||
<div class="test-result error">
|
||||
formatErrorCategory test failed: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Test formatRelativeTime
|
||||
try {
|
||||
const now = Date.now();
|
||||
const fiveMinutesAgo = now - (5 * 60 * 1000);
|
||||
const formatted = testManager.formatRelativeTime(fiveMinutesAgo);
|
||||
const success = formatted.includes('5 minutes ago');
|
||||
results += `
|
||||
<div class="test-result ${success ? 'success' : 'error'}">
|
||||
formatRelativeTime test: ${success ? 'PASSED' : 'FAILED'} (${formatted})
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
results += `
|
||||
<div class="test-result error">
|
||||
formatRelativeTime test failed: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
// Test isTransientError
|
||||
try {
|
||||
const isTransient = testManager.isTransientError('network_error');
|
||||
const isNotTransient = !testManager.isTransientError('invalid_url');
|
||||
const success = isTransient && isNotTransient;
|
||||
results += `
|
||||
<div class="test-result ${success ? 'success' : 'error'}">
|
||||
isTransientError test: ${success ? 'PASSED' : 'FAILED'}
|
||||
</div>
|
||||
`;
|
||||
} catch (error) {
|
||||
results += `
|
||||
<div class="test-result error">
|
||||
isTransientError test failed: ${error.message}
|
||||
</div>
|
||||
`;
|
||||
}
|
||||
|
||||
resultsDiv.innerHTML = results;
|
||||
}
|
||||
|
||||
// Show initial config on load
|
||||
window.onload = function() {
|
||||
showCurrentConfig();
|
||||
};
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user