89 lines
2.8 KiB
Dart
89 lines
2.8 KiB
Dart
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:fuel_tax_tracker/services/database_service.dart';
|
|
import 'package:path/path.dart' as p;
|
|
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
|
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
import 'package:sqflite_common_ffi/sqflite_ffi.dart';
|
|
|
|
/// Receipts are filed under `receipts/<vin>/<yyyy.mm>/` locally (mirroring
|
|
/// the `Receipts/<vin>/<yyyy.mm>` layout the cloud side uses — see
|
|
/// `cloud_sync_service_test.dart`), so browsing either one shows which
|
|
/// vehicle and month a photo belongs to.
|
|
void main() {
|
|
late Directory tempDir;
|
|
late DatabaseService database;
|
|
|
|
setUpAll(() {
|
|
sqfliteFfiInit();
|
|
databaseFactory = databaseFactoryFfi;
|
|
});
|
|
|
|
setUp(() async {
|
|
tempDir = await Directory.systemTemp.createTemp('receipt_storage_test_');
|
|
PathProviderPlatform.instance = _FakePathProviderPlatform(tempDir.path);
|
|
database = DatabaseService();
|
|
await database.init();
|
|
});
|
|
|
|
tearDown(() async {
|
|
await database.rawDb.close();
|
|
await tempDir.delete(recursive: true);
|
|
});
|
|
|
|
test('storeReceiptImage files the photo under receipts/<vin>/<yyyy.mm>/', () async {
|
|
final source = File(p.join(tempDir.path, 'source.jpg'))..writeAsBytesSync([1, 2, 3]);
|
|
|
|
final storedPath = await database.storeReceiptImage(
|
|
source,
|
|
'entry-1',
|
|
'1FMPU18L1TLB51349',
|
|
DateTime(2026, 8, 13),
|
|
);
|
|
|
|
expect(
|
|
p.dirname(storedPath),
|
|
p.join(database.receiptsDirectory.path, '1FMPU18L1TLB51349', '2026.08'),
|
|
);
|
|
expect(p.basename(storedPath), 'entry-1.jpg');
|
|
expect(await File(storedPath).exists(), isTrue);
|
|
});
|
|
|
|
test('storeReceiptImage sanitizes a VIN with characters unsafe in a path', () async {
|
|
final source = File(p.join(tempDir.path, 'source.jpg'))..writeAsBytesSync([1, 2, 3]);
|
|
|
|
final storedPath = await database.storeReceiptImage(
|
|
source,
|
|
'entry-2',
|
|
'../etc/passwd',
|
|
DateTime(2026, 1, 5),
|
|
);
|
|
|
|
expect(
|
|
p.dirname(storedPath),
|
|
p.join(
|
|
database.receiptsDirectory.path,
|
|
sanitizedPathSegment('../etc/passwd'),
|
|
'2026.01',
|
|
),
|
|
);
|
|
expect(p.isWithin(database.receiptsDirectory.path, storedPath), isTrue);
|
|
});
|
|
|
|
test('monthFolderName pads single-digit months', () {
|
|
expect(monthFolderName(DateTime(2026, 1, 5)), '2026.01');
|
|
expect(monthFolderName(DateTime(2026, 12, 5)), '2026.12');
|
|
});
|
|
}
|
|
|
|
class _FakePathProviderPlatform extends PathProviderPlatform with MockPlatformInterfaceMixin {
|
|
final String tempPath;
|
|
_FakePathProviderPlatform(this.tempPath);
|
|
|
|
@override
|
|
Future<String?> getApplicationDocumentsPath() async => tempPath;
|
|
|
|
@override
|
|
Future<String?> getTemporaryPath() async => tempPath;
|
|
}
|