import 'package:flutter_test/flutter_test.dart'; import 'package:matrix/matrix.dart'; import 'package:pyramid/features/search/local_message_search.dart'; import 'package:sqflite_common_ffi/sqflite_ffi.dart'; import 'support/synthetic_matrix_data.dart'; /// Vertragstests für die lokale Nachrichtensuche (Suchdialog, Strg+K). /// Laufen auf der echten SDK-Datenbankschicht mit normalem sqlite – die /// Suche selbst ist von der Verschlüsselung unabhängig (die misst /// `benchmark/db_benchmark_test.dart`). void main() { late Client client; Future setUpClient({ required int rooms, required int eventsPerRoom, int? needleEvery, }) async { final factory = createDatabaseFactoryFfi( ffiInit: sqfliteFfiInit, noIsolate: true, ); final db = await factory.openDatabase(inMemoryDatabasePath); client = await SyntheticMatrixData.openClient( db, name: 'PyramidSearchTest', ); await SyntheticMatrixData.populate( client, rooms: rooms, eventsPerRoom: eventsPerRoom, needleEvery: needleEvery, ); await SyntheticMatrixData.loadRooms(client); } tearDown(() async { await client.dispose(closeDatabase: true); }); test( 'findet genau die Nachrichten mit dem Suchwort, neueste zuerst', () async { // 3 Räume × 40 Nachrichten, jede 7. Text-Nachricht trägt das Suchwort. // Jede 10. Nachricht ist ein Bild (ohne Suchwort im Dateinamen). await setUpClient(rooms: 3, eventsPerRoom: 40, needleEvery: 7); final expectedPerRoom = [ for (var i = 0; i < 40; i += 7) i, ].where((i) => i % 10 != 9).length; final results = await localMessageSearch(client, 'nadelwort'); expect(results, hasLength(3 * expectedPerRoom)); expect(results.every((e) => e.body.contains('nadelwort')), isTrue); for (var i = 1; i < results.length; i++) { expect( results[i - 1].originServerTs.isAfter(results[i].originServerTs) || results[i - 1].originServerTs == results[i].originServerTs, isTrue, reason: 'Ergebnisse müssen absteigend nach Zeit sortiert sein', ); } }, ); test('Groß-/Kleinschreibung egal, auch bei Umlauten', () async { await setUpClient(rooms: 1, eventsPerRoom: 30, needleEvery: 3); for (final (lowerQuery, upperQuery) in [ ('nadelwort', 'NADELWORT'), ('übermorgen', 'ÜBERMORGEN'), ]) { final lower = await localMessageSearch(client, lowerQuery); final upper = await localMessageSearch(client, upperQuery); expect(lower, isNotEmpty, reason: '"$lowerQuery" kommt in den Daten vor'); expect(upper.map((e) => e.eventId), lower.map((e) => e.eventId)); } }); test( 'Medien-Filter liefert nur Medien-Nachrichten (Dateiname passt)', () async { await setUpClient(rooms: 2, eventsPerRoom: 30); // "g" steckt in "IMG_…jpg" UND in vielen Textwörtern ("morgen", …). final all = await localMessageSearch(client, 'g'); final media = await localMessageSearch(client, 'g', mediaOnly: true); expect( all.any((e) => e.content['msgtype'] == 'm.text'), isTrue, reason: 'ohne Filter müssen auch Text-Treffer kommen', ); // Jede 10. Nachricht ist ein Bild: i = 9, 19, 29 → 3 pro Raum. expect(media, hasLength(6)); expect(media.every((e) => e.content['msgtype'] == 'm.image'), isTrue); }, ); test('Trefferzahl ist auf searchTotalCap gedeckelt', () async { // Jede Nachricht trägt das Suchwort → weit mehr Treffer als der Deckel. await setUpClient(rooms: 2, eventsPerRoom: 80, needleEvery: 1); final results = await localMessageSearch(client, 'nadelwort'); expect(results, hasLength(searchTotalCap)); }); test('kein Treffer → leere Liste, kein Fehler', () async { await setUpClient(rooms: 2, eventsPerRoom: 20); expect(await localMessageSearch(client, 'gibtsnicht'), isEmpty); }); }