Merge branch 'main' into spike/block2-derisking

# Conflicts:
#	apps/app_seeds/lib/app.dart
#	apps/app_seeds/lib/i18n/strings.g.dart
#	apps/app_seeds/lib/main.dart
This commit is contained in:
vjrj 2026-07-10 10:50:16 +02:00
commit cf99b7ff87
23 changed files with 607 additions and 25 deletions

View file

@ -0,0 +1,86 @@
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:tane/db/database.dart';
import 'package:tane/security/secure_key_store.dart';
import 'package:tane/services/auto_backup_service.dart';
import 'package:tane/services/auto_backup_store.dart';
import 'package:tane/services/export_import_service.dart';
import 'package:tane/services/file_service.dart';
import 'package:tane/ui/auto_backup_gate.dart';
import '../support/test_support.dart';
class _NoFiles implements FileService {
@override
Future<String?> saveFile({
required String suggestedName,
required Uint8List bytes,
}) async => null;
@override
Future<Uint8List?> pickFileBytes({List<String>? allowedExtensions}) async =>
null;
}
/// Counts how many times the lifecycle asked for a backup.
class _SpyAutoBackup extends AutoBackupService {
_SpyAutoBackup(AppDatabase db)
: super(
exporter: ExportImportService(
repository: newTestRepository(db),
files: _NoFiles(),
keys: SecureKeyStore(store: InMemorySecretStore()),
),
store: AutoBackupStore(InMemorySecretStore()),
directory: () async => throw UnimplementedError(),
);
int calls = 0;
@override
Future<bool> runIfDue({Duration interval = const Duration(days: 7)}) async {
calls++;
return false;
}
}
void main() {
late AppDatabase db;
setUp(() => db = newTestDatabase());
tearDown(() => db.close());
testWidgets('runs a backup on startup', (tester) async {
final spy = _SpyAutoBackup(db);
await tester.pumpWidget(
AutoBackupGate(service: spy, child: const SizedBox()),
);
await tester.pump(); // let the post-frame callback fire
expect(spy.calls, 1);
});
testWidgets('runs a backup again when the app is hidden', (tester) async {
final spy = _SpyAutoBackup(db);
await tester.pumpWidget(
AutoBackupGate(service: spy, child: const SizedBox()),
);
await tester.pump(); // startup backup
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.inactive);
tester.binding.handleAppLifecycleStateChanged(AppLifecycleState.hidden);
expect(spy.calls, 2);
});
testWidgets('does nothing when no service is provided', (tester) async {
await tester.pumpWidget(
const AutoBackupGate(child: Text('ok', textDirection: TextDirection.ltr)),
);
await tester.pump();
expect(find.text('ok'), findsOneWidget);
});
}