- 登录: QQ/163/126/189/搜狐/Gmail/Outlook/Yahoo/iCloud/企业邮/自定义, 自动识别+测试连接 - 收信: IMAP 多文件夹同步/未读/星标/搜索(本地+服务器) - 读信: HTML渲染/CID内嵌图/原文查看/附件下载 - 写信: 收件人/抄送/密送/附件/富文本/草稿/回复转发 - 存储: SQLite + 系统安全存储(DPAPI/Keychain/Keystore) - 测试: 单元测试 + Mock IMAP/SMTP 服务器集成测试(23项全过) - 可迁移: Windows/Android/iOS/macOS/Linux 一套代码
95 lines
3.0 KiB
Dart
95 lines
3.0 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:mail_hub/core/models/address.dart';
|
|
import 'package:mail_hub/core/protocol/mime/mime_builder.dart';
|
|
import 'package:mail_hub/core/protocol/smtp/smtp_client.dart';
|
|
import 'package:mail_hub/core/protocol/smtp/smtp_exception.dart';
|
|
|
|
/// SMTP 客户端集成测试:连接本地 Mock 服务器,验证完整发信流程
|
|
void main() {
|
|
Process? server;
|
|
int port = 0;
|
|
final outFile = '${Directory.systemTemp.path}/mock_smtp_${DateTime.now().millisecondsSinceEpoch}.eml';
|
|
|
|
setUpAll(() async {
|
|
final probe = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
|
|
port = probe.port;
|
|
await probe.close();
|
|
|
|
server = await Process.start('python3', [
|
|
'test/mock_smtp_server.py',
|
|
'$port',
|
|
'--auth-user',
|
|
'test@example.com',
|
|
'--auth-pass',
|
|
'secret',
|
|
'--out',
|
|
outFile,
|
|
]);
|
|
await server!.stdout.transform(utf8.decoder).first;
|
|
});
|
|
|
|
tearDownAll(() async {
|
|
server?.kill();
|
|
final f = File(outFile);
|
|
if (await f.exists()) await f.delete();
|
|
});
|
|
|
|
test('完整流程:连接→EHLO→AUTH→发送→QUIT', () async {
|
|
final client = SmtpClient();
|
|
await client.connect('127.0.0.1', port, ssl: false);
|
|
expect(client.isConnected, true);
|
|
expect(client.hasCapability('AUTH'), true);
|
|
|
|
await client.auth('test@example.com', 'secret');
|
|
|
|
final raw = MimeBuilder.build(
|
|
from: const MailAddress('张三', 'zhangsan@example.com'),
|
|
to: const [MailAddress('李四', 'lisi@example.com')],
|
|
cc: const [MailAddress('王五', 'wangwu@example.com')],
|
|
subject: 'SMTP 集成测试',
|
|
textBody: '这是正文',
|
|
htmlBody: '<b>这是正文</b>',
|
|
);
|
|
await client.sendMail('zhangsan@example.com', [
|
|
'lisi@example.com',
|
|
'wangwu@example.com',
|
|
'secret-bcc@example.com',
|
|
], raw);
|
|
client.quit();
|
|
client.disconnect();
|
|
expect(client.isConnected, false);
|
|
|
|
// 校验 mock 收到的邮件内容
|
|
final eml = await File(outFile).readAsString();
|
|
expect(eml, contains('From: =?UTF-8?B?5byg5LiJ?= <zhangsan@example.com>'));
|
|
expect(eml, contains('To: =?UTF-8?B?5p2O5Zub?= <lisi@example.com>'));
|
|
expect(eml, contains('Cc: =?UTF-8?B?546L5LqU?= <wangwu@example.com>'));
|
|
expect(eml, contains('Subject: =?UTF-8?B?U01UUCDpm4bmiJDmtYvor5U=?='));
|
|
expect(eml, contains('multipart/alternative'));
|
|
// 无附件时不包裹 mixed
|
|
expect(eml, isNot(contains('multipart/mixed')));
|
|
expect(eml, contains('MOCK_MSG_END'));
|
|
});
|
|
|
|
test('认证失败抛出 SmtpException', () async {
|
|
final client = SmtpClient();
|
|
await client.connect('127.0.0.1', port, ssl: false);
|
|
await expectLater(
|
|
client.auth('test@example.com', 'wrong'),
|
|
throwsA(isA<SmtpException>()),
|
|
);
|
|
client.disconnect();
|
|
});
|
|
|
|
test('连接被拒抛出 SmtpException', () async {
|
|
final client = SmtpClient();
|
|
await expectLater(
|
|
client.connect('127.0.0.1', 1, ssl: false),
|
|
throwsA(isA<SmtpException>()),
|
|
);
|
|
});
|
|
}
|