Files
moyosapp_beta.0.0.3.3_beta/scripts/test-email-tracking.ts
2026-01-16 19:04:48 +02:00

208 lines
5.9 KiB
TypeScript
Executable File
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env tsx
/**
* Test script for email tracking system
* Tests:
* 1. Webhook endpoint
* 2. Email logs API
* 3. Email status checking
*/
import { ENV } from '../src/lib/env';
const BASE_URL = process.env.NEXT_PUBLIC_APP_URL || 'http://localhost:3001';
const ADMIN_EMAIL = process.env.TEST_ADMIN_EMAIL || 'admin@themoyos.co.za';
const ADMIN_PASSWORD = process.env.ADMIN_PASSWORD || 'TSD107AS';
interface TestResult {
name: string;
passed: boolean;
message: string;
data?: any;
}
const results: TestResult[] = [];
async function test(name: string, fn: () => Promise<any>): Promise<void> {
try {
console.log(`\n🧪 Testing: ${name}`);
const result = await fn();
results.push({ name, passed: true, message: 'Passed', data: result });
console.log(`${name}: PASSED`);
if (result && typeof result === 'object') {
console.log(JSON.stringify(result, null, 2));
}
} catch (error) {
const message = error instanceof Error ? error.message : String(error);
results.push({ name, passed: false, message });
console.log(`${name}: FAILED - ${message}`);
}
}
async function login(): Promise<string> {
const response = await fetch(`${BASE_URL}/api/admin/auth`, {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
username: 'admin',
password: ADMIN_PASSWORD,
}),
});
if (!response.ok) {
throw new Error(`Login failed: ${response.status} ${response.statusText}`);
}
const cookies = response.headers.get('set-cookie');
if (!cookies) {
throw new Error('No session cookie received');
}
// Extract session cookie
const sessionMatch = cookies.match(/session=([^;]+)/);
if (!sessionMatch) {
throw new Error('Could not extract session cookie');
}
return sessionMatch[1];
}
async function main() {
console.log('📧 Email Tracking System Test');
console.log('='.repeat(50));
console.log(`Base URL: ${BASE_URL}`);
console.log(`Webhook URL: ${ENV.APP_URL}/api/webhooks/resend`);
// Test 1: Webhook endpoint (GET)
await test('Webhook Endpoint (GET)', async () => {
const response = await fetch(`${BASE_URL}/api/webhooks/resend`);
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (!data.webhookUrl || !data.status) {
throw new Error('Invalid webhook response');
}
return data;
});
// Test 2: Login to get session
let sessionCookie = '';
await test('Admin Login', async () => {
sessionCookie = await login();
return { message: 'Logged in successfully' };
});
if (!sessionCookie) {
console.log('\n⚠ Cannot continue tests without admin session');
return;
}
// Test 3: Get email logs
await test('Get Email Logs', async () => {
const response = await fetch(`${BASE_URL}/api/admin/emails?limit=10`, {
headers: {
'Cookie': `session=${sessionCookie}`,
},
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
if (!data.success || !Array.isArray(data.emails)) {
throw new Error('Invalid email logs response');
}
return {
total: data.pagination?.total || 0,
emails: data.emails.length,
sample: data.emails[0] || null,
};
});
// Test 4: Webhook signature verification (if secret is set)
if (process.env.RESEND_WEBHOOK_SECRET) {
await test('Webhook Signature Verification', async () => {
const crypto = await import('crypto');
const secret = process.env.RESEND_WEBHOOK_SECRET!;
const testPayload = JSON.stringify({ type: 'email.sent', data: { id: 'test-123' } });
const signature = crypto
.createHmac('sha256', secret)
.update(testPayload)
.digest('hex');
const response = await fetch(`${BASE_URL}/api/webhooks/resend`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'resend-signature': signature,
},
body: testPayload,
});
// Should accept valid signature (or return 200/400, not 401)
if (response.status === 401) {
throw new Error('Webhook rejected valid signature');
}
return { message: 'Signature verification working' };
});
} else {
console.log('\n⚠ RESEND_WEBHOOK_SECRET not set, skipping signature test');
}
// Test 5: Simulate webhook event (without signature for testing)
await test('Simulate Webhook Event (email.sent)', async () => {
const testPayload = {
type: 'email.sent',
data: {
email_id: 'test-webhook-' + Date.now(),
created_at: new Date().toISOString(),
},
};
const response = await fetch(`${BASE_URL}/api/webhooks/resend`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify(testPayload),
});
// Should accept webhook (might return 200 or 400 if email not found, but not 500)
if (response.status >= 500) {
throw new Error(`Server error: ${response.status}`);
}
const data = await response.json();
return { message: 'Webhook processed', status: response.status, data };
});
// Summary
console.log('\n' + '='.repeat(50));
console.log('📊 Test Summary');
console.log('='.repeat(50));
const passed = results.filter(r => r.passed).length;
const failed = results.filter(r => !r.passed).length;
results.forEach(result => {
const icon = result.passed ? '✅' : '❌';
console.log(`${icon} ${result.name}: ${result.message}`);
});
console.log('\n' + '='.repeat(50));
console.log(`Total: ${results.length} | Passed: ${passed} | Failed: ${failed}`);
console.log('='.repeat(50));
if (failed > 0) {
process.exit(1);
}
}
main().catch(error => {
console.error('Fatal error:', error);
process.exit(1);
});