feat(storage): add comprehensive tests for StorageManager with memory, filesystem, and custom function backends
feat(email): implement EmailSendJob class for robust email delivery with retry logic and MX record resolution feat(mail): restructure mail module exports for simplified access to core and delivery functionalities
This commit is contained in:
		| @@ -0,0 +1,207 @@ | ||||
| import { tap, expect } from '@git.zone/tstest/tapbundle'; | ||||
| import { startTestServer, stopTestServer, type ITestServer } from '../../helpers/server.loader.ts'; | ||||
| import { createSmtpClient } from '../../../ts/mail/delivery/smtpclient/index.ts'; | ||||
| import { Email } from '../../../ts/mail/core/classes.email.ts'; | ||||
| import * as net from 'net'; | ||||
|  | ||||
| let testServer: ITestServer; | ||||
|  | ||||
| tap.test('setup test SMTP server', async () => { | ||||
|   testServer = await startTestServer({ | ||||
|     port: 2601, | ||||
|     tlsEnabled: false, | ||||
|     authRequired: false | ||||
|   }); | ||||
|   expect(testServer).toBeTruthy(); | ||||
|   expect(testServer.port).toEqual(2601); | ||||
| }); | ||||
|  | ||||
| tap.test('CREL-02: Handle network interruption during verification', async () => { | ||||
|   // Create a server that drops connections mid-session | ||||
|   const interruptServer = net.createServer((socket) => { | ||||
|     socket.write('220 Interrupt Test Server\r\n'); | ||||
|      | ||||
|     socket.on('data', (data) => { | ||||
|       const command = data.toString().trim(); | ||||
|       console.log(`Server received: ${command}`); | ||||
|        | ||||
|       if (command.startsWith('EHLO')) { | ||||
|         // Start sending multi-line response then drop | ||||
|         socket.write('250-test.server\r\n'); | ||||
|         socket.write('250-PIPELINING\r\n'); | ||||
|          | ||||
|         // Simulate network interruption | ||||
|         setTimeout(() => { | ||||
|           console.log('Simulating network interruption...'); | ||||
|           socket.destroy(); | ||||
|         }, 100); | ||||
|       } | ||||
|     }); | ||||
|   }); | ||||
|  | ||||
|   await new Promise<void>((resolve) => { | ||||
|     interruptServer.listen(2602, () => resolve()); | ||||
|   }); | ||||
|  | ||||
|   const smtpClient = createSmtpClient({ | ||||
|     host: '127.0.0.1', | ||||
|     port: 2602, | ||||
|     secure: false, | ||||
|     connectionTimeout: 2000, | ||||
|     debug: true | ||||
|   }); | ||||
|  | ||||
|   // Should handle the interruption gracefully | ||||
|   const result = await smtpClient.verify(); | ||||
|   expect(result).toBeFalse(); | ||||
|   console.log('✅ Handled network interruption during verification'); | ||||
|  | ||||
|   await new Promise<void>((resolve) => { | ||||
|     interruptServer.close(() => resolve()); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| tap.test('CREL-02: Recovery after brief network glitch', async () => { | ||||
|   const smtpClient = createSmtpClient({ | ||||
|     host: testServer.hostname, | ||||
|     port: testServer.port, | ||||
|     secure: false, | ||||
|     connectionTimeout: 5000, | ||||
|     debug: true | ||||
|   }); | ||||
|  | ||||
|   // Send email successfully | ||||
|   const email1 = new Email({ | ||||
|     from: 'sender@example.com', | ||||
|     to: ['recipient@example.com'], | ||||
|     subject: 'Before Glitch', | ||||
|     text: 'First email before network glitch' | ||||
|   }); | ||||
|  | ||||
|   const result1 = await smtpClient.sendMail(email1); | ||||
|   expect(result1.success).toBeTrue(); | ||||
|   console.log('First email sent successfully'); | ||||
|  | ||||
|   // Close to simulate brief network issue | ||||
|   await smtpClient.close(); | ||||
|   console.log('Simulating brief network glitch...'); | ||||
|    | ||||
|   // Wait a moment | ||||
|   await new Promise(resolve => setTimeout(resolve, 500)); | ||||
|  | ||||
|   // Try to send another email - should reconnect automatically | ||||
|   const email2 = new Email({ | ||||
|     from: 'sender@example.com', | ||||
|     to: ['recipient@example.com'], | ||||
|     subject: 'After Glitch', | ||||
|     text: 'Second email after network recovery' | ||||
|   }); | ||||
|  | ||||
|   const result2 = await smtpClient.sendMail(email2); | ||||
|   expect(result2.success).toBeTrue(); | ||||
|   console.log('✅ Recovered from network glitch successfully'); | ||||
|  | ||||
|   await smtpClient.close(); | ||||
| }); | ||||
|  | ||||
| tap.test('CREL-02: Handle server becoming unresponsive', async () => { | ||||
|   // Create a server that stops responding | ||||
|   const unresponsiveServer = net.createServer((socket) => { | ||||
|     socket.write('220 Unresponsive Server\r\n'); | ||||
|     let commandCount = 0; | ||||
|      | ||||
|     socket.on('data', (data) => { | ||||
|       const command = data.toString().trim(); | ||||
|       commandCount++; | ||||
|       console.log(`Command ${commandCount}: ${command}`); | ||||
|        | ||||
|       // Stop responding after first command | ||||
|       if (commandCount === 1 && command.startsWith('EHLO')) { | ||||
|         console.log('Server becoming unresponsive...'); | ||||
|         // Don't send any response - simulate hung server | ||||
|       } | ||||
|     }); | ||||
|      | ||||
|     // Don't close the socket, just stop responding | ||||
|   }); | ||||
|  | ||||
|   await new Promise<void>((resolve) => { | ||||
|     unresponsiveServer.listen(2604, () => resolve()); | ||||
|   }); | ||||
|  | ||||
|   const smtpClient = createSmtpClient({ | ||||
|     host: '127.0.0.1', | ||||
|     port: 2604, | ||||
|     secure: false, | ||||
|     connectionTimeout: 2000, // Short timeout to detect unresponsiveness | ||||
|     debug: true | ||||
|   }); | ||||
|  | ||||
|   // Should timeout when server doesn't respond | ||||
|   const result = await smtpClient.verify(); | ||||
|   expect(result).toBeFalse(); | ||||
|   console.log('✅ Detected unresponsive server'); | ||||
|  | ||||
|   await new Promise<void>((resolve) => { | ||||
|     unresponsiveServer.close(() => resolve()); | ||||
|   }); | ||||
| }); | ||||
|  | ||||
| tap.test('CREL-02: Handle large email successfully', async () => { | ||||
|   const smtpClient = createSmtpClient({ | ||||
|     host: testServer.hostname, | ||||
|     port: testServer.port, | ||||
|     secure: false, | ||||
|     connectionTimeout: 10000, | ||||
|     socketTimeout: 10000, | ||||
|     debug: true | ||||
|   }); | ||||
|  | ||||
|   // Create a large email | ||||
|   const largeText = 'x'.repeat(10000); // 10KB of text | ||||
|   const email = new Email({ | ||||
|     from: 'sender@example.com', | ||||
|     to: ['recipient@example.com'], | ||||
|     subject: 'Large Email Test', | ||||
|     text: largeText | ||||
|   }); | ||||
|  | ||||
|   // Should complete successfully despite size | ||||
|   const result = await smtpClient.sendMail(email); | ||||
|   expect(result.success).toBeTrue(); | ||||
|   console.log('✅ Large email sent successfully'); | ||||
|  | ||||
|   await smtpClient.close(); | ||||
| }); | ||||
|  | ||||
| tap.test('CREL-02: Rapid reconnection after interruption', async () => { | ||||
|   const smtpClient = createSmtpClient({ | ||||
|     host: testServer.hostname, | ||||
|     port: testServer.port, | ||||
|     secure: false, | ||||
|     connectionTimeout: 5000, | ||||
|     debug: true | ||||
|   }); | ||||
|  | ||||
|   // Rapid cycle of verify, close, verify | ||||
|   for (let i = 0; i < 3; i++) { | ||||
|     const result = await smtpClient.verify(); | ||||
|     expect(result).toBeTrue(); | ||||
|      | ||||
|     await smtpClient.close(); | ||||
|     console.log(`Rapid cycle ${i + 1} completed`); | ||||
|      | ||||
|     // Very short delay | ||||
|     await new Promise(resolve => setTimeout(resolve, 50)); | ||||
|   } | ||||
|  | ||||
|   console.log('✅ Rapid reconnection handled successfully'); | ||||
| }); | ||||
|  | ||||
| tap.test('cleanup test SMTP server', async () => { | ||||
|   if (testServer) { | ||||
|     await stopTestServer(testServer); | ||||
|   } | ||||
| }); | ||||
|  | ||||
| export default tap.start(); | ||||
		Reference in New Issue
	
	Block a user