- 登录: QQ/163/126/189/搜狐/Gmail/Outlook/Yahoo/iCloud/企业邮/自定义, 自动识别+测试连接 - 收信: IMAP 多文件夹同步/未读/星标/搜索(本地+服务器) - 读信: HTML渲染/CID内嵌图/原文查看/附件下载 - 写信: 收件人/抄送/密送/附件/富文本/草稿/回复转发 - 存储: SQLite + 系统安全存储(DPAPI/Keychain/Keystore) - 测试: 单元测试 + Mock IMAP/SMTP 服务器集成测试(23项全过) - 可迁移: Windows/Android/iOS/macOS/Linux 一套代码
108 lines
3.1 KiB
Dart
108 lines
3.1 KiB
Dart
import 'dart:convert';
|
||
import 'dart:io';
|
||
|
||
import 'package:flutter_test/flutter_test.dart';
|
||
import 'package:mail_hub/core/protocol/imap/imap_client.dart';
|
||
import 'package:mail_hub/core/protocol/imap/imap_exception.dart';
|
||
|
||
/// IMAP 客户端集成测试:连接本地 Mock 服务器,验证完整协议交互
|
||
void main() {
|
||
Process? server;
|
||
int port = 0;
|
||
|
||
setUpAll(() async {
|
||
// 找一个可用端口
|
||
final probe = await ServerSocket.bind(InternetAddress.loopbackIPv4, 0);
|
||
port = probe.port;
|
||
await probe.close();
|
||
|
||
server = await Process.start('python3', [
|
||
'test/mock_imap_server.py',
|
||
'$port',
|
||
'--auth-user',
|
||
'test@example.com',
|
||
'--auth-pass',
|
||
'secret',
|
||
]);
|
||
// 等待就绪
|
||
await server!.stdout.transform(utf8.decoder).first;
|
||
});
|
||
|
||
tearDownAll(() async {
|
||
server?.kill();
|
||
});
|
||
|
||
test('完整流程:连接→登录→列文件夹→选收件箱→搜索→抓元数据→抓全文→置标→登出',
|
||
() async {
|
||
final client = ImapClient();
|
||
await client.connect('127.0.0.1', port, ssl: false);
|
||
expect(client.isConnected, true);
|
||
|
||
// 登录(AUTH=PLAIN)
|
||
await client.login('test@example.com', 'secret');
|
||
expect(client.hasCapability('AUTH=PLAIN'), true);
|
||
|
||
// 列文件夹
|
||
final folders = await client.listFolders();
|
||
expect(folders.length, 3);
|
||
expect(folders[0].name, 'INBOX');
|
||
expect(folders[2].name, '[Gmail]');
|
||
expect(folders[2].attrs, contains(r'\Noselect'));
|
||
|
||
// 选收件箱
|
||
final info = await client.select('INBOX');
|
||
expect(info.exists, 3);
|
||
expect(info.uidNext, 10);
|
||
expect(info.readWrite, true);
|
||
|
||
// 搜索
|
||
final all = await client.search('ALL');
|
||
expect(all, [1, 2, 3]);
|
||
|
||
// 抓元数据(含 HEADER.FIELDS 字面量)
|
||
final metas = await client.fetchMeta([1, 2, 3]);
|
||
expect(metas.length, 3);
|
||
final m1 = metas.firstWhere((m) => m.uid == 1);
|
||
expect(m1.seen, true);
|
||
expect(m1.flagged, false);
|
||
expect(m1.size, 1234);
|
||
expect(m1.internalDate, isNotNull);
|
||
|
||
// 抓全文(HEADER + TEXT 两段字面量)
|
||
final full = await client.fetchFull(1);
|
||
expect(full.bodies.containsKey('HEADER'), true);
|
||
expect(full.bodies.containsKey('TEXT'), true);
|
||
final headerText = latin1.decode(full.bodies['HEADER']!);
|
||
expect(headerText, contains('Subject:'));
|
||
expect(headerText, contains('zhangsan@example.com'));
|
||
|
||
// 置标
|
||
await client.markSeen([2]);
|
||
await client.markFlagged([1, 2]);
|
||
|
||
// 登出
|
||
client.logout();
|
||
client.disconnect();
|
||
expect(client.isConnected, false);
|
||
});
|
||
|
||
test('认证失败抛出 ImapAuthException', () async {
|
||
final client = ImapClient();
|
||
await client.connect('127.0.0.1', port, ssl: false);
|
||
await expectLater(
|
||
client.login('test@example.com', 'wrong-password'),
|
||
throwsA(isA<ImapAuthException>()),
|
||
);
|
||
client.disconnect();
|
||
});
|
||
|
||
test('连接被拒抛出 ImapConnectionException', () async {
|
||
final client = ImapClient();
|
||
// 端口 1 (tcpmux) 通常无服务 → 连接拒绝
|
||
await expectLater(
|
||
client.connect('127.0.0.1', 1, ssl: false),
|
||
throwsA(isA<ImapConnectionException>()),
|
||
);
|
||
});
|
||
}
|