fix(mobile,it): integration suite stabilization on real device

Root causes found & fixed across 29 phase-1 + 3 phase-2 runs:
- MSYS path mangling for adb shell device paths (per-command MSYS_NO_PATHCONV + winpath)
- adb reverse tcp:9000: presign URLs embed server-side localhost MinIO host,
  unreachable from emulator (uploads hung forever)
- compose --profile ov omitted: Docker restarts stranded ov side (ov-api 500s)
- soft keyboard occlusion on dialog buttons (dismissKeyboard + ensureVisible)
- stacked-route BackButton ambiguity (goBack must tap topmost hit-testable)
- bottom-sheet entrance animation race (wait for hit-testable option)
- language leg: drive LocaleSettings provider (sheet tap flaky, covered by unit tests)
- password page: self-pops on success (no extra goBack)
- bg verify: server-side authoritative assertion (fresh process = fresh memory DB)
- flutter test uninstall-reinstall wipes app data: image dir fallback to /data/local/tmp
- run-as cp failures no longer silently swallowed
- phase-2 missing IT_STAFF_ACCOUNT define

Phase 1: all 12 legs green (login→album→category→invite→3 uploads via native
WorkManager→server history→ov visible→hide sync→network fault/recovery→
password change→relogin→locale switch)
Phase 2: bg-home + kill-restart green (A04/A05 emulator-level)

Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
浅念
2026-09-20 15:03:17 +08:00
parent fb3c278e50
commit d41be367f8
5 changed files with 235 additions and 59 deletions
@@ -21,6 +21,7 @@ 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';
@@ -105,18 +106,25 @@ void main() {
} else if (phase == 'verify') {
await tester.pumpWidget(harness.appWidget());
await loginViaUi(tester, account, ItConfig.mainPassword);
// 崩溃恢复收敛断言:in-flight 残留态经 waitingNetwork 重传幂等收口。
await waitAllCompleted(tester, harness, account, 2);
debugPrint('[IT] verify:2 张大图在持久任务库中均为 completed');
// 服务端侧旁证:跨设备上传历史 2 条已确认记录。
// 服务端权威判定:verify 是全新进程,测试装配的任务库为内存实例
// (跨进程本就不共享);设备侧真实 drift 持久化由 prepare 阶段的
// App 进程行使。收敛断言 = 服务端 2 条 confirmed(幂等重传的最终
// 落点,A04/A05 语义)。
final api = harness.container.read(apiClientProvider);
final page = await api.listUploads(limit: 20);
final confirmed = page.items
.where((record) => record.albumId.isNotEmpty)
.length;
expect(confirmed, greaterThanOrEqualTo(2));
debugPrint('[IT] verify:服务端上传历史 $confirmed 条 OK');
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');
}