zcbot/tests/test_literature_merge.py

111 lines
4.7 KiB
Python

import json
import tempfile
import unittest
from pathlib import Path
from core.skills import SkillRegistry
from skills.literature.scripts.merge_publications import (
exact_key,
main,
merge_publications,
normalize_doi,
normalize_isbn,
normalize_record,
)
ROOT = Path(__file__).resolve().parents[1]
class LiteratureSkillTests(unittest.TestCase):
def test_literature_is_the_only_publication_search_skill(self) -> None:
registry = SkillRegistry(ROOT / "skills")
self.assertIn("literature", registry.skills)
self.assertNotIn("research", registry.skills)
self.assertNotIn("documents", registry.skills)
class LiteratureNormalizationTests(unittest.TestCase):
def test_normalizes_doi_and_isbn(self) -> None:
self.assertEqual(normalize_doi("https://doi.org/10.1000/ABC.1."), "10.1000/abc.1")
self.assertEqual(normalize_isbn("0-306-40615-2"), "9780306406157")
def test_preserves_unknown_raw_type(self) -> None:
record = normalize_record({"title": "A", "type": "future-object"}, "future")
self.assertEqual(record["type"], "other")
self.assertEqual(record["raw_type"], "future-object")
def test_different_book_editions_have_different_keys(self) -> None:
first = normalize_record(
{"title": "Materials", "type": "book", "isbn": "9780306406157", "edition": "1st"},
"books",
)
second = normalize_record(
{"title": "Materials", "type": "book", "isbn": "9780306406157", "edition": "2nd"},
"books",
)
self.assertNotEqual(exact_key(first), exact_key(second))
def test_preserves_date_and_relations(self) -> None:
record = normalize_record(
{
"title": "Chapter",
"type": "book-chapter",
"publication_date": "2025-03-09",
"is_part_of": "Book A",
},
"books",
)
self.assertEqual(record["issued"], {"year": 2025, "month": 3, "day": 9})
self.assertEqual(record["relations"]["is_part_of"], "Book A")
class LiteratureMergeTests(unittest.TestCase):
def test_merges_same_doi_and_preserves_sources(self) -> None:
result = merge_publications([
("paper_server", {"id": "p1", "doi": "10.1/ABC", "title": "Title", "abstract": "A"}),
("materials_library", {"id": "m1", "doi": "https://doi.org/10.1/abc", "title": "Title"}),
])
self.assertEqual(len(result["publications"]), 1)
self.assertEqual(
{source["backend"] for source in result["publications"][0]["sources"]},
{"paper_server", "materials_library"},
)
def test_does_not_merge_chapter_with_book(self) -> None:
result = merge_publications([
("source", {"title": "Hydration", "type": "book", "first_author": "Li", "year": 2024}),
("source", {"title": "Hydration", "type": "book_chapter", "first_author": "Li", "year": 2024}),
])
self.assertEqual(len(result["publications"]), 2)
def test_keeps_standard_revisions_and_patent_family_members(self) -> None:
result = merge_publications([
("standards", {"title": "Method", "type": "standard", "standard_number": "GB/T 1", "publisher": "SAC", "year": 2020}),
("standards", {"title": "Method", "type": "standard", "standard_number": "GB/T 1", "publisher": "SAC", "year": 2025}),
("patents", {"title": "Binder", "type": "patent", "patent_publication_number": "CN100A", "patent_family": "F1"}),
("patents", {"title": "Binder", "type": "patent", "patent_publication_number": "US100B", "patent_family": "F1"}),
])
self.assertEqual(len(result["publications"]), 4)
def test_marks_similar_titles_as_possible_duplicates(self) -> None:
result = merge_publications([
("a", {"title": "Hydration of low carbon cement systems", "type": "article", "year": 2024}),
("b", {"title": "Hydration of low-carbon cement system", "type": "article", "year": 2024}),
])
self.assertEqual(len(result["publications"]), 2)
self.assertEqual(len(result["possible_duplicates"]), 1)
def test_cli_writes_output(self) -> None:
with tempfile.TemporaryDirectory() as tmp:
root = Path(tmp)
source = root / "source.json"
output = root / "output.json"
source.write_text(json.dumps([{"title": "A", "doi": "10.1/a"}]), encoding="utf-8")
self.assertEqual(main(["--input", f"paper_server={source}", "--output", str(output)]), 0)
self.assertEqual(len(json.loads(output.read_text(encoding="utf-8"))["publications"]), 1)
if __name__ == "__main__":
unittest.main()