Files
photo-live/apps/mobile/integration_test/p3_background_test.dart
T

137 lines
6.1 KiB
Dart
Raw Normal View History

/// P3 Task 11 集成测试 · 后台延续与杀进程恢复(A04/A05,模拟器级)。
///
/// 由 `scripts/run-mobile-it.sh --background` 编排,两阶段经
/// `--dart-define IT_PHASE=` 区分(App 无法自己 HOME/杀自己——设备动作
/// 由脚本在见到 MARKER 后执行,属计划明示的 adb 编排场景):
///
/// - **prepare**:登录(本次运行专属账号)→ API 建相册 → 直接经准备器
/// 入队 2 张大图(~15MB,保证传输有可被中断的时间窗)→ 等任务进入
/// uploading → 打 `MARKER:UPLOADING`(脚本此刻 HOME 或 force-stop)→
/// 就地等待全部 completed(HOME 场景:WorkManager 后台续传完成后本
/// 测试自然通过;KILL 场景:进程被杀,本测试非零退出是预期)。
/// - **verify**:全新进程登录同账号 → 断言 2 张全部 completed(KILL 场景
/// 覆盖 A05:drift 任务库持久 + 崩溃恢复 uploading→waitingNetwork→
/// 幂等重传)→ 服务端历史可见。
library;
import 'package:flutter/foundation.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:integration_test/integration_test.dart';
import 'package:path/path.dart' as p;
import 'package:path_provider/path_provider.dart';
import 'package:photo_live/core/api_client.dart';
import 'package:photo_live/features/albums/models.dart';
import 'package:photo_live/features/uploads/models.dart';
import 'package:photo_live/features/uploads/providers.dart';
import 'package:photo_live/features/uploads/task_state.dart';
import 'helpers/app_boot.dart';
import 'helpers/it_config.dart';
import 'helpers/ui_steps.dart';
void main() {
IntegrationTestWidgetsFlutterBinding.ensureInitialized();
final phase = const String.fromEnvironment('IT_PHASE', defaultValue: '');
if (phase.isEmpty) {
fail('缺少 --dart-define IT_PHASE=prepare|verify(经 run-mobile-it.sh 运行)');
}
final harness = ItAppHarness.create();
testWidgets(
'P3 后台延续/杀进程恢复 [$phase]',
(tester) async {
final account = ItConfig.mainAccount;
if (phase == 'prepare') {
await tester.pumpWidget(harness.appWidget());
await loginViaUi(tester, account, ItConfig.mainPassword);
// 相册走 API(建相册的 UI 驱动已在主流程覆盖;本场景主题是传输)。
final api = harness.container.read(apiClientProvider);
final album = await api.createAlbum(
AlbumCreateRequest(
translations: [
AlbumTranslation(
locale: 'zh-CN',
title: '后台测试相册 ${ItConfig.runStamp}',
),
AlbumTranslation(
locale: 'en',
title: 'BG Album ${ItConfig.runStamp}',
),
],
defaultLocale: 'zh-CN',
startAt: DateTime.now().toUtc(),
),
);
debugPrint('[IT] 后台场景相册:${album.id}');
// 大图经准备器直接入队(选图 UI 已在主流程覆盖)。
final support = await getApplicationSupportDirectory();
final dir = await resolveImageDir(support.path);
final largeFiles = [ItConfig.largeFileA, ItConfig.largeFileB]
.map((name) => p.join(dir, name))
.toList();
for (final file in largeFiles) {
final task = await harness
.container
.read(uploadPreparerProvider)
.prepare(
accountId: account,
albumId: album.id,
sourcePath: file,
);
expect(task.state, UploadTaskState.queued, reason: '入队 $file');
}
debugPrint('[IT] 2 张大图已入队:$largeFiles');
// 等首个任务进入 uploading(引擎已领取、原生桥接已注册)。
await pollUntil<Map<UploadTaskState, int>>(
tester,
() => queueSnapshot(harness, account),
(counts) =>
(counts[UploadTaskState.uploading] ?? 0) >= 1 ||
(counts[UploadTaskState.uploadedConfirming] ?? 0) >= 1 ||
(counts[UploadTaskState.completed] ?? 0) >= 1,
timeout: const Duration(seconds: 60),
because: '任务进入传输中(脚本随后执行 HOME/force-stop)',
);
// 脚本协调点:轮询本行出现在 flutter test 输出后立即动作。
debugPrint('[IT] MARKER:UPLOADING');
// HOME 场景:后台完成 → 本断言通过;KILL 场景:进程死于此循环
// (非零退出是预期,脚本以 || true 容忍并以 verify 阶段收口)。
await waitAllCompleted(tester, harness, account, 2);
debugPrint('[IT] MARKER:BG_COMPLETED');
} else if (phase == 'verify') {
await tester.pumpWidget(harness.appWidget());
await loginViaUi(tester, account, ItConfig.mainPassword);
// 服务端权威判定:verify 是全新进程,测试装配的任务库为内存实例
// (跨进程本就不共享);设备侧真实 drift 持久化由 prepare 阶段的
// App 进程行使。收敛断言 = 服务端 2 条 confirmed(幂等重传的最终
// 落点,A04/A05 语义)。
final api = harness.container.read(apiClientProvider);
int confirmed = 0;
final deadline = DateTime.now().add(const Duration(minutes: 3));
while (DateTime.now().isBefore(deadline)) {
await tester.pump();
final page = await api.listUploads(limit: 20);
confirmed = page.items
.where((record) => record.state == UploadSessionState.confirmed)
.length;
if (confirmed >= 2) break;
await Future<void>.delayed(const Duration(seconds: 3));
}
expect(confirmed, greaterThanOrEqualTo(2),
reason: '服务端 confirmed 上传记录应 ≥2(后台/重启后收敛)');
debugPrint('[IT] verify:服务端上传历史 $confirmed 条 confirmed OK');
} else {
fail('未知 IT_PHASE:$phase');
}
},
timeout: const Timeout(Duration(minutes: 10)),
);
tearDownAll(harness.container.dispose);
}