Files
pyramid/test/local_message_search_test.dart
Bernd SteckmeisterandClaude Opus 5.5 d5ff7982a0 refactor: lokale Nachrichtensuche als eigenes Modul (lib/features/search) + 5 Vertragstests
Verhaltensgleich aus search_modal.dart herausgeloest (gleiche Deckel 1500/60/500,
gleiche Schleife; Medien-Filter jetzt Parameter). Tests laufen ueber die echte
SDK-DB-Schicht mit synthetischen Daten (test/support/synthetic_matrix_data.dart).

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-07 11:55:28 +02:00

110 lines
3.9 KiB
Dart
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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<void> 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);
});
}