mirror of
https://github.com/twonlyapp/twonly-app.git
synced 2026-10-09 02:54:14 +00:00
224 lines
6.6 KiB
Dart
224 lines
6.6 KiB
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/tables/mediafiles.table.dart';
|
|
import 'package:twonly/src/database/tables/messages.table.dart';
|
|
import 'package:twonly/src/database/twonly.db.dart';
|
|
import 'package:twonly/src/services/user.service.dart';
|
|
|
|
import '../mocks/user_config.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 = testUserConfig(
|
|
userId: 1,
|
|
username: 'test_user',
|
|
displayName: 'Test User',
|
|
subscriptionPlan: 'Free',
|
|
currentSetupPage: null,
|
|
appVersion: 100,
|
|
);
|
|
userService.isUserCreated = true;
|
|
});
|
|
|
|
tearDown(() async {
|
|
await twonlyDB.close();
|
|
});
|
|
|
|
test(
|
|
'watchByGroupId stream emits updates when a new text message is inserted',
|
|
() async {
|
|
// 1. Create a group
|
|
await twonlyDB.groupsDao.createNewGroup(
|
|
GroupsCompanion.insert(
|
|
groupId: 'test_group_stream',
|
|
groupName: 'Test Group Stream',
|
|
),
|
|
);
|
|
|
|
// 2. Start watching
|
|
final stream = await twonlyDB.messagesDao.watchByGroupId(
|
|
'test_group_stream',
|
|
);
|
|
|
|
final emissions = <List<Message>>[];
|
|
final subscription = stream.listen(emissions.add);
|
|
|
|
// Wait briefly for initial empty emission
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
|
expect(emissions, hasLength(1));
|
|
expect(emissions.first, isEmpty);
|
|
|
|
// 3. Insert a message
|
|
await twonlyDB.messagesDao.insertMessage(
|
|
MessagesCompanion.insert(
|
|
messageId: 'msg_1',
|
|
groupId: 'test_group_stream',
|
|
type: 'text',
|
|
content: const Value('Hello world'),
|
|
),
|
|
);
|
|
|
|
// Wait for the stream to propagate the event
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
|
// Verify it emitted the new list containing our message
|
|
expect(emissions, hasLength(2));
|
|
expect(emissions[1], hasLength(1));
|
|
expect(emissions[1].first.messageId, equals('msg_1'));
|
|
|
|
await subscription.cancel();
|
|
},
|
|
);
|
|
|
|
test(
|
|
'simulate sending flow with group updates and verify messages stream emits',
|
|
() async {
|
|
await twonlyDB.groupsDao.createNewGroup(
|
|
GroupsCompanion.insert(
|
|
groupId: 'test_group_2',
|
|
groupName: 'Test Group 2',
|
|
),
|
|
);
|
|
|
|
final msgStream = await twonlyDB.messagesDao.watchByGroupId(
|
|
'test_group_2',
|
|
);
|
|
final messagesEmissions = <List<Message>>[];
|
|
final msgSub = msgStream.listen(messagesEmissions.add);
|
|
|
|
final groupStream = twonlyDB.groupsDao.watchGroup('test_group_2');
|
|
final groupEmissions = <Group?>[];
|
|
final groupSub = groupStream.listen(groupEmissions.add);
|
|
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
expect(messagesEmissions, hasLength(1));
|
|
expect(groupEmissions, hasLength(1));
|
|
|
|
// Simulate sending:
|
|
// 1. Update group draftMessage to null (as done in insertAndSendTextMessage)
|
|
await twonlyDB.groupsDao.updateGroup(
|
|
'test_group_2',
|
|
const GroupsCompanion(
|
|
draftMessage: Value(null),
|
|
),
|
|
);
|
|
|
|
// 2. Insert message
|
|
final message = await twonlyDB.messagesDao.insertMessage(
|
|
MessagesCompanion(
|
|
groupId: const Value('test_group_2'),
|
|
content: const Value('Hello from simulation'),
|
|
type: Value(MessageType.text.name),
|
|
),
|
|
);
|
|
|
|
expect(message, isNotNull);
|
|
|
|
await Future.delayed(const Duration(milliseconds: 100));
|
|
|
|
// Verify group stream received updates
|
|
expect(groupEmissions.length, greaterThan(1));
|
|
|
|
// Verify message stream received updates containing the new message
|
|
expect(messagesEmissions.length, greaterThan(1));
|
|
expect(
|
|
messagesEmissions.last.any((m) => m.messageId == message!.messageId),
|
|
isTrue,
|
|
);
|
|
|
|
await msgSub.cancel();
|
|
await groupSub.cancel();
|
|
},
|
|
);
|
|
|
|
test(
|
|
'chat summaries ignore unopened media after its file is removed',
|
|
() async {
|
|
const groupId = 'group_with_removed_media';
|
|
final now = DateTime(2026, 10, 6, 12);
|
|
|
|
await twonlyDB.groupsDao.createNewGroup(
|
|
GroupsCompanion.insert(groupId: groupId, groupName: 'Removed media'),
|
|
);
|
|
await twonlyDB.contactsDao.insertOnConflictUpdate(
|
|
ContactsCompanion.insert(
|
|
userId: const Value(2),
|
|
username: 'sender',
|
|
),
|
|
);
|
|
await twonlyDB.messagesDao.insertMessage(
|
|
MessagesCompanion.insert(
|
|
messageId: 'older_text',
|
|
groupId: groupId,
|
|
senderId: const Value(2),
|
|
type: MessageType.text.name,
|
|
content: const Value('Still visible'),
|
|
createdAt: Value(now),
|
|
),
|
|
);
|
|
await twonlyDB.mediaFilesDao.insertOrUpdateMedia(
|
|
MediaFilesCompanion.insert(
|
|
mediaId: 'removed_media',
|
|
type: MediaType.image,
|
|
downloadState: const Value(DownloadState.ready),
|
|
),
|
|
);
|
|
await twonlyDB.messagesDao.insertMessage(
|
|
MessagesCompanion.insert(
|
|
messageId: 'broken_media_message',
|
|
groupId: groupId,
|
|
senderId: const Value(2),
|
|
type: MessageType.media.name,
|
|
mediaId: const Value('removed_media'),
|
|
createdAt: Value(now.add(const Duration(minutes: 1))),
|
|
),
|
|
);
|
|
|
|
await twonlyDB.mediaFilesDao.deleteMediaFile('removed_media');
|
|
|
|
final brokenMessage = await twonlyDB.messagesDao
|
|
.getMessageById('broken_media_message')
|
|
.getSingleOrNull();
|
|
expect(brokenMessage, isNotNull);
|
|
expect(brokenMessage!.mediaId, isNull);
|
|
expect(await twonlyDB.messagesDao.watchMessageNotOpened(groupId).first, [
|
|
isA<Message>().having(
|
|
(message) => message.messageId,
|
|
'messageId',
|
|
'older_text',
|
|
),
|
|
]);
|
|
|
|
final allUnopened = await twonlyDB.messagesDao
|
|
.watchAllMessagesNotOpened()
|
|
.first;
|
|
expect(
|
|
allUnopened.map((message) => message.messageId),
|
|
isNot(contains('broken_media_message')),
|
|
);
|
|
expect(
|
|
(await twonlyDB.messagesDao.watchLatestMessagesByGroup().first)
|
|
.single
|
|
.messageId,
|
|
'older_text',
|
|
);
|
|
},
|
|
);
|
|
}
|