- auto_credit.py: scrape Trading Economics Thailand total-vehicle-sales HTML -> total sales + new car sales YoY + vehicle production/passenger/exports - refining_energy.py: scrape EIA prices.php 3:2:1 crack spread (Gulf LLS) + WTI/Brent/gasoline - Both server-rendered HTML scrapers (har-derived-api-client pattern); EIA used instead of RBN (RBN is Cloudflare-challenged, 403 via urllib; EIA is open US gov data, HTTP 200 direct) - collect_auto_credit.py / collect_refining.py CLI -> JSON snapshot - 8 tests; full backend suite 148 OK; live verified (auto 59198/20% YoY; crack 71.10 $/bbl); static scan clean
59 lines
2.4 KiB
Python
59 lines
2.4 KiB
Python
"""Tests for the Refining/Energy (EIA) parser."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import unittest
|
|
|
|
from app import refining_energy
|
|
|
|
|
|
def _eia_html() -> str:
|
|
return """
|
|
<html><body>
|
|
<h2>Wholesale Spot Petroleum Prices, 8/21/26 Close</h2>
|
|
<table>
|
|
<tr><th>Product</th><th>Area</th><th>Price</th><th>PercentChange*</th></tr>
|
|
<tr><td>Crude Oil ($/barrel)</td><td>WTI</td><td>87.21</td><td>-2.8</td></tr>
|
|
<tr><td></td><td>Brent</td><td>96.92</td><td>+3.1</td></tr>
|
|
<tr><td></td><td>Louisiana Light</td><td>90.71</td><td>-2.7</td></tr>
|
|
<tr><td>Gasoline (RBOB) ($/gallon)</td><td>NY Harbor</td><td>3.42</td><td>+2.3</td></tr>
|
|
<tr><td></td><td>Gulf Coast</td><td>3.52</td><td>+1.3</td></tr>
|
|
<tr><td>3:2:1 Crack Spread ($/barrel)</td><td>Gulf Coast (LLS)</td><td>71.10</td><td>+5.7</td></tr>
|
|
<tr><td>Low-Sulfur Diesel ($/gallon)</td><td>NY Harbor</td><td>4.47</td><td>-2.1</td></tr>
|
|
</table>
|
|
</body></html>
|
|
"""
|
|
|
|
|
|
class RefiningParseTest(unittest.TestCase):
|
|
def test_parses_crack_spread_and_crudes(self) -> None:
|
|
snap = refining_energy.parse_refining_html(_eia_html())
|
|
self.assertEqual(snap.crack_spread, 71.10)
|
|
self.assertEqual(snap.crack_spread_change, 5.7)
|
|
self.assertEqual(snap.wti_crude, 87.21)
|
|
self.assertEqual(snap.brent_crude, 96.92)
|
|
self.assertEqual(snap.gasoline_gulf, 3.52)
|
|
|
|
def test_missing_table_raises(self) -> None:
|
|
with self.assertRaises(refining_energy.RefiningError):
|
|
refining_energy.parse_refining_html("<html>no data</html>")
|
|
|
|
def test_negative_change_parses(self) -> None:
|
|
html_text = """
|
|
<table><tr><td>3:2:1 Crack Spread ($/barrel)</td><td>Gulf Coast (LLS)</td><td>60.00</td><td>-2.5</td></tr></table>"""
|
|
snap = refining_energy.parse_refining_html(html_text)
|
|
self.assertEqual(snap.crack_spread, 60.00)
|
|
self.assertEqual(snap.crack_spread_change, -2.5)
|
|
|
|
def test_crack_spread_fallback_when_area_missing(self) -> None:
|
|
# If the row lacks the "Gulf Coast" area cell, fall back to last two cells.
|
|
html_text = """
|
|
<table><tr><td>3:2:1 Crack Spread ($/barrel)</td><td>71.10</td><td>+5.7</td></tr></table>"""
|
|
snap = refining_energy.parse_refining_html(html_text)
|
|
self.assertEqual(snap.crack_spread, 71.10)
|
|
self.assertEqual(snap.crack_spread_change, 5.7)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
unittest.main()
|