OmniFile-Processor / tests /test_gemini_modules.py
Dr. Abdulmalek
deploy: OmniFile AI Processor v4.3.0
900df0b
"""
اختبارات الوحدات الجديدة v4.2.1 — Gemini Integration
========================================================
اختبارات شاملة للوحدات المضافة:
- file_fingerprint
- classifier (Medical)
- watchdog_service
- language_corrector
- dataset_generator
- search_engine
"""
import json
import os
import sys
import tempfile
import unittest
from unittest.mock import patch, MagicMock
# إضافة مسار المشروع
PROJECT_ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if PROJECT_ROOT not in sys.path:
sys.path.insert(0, PROJECT_ROOT)
class TestFileFingerprintManager(unittest.TestCase):
"""اختبارات نظام بصمة الملفات."""
def setUp(self):
"""إنشاء بيئة اختبار."""
self.temp_dir = tempfile.mkdtemp()
self.db_path = os.path.join(self.temp_dir, "test_fingerprints.db")
self.test_file = os.path.join(self.temp_dir, "test.txt")
with open(self.test_file, "w", encoding="utf-8") as f:
f.write("ملف اختبار لبصمة SHA-256")
def tearDown(self):
"""تنظيف بيئة الاختبار."""
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_calculate_hash(self):
"""اختبار حساب بصمة الملف."""
from modules.core.file_fingerprint import FileFingerprintManager
hash1 = FileFingerprintManager.calculate_hash(self.test_file)
hash2 = FileFingerprintManager.calculate_hash(self.test_file)
# نفس الملف = نفس البصمة
self.assertEqual(hash1, hash2)
# البصمة بطول 64 حرف (SHA-256 hex)
self.assertEqual(len(hash1), 64)
# بصمة hex فقط
self.assertTrue(all(c in '0123456789abcdef' for c in hash1))
def test_calculate_hash_different_files(self):
"""اختبار بصمات ملفات مختلفة."""
from modules.core.file_fingerprint import FileFingerprintManager
other_file = os.path.join(self.temp_dir, "other.txt")
with open(other_file, "w") as f:
f.write("محتوى مختلف تماماً")
hash1 = FileFingerprintManager.calculate_hash(self.test_file)
hash2 = FileFingerprintManager.calculate_hash(other_file)
self.assertNotEqual(hash1, hash2)
def test_md5_hash(self):
"""اختبار حساب بصمة MD5."""
from modules.core.file_fingerprint import FileFingerprintManager
hash_md5 = FileFingerprintManager.calculate_hash(self.test_file, algorithm="md5")
self.assertEqual(len(hash_md5), 32)
def test_is_new_file(self):
"""اختبار كشف الملفات الجديدة."""
from modules.core.file_fingerprint import FileFingerprintManager
mgr = FileFingerprintManager(self.db_path)
try:
self.assertTrue(mgr.is_new_file(self.test_file))
mgr.mark_processed(self.test_file, category="test")
self.assertFalse(mgr.is_new_file(self.test_file))
finally:
mgr.close()
def test_mark_processed(self):
"""اختبار تسجيل ملف كمعالج."""
from modules.core.file_fingerprint import FileFingerprintManager
mgr = FileFingerprintManager(self.db_path)
try:
result = mgr.mark_processed(
self.test_file,
category="orthopedic",
subcategory="fracture",
ocr_engine="easyocr",
confidence_score=0.95
)
self.assertTrue(result)
info = mgr.get_file_info(self.test_file)
self.assertIsNotNone(info)
self.assertEqual(info["category"], "orthopedic")
self.assertEqual(info["ocr_engine"], "easyocr")
finally:
mgr.close()
def test_mark_duplicate(self):
"""اختبار تجاهل الملفات المكررة."""
from modules.core.file_fingerprint import FileFingerprintManager
mgr = FileFingerprintManager(self.db_path)
try:
mgr.mark_processed(self.test_file)
# الملف مسجل مسبقاً — is_new_file يرجع False
self.assertFalse(mgr.is_new_file(self.test_file))
finally:
mgr.close()
def test_get_statistics(self):
"""اختبار الإحصائيات."""
from modules.core.file_fingerprint import FileFingerprintManager
mgr = FileFingerprintManager(self.db_path)
try:
mgr.mark_processed(self.test_file, category="test")
stats = mgr.get_statistics()
self.assertEqual(stats["total_files"], 1)
self.assertIn("by_category", stats)
self.assertIn("by_extension", stats)
self.assertGreater(stats["total_size_bytes"], 0)
finally:
mgr.close()
def test_context_manager(self):
"""اختبار مدير السياق."""
from modules.core.file_fingerprint import FileFingerprintManager
with FileFingerprintManager(self.db_path) as mgr:
mgr.mark_processed(self.test_file)
self.assertFalse(mgr.is_new_file(self.test_file))
def test_export_fingerprints(self):
"""اختبار تصدير البصمات."""
from modules.core.file_fingerprint import FileFingerprintManager
mgr = FileFingerprintManager(self.db_path)
try:
mgr.mark_processed(self.test_file)
export_path = os.path.join(self.temp_dir, "export.json")
result = mgr.export_fingerprints(export_path)
self.assertTrue(result)
self.assertTrue(os.path.exists(export_path))
with open(export_path, "r", encoding="utf-8") as f:
data = json.load(f)
self.assertEqual(len(data), 1)
finally:
mgr.close()
class TestMedicalClassifier(unittest.TestCase):
"""اختبارات مصنف المحتوى الطبي."""
def setUp(self):
from modules.core.classifier import MedicalClassifier
self.classifier = MedicalClassifier()
def test_orthopedic_text(self):
"""اختبار تصنيف نص جراحة العظام."""
result = self.classifier.classify(
"المريض يعاني من كسر في عظم الفخذ مع تمزق في الرباط الصليبي الأمامي"
)
self.assertEqual(result["category"], "orthopedic")
self.assertGreater(result["confidence"], 0.3)
def test_cardiology_text(self):
"""اختبار تصنيف نص القلب."""
result = self.classifier.classify(
"المريض يعاني من ذبحة صدرية مع ضغط دم مرتفع"
)
self.assertEqual(result["category"], "cardiology")
self.assertGreater(result["confidence"], 0.2)
def test_research_text(self):
"""اختبار تصنيف نص بحث علمي."""
result = self.classifier.classify(
"هذه دراسة إحصائية بعينة من 200 مريض مع تحليل انحدار"
)
self.assertIn(result["category"], ["research", "general", "orthopedic"])
def test_general_text(self):
"""اختبار نص عام."""
result = self.classifier.classify(
"اليوم كان الجو جميلاً وذهبت في نزهة"
)
self.assertEqual(result["category"], "general")
def test_empty_text(self):
"""اختبار نص فارغ."""
result = self.classifier.classify("")
self.assertEqual(result["category"], "general")
self.assertEqual(result["confidence"], 0.0)
def test_fallback(self):
"""اختبار التصنيف مع مستوى ثقة أدنى."""
result = self.classifier.classify_with_fallback(
"نص غامض جداً", min_confidence=0.5
)
self.assertEqual(result["category"], "general")
def test_get_categories(self):
"""اختبار قائمة التصنيفات."""
cats = self.classifier.get_categories()
self.assertIn("orthopedic", cats)
self.assertIn("cardiology", cats)
self.assertIn("general", cats)
self.assertGreater(len(cats), 5)
def test_add_category(self):
"""اختبار إضافة تصنيف مخصص."""
self.classifier.add_category(
"custom_test",
critical=["اختبار خاص", "test term"],
)
result = self.classifier.classify("هذا اختبار خاص جداً")
# قد يكون custom_test أو general
self.assertIn(result["category"], ["custom_test", "general"])
def test_english_orthopedic(self):
"""اختبار تصنيف نص إنجليزي جراحة العظام."""
result = self.classifier.classify(
"The patient has a femoral neck fracture with ACL tear"
)
self.assertEqual(result["category"], "orthopedic")
def test_scores_structure(self):
"""اختبار هيكل النتيجة."""
result = self.classifier.classify("كسر في الركبة")
self.assertIn("category", result)
self.assertIn("confidence", result)
self.assertIn("scores", result)
self.assertIn("keywords_found", result)
self.assertIn("top_keywords", result)
class TestLanguageCorrector(unittest.TestCase):
"""اختبارات المدقق اللغوي."""
def setUp(self):
from modules.nlp.language_corrector import LanguageCorrector
self.corrector = LanguageCorrector(lang='ar')
def test_empty_text(self):
"""اختبار نص فارغ."""
result = self.corrector.check("")
self.assertEqual(result["error_count"], 0)
def test_basic_check(self):
"""اختبار الفحص الأساسي (بدون LanguageTool)."""
# LanguageTool قد لا يكون مثبتاً
result = self.corrector.check("نص بسيط للاختبار")
self.assertIn("method", result)
self.assertIn("corrected", result)
self.assertIn("errors", result)
def test_protected_terms(self):
"""اختبار حماية المصطلحات الطبية."""
self.corrector.add_protected_term("مصطلح طبي خاص")
self.assertTrue(self.corrector._is_protected("مصطلح طبي خاص"))
self.assertTrue(self.corrector._is_protected("يحتوي مصطلح طبي خاص هنا"))
def test_error_summary(self):
"""اختبار ملخص الأخطاء."""
result = {
"errors": [],
"error_count": 0,
}
summary = self.corrector.get_error_summary(result)
self.assertIn("لم يتم كشف", summary)
class TestDatasetGenerator(unittest.TestCase):
"""اختبارات مولد بيانات التدريب."""
def setUp(self):
from modules.core.dataset_generator import DatasetGenerator
self.temp_dir = tempfile.mkdtemp()
self.gen = DatasetGenerator(
output_dir=self.temp_dir,
specialty="orthopedic",
)
def tearDown(self):
import shutil
shutil.rmtree(self.temp_dir, ignore_errors=True)
def test_add_entry(self):
"""اختبار إضافة إدخال."""
result = self.gen.add_entry(
input_text="نص خام",
output_text="نص مصحح",
quality="verified",
)
self.assertTrue(result)
self.assertEqual(len(self.gen), 1)
def test_add_empty_entry(self):
"""اختبار إضافة إدخال فارغ."""
result = self.gen.add_entry(output_text="")
self.assertFalse(result)
def test_add_ocr_pair(self):
"""اختبار إضافة زوج OCR."""
result = self.gen.add_ocr_pair(
raw_text="المريض يعاني من الم",
corrected_text="المريض يعاني من ألم",
ocr_engine="easyocr",
confidence=0.9,
)
self.assertTrue(result)
def test_export_jsonl(self):
"""اختبار تصدير JSONL."""
self.gen.add_entry(input_text="a", output_text="b")
self.gen.add_entry(input_text="c", output_text="d")
files = self.gen.export("jsonl", filename="test_export")
self.assertIn("full", files)
self.assertTrue(os.path.exists(files["full"]))
# التحقق من محتوى الملف
with open(files["full"], "r", encoding="utf-8") as f:
lines = f.readlines()
self.assertEqual(len(lines), 2)
def test_export_with_split(self):
"""اختبار التصدير مع تقسيم."""
for i in range(10):
self.gen.add_entry(
input_text=f"input_{i}",
output_text=f"output_{i}",
quality="verified",
)
files = self.gen.export(
"jsonl",
split_ratios={"train": 0.8, "test": 0.2}
)
self.assertIn("train", files)
self.assertIn("test", files)
def test_statistics(self):
"""اختبار الإحصائيات."""
self.gen.add_entry(input_text="أ", output_text="ب", specialty="orthopedic")
self.gen.add_entry(input_text="ج", output_text="د", specialty="cardiology")
stats = self.gen.get_statistics()
self.assertEqual(stats["current_entries"], 2)
self.assertIn("by_specialty", stats)
self.assertIn("by_quality", stats)
def test_clear(self):
"""اختبار المسح."""
self.gen.add_entry(input_text="a", output_text="b")
self.gen.clear()
self.assertEqual(len(self.gen), 0)
def test_max_entries(self):
"""اختبار الحد الأقصى."""
from modules.core.dataset_generator import DatasetGenerator
gen = DatasetGenerator(output_dir=self.temp_dir, max_entries=2)
gen.add_entry(input_text="a", output_text="b")
gen.add_entry(input_text="c", output_text="d")
result = gen.add_entry(input_text="e", output_text="f")
self.assertFalse(result)
self.assertEqual(len(gen), 2)
class TestSearchEngine(unittest.TestCase):
"""اختبارات محرك البحث."""
def test_parse_advanced_query(self):
"""اختبار تحليل الاستعلام المتقدم."""
from modules.core.search_engine import SearchEngine
# AND
result = SearchEngine._parse_advanced_query("كسر AND فخذ")
operators = [op for op, _ in result]
terms = [t for _, t in result]
self.assertIn("AND", operators)
self.assertIn("كسر", terms)
# NOT
result = SearchEngine._parse_advanced_query("جراحة NOT قلب")
operators = [op for op, _ in result]
self.assertIn("NOT", operators)
# Simple
result = SearchEngine._parse_advanced_query("بسيط")
self.assertEqual(len(result), 1)
def test_extract_context(self):
"""اختبار استخراج السياق."""
from modules.core.search_engine import SearchEngine
text = "المريض يعاني من ألم شديد في الركبة اليسرى منذ فترة طويلة ويحتاج إلى تقييم متخصص"
result = SearchEngine._extract_context(text, "الركبة", context_length=20)
self.assertIn("الركبة", result)
def test_fts5_query(self):
"""اختبار تحويل استعلام FTS5."""
from modules.core.search_engine import SearchEngine
result = SearchEngine._to_fts5_query("كسر فخذ")
self.assertIn('"كسر"', result)
self.assertIn('"فخذ"', result)
def test_search_files(self):
"""اختبار البحث في الملفات."""
from modules.core.search_engine import SearchEngine
temp_dir = tempfile.mkdtemp()
try:
test_file = os.path.join(temp_dir, "test.txt")
with open(test_file, "w", encoding="utf-8") as f:
f.write("هذا نص اختبار يحتوي على كلمة كسر")
engine = SearchEngine()
results = engine.search_files(temp_dir, "كسر")
self.assertGreater(len(results), 0)
self.assertIn("كسر", results[0]["snippet"])
engine.close()
finally:
import shutil
shutil.rmtree(temp_dir, ignore_errors=True)
class TestWatchdogService(unittest.TestCase):
"""اختبارات خدمة مراقبة المجلدات."""
def test_initialization(self):
"""اختبار التهيئة."""
from modules.core.watchdog_service import FolderWatchdog
temp_dir = tempfile.mkdtemp()
try:
wd = FolderWatchdog(
watch_dir=temp_dir,
callback=lambda x: None,
poll_interval=1.0,
)
self.assertEqual(wd.watch_dir, temp_dir)
self.assertFalse(wd._running)
wd.stop()
finally:
import shutil
shutil.rmtree(temp_dir, ignore_errors=True)
def test_valid_extensions(self):
"""اختبار فلاتر الامتدادات."""
from modules.core.watchdog_service import FolderWatchdog
wd = FolderWatchdog.__new__(FolderWatchdog)
wd.extensions = {'.pdf', '.png'}
self.assertTrue(wd._is_valid_file("/path/to/file.pdf"))
self.assertTrue(wd._is_valid_file("/path/to/file.png"))
self.assertFalse(wd._is_valid_file("/path/to/file.doc"))
def test_statistics(self):
"""اختبار إحصائيات المراقب."""
from modules.core.watchdog_service import FolderWatchdog
temp_dir = tempfile.mkdtemp()
try:
wd = FolderWatchdog(
watch_dir=temp_dir,
callback=lambda x: None,
)
stats = wd.get_statistics()
self.assertEqual(stats["watch_dir"], temp_dir)
self.assertFalse(stats["running"])
wd.stop()
finally:
import shutil
shutil.rmtree(temp_dir, ignore_errors=True)
def test_repr(self):
"""اختبار التمثيل النصي."""
from modules.core.watchdog_service import FolderWatchdog
temp_dir = tempfile.mkdtemp()
try:
wd = FolderWatchdog(watch_dir=temp_dir, callback=lambda x: None)
repr_str = repr(wd)
self.assertIn("FolderWatchdog", repr_str)
self.assertIn("stopped", repr_str)
wd.stop()
finally:
import shutil
shutil.rmtree(temp_dir, ignore_errors=True)
if __name__ == "__main__":
unittest.main()