You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
89 lines
2.7 KiB
Python
89 lines
2.7 KiB
Python
"""
|
|
Unit tests for activist ownership cover-page parsing (HTML + XML).
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from app.services.activist_ownership_service import _parse_cover_html, _parse_cover_xml
|
|
|
|
|
|
# Minimal 13D HTML cover page (simplified)
|
|
SAMPLE_13D_HTML = """
|
|
<html><body>
|
|
<table>
|
|
<tr><td>Item 11. Aggregate Amount Beneficially Owned by Each Reporting Person</td><td>12,500,000</td></tr>
|
|
<tr><td>Item 13. Percent of Class Represented by Amount in Row (11)</td><td>7.2%</td></tr>
|
|
</table>
|
|
</body></html>
|
|
"""
|
|
|
|
SAMPLE_13G_HTML = """
|
|
<html><body>
|
|
<p>Percent of Class Represented by Amount in Row (11): 5.4 %</p>
|
|
<p>Aggregate Amount Beneficially Owned: 8,000,000</p>
|
|
</body></html>
|
|
"""
|
|
|
|
SAMPLE_COVER_XML = """<?xml version="1.0" encoding="UTF-8"?>
|
|
<coverPage>
|
|
<aggregateAmount>15000000</aggregateAmount>
|
|
<percentClass>9.1</percentClass>
|
|
</coverPage>
|
|
"""
|
|
|
|
SAMPLE_COVER_XML_NAMESPACED = """<?xml version="1.0" encoding="UTF-8"?>
|
|
<sc13d xmlns:sc="http://www.sec.gov/sc13d">
|
|
<sc:aggregateAmount>3000000</sc:aggregateAmount>
|
|
<sc:percentClass>3.5</sc:percentClass>
|
|
</sc13d>
|
|
"""
|
|
|
|
|
|
class TestParseCoverHtml:
|
|
def test_extract_ownership_pct(self):
|
|
pct, shares = _parse_cover_html(SAMPLE_13D_HTML)
|
|
assert pct == pytest.approx(7.2)
|
|
|
|
def test_extract_shares_owned(self):
|
|
pct, shares = _parse_cover_html(SAMPLE_13D_HTML)
|
|
assert shares == pytest.approx(12_500_000)
|
|
|
|
def test_13g_format(self):
|
|
pct, shares = _parse_cover_html(SAMPLE_13G_HTML)
|
|
assert pct == pytest.approx(5.4)
|
|
assert shares == pytest.approx(8_000_000)
|
|
|
|
def test_empty_html(self):
|
|
pct, shares = _parse_cover_html("")
|
|
assert pct is None
|
|
assert shares is None
|
|
|
|
def test_no_pct_in_html(self):
|
|
pct, shares = _parse_cover_html("<p>Aggregate Amount Beneficially Owned: 1,000,000</p>")
|
|
assert pct is None
|
|
assert shares == pytest.approx(1_000_000)
|
|
|
|
|
|
class TestParseCoverXml:
|
|
def test_structured_xml(self):
|
|
pct, shares = _parse_cover_xml(SAMPLE_COVER_XML)
|
|
assert pct == pytest.approx(9.1)
|
|
assert shares == pytest.approx(15_000_000)
|
|
|
|
def test_namespaced_xml(self):
|
|
# Namespace-prefixed elements should be found via {*} wildcard search
|
|
pct, shares = _parse_cover_xml(SAMPLE_COVER_XML_NAMESPACED)
|
|
# Either finds or gracefully returns None — no crash
|
|
assert pct is None or isinstance(pct, float)
|
|
assert shares is None or isinstance(shares, float)
|
|
|
|
def test_invalid_xml(self):
|
|
pct, shares = _parse_cover_xml("<not valid xml>>>")
|
|
assert pct is None
|
|
assert shares is None
|
|
|
|
def test_empty_xml(self):
|
|
pct, shares = _parse_cover_xml("")
|
|
assert pct is None
|
|
assert shares is None
|