import assert from 'node:assert/strict' import test from 'node:test' import { encryptEmailPassword, fetchEmailMessages, markEmailUidsSeen, sendEmailMessage, verifyEmailConnection, } from '../src/email.js' const enabled = process.env.RUN_EMAIL_PROTOCOL_TEST === '1' function channel(account) { return { id: `protocol-${account}`, type: 'email', account, displayName: account.startsWith('sender') ? 'Lingxi Sender' : 'Lingxi Receiver', encryptedPassword: encryptEmailPassword('pwd'), imapHost: process.env.EMAIL_TEST_HOST || '127.0.0.1', imapPort: Number(process.env.EMAIL_TEST_IMAP_PORT || 3143), imapSecure: false, imapFolder: 'INBOX', smtpHost: process.env.EMAIL_TEST_HOST || '127.0.0.1', smtpPort: Number(process.env.EMAIL_TEST_SMTP_PORT || 3025), smtpSecure: false, syncDays: 7, } } async function waitForMessage(target, predicate) { for (let attempt = 0; attempt < 20; attempt += 1) { const result = await fetchEmailMessages(target, { full: true }) const message = result.messages.find(predicate) if (message) return { message, result } await new Promise((resolve) => setTimeout(resolve, 150)) } throw new Error('等待邮件进入 IMAP INBOX 超时') } test('真实 SMTP 发件、IMAP 收件、附件解析、已读与回复头', { skip: !enabled }, async () => { process.env.JWT_SECRET ||= 'lingxi-email-protocol-test-secret' const sender = channel('sender@local.test') const receiver = channel('receiver@local.test') const subject = `Lingxi protocol ${Date.now()}` const senderCheck = await verifyEmailConnection(sender, 'pwd') const receiverCheck = await verifyEmailConnection(receiver, 'pwd') assert.ok(senderCheck.latencyMs >= 0) assert.ok(receiverCheck.latencyMs >= 0) assert.ok(!sender.encryptedPassword.includes('pwd')) const sent = await sendEmailMessage(sender, { to: receiver.account, subject, text: 'Lingxi IMAP/SMTP real protocol message', attachments: [{ filename: 'lingxi-proof.txt', content: Buffer.from('real email attachment'), contentType: 'text/plain', }], }) assert.ok(sent.messageId) const received = await waitForMessage(receiver, (message) => message.subject === subject) assert.equal(received.message.text, 'Lingxi IMAP/SMTP real protocol message') assert.equal(received.message.from.email, sender.account) assert.equal(received.message.attachments.length, 1) assert.equal(received.message.attachments[0].name, 'lingxi-proof.txt') assert.equal(Buffer.from(received.message.attachments[0].base64, 'base64').toString(), 'real email attachment') await markEmailUidsSeen(receiver, [received.message.uid]) const marked = await fetchEmailMessages(receiver, { full: true }) assert.equal(marked.messages.find((message) => message.uid === received.message.uid)?.seen, true) const reply = await sendEmailMessage(receiver, { to: sender.account, subject: `Re: ${subject}`, text: 'Reply received by Lingxi', inReplyTo: received.message.messageId, references: [received.message.messageId], }) const replyReceived = await waitForMessage(sender, (message) => message.messageId === reply.messageId) assert.equal(replyReceived.message.inReplyTo, received.message.messageId) assert.ok(replyReceived.message.references.includes(received.message.messageId)) })