import 'package:flutter_test/flutter_test.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/core/notification_content.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'support/synthetic_matrix_data.dart'; /// Vertragstests für den Inhalt der Push-Benachrichtigungen (Bernds /// Beta-Feedback 2026-10-07: „beim Aufklappen viel weniger Text“, „keine /// Bildvorschau“). Native Darstellung (Verlauf, Bild) prüft das Gerät. void main() { late Client client; late Room room; setUp(() async { final factory = createDatabaseFactoryFfi( ffiInit: sqfliteFfiInit, noIsolate: true, ); final db = await factory.openDatabase(inMemoryDatabasePath); client = await SyntheticMatrixData.openClient(db, name: 'PyramidNotifTest'); room = Room(id: '!raum:bench.invalid', client: client); room.setState( Event( type: EventTypes.RoomName, content: {'name': 'Familie'}, eventId: r'$name', senderId: '@uta:bench.invalid', stateKey: '', originServerTs: DateTime(2026), room: room, ), ); room.setState( Event( type: EventTypes.RoomMember, content: {'membership': 'join', 'displayname': 'Uta'}, eventId: r'$member', senderId: '@uta:bench.invalid', stateKey: '@uta:bench.invalid', originServerTs: DateTime(2026), room: room, ), ); }); tearDown(() async { await client.dispose(closeDatabase: true); }); Event message( Map content, { String type = EventTypes.Message, String sender = '@uta:bench.invalid', }) => Event( type: type, content: content, eventId: r'$ev1', senderId: sender, originServerTs: DateTime(2026, 10, 7, 12, 30), room: room, ); test('Text wird NICHT mehr auf 100 Zeichen gekürzt', () { final text = 'Wort ' * 60; // 300 Zeichen final body = notificationBody(message({'msgtype': 'm.text', 'body': text})); expect(body, text.trim()); }); test('sehr lange Texte werden bei 1000 Zeichen gekappt', () { final body = notificationBody( message({'msgtype': 'm.text', 'body': 'x' * 5000}), )!; expect(body.length, notificationBodyMaxChars + 1); expect(body.endsWith('…'), isTrue); }); test('Antwort-Zitat (Reply-Fallback) wird entfernt', () { final body = notificationBody( message({ 'msgtype': 'm.text', 'body': '> <@bernd:bench.invalid> Kommst du heute?\n\nJa, um acht!', }), ); expect(body, 'Ja, um acht!'); }); test('Medien bekommen verständliche Bezeichnungen', () { expect( notificationBody(message({'msgtype': 'm.image', 'body': 'IMG_1.jpg'})), '📷 Foto', ); expect( notificationBody( message({ 'msgtype': 'm.image', 'body': 'Unser Garten', 'filename': 'IMG_2.jpg', }), ), '📷 Unser Garten', reason: 'MSC2530: abweichender body neben filename = Bildunterschrift', ); expect( notificationBody(message({'msgtype': 'm.video', 'body': 'v.mp4'})), '🎥 Video', ); expect( notificationBody( message({ 'msgtype': 'm.audio', 'body': 'Sprachnachricht.ogg', 'org.matrix.msc3245.voice': {}, }), ), '🎤 Sprachnachricht', ); expect( notificationBody(message({'msgtype': 'm.audio', 'body': 'lied.mp3'})), '🎵 Audio', ); expect( notificationBody( message({ 'msgtype': 'm.file', 'body': 'rechnung.pdf', 'filename': 'rechnung.pdf', }), ), '📎 rechnung.pdf', ); expect( notificationBody(message({'body': 'x'}, type: EventTypes.Sticker)), '🏷️ Sticker', ); }); test('nicht entschlüsselte Nachricht → null (Platzhalter bleibt)', () { expect( notificationBody( message({ 'algorithm': 'm.megolm.v1.aes-sha2', 'ciphertext': 'abc', }, type: EventTypes.Encrypted), ), isNull, ); expect( notificationBody( message({ 'msgtype': MessageTypes.BadEncrypted, 'body': '** Unable to decrypt: …', }), ), isNull, ); }); test('Markdown-Fett am Anfang ist KEIN Entschlüsselungsfehler', () { // Früher galt jeder Text mit "**" am Anfang als nicht entschlüsselt. expect( notificationBody( message({'msgtype': 'm.text', 'body': '**Wichtig** heute'}), ), '**Wichtig** heute', ); }); test( 'Inhalt: Absender-Anzeigename, Raumname, Bild-Flag, Native-Argumente', () { final content = MessageNotificationContent.fromEvent( message({'msgtype': 'm.image', 'body': 'IMG_1.jpg'}), )!; expect(content.senderName, 'Uta'); expect(content.roomName, 'Familie'); expect(content.isGroup, isTrue); expect(content.isImage, isTrue); expect(content.title, 'Uta · Familie'); final args = content.toNativeArgs(notifId: 42, imagePath: '/x/img'); expect(args['notifId'], 42); expect(args['eventId'], r'$ev1'); expect(args['senderId'], '@uta:bench.invalid'); expect(args['imagePath'], '/x/img'); expect( args['timestamp'], DateTime(2026, 10, 7, 12, 30).millisecondsSinceEpoch, ); }, ); test('unbekannter Absender → Localpart statt leerem Namen', () { final content = MessageNotificationContent.fromEvent( message({ 'msgtype': 'm.text', 'body': 'Hi', }, sender: '@jochen:bench.invalid'), )!; expect(content.senderName, 'jochen'); }); test('Vorschau aus → kein Inhalt, kein Bild', () { final content = MessageNotificationContent.fromEvent( message({'msgtype': 'm.image', 'body': 'IMG_1.jpg'}), showPreview: false, )!; expect(content.body, notificationPlaceholderBody); expect(content.isImage, isFalse); }); }