mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-06-13 13:02:13 +00:00
add new test
Some checks failed
Flutter analyze & test / flutter_analyze_and_test (push) Has been cancelled
Some checks failed
Flutter analyze & test / flutter_analyze_and_test (push) Has been cancelled
This commit is contained in:
parent
67b3ad2275
commit
da97fe5f3d
2 changed files with 162 additions and 42 deletions
|
|
@ -95,7 +95,8 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
milliseconds: group!.deleteMessagesAfterMilliseconds,
|
milliseconds: group!.deleteMessagesAfterMilliseconds,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
final query = select(messages).join([
|
final query =
|
||||||
|
select(messages).join([
|
||||||
leftOuterJoin(
|
leftOuterJoin(
|
||||||
mediaFiles,
|
mediaFiles,
|
||||||
mediaFiles.mediaId.equalsExp(messages.mediaId),
|
mediaFiles.mediaId.equalsExp(messages.mediaId),
|
||||||
|
|
@ -171,7 +172,8 @@ class MessagesDao extends DatabaseAccessor<TwonlyDB> with _$MessagesDaoMixin {
|
||||||
m.isDeletedFromSender.equals(true)) |
|
m.isDeletedFromSender.equals(true)) |
|
||||||
m.mediaStored.equals(false)) &
|
m.mediaStored.equals(false)) &
|
||||||
// Only remove the message when ALL members have seen it. Otherwise the receipt will also be deleted which could cause issues in case a member opens the image later..
|
// Only remove the message when ALL members have seen it. Otherwise the receipt will also be deleted which could cause issues in case a member opens the image later..
|
||||||
(m.openedByAll.isSmallerThanValue(deletionTime) |
|
((m.openedByAll.isNotNull() &
|
||||||
|
m.openedByAll.isSmallerThanValue(deletionTime)) |
|
||||||
(m.isDeletedFromSender.equals(true) &
|
(m.isDeletedFromSender.equals(true) &
|
||||||
m.createdAt.isSmallerThanValue(deletionTime))),
|
m.createdAt.isSmallerThanValue(deletionTime))),
|
||||||
))
|
))
|
||||||
|
|
|
||||||
118
test/services/messages_purge_test.dart
Normal file
118
test/services/messages_purge_test.dart
Normal file
|
|
@ -0,0 +1,118 @@
|
||||||
|
import 'package:clock/clock.dart';
|
||||||
|
import 'package:drift/drift.dart' hide isNotNull, isNull;
|
||||||
|
import 'package:drift/native.dart';
|
||||||
|
import 'package:flutter_test/flutter_test.dart';
|
||||||
|
import 'package:twonly/locator.dart';
|
||||||
|
import 'package:twonly/src/database/twonly.db.dart';
|
||||||
|
import 'package:twonly/src/model/json/userdata.model.dart';
|
||||||
|
import 'package:twonly/src/services/user.service.dart';
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
TestWidgetsFlutterBinding.ensureInitialized();
|
||||||
|
|
||||||
|
setUp(() async {
|
||||||
|
await locator.reset();
|
||||||
|
locator
|
||||||
|
..registerSingleton<TwonlyDB>(
|
||||||
|
TwonlyDB.forTesting(
|
||||||
|
DatabaseConnection(
|
||||||
|
NativeDatabase.memory(),
|
||||||
|
closeStreamsSynchronously: true,
|
||||||
|
),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
..registerSingleton<UserService>(UserService());
|
||||||
|
|
||||||
|
userService.currentUser = UserData(
|
||||||
|
userId: 1,
|
||||||
|
username: 'test_user',
|
||||||
|
displayName: 'Test User',
|
||||||
|
subscriptionPlan: 'Free',
|
||||||
|
currentSetupPage: null,
|
||||||
|
appVersion: 100,
|
||||||
|
);
|
||||||
|
userService.isUserCreated = true;
|
||||||
|
});
|
||||||
|
|
||||||
|
tearDown(() async {
|
||||||
|
await twonlyDB.close();
|
||||||
|
});
|
||||||
|
|
||||||
|
test('purgeMessageTable preserves unopened messages and deletes expired ones', () async {
|
||||||
|
final now = clock.now();
|
||||||
|
const retentionMs = 7200000; // 2 hours
|
||||||
|
final deletionLimit = now.subtract(const Duration(milliseconds: retentionMs));
|
||||||
|
|
||||||
|
// 1. Insert a group with 2 hour retention policy
|
||||||
|
await twonlyDB.groupsDao.createNewGroup(
|
||||||
|
GroupsCompanion.insert(
|
||||||
|
groupId: 'test_group',
|
||||||
|
groupName: 'Test Group',
|
||||||
|
deleteMessagesAfterMilliseconds: const Value(retentionMs),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// 2. Insert test messages:
|
||||||
|
// Msg A: Unopened (openedByAll is null)
|
||||||
|
await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion.insert(
|
||||||
|
messageId: 'msg_a_unopened',
|
||||||
|
groupId: 'test_group',
|
||||||
|
type: 'text',
|
||||||
|
createdAt: Value(deletionLimit.subtract(const Duration(minutes: 5))), // older than deletion threshold
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Msg B: Opened long ago (openedByAll is older than deletion threshold)
|
||||||
|
await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion.insert(
|
||||||
|
messageId: 'msg_b_opened_expired',
|
||||||
|
groupId: 'test_group',
|
||||||
|
type: 'text',
|
||||||
|
openedByAll: Value(deletionLimit.subtract(const Duration(minutes: 5))),
|
||||||
|
createdAt: Value(deletionLimit.subtract(const Duration(minutes: 30))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Msg C: Opened recently (openedByAll is newer than deletion threshold)
|
||||||
|
await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion.insert(
|
||||||
|
messageId: 'msg_c_opened_recent',
|
||||||
|
groupId: 'test_group',
|
||||||
|
type: 'text',
|
||||||
|
openedByAll: Value(deletionLimit.add(const Duration(minutes: 5))),
|
||||||
|
createdAt: Value(deletionLimit.subtract(const Duration(minutes: 10))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Msg D: Deleted from sender, older than threshold
|
||||||
|
await twonlyDB.messagesDao.insertMessage(
|
||||||
|
MessagesCompanion.insert(
|
||||||
|
messageId: 'msg_d_sender_deleted_expired',
|
||||||
|
groupId: 'test_group',
|
||||||
|
type: 'text',
|
||||||
|
isDeletedFromSender: const Value(true),
|
||||||
|
createdAt: Value(deletionLimit.subtract(const Duration(minutes: 5))),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
// Run purge
|
||||||
|
await twonlyDB.messagesDao.purgeMessageTable();
|
||||||
|
|
||||||
|
// Verify database state
|
||||||
|
final allMessages = await twonlyDB.select(twonlyDB.messages).get();
|
||||||
|
final remainingIds = allMessages.map((m) => m.messageId).toList();
|
||||||
|
|
||||||
|
// msg_a_unopened should be preserved because it was never opened (openedByAll was null)
|
||||||
|
expect(remainingIds.contains('msg_a_unopened'), isTrue);
|
||||||
|
|
||||||
|
// msg_b_opened_expired should be deleted because openedByAll < deletionLimit
|
||||||
|
expect(remainingIds.contains('msg_b_opened_expired'), isFalse);
|
||||||
|
|
||||||
|
// msg_c_opened_recent should be preserved because openedByAll >= deletionLimit
|
||||||
|
expect(remainingIds.contains('msg_c_opened_recent'), isTrue);
|
||||||
|
|
||||||
|
// msg_d_sender_deleted_expired should be deleted because isDeletedFromSender is true and createdAt < deletionLimit
|
||||||
|
expect(remainingIds.contains('msg_d_sender_deleted_expired'), isFalse);
|
||||||
|
});
|
||||||
|
}
|
||||||
Loading…
Reference in a new issue