/** * CSV Rate API Test Script (Node.js) * * Usage: node test-csv-api.js * * Tests all CSV rate endpoints and verifies comparator functionality */ const API_URL = 'http://localhost:4000'; // Color codes for terminal output const colors = { reset: '\x1b[0m', red: '\x1b[31m', green: '\x1b[32m', yellow: '\x1b[33m', blue: '\x1b[34m', }; function printTest(number, description) { console.log(`${colors.yellow}[TEST ${number}] ${description}${colors.reset}`); } function printSuccess(message) { console.log(`${colors.green}✓ ${message}${colors.reset}`); } function printError(message) { console.log(`${colors.red}✗ ${message}${colors.reset}`); } function printInfo(message) { console.log(`${colors.blue}→ ${message}${colors.reset}`); } async function authenticateUser() { printTest(1, 'Authenticating as regular user'); const response = await fetch(`${API_URL}/api/v1/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: 'test4@xpeditis.com', password: 'SecurePassword123', }), }); const data = await response.json(); if (data.accessToken) { printSuccess('Regular user authenticated'); printInfo(`Token: ${data.accessToken.substring(0, 20)}...`); return data.accessToken; } else { printError('Failed to authenticate regular user'); console.log('Response:', data); throw new Error('Authentication failed'); } } async function testGetCompanies(token) { printTest(2, 'GET /rates/companies - Get available companies'); const response = await fetch(`${API_URL}/api/v1/rates/companies`, { headers: { Authorization: `Bearer ${token}` }, }); const data = await response.json(); console.log(JSON.stringify(data, null, 2)); if (data.total === 5) { printSuccess('Got 5 companies (including Test Maritime Express)'); printInfo(`Companies: ${data.companies.join(', ')}`); } else { printError(`Expected 5 companies, got ${data.total}`); } console.log(''); return data; } async function testGetFilterOptions(token) { printTest(3, 'GET /rates/filters/options - Get filter options'); const response = await fetch(`${API_URL}/api/v1/rates/filters/options`, { headers: { Authorization: `Bearer ${token}` }, }); const data = await response.json(); console.log(JSON.stringify(data, null, 2)); printSuccess('Filter options retrieved'); console.log(''); return data; } async function testBasicSearch(token) { printTest(4, 'POST /rates/search-csv - Basic rate search (NLRTM → USNYC)'); const response = await fetch(`${API_URL}/api/v1/rates/search-csv`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ origin: 'NLRTM', destination: 'USNYC', volumeCBM: 25.5, weightKG: 3500, palletCount: 10, containerType: 'LCL', }), }); const data = await response.json(); console.log(JSON.stringify(data, null, 2)); printInfo(`Total results: ${data.totalResults}`); // Check if Test Maritime Express is in results const hasTestMaritime = data.results.some(r => r.companyName === 'Test Maritime Express'); if (hasTestMaritime) { printSuccess('Test Maritime Express found in results'); const testPrice = data.results.find(r => r.companyName === 'Test Maritime Express').totalPrice .amount; printInfo(`Test Maritime Express price: $${testPrice}`); } else { printError('Test Maritime Express NOT found in results'); } // Count unique companies const uniqueCompanies = [...new Set(data.results.map(r => r.companyName))]; printInfo(`Results from ${uniqueCompanies.length} different companies`); if (uniqueCompanies.length >= 3) { printSuccess('Multiple companies in comparator ✓'); } else { printError(`Expected multiple companies, got ${uniqueCompanies.length}`); } console.log(''); return data; } async function testCompanyFilter(token) { printTest(5, 'POST /rates/search-csv - Filter by Test Maritime Express only'); const response = await fetch(`${API_URL}/api/v1/rates/search-csv`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ origin: 'NLRTM', destination: 'USNYC', volumeCBM: 25.5, weightKG: 3500, palletCount: 10, containerType: 'LCL', filters: { companies: ['Test Maritime Express'], }, }), }); const data = await response.json(); console.log(JSON.stringify(data.results.slice(0, 3), null, 2)); const uniqueCompanies = [...new Set(data.results.map(r => r.companyName))]; if (uniqueCompanies.length === 1 && uniqueCompanies[0] === 'Test Maritime Express') { printSuccess('Company filter working correctly'); } else { printError(`Company filter not working - got: ${uniqueCompanies.join(', ')}`); } console.log(''); return data; } async function testPriceFilter(token) { printTest(6, 'POST /rates/search-csv - Filter by price range ($900-$1200)'); const response = await fetch(`${API_URL}/api/v1/rates/search-csv`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ origin: 'NLRTM', destination: 'USNYC', volumeCBM: 25.5, weightKG: 3500, palletCount: 10, containerType: 'LCL', filters: { minPrice: 900, maxPrice: 1200, currency: 'USD', }, }), }); const data = await response.json(); printInfo(`Results in price range $900-$1200: ${data.totalResults}`); const prices = data.results.map(r => r.totalPrice.amount); const minPrice = Math.min(...prices); const maxPrice = Math.max(...prices); if (minPrice >= 900 && maxPrice <= 1200) { printSuccess(`Price filter working correctly (range: $${minPrice} - $${maxPrice})`); } else { printError(`Price filter not working - got range: $${minPrice} - $${maxPrice}`); } console.log(''); return data; } async function testTransitFilter(token) { printTest(7, 'POST /rates/search-csv - Filter by max transit days (≤23 days)'); const response = await fetch(`${API_URL}/api/v1/rates/search-csv`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ origin: 'NLRTM', destination: 'USNYC', volumeCBM: 25.5, weightKG: 3500, containerType: 'LCL', filters: { maxTransitDays: 23, }, }), }); const data = await response.json(); printInfo(`Results with transit ≤23 days: ${data.totalResults}`); const maxTransit = Math.max(...data.results.map(r => r.transitDays)); if (maxTransit <= 23) { printSuccess(`Transit filter working correctly (max: ${maxTransit} days)`); } else { printError(`Transit filter not working - max transit: ${maxTransit} days`); } console.log(''); return data; } async function testSurchargeFilter(token) { printTest(8, 'POST /rates/search-csv - Filter for rates without surcharges (all-in prices)'); const response = await fetch(`${API_URL}/api/v1/rates/search-csv`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ origin: 'NLRTM', destination: 'USNYC', volumeCBM: 25.5, weightKG: 3500, containerType: 'LCL', filters: { withoutSurcharges: true, }, }), }); const data = await response.json(); printInfo(`Results without surcharges: ${data.totalResults}`); const withSurcharges = data.results.filter(r => r.hasSurcharges).length; if (withSurcharges === 0) { printSuccess('Surcharge filter working correctly'); } else { printError(`Surcharge filter not working - found ${withSurcharges} results with surcharges`); } console.log(''); return data; } async function testComparator(token) { printTest(9, 'COMPARATOR TEST - Show all 5 companies for NLRTM → USNYC'); const response = await fetch(`${API_URL}/api/v1/rates/search-csv`, { method: 'POST', headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${token}`, }, body: JSON.stringify({ origin: 'NLRTM', destination: 'USNYC', volumeCBM: 25, weightKG: 3500, palletCount: 10, containerType: 'LCL', }), }); const data = await response.json(); console.log('\nCompany Comparison Table:'); console.log('========================='); data.results.slice(0, 10).forEach(result => { console.log( `${result.companyName}: $${result.totalPrice.amount} ${result.totalPrice.currency} - ${result.transitDays} days - Match: ${result.matchScore}%` ); }); const uniqueCompanies = [...new Set(data.results.map(r => r.companyName))]; printInfo('Companies in results:'); uniqueCompanies.forEach(company => console.log(` - ${company}`)); // Check if Test Maritime Express has lowest price const sortedByPrice = [...data.results].sort((a, b) => a.totalPrice.amount - b.totalPrice.amount); const lowestPriceCompany = sortedByPrice[0].companyName; const lowestPrice = sortedByPrice[0].totalPrice.amount; if (lowestPriceCompany === 'Test Maritime Express') { printSuccess('Test Maritime Express has the lowest price ✓'); printInfo(`Lowest price: $${lowestPrice} (Test Maritime Express)`); } else { printError( `Expected Test Maritime Express to have lowest price, but got: ${lowestPriceCompany}` ); } console.log(''); return data; } async function runTests() { console.log(`${colors.blue}========================================${colors.reset}`); console.log(`${colors.blue}CSV Rate API Test Script${colors.reset}`); console.log(`${colors.blue}========================================${colors.reset}`); console.log(''); try { // Authenticate const token = await authenticateUser(); console.log(''); // Run all tests await testGetCompanies(token); await testGetFilterOptions(token); await testBasicSearch(token); await testCompanyFilter(token); await testPriceFilter(token); await testTransitFilter(token); await testSurchargeFilter(token); await testComparator(token); console.log(`${colors.blue}========================================${colors.reset}`); console.log(`${colors.green}✓ All public endpoint tests completed!${colors.reset}`); console.log(`${colors.blue}========================================${colors.reset}`); console.log(''); console.log('Next steps:'); console.log('1. Run admin tests with an admin account'); console.log('2. Test CSV upload functionality'); console.log('3. Test CSV validation endpoint'); } catch (error) { printError(`Test failed: ${error.message}`); console.error(error); process.exit(1); } } // Run tests runTests();