import os
import tempfile
import types
import unittest
from unittest.mock import patch
from apps.resm.title_utils import (
assess_title,
extract_title_from_pdf,
extract_title_from_xml,
is_better_title,
)
class TitleAssessmentTests(unittest.TestCase):
def test_normal_title_is_not_suspect(self):
result = assess_title("Optimal Nonlinear PID Speed Control for an Electric Vehicle")
self.assertFalse(result.suspect)
def test_abstract_stored_as_title_is_suspect(self):
text = (
"In many situations, a beam opening is necessary near a plastic hinge. "
"However, few studies investigated the resulting behavior. "
"In this study, nine full-scale joints were tested with several configurations. "
"The results show that additional reinforcement improves strength and ductility. "
) * 2
result = assess_title(text)
self.assertTrue(result.suspect)
self.assertIn("abstract_like_prose", result.reasons)
def test_mathml_markup_does_not_trigger_raw_length_false_positive(self):
markup = "" + "x" * 100 + ""
result = assess_title(f"Behavior of {markup} under pressure")
self.assertFalse(result.suspect)
def test_short_candidate_is_better_than_abstract(self):
old = ("This study investigates reinforced concrete joints. " * 20).strip()
new = "Cyclic Behavior of Reinforced Concrete Beam-Column Joints"
self.assertTrue(is_better_title(old, new))
class XmlTitleExtractionTests(unittest.TestCase):
def _xml_file(self, body):
handle = tempfile.NamedTemporaryFile("w", suffix=".xml", delete=False, encoding="utf-8")
self.addCleanup(lambda: os.path.exists(handle.name) and os.unlink(handle.name))
handle.write(body)
handle.close()
return handle.name
def test_extracts_elsevier_title_and_verifies_doi(self):
path = self._xml_file("""
10.1234/example.1
Correct & Verified Article Title
""")
candidate = extract_title_from_xml(path, "10.1234/example.1")
self.assertIsNotNone(candidate)
self.assertEqual(candidate.title, "Correct & Verified Article Title")
self.assertEqual(candidate.confidence, "high")
def test_rejects_xml_for_another_doi(self):
path = self._xml_file("""
10.1234/wrong
A Correct Looking Title
""")
candidate = extract_title_from_xml(path, "10.1234/expected")
self.assertIsNone(candidate)
class PdfTitleExtractionTests(unittest.TestCase):
def test_pdf_metadata_requires_identity_evidence_and_stays_medium(self):
handle = tempfile.NamedTemporaryFile("wb", suffix=".pdf", delete=False)
self.addCleanup(lambda: os.path.exists(handle.name) and os.unlink(handle.name))
handle.write(b"%PDF-placeholder")
handle.close()
page = types.SimpleNamespace(
extract_text=lambda: (
"Correct Article Title\nMohamed Shamseldin\n"
"https://doi.org/10.1234/example.1\nAbstract"
)
)
reader = types.SimpleNamespace(
pages=[page],
metadata=types.SimpleNamespace(title="Correct Article Title"),
)
module = types.SimpleNamespace(PdfReader=lambda *args, **kwargs: reader)
with patch.dict("sys.modules", {"pypdf": module}):
candidate = extract_title_from_pdf(
handle.name, "10.1234/example.1", "Mohamed Shamseldin"
)
self.assertIsNotNone(candidate)
self.assertEqual(candidate.title, "Correct Article Title")
self.assertEqual(candidate.confidence, "medium")
self.assertEqual(candidate.evidence, "pdf_metadata+doi")
def test_rejects_layout_filename_and_keeps_first_page_low_confidence(self):
handle = tempfile.NamedTemporaryFile("wb", suffix=".pdf", delete=False)
self.addCleanup(lambda: os.path.exists(handle.name) and os.unlink(handle.name))
handle.write(b"%PDF-placeholder")
handle.close()
page = types.SimpleNamespace(
extract_text=lambda: (
"Correct Article Title\nMohamed Shamseldin\n"
"https://doi.org/10.1234/example.1\nAbstract"
)
)
reader = types.SimpleNamespace(
pages=[page], metadata=types.SimpleNamespace(title="article-layout.indd")
)
module = types.SimpleNamespace(PdfReader=lambda *args, **kwargs: reader)
with patch.dict("sys.modules", {"pypdf": module}):
candidate = extract_title_from_pdf(
handle.name, "10.1234/example.1", "Mohamed Shamseldin"
)
self.assertIsNotNone(candidate)
self.assertEqual(candidate.source, "pdf_first_page")
self.assertEqual(candidate.confidence, "low")
if __name__ == "__main__":
unittest.main()