Enable core library desugaring and update RentController to accept a notification service; add backup compatibility tests
Signed-off-by: Hans Kokx <hans.d.kokx@gmail.com>
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
import 'dart:io';
|
||||
|
||||
import 'package:flutter_test/flutter_test.dart';
|
||||
import 'package:http/http.dart' as http;
|
||||
import 'package:rental_income_tracker/models/app_settings.dart';
|
||||
import 'package:rental_income_tracker/models/monthly_rent_record.dart';
|
||||
import 'package:rental_income_tracker/services/notification_service.dart';
|
||||
import 'package:rental_income_tracker/services/open_exchange_service.dart';
|
||||
import 'package:rental_income_tracker/services/storage_service.dart';
|
||||
import 'package:rental_income_tracker/state/rent_controller.dart';
|
||||
|
||||
class _FakeStorageService extends StorageService {
|
||||
AppSettings _settings = AppSettings.defaults();
|
||||
Map<String, MonthlyRentRecord> _records = <String, MonthlyRentRecord>{};
|
||||
|
||||
@override
|
||||
Future<AppSettings> loadSettings() async => _settings;
|
||||
|
||||
@override
|
||||
Future<void> saveSettings(AppSettings settings) async {
|
||||
_settings = settings;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<Map<String, MonthlyRentRecord>> loadRecords() async =>
|
||||
Map<String, MonthlyRentRecord>.from(_records);
|
||||
|
||||
@override
|
||||
Future<void> saveRecords(Map<String, MonthlyRentRecord> records) async {
|
||||
_records = Map<String, MonthlyRentRecord>.from(records);
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeExchangeService extends ForexRateApiService {
|
||||
_FakeExchangeService({required this.quote})
|
||||
: super(httpClient: http.Client(), apiKey: 'test-key');
|
||||
|
||||
final double quote;
|
||||
|
||||
@override
|
||||
Future<ForexConversionResult> convertUsdToSek({
|
||||
required DateTime estDate,
|
||||
required double amount,
|
||||
}) async {
|
||||
return ForexConversionResult(quote: quote, result: quote * amount);
|
||||
}
|
||||
}
|
||||
|
||||
class _FakeNotificationService extends NotificationService {
|
||||
_FakeNotificationService() : super((_) async {});
|
||||
|
||||
@override
|
||||
Future<void> initialize() async {}
|
||||
|
||||
@override
|
||||
Future<void> processPendingActions() async {}
|
||||
|
||||
@override
|
||||
Future<void> scheduleForMonth({
|
||||
required int year,
|
||||
required int month,
|
||||
required int quietHourStart,
|
||||
required int fallbackHour,
|
||||
}) async {}
|
||||
|
||||
@override
|
||||
Future<void> cancelForMonth(int year, int month) async {}
|
||||
|
||||
@override
|
||||
Future<void> cancelAll() async {}
|
||||
}
|
||||
|
||||
void main() {
|
||||
group('backup/import compatibility', () {
|
||||
test(
|
||||
'round-trips current backup format through export and import',
|
||||
() async {
|
||||
final sourceStorage = _FakeStorageService();
|
||||
final sourceController = RentController(
|
||||
storageService: sourceStorage,
|
||||
exchangeService: _FakeExchangeService(quote: 10.5),
|
||||
notificationService: _FakeNotificationService(),
|
||||
);
|
||||
|
||||
await sourceController.updateRentUsd(2450);
|
||||
await sourceController.setMonthStatusManually(
|
||||
year: 2025,
|
||||
month: 1,
|
||||
status: PaymentStatus.onTime,
|
||||
paidUsd: 2000,
|
||||
);
|
||||
await sourceController.setMonthStatusManually(
|
||||
year: 2025,
|
||||
month: 2,
|
||||
status: PaymentStatus.late,
|
||||
paidUsd: 1800,
|
||||
);
|
||||
|
||||
final tempDir = await Directory.systemTemp.createTemp('backup_test_');
|
||||
addTearDown(() async {
|
||||
if (await tempDir.exists()) {
|
||||
await tempDir.delete(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
final backupPath = await sourceController.exportBackupToDirectory(
|
||||
tempDir.path,
|
||||
);
|
||||
|
||||
final targetStorage = _FakeStorageService();
|
||||
final targetController = RentController(
|
||||
storageService: targetStorage,
|
||||
exchangeService: _FakeExchangeService(quote: 99),
|
||||
notificationService: _FakeNotificationService(),
|
||||
);
|
||||
|
||||
await targetController.importBackupFromFile(backupPath);
|
||||
|
||||
expect(targetController.settings.rentUsd, 2450);
|
||||
|
||||
targetController.selectYear(2025);
|
||||
final rows = targetController.buildRowsForSelectedYear();
|
||||
final january = rows.firstWhere((row) => row.month == 1);
|
||||
final february = rows.firstWhere((row) => row.month == 2);
|
||||
|
||||
expect(january.status, PaymentStatus.onTime);
|
||||
expect(january.usdAmount, 2000);
|
||||
expect(january.sekAmount, 21000);
|
||||
expect(january.effectiveUsdToSekRate, 10.5);
|
||||
|
||||
expect(february.status, PaymentStatus.late);
|
||||
expect(february.usdAmount, 1800);
|
||||
expect(february.sekAmount, 18900);
|
||||
expect(february.effectiveUsdToSekRate, 10.5);
|
||||
|
||||
final persistedSettings = await targetStorage.loadSettings();
|
||||
final persistedRecords = await targetStorage.loadRecords();
|
||||
expect(persistedSettings.rentUsd, 2450);
|
||||
expect(
|
||||
persistedRecords.keys,
|
||||
containsAll(<String>['2025-01', '2025-02']),
|
||||
);
|
||||
},
|
||||
);
|
||||
|
||||
test(
|
||||
'imports legacy export payloads with missing optional fields',
|
||||
() async {
|
||||
final tempDir = await Directory.systemTemp.createTemp('backup_legacy_');
|
||||
addTearDown(() async {
|
||||
if (await tempDir.exists()) {
|
||||
await tempDir.delete(recursive: true);
|
||||
}
|
||||
});
|
||||
|
||||
final legacyFile = File('${tempDir.path}/legacy_export.json');
|
||||
await legacyFile.writeAsString('''
|
||||
{
|
||||
"exportedAtUtc": "2026-03-20T10:00:00.000Z",
|
||||
"settings": {
|
||||
"rentUsd": 1995.0,
|
||||
"occupied": true
|
||||
},
|
||||
"records": [
|
||||
{
|
||||
"year": 2024,
|
||||
"month": 12,
|
||||
"usdAmount": 1995.0,
|
||||
"sekAmount": 21446.25,
|
||||
"usdToSekRate": 10.75,
|
||||
"paidAtUtc": "2024-12-02T12:00:00.000Z",
|
||||
"onTime": false
|
||||
}
|
||||
]
|
||||
}
|
||||
''');
|
||||
|
||||
final controller = RentController(
|
||||
storageService: _FakeStorageService(),
|
||||
exchangeService: _FakeExchangeService(quote: 1),
|
||||
notificationService: _FakeNotificationService(),
|
||||
);
|
||||
|
||||
await controller.importBackupFromFile(legacyFile.path);
|
||||
|
||||
expect(controller.settings.rentUsd, 1995.0);
|
||||
expect(controller.settings.localFallbackHour, 9);
|
||||
expect(controller.settings.localQuietHourStart, 23);
|
||||
|
||||
controller.selectYear(2024);
|
||||
final december = controller.buildRowsForSelectedYear().firstWhere(
|
||||
(row) => row.month == 12,
|
||||
);
|
||||
|
||||
expect(december.status, PaymentStatus.late);
|
||||
expect(december.usdAmount, 1995.0);
|
||||
expect(december.sekAmount, 21446.25);
|
||||
expect(december.effectiveUsdToSekRate, 10.75);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in New Issue
Block a user