diff --git a/test/features/composer/presentation/manager/attachment_text_detector_test.dart b/test/features/composer/presentation/manager/attachment_text_detector_test.dart index 7edea0221..6004d43b8 100644 --- a/test/features/composer/presentation/manager/attachment_text_detector_test.dart +++ b/test/features/composer/presentation/manager/attachment_text_detector_test.dart @@ -274,8 +274,10 @@ void main() { final matches = AttachmentTextDetector.matchedKeywordsUnique(email); - expect(matches, containsAll(['attach', 'tài liệu', 'đính kèm', 'приложение'])); - expect(matches.toSet().length, matches.length, reason: 'No duplicates allowed'); + expect(matches, + containsAll(['attach', 'tài liệu', 'đính kèm', 'приложение'])); + expect(matches.toSet().length, matches.length, + reason: 'No duplicates allowed'); }); test('should return unique matches even if repeated multiple times', () { @@ -287,8 +289,10 @@ void main() { final matches = AttachmentTextDetector.matchedKeywordsUnique(email); - expect(matches, containsAll(['file', 'attach', 'đính kèm', 'приложение'])); - expect(matches.toSet().length, matches.length, reason: 'Duplicates must be removed'); + expect( + matches, containsAll(['file', 'attach', 'đính kèm', 'приложение'])); + expect(matches.toSet().length, matches.length, + reason: 'Duplicates must be removed'); }); test('should return empty list if no keywords present', () { @@ -376,4 +380,286 @@ void main() { }); } }); + + /// Test input validation and edge cases + group('AttachmentTextDetector Input Validation', () { + test('should handle empty input gracefully', () { + expect(AttachmentTextDetector.containsAnyAttachmentKeyword(''), isFalse); + expect(AttachmentTextDetector.matchedKeywordsUnique(''), isEmpty); + expect(AttachmentTextDetector.matchedKeywordsAll(''), isEmpty); + }); + + test('should handle special characters and symbols', () { + const email = "attach@#\$%^&*()_+ pièce jointe!@#\$%"; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + final matches = AttachmentTextDetector.matchedKeywordsUnique(email); + expect(matches, containsAll(['attach', 'pièce jointe'])); + }); + + test('should handle whitespace-only input', () { + const email = " \n\t\r "; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isFalse); + expect(AttachmentTextDetector.matchedKeywordsUnique(email), isEmpty); + }); + + test('should handle newlines and tabs in content', () { + const email = "Please\nsee\tthe\r\nattached\tdocument."; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + }); + }); + + /// Test language code edge cases and validation + group('AttachmentTextDetector Language Code Validation', () { + test('should handle invalid language codes', () { + expect( + AttachmentTextDetector.containsAttachmentKeyword("attach", lang: ""), + isFalse); + expect( + AttachmentTextDetector.containsAttachmentKeyword("attach", + lang: "invalid"), + isFalse); + expect( + AttachmentTextDetector.containsAttachmentKeyword("attach", + lang: "xyz"), + isFalse); + }); + + test('should handle language code case variations', () { + expect( + AttachmentTextDetector.containsAttachmentKeyword("attach", + lang: "EN"), + isTrue); + expect( + AttachmentTextDetector.containsAttachmentKeyword("attach", + lang: "En"), + isTrue); + expect( + AttachmentTextDetector.containsAttachmentKeyword("pièce jointe", + lang: "FR"), + isTrue); + expect( + AttachmentTextDetector.containsAttachmentKeyword("pièce jointe", + lang: "Fr"), + isTrue); + }); + + test('should return empty results for unsupported languages', () { + expect(AttachmentTextDetector.matchedKeywords("attach", lang: "de"), + isEmpty); + expect(AttachmentTextDetector.matchedKeywords("attach", lang: "es"), + isEmpty); + expect(AttachmentTextDetector.matchedKeywords("attach", lang: "ja"), + isEmpty); + }); + }); + + /// Test boundary conditions and word matching + group('AttachmentTextDetector Boundary Conditions', () { + test('should handle keywords at text boundaries', () { + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attach"), + isTrue); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attachment."), + isTrue); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword(".attach"), + isTrue); + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword("(attachment)"), + isTrue); + }); + + test('should handle partial word matches correctly', () { + // Should NOT match "attach" in words where it's just a substring + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("detachment"), + isFalse); + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword("reattachment"), + isTrue); // contains "attachment" + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attachments"), + isTrue); // contains "attachment" + }); + + test('should handle keywords with punctuation', () { + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attach,"), + isTrue); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attach;"), + isTrue); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attach:"), + isTrue); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attach?"), + isTrue); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("attach!"), + isTrue); + }); + + test('should handle single character boundaries', () { + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("a"), isFalse); + expect(AttachmentTextDetector.containsAnyAttachmentKeyword("file"), + isTrue); // Vietnamese keyword + }); + }); + + /// Test Unicode and encoding edge cases + group('AttachmentTextDetector Unicode Handling', () { + test('should handle emoji and special Unicode characters', () { + const email = "📎 attachment 📄 file 🔗 pièce jointe"; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + final matches = AttachmentTextDetector.matchedKeywordsUnique(email); + expect(matches, containsAll(['attachment', 'file', 'pièce jointe'])); + }); + + test('should handle mixed Unicode scripts', () { + const email = "文档 attachment документ pièce jointe تقرير"; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + }); + + test('should handle Unicode normalization variations', () { + // Test composed vs decomposed Unicode characters for French + const email1 = "pièce jointe"; // é as single character (NFC) + const email2 = + "pie\u0300ce jointe"; // é as e + combining grave accent (NFD) + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email1), isTrue); + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email2), isTrue); + }); + + test('should handle zero-width characters', () { + const email = "attach\u200Bment"; // Zero-width space + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + }); + }); + + /// Test real-world scenarios and integration cases + group('AttachmentTextDetector Real-World Scenarios', () { + test('should handle common email signatures and footers', () { + const email = """ + Please review the attached document. + + Best regards, + John Doe + + This email and any attachments are confidential and may be privileged. + """; + final matches = AttachmentTextDetector.matchedKeywordsUnique(email); + expect(matches, containsAll(['attach', 'attachment'])); + }); + + test('should handle HTML-like content', () { + const email = + "Please see the attached document in the <file> section."; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + }); + + test('should handle quoted email content', () { + const email = """ + Hi John, + + Please find the attachment below. + + > On Mon, Jan 1, 2024, Jane wrote: + > I need the document attached to your previous email. + > Can you resend the file? + + Thanks! + """; + final matches = AttachmentTextDetector.matchedKeywordsUnique(email); + expect(matches, containsAll(['attachment', 'attach', 'file'])); + }); + + test('should handle multiple languages in single email', () { + const email = """ + English: Please see the attached file. + French: Veuillez voir le fichier joint. + Vietnamese: Vui lòng xem tài liệu đính kèm. + Russian: Пожалуйста, смотрите приложение. + """; + final matchesAll = AttachmentTextDetector.matchedKeywordsAll(email); + expect(matchesAll.keys, containsAll(['en', 'fr', 'vi', 'ru'])); + + final matchesUnique = AttachmentTextDetector.matchedKeywordsUnique(email); + expect( + matchesUnique, + containsAll([ + 'attach', + 'file', + 'fichier joint', + 'joint', + 'tài liệu', + 'đính kèm', + 'приложение' + ])); + }); + + test('should handle email threading and forwarding markers', () { + const email = """ + FW: Important Document + + Please see attached report. + + -----Original Message----- + From: sender@example.com + The attachment contains sensitive information. + """; + expect( + AttachmentTextDetector.containsAnyAttachmentKeyword(email), isTrue); + }); + }); + + /// Test performance and memory edge cases + group('AttachmentTextDetector Performance Edge Cases', () { + test('should handle extremely large input without memory issues', () { + // Test with 5MB+ content + final hugeEmail = generateLongEmail(5000000, includeKeywords: false); + expect( + () => AttachmentTextDetector.containsAnyAttachmentKeyword(hugeEmail), + returnsNormally); + }); + + test('should handle repeated keyword patterns efficiently', () { + final email = "attach " * 10000; // 10K repetitions + final sw = Stopwatch()..start(); + final result = AttachmentTextDetector.matchedKeywordsUnique(email); + sw.stop(); + + expect(result, equals(['attach'])); + expect(sw.elapsedMilliseconds, lessThan(100)); // Should complete quickly + }); + + test('should handle very long single line efficiently', () { + final longLine = "word " * 100000 + "attachment"; // 100K words + keyword + final sw = Stopwatch()..start(); + final result = + AttachmentTextDetector.containsAnyAttachmentKeyword(longLine); + sw.stop(); + + expect(result, isTrue); + expect(sw.elapsedMilliseconds, + lessThan(500)); // Should complete within 500ms + }); + + test('should handle many different keywords in large text', () { + final email = """ + attach attachment file document report + pièce jointe fichier joint document joint + приложение документ файл отчёт вложение + đính kèm tài liệu tệp báo cáo + """ * + 1000; // Repeat 1000 times + + final sw = Stopwatch()..start(); + final matches = AttachmentTextDetector.matchedKeywordsUnique(email); + sw.stop(); + + expect(matches.length, greaterThan(10)); + expect(sw.elapsedMilliseconds, + lessThan(1000)); // Should complete within 1 second + }); + }); }