Files
rental_income_tracker/test/backup_restore_compatibility_test.dart
T

203 lines
6.1 KiB
Dart

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 FrankfurterApiService {
_FakeExchangeService({required this.quote})
: super(httpClient: http.Client());
final double quote;
@override
Future<ConversionResult> convertUsdToSek({
required DateTime estDate,
required double amount,
}) async {
return ConversionResult(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);
},
);
});
}