Files
wehub-resource-sync 7a0da7932b
OSV-Scanner (Scheduled) / scan-scheduled (push) Failing after 0s
Create Release / test-gate (push) Has been cancelled
Create Release / release-gate (push) Has been cancelled
Create Release / ci-gate (push) Has been cancelled
Create Release / version-check (push) Has been cancelled
Create Release / e2e-test-gate (push) Has been cancelled
Create Release / responsive-test-gate (push) Has been cancelled
Create Release / compat-test-gate (push) Has been cancelled
Create Release / compose-integration-gate (push) Has been cancelled
Create Release / vulture-gate (push) Has been cancelled
Create Release / build (push) Has been cancelled
Create Release / provenance (push) Has been cancelled
Create Release / prerelease-docker (push) Has been cancelled
Create Release / publish-docker (push) Has been cancelled
Create Release / create-release (push) Has been cancelled
Create Release / cleanup-changelog (push) Has been cancelled
Create Release / trigger-pypi (push) Has been cancelled
Create Release / monitor-pypi (push) Has been cancelled
Create Release / Clean up orphan prerelease tags and signatures (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-form] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-metrics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [research-workflow] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-core] (push) Has been cancelled
CodeQL Advanced / Analyze (javascript-typescript) (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [history-news] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [library] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [link-analytics] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-core] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [chat-lifecycle] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [error-benchmark] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [settings-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) (push) Has been cancelled
Docker Tests (Consolidated) / Accessibility Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Unit Tests (push) Has been cancelled
Docker Tests (Consolidated) / LLM Example Tests (push) Has been cancelled
Docker Tests (Consolidated) / Production Image Smoke Test (push) Has been cancelled
Docker Tests (Consolidated) / Infrastructure Tests (push) Has been cancelled
OSSF Scorecard / OSSF Security Scorecard Analysis (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [mobile] (push) Has been cancelled
Backwards Compatibility / Verify Encryption Constants (push) Has been cancelled
Backwards Compatibility / PyPI Version Compatibility (push) Has been cancelled
Backwards Compatibility / Database Migration Tests (push) Has been cancelled
CodeQL Advanced / Analyze (python) (push) Has been cancelled
Docker Tests (Consolidated) / detect-changes (push) Has been cancelled
Docker Tests (Consolidated) / Build Test Image (push) Has been cancelled
Docker Tests (Consolidated) / All Pytest Tests + Coverage (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [accessibility] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [api-crud] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-login] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-pages] (push) Has been cancelled
Docker Tests (Consolidated) / UI Tests (Puppeteer) [auth-register] (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 13:08:55 +08:00

335 lines
10 KiB
Python

"""
Tests for citation export formats (RIS, LaTeX, Quarto) to ensure
they handle edge cases correctly.
"""
import pytest
from local_deep_research.text_optimization.citation_formatter import (
CitationFormatter,
CitationMode,
RISExporter,
LaTeXExporter,
QuartoExporter,
)
class TestCitationExportFormats:
"""Test various export formats for citations."""
def test_ris_export_with_multiple_citations(self):
"""Test RIS export handles multiple citations to same source."""
content = """# Research Report
Multiple citations [1] to same [1] source [1]. And another [2].
## Sources
[1] Machine Learning Paper
URL: https://arxiv.org/abs/2023.12345
[2] Nature Article
URL: https://nature.com/articles/s41586-023-06789-9"""
exporter = RISExporter()
ris_output = exporter.export_to_ris(content)
# Should have exactly 2 RIS entries (not 3)
assert ris_output.count("TY - ELEC") == 2
assert ris_output.count("ER -") == 2
# Should include both URLs
assert "https://arxiv.org/abs/2023.12345" in ris_output
assert "https://nature.com/articles/s41586-023-06789-9" in ris_output
def test_ris_export_with_special_characters(self):
"""Test RIS export handles special characters properly."""
content = """# Research Report
Citation [1] with special chars.
## Sources
[1] Paper with Special Chars: Ñoño, Müller & 中文
URL: https://example.com/special-chars?param=value&other=test"""
exporter = RISExporter()
ris_output = exporter.export_to_ris(content)
# Should handle special characters
assert "Ñoño" in ris_output
assert "Müller" in ris_output
assert "中文" in ris_output
assert "param=value&other=test" in ris_output
def test_latex_export_with_equations(self):
"""Test LaTeX export with equations in content."""
content = r"""# Research Report
The equation $E = mc^2$ from [1] shows mass-energy equivalence.
Consider the integral $$\int_0^\infty e^{-x^2} dx = \frac{\sqrt{\pi}}{2}$$ from [2].
## Sources
[1] Einstein's Original Paper
URL: https://physics.org/einstein-1905
[2] Mathematical Methods
URL: https://math.edu/integrals"""
exporter = LaTeXExporter()
latex_output = exporter.export_to_latex(content)
# Should preserve math mode
assert r"$E = mc^2$" in latex_output
assert r"$$\int_0^\infty" in latex_output
# Should have proper bibliography
assert r"\bibitem{1}" in latex_output
assert r"\bibitem{2}" in latex_output
def test_latex_export_special_characters_escaping(self):
"""Test LaTeX export properly escapes special characters."""
content = """# Research Report
Citation [1] with special LaTeX chars: $, %, &, #, _, {, }.
## Sources
[1] Paper & Title with % Special # Chars
URL: https://example.com/paper_with_underscore"""
exporter = LaTeXExporter()
latex_output = exporter.export_to_latex(content)
# Should escape special LaTeX characters in text
assert r"\%" in latex_output
assert r"\&" in latex_output
assert r"\#" in latex_output
assert (
r"\_" in latex_output or "_" in latex_output
) # URLs might not be escaped
def test_quarto_export_with_metadata(self):
"""Test Quarto export includes proper metadata."""
content = """# Deep Learning Research Report
This reviews [1] and extends [2].
## Sources
[1] Attention Is All You Need
URL: https://arxiv.org/abs/1706.03762
[2] BERT: Pre-training of Deep Bidirectional Transformers
URL: https://arxiv.org/abs/1810.04805"""
exporter = QuartoExporter()
quarto_output = exporter.export_to_quarto(content)
# Should have YAML frontmatter
assert "---" in quarto_output
assert "title:" in quarto_output
assert "bibliography:" in quarto_output
# Should extract title
assert "Deep Learning Research Report" in quarto_output
# Should have citations in Quarto format
assert "[@ref1]" in quarto_output or "@ref1" in quarto_output
assert "[@ref2]" in quarto_output or "@ref2" in quarto_output
def test_export_with_missing_sources_section(self):
"""Test export formats handle missing sources section gracefully."""
content = """# Research Report
This has citations [1] and [2] but no sources section."""
# Test RIS
ris_exporter = RISExporter()
ris_output = ris_exporter.export_to_ris(content)
assert ris_output == "" # Should return empty for no sources
# Test LaTeX
latex_exporter = LaTeXExporter()
latex_output = latex_exporter.export_to_latex(content)
assert "bibliography" not in latex_output.lower()
# Test Quarto
quarto_exporter = QuartoExporter()
quarto_output = quarto_exporter.export_to_quarto(content)
# Should still have content but no bibliography
assert "This has citations" in quarto_output
def test_export_with_duplicate_urls(self):
"""Test export formats handle duplicate URLs correctly."""
content = """# Report
Cite [1] and [2].
## Sources
[1] First Title
URL: https://same.url/paper
[2] Different Title Same URL
URL: https://same.url/paper"""
# RIS should have separate entries
ris_exporter = RISExporter()
ris_output = ris_exporter.export_to_ris(content)
assert ris_output.count("TY - ELEC") == 2
# The fixed exporter emits clean RIS records only (no echo of the
# raw source text), so the shared URL appears once per entry → 2.
assert ris_output.count("https://same.url/paper") == 2
# LaTeX should have separate bibitems
latex_exporter = LaTeXExporter()
latex_output = latex_exporter.export_to_latex(content)
assert r"\bibitem{1}" in latex_output
assert r"\bibitem{2}" in latex_output
def test_export_preserves_markdown_structure(self):
"""Test that exports preserve document structure."""
content = """# Main Title
## Introduction
This is **bold** and *italic* text [1].
### Subsection
- List item [2]
- Another item
- Nested [3]
## Sources
[1] Source One
URL: https://one.com
[2] Source Two
URL: https://two.com
[3] Source Three
URL: https://three.com"""
# Test LaTeX preserves structure
latex_exporter = LaTeXExporter()
latex_output = latex_exporter.export_to_latex(content)
assert r"\section" in latex_output
assert r"\subsection" in latex_output
assert r"\textbf{bold}" in latex_output
assert r"\textit{italic}" in latex_output
assert r"\begin{itemize}" in latex_output
def test_quarto_bibliography_generation(self):
"""Test Quarto generates proper bibliography entries."""
content = """# Research
Citation [1].
## Sources
[1] 2023 AI Paper by Smith, J. and Doe, J.
URL: https://ai-journal.com/2023/paper"""
exporter = QuartoExporter()
quarto_output = exporter.export_to_quarto(content)
# Should generate a references section
assert "references:" in quarto_output.lower() or "@" in quarto_output
def test_export_with_malformed_content(self):
"""Test exports handle malformed content gracefully."""
content = """# Report
[1] [2 [3]] [[4]
## Sources
[1 Source One
URL: https://one.com
[2] Source Two
No URL
[3]
URL: https://three.com"""
# All exporters should handle this without crashing
exporters = [RISExporter(), LaTeXExporter(), QuartoExporter()]
for exporter in exporters:
try:
if isinstance(exporter, RISExporter):
output = exporter.export_to_ris(content)
elif isinstance(exporter, LaTeXExporter):
output = exporter.export_to_latex(content)
elif isinstance(exporter, QuartoExporter):
output = exporter.export_to_quarto(content)
assert output is not None
except Exception as e:
pytest.fail(
f"{exporter.__class__.__name__} failed on malformed content: {e}"
)
def test_ris_export_field_extraction(self):
"""Test RIS export extracts various metadata fields."""
content = """# Report
Citation [1].
## Sources
[1] Deep Learning in Nature 2023 by LeCun, Y., Bengio, Y., & Hinton, G.
Published in Nature Machine Intelligence, Volume 5, Pages 123-145
DOI: 10.1038/s42256-023-00001-2
URL: https://nature.com/articles/s42256-023-00001-2"""
exporter = RISExporter()
ris_output = exporter.export_to_ris(content)
# Should extract various fields
assert "TI - Deep Learning in Nature 2023" in ris_output
assert "AU - LeCun, Y." in ris_output or "LeCun" in ris_output
assert "PY - 2023" in ris_output
assert "DO - 10.1038/s42256-023-00001-2" in ris_output
def test_concurrent_export_formats(self):
"""Test that different export formats can be used concurrently."""
content = """# Research
Multiple [1] citations [1] here [2].
## Sources
[1] First Paper
URL: https://first.com
[2] Second Paper
URL: https://second.com"""
# Format for different uses
formatter_domain = CitationFormatter(
mode=CitationMode.DOMAIN_HYPERLINKS
)
formatted_domain = formatter_domain.format_document(content)
formatter_number = CitationFormatter(
mode=CitationMode.NUMBER_HYPERLINKS
)
formatted_number = formatter_number.format_document(content)
# Export to different formats
ris_output = RISExporter().export_to_ris(content)
latex_output = LaTeXExporter().export_to_latex(content)
quarto_output = QuartoExporter().export_to_quarto(content)
# All should work independently
assert "[first.com]" in formatted_domain
assert "[[1]](https://first.com)" in formatted_number
assert "TY - ELEC" in ris_output
assert r"\bibitem" in latex_output
assert "bibliography" in quarto_output or "@" in quarto_output