mirror of
https://github.com/semantica-agi/semantica.git
synced 2026-08-29 04:26:20 +00:00
- Robust ID extraction in CentralityCalculator, CommunityDetector, and ConnectivityAnalyzer - Support for direct Entity objects and dictionaries as node identifiers - Improved Entity hashability in utils/types.py - Added integration test to verify fix and prevent regression
299 lines
10 KiB
Python
299 lines
10 KiB
Python
import unittest
|
|
from unittest.mock import MagicMock, patch, mock_open
|
|
import tempfile
|
|
import os
|
|
import json
|
|
import csv
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
from semantica.parse.document_parser import DocumentParser, PDFParser, DOCXParser, HTMLParser
|
|
from semantica.parse.pptx_parser import PPTXParser
|
|
from semantica.parse.excel_parser import ExcelParser
|
|
from semantica.parse.structured_data_parser import StructuredDataParser, JSONParser, CSVParser, XMLParser
|
|
from semantica.parse.email_parser import EmailParser
|
|
from semantica.parse.code_parser import CodeParser
|
|
from semantica.parse.media_parser import MediaParser, ImageParser
|
|
from semantica.parse.web_parser import WebParser
|
|
from semantica.parse.registry import MethodRegistry
|
|
from semantica.parse.config import ParseConfig
|
|
|
|
pytestmark = pytest.mark.integration
|
|
|
|
class TestParseComprehensive(unittest.TestCase):
|
|
|
|
def setUp(self):
|
|
# Common mocks
|
|
self.mock_logger = MagicMock()
|
|
self.mock_tracker = MagicMock()
|
|
|
|
# Patch loggers and trackers
|
|
self.patchers = []
|
|
modules_to_patch = [
|
|
'semantica.parse.document_parser',
|
|
'semantica.parse.structured_data_parser',
|
|
'semantica.parse.email_parser',
|
|
'semantica.parse.code_parser',
|
|
'semantica.parse.media_parser',
|
|
'semantica.parse.web_parser',
|
|
'semantica.parse.pdf_parser',
|
|
'semantica.parse.docx_parser',
|
|
'semantica.parse.pptx_parser',
|
|
'semantica.parse.excel_parser',
|
|
'semantica.parse.html_parser',
|
|
'semantica.parse.json_parser',
|
|
'semantica.parse.csv_parser',
|
|
'semantica.parse.xml_parser',
|
|
'semantica.parse.image_parser'
|
|
]
|
|
|
|
for module_name in modules_to_patch:
|
|
# Patch get_logger
|
|
try:
|
|
p1 = patch(f'{module_name}.get_logger', return_value=self.mock_logger)
|
|
p1.start()
|
|
self.patchers.append(p1)
|
|
except AttributeError:
|
|
pass
|
|
|
|
# Patch get_progress_tracker
|
|
# Check if module has get_progress_tracker before patching to avoid AttributeError
|
|
try:
|
|
# We need to import the module to check attributes
|
|
mod = __import__(module_name, fromlist=['get_progress_tracker'])
|
|
if hasattr(mod, 'get_progress_tracker'):
|
|
p2 = patch(f'{module_name}.get_progress_tracker', return_value=self.mock_tracker)
|
|
p2.start()
|
|
self.patchers.append(p2)
|
|
except ImportError:
|
|
pass
|
|
|
|
def tearDown(self):
|
|
for p in self.patchers:
|
|
p.stop()
|
|
|
|
# --- Structured Data Parser Tests ---
|
|
|
|
def test_json_parser(self):
|
|
parser = JSONParser()
|
|
data = {'key': 'value', 'list': [1, 2, 3]}
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as tmp:
|
|
json.dump(data, tmp)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse(tmp_path)
|
|
self.assertEqual(result.data['key'], 'value')
|
|
self.assertEqual(result.data['list'], [1, 2, 3])
|
|
# Metadata depends on implementation, source/type are likely keys
|
|
self.assertIn('source', result.metadata)
|
|
self.assertIn('type', result.metadata)
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
def test_csv_parser(self):
|
|
parser = CSVParser()
|
|
rows = [['name', 'age'], ['Alice', '30'], ['Bob', '25']]
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.csv', newline='') as tmp:
|
|
writer = csv.writer(tmp)
|
|
writer.writerows(rows)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse(tmp_path)
|
|
# CSVData has rows attribute
|
|
self.assertEqual(len(result.rows), 2) # Header is not data
|
|
self.assertEqual(result.rows[0]['name'], 'Alice')
|
|
self.assertEqual(result.rows[1]['age'], '25')
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
def test_xml_parser(self):
|
|
parser = XMLParser()
|
|
xml_content = """<?xml version="1.0"?>
|
|
<root>
|
|
<person>
|
|
<name>Alice</name>
|
|
<age>30</age>
|
|
</person>
|
|
</root>
|
|
"""
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.xml') as tmp:
|
|
tmp.write(xml_content)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse(tmp_path)
|
|
# XMLData has root attribute
|
|
self.assertIsNotNone(result.root)
|
|
self.assertEqual(result.root.tag, 'root')
|
|
# Check children if accessible or logic
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
# --- Document Parser Tests ---
|
|
|
|
@patch('semantica.parse.pdf_parser.pdfplumber')
|
|
def test_pdf_parser(self, mock_pdfplumber):
|
|
parser = PDFParser()
|
|
mock_pdf = MagicMock()
|
|
mock_page = MagicMock()
|
|
mock_page.extract_text.return_value = "Page text"
|
|
mock_pdf.pages = [mock_page]
|
|
# Ensure metadata is a dict, not a property object if that's an issue
|
|
mock_pdf.metadata = {"Title": "Test PDF"}
|
|
|
|
# Setup the context manager
|
|
mock_context_manager = MagicMock()
|
|
mock_context_manager.__enter__.return_value = mock_pdf
|
|
mock_context_manager.__exit__.return_value = None
|
|
mock_pdfplumber.open.return_value = mock_context_manager
|
|
|
|
# We don't need a real file if we mock open, but the parser likely checks file existence
|
|
with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.pdf') as tmp:
|
|
tmp.write(b"dummy pdf content")
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse(tmp_path)
|
|
# Returns dict with full_text
|
|
self.assertIn("Page text", result["full_text"])
|
|
self.assertEqual(result["metadata"].get("title"), "Test PDF")
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
@patch('semantica.parse.docx_parser.Document')
|
|
def test_docx_parser(self, mock_document_cls):
|
|
parser = DOCXParser()
|
|
mock_doc = MagicMock()
|
|
p1 = MagicMock()
|
|
p1.text = "Paragraph 1"
|
|
p2 = MagicMock()
|
|
p2.text = "Paragraph 2"
|
|
mock_doc.paragraphs = [p1, p2]
|
|
mock_doc.core_properties.title = "Test DOCX"
|
|
mock_document_cls.return_value = mock_doc
|
|
|
|
with tempfile.NamedTemporaryFile(mode='wb', delete=False, suffix='.docx') as tmp:
|
|
tmp.write(b"dummy docx")
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse(tmp_path)
|
|
# Returns dict with full_text
|
|
self.assertIn("Paragraph 1", result["full_text"])
|
|
self.assertIn("Paragraph 2", result["full_text"])
|
|
self.assertEqual(result["metadata"].get("title"), "Test DOCX")
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
# --- Code Parser Tests ---
|
|
|
|
def test_code_parser_python(self):
|
|
parser = CodeParser()
|
|
code_content = """
|
|
def hello():
|
|
print("Hello")
|
|
|
|
class MyClass:
|
|
pass
|
|
"""
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.py') as tmp:
|
|
tmp.write(code_content)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
# CodeParser has parse_code method
|
|
result = parser.parse_code(tmp_path)
|
|
# Result is a dict containing structure dict
|
|
structure = result['structure']
|
|
self.assertTrue(any(f['name'] == 'hello' for f in structure['functions']))
|
|
self.assertTrue(any(c['name'] == 'MyClass' for c in structure['classes']))
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
# --- Email Parser Tests ---
|
|
|
|
def test_email_parser(self):
|
|
parser = EmailParser()
|
|
email_content = """From: sender@example.com
|
|
To: recipient@example.com
|
|
Subject: Test Email
|
|
|
|
This is the body.
|
|
"""
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.eml') as tmp:
|
|
tmp.write(email_content)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
# EmailParser has parse_email method
|
|
result = parser.parse_email(tmp_path)
|
|
self.assertEqual(result.headers.subject, "Test Email")
|
|
self.assertEqual(result.headers.from_address, "sender@example.com")
|
|
# Body text might be None if not found, but simple case should find it
|
|
self.assertIn("This is the body", result.body.text)
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
# --- HTML Parser Tests ---
|
|
|
|
def test_html_parser(self):
|
|
parser = HTMLParser()
|
|
html_content = """<html>
|
|
<head><title>Test HTML</title></head>
|
|
<body><p>Hello World</p></body>
|
|
</html>"""
|
|
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.html') as tmp:
|
|
tmp.write(html_content)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse(tmp_path)
|
|
# Returns HTMLData (dataclass) - I modified it to return HTMLData with metadata as dict
|
|
self.assertEqual(result.metadata.get('title'), 'Test HTML')
|
|
self.assertIn('Hello World', result.text)
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
# --- Document Parser Tests (General) ---
|
|
|
|
def test_document_parser_txt(self):
|
|
parser = DocumentParser()
|
|
content = "Simple text file."
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.txt') as tmp:
|
|
tmp.write(content)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
text = parser.extract_text(tmp_path)
|
|
self.assertEqual(text, content)
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
# --- Structured Data Parser Tests (Delegation) ---
|
|
|
|
def test_structured_data_parser_json_delegation(self):
|
|
parser = StructuredDataParser()
|
|
data = {'key': 'value'}
|
|
with tempfile.NamedTemporaryFile(mode='w', delete=False, suffix='.json') as tmp:
|
|
json.dump(data, tmp)
|
|
tmp_path = tmp.name
|
|
|
|
try:
|
|
result = parser.parse_data(tmp_path, data_format='json')
|
|
# Returns dict (JSONData.__dict__)
|
|
# JSONData has .data field
|
|
self.assertEqual(result['data']['key'], 'value')
|
|
finally:
|
|
os.remove(tmp_path)
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|