import { test, expect } from '@playwright/test'; import { SMTPServer } from 'smtp-server'; import { readFileSync } from 'node:fs'; import { join } from 'node:path'; import postgres from 'postgres'; test('encrypted SMTP credentials, real TLS delivery and optional login alerts', async ({ request, }) => { const messages: string[] = []; const recipients: string[] = []; const server = new SMTPServer({ secure: true, key: readFileSync(join(process.env.TEST_TLS_DIR!, 'key.pem')), cert: readFileSync(join(process.env.TEST_TLS_DIR!, 'cert.pem')), onAuth(auth, _session, done) { if (auth.username === 'testmailer' && auth.password === 'smtp-test-password') done(null, { user: 'testmailer' }); else done(new Error('Invalid credentials')); }, onData(stream, session, done) { let message = ''; stream.on('data', (chunk) => { message += chunk.toString(); }); stream.on('end', () => { messages.push(message); recipients.push(...session.envelope.rcptTo.map((r) => r.address)); done(); }); }, }); await new Promise((resolve, reject) => { server.on('error', reject); server.listen(15440, '127.0.0.1', resolve); }); const db = postgres(process.env.TEST_DATABASE_URL!, { max: 1 }); const password = process.env.TEST_PASSWORD!; const headers = { origin: 'http://localhost:3100', 'x-forwarded-for': '1.2.3.4, 2001:db8::123', 'user-agent': 'HamHam-TestBrowser', }; const mail = { host: '127.0.0.1', port: 15440, secure: true, username: 'testmailer', password: 'smtp-test-password', from: 'hamham@example.org', }; try { expect( ( await request.post('/api/auth/login', { headers, data: { username: 'testadmin', password }, }) ).status(), ).toBe(200); expect( ( await request.put('/api/settings/mail', { headers, data: { password, email: 'admin@example.org', loginNotifications: true, mail }, }) ).status(), ).toBe(200); expect((await request.post('/api/settings/mail/test', { headers })).status()).toBe(200); expect(messages).toHaveLength(1); expect(recipients).toEqual(['admin@example.org']); await request.post('/api/auth/logout', { headers }); expect( ( await request.post('/api/auth/login', { headers, data: { username: 'testadmin', password }, }) ).status(), ).toBe(200); await expect .poll( async () => (await (await request.get('/api/auth/security')).json()).events[0].mail_status, ) .toBe('sent'); expect(messages).toHaveLength(2); expect(messages[1]).toContain('HamHam-TestBrowser'); expect(messages[1]).toContain('2001:db8::123'); expect(messages[1]).not.toContain('1.2.3.4'); expect(messages[1]).not.toContain(password); expect( ( await request.put('/api/settings/mail', { headers, data: { password, email: 'admin@example.org', loginNotifications: false, mail: { ...mail, password: '' }, }, }) ).status(), ).toBe(200); expect((await request.post('/api/settings/mail/test', { headers })).status()).toBe(200); expect(messages).toHaveLength(3); // Blank password preserved the encrypted credential. await request.post('/api/auth/logout', { headers }); await request.post('/api/auth/login', { headers, data: { username: 'testadmin', password } }); expect((await (await request.get('/api/auth/security')).json()).events[0].mail_status).toBe( 'disabled', ); expect(messages).toHaveLength(3); } finally { await db`UPDATE admin_security SET login_notifications = false`; await db`DELETE FROM mail_config`; await db.end(); await new Promise((resolve) => server.close(resolve)); } });