145 lines
4.7 KiB
Dart
145 lines
4.7 KiB
Dart
import 'dart:convert';
|
|
import 'dart:io';
|
|
|
|
import 'package:flutter_test/flutter_test.dart';
|
|
import 'package:ikar/backend/modules/chats.dart';
|
|
import 'package:ikar/backend/modules/messages.dart';
|
|
import 'package:ikar/core/config/qlyra_settings.dart';
|
|
import 'package:ikar/core/media/deleted_media_keeper.dart';
|
|
import 'package:ikar/core/storage/app_database.dart';
|
|
import 'package:ikar/core/utils/media_cache.dart';
|
|
import 'package:path_provider_platform_interface/path_provider_platform_interface.dart';
|
|
import 'package:plugin_platform_interface/plugin_platform_interface.dart';
|
|
|
|
class _SyntheticPathProvider extends PathProviderPlatform
|
|
with MockPlatformInterfaceMixin {
|
|
final String directory;
|
|
|
|
_SyntheticPathProvider(this.directory);
|
|
|
|
@override
|
|
Future<String?> getApplicationSupportPath() async => directory;
|
|
|
|
@override
|
|
Future<String?> getTemporaryPath() async => directory;
|
|
}
|
|
|
|
const _accountId = 1;
|
|
const _chatId = 900101;
|
|
const _photoId = 555001;
|
|
const _photoUrl = 'https://synthetic.invalid/photo/555001';
|
|
final _photoBytes = List<int>.generate(64, (i) => i);
|
|
|
|
Map<String, dynamic> _photoMessageRow(String id) => CachedMessage(
|
|
id: id,
|
|
accountId: _accountId,
|
|
chatId: _chatId,
|
|
senderId: 20,
|
|
time: 1000,
|
|
payload: {
|
|
'attaches': [
|
|
{'_type': 'PHOTO', 'photoId': _photoId, 'baseUrl': _photoUrl},
|
|
],
|
|
},
|
|
).toDbRow();
|
|
|
|
Future<Map<String, dynamic>?> _attachOf(String id) async {
|
|
final row = await AppDatabase.loadMessage(_accountId, _chatId, id);
|
|
final payload = jsonDecode(row!['payload'] as String) as Map;
|
|
return Map<String, dynamic>.from((payload['attaches'] as List).first as Map);
|
|
}
|
|
|
|
void main() {
|
|
TestWidgetsFlutterBinding.ensureInitialized();
|
|
|
|
setUp(() async {
|
|
final directory = Directory.systemTemp.createTempSync(
|
|
'synthetic_deleted_media_test',
|
|
);
|
|
PathProviderPlatform.instance = _SyntheticPathProvider(directory.path);
|
|
MediaCache.resetForTesting();
|
|
addTearDown(() async {
|
|
await AppDatabase.close();
|
|
MediaCache.resetForTesting();
|
|
QlyraSettings.viewDeleted.value = false;
|
|
if (directory.existsSync()) directory.deleteSync(recursive: true);
|
|
});
|
|
await AppDatabase.init();
|
|
await AppDatabase.saveProfile(
|
|
ProfileData(
|
|
id: _accountId,
|
|
firstName: 'Synthetic owner',
|
|
phone: 100000,
|
|
country: 'ZZ',
|
|
accountStatus: 0,
|
|
updateTime: 1,
|
|
),
|
|
);
|
|
await AppDatabase.saveChats([
|
|
CachedChat(
|
|
id: _chatId,
|
|
accountId: _accountId,
|
|
type: 'DIALOG',
|
|
title: 'Synthetic dialog',
|
|
unreadCount: 0,
|
|
lastEventTime: 0,
|
|
cachedAt: 0,
|
|
dontDisturbUntil: 0,
|
|
isOnline: false,
|
|
seenTime: 0,
|
|
participants: const {_accountId: 0, 20: 0},
|
|
).toDbRow()..['in_list'] = 1,
|
|
]);
|
|
QlyraSettings.viewDeleted.value = true;
|
|
});
|
|
|
|
test('a deleted photo keeps a pinned copy the payload points at', () async {
|
|
await AppDatabase.saveMessages([_photoMessageRow('101')]);
|
|
final downloaded = await MediaCache.fileFor('photo_$_photoId.jpg');
|
|
await downloaded.writeAsBytes(_photoBytes);
|
|
|
|
await keepDeletedMedia(_accountId, _chatId, const ['101']);
|
|
|
|
final attach = await _attachOf('101');
|
|
final kept = attach!['localPath'] as String?;
|
|
expect(kept, isNotNull);
|
|
expect(kept, contains(MediaCache.keptPrefix));
|
|
expect(await File(kept!).readAsBytes(), _photoBytes);
|
|
});
|
|
|
|
test('without the setting nothing is copied', () async {
|
|
QlyraSettings.viewDeleted.value = false;
|
|
await AppDatabase.saveMessages([_photoMessageRow('102')]);
|
|
final downloaded = await MediaCache.fileFor('photo_$_photoId.jpg');
|
|
await downloaded.writeAsBytes(_photoBytes);
|
|
|
|
await keepDeletedMedia(_accountId, _chatId, const ['102']);
|
|
|
|
expect((await _attachOf('102'))!['localPath'], isNull);
|
|
});
|
|
|
|
test('media payload update cannot overwrite a concurrent edit', () async {
|
|
final original = _photoMessageRow('104');
|
|
await AppDatabase.saveMessages([original]);
|
|
await AppDatabase.saveMessages([
|
|
Map<String, dynamic>.from(original)..['text'] = 'edited'
|
|
..['payload'] = jsonEncode({'text': 'edited'}),
|
|
]);
|
|
await AppDatabase.updateMessageMediaPayload(
|
|
_accountId, _chatId, '104', original['payload'] as String,
|
|
jsonEncode({'attaches': [], 'localPath': 'obsolete'}),
|
|
);
|
|
final current = await AppDatabase.loadMessage(_accountId, _chatId, '104');
|
|
expect(current!['text'], 'edited');
|
|
expect(jsonDecode(current['payload'] as String), {'text': 'edited'});
|
|
});
|
|
|
|
test('a dead link without a cached copy leaves the payload alone', () async {
|
|
await AppDatabase.saveMessages([_photoMessageRow('103')]);
|
|
|
|
await keepDeletedMedia(_accountId, _chatId, const ['103']);
|
|
|
|
expect((await _attachOf('103'))!['localPath'], isNull);
|
|
});
|
|
}
|