Delivers MIL-002 (lecture Step 1): BeautifulSoup reads the title and the price (as a float, 99.99) from the practice page. Layers: domain (Product, errors), application (ports, read_product), infrastructure (scraper, HTTP client), main as composition root. Tests use a trimmed fixture of the practice page and never the network. Review: code against QC-PY-001 as RC-011 (Go, autonomous, not independent). 15 pytest tests, ruff and mypy --strict pass; a real run printed 99.99. Screenshots: docs/images/step-1-*.png. Task: MIL-002#1 Task: MIL-002#2 Task: MIL-002#3 Task: MIL-002#4 Task: MIL-002#5
35 lines
999 B
Python
35 lines
999 B
Python
from amazon_price_tracker.application.read_product import read_product
|
|
from amazon_price_tracker.domain.product import Product
|
|
|
|
|
|
class FakeFetcher:
|
|
def __init__(self, html: str) -> None:
|
|
self.html = html
|
|
self.urls: list[str] = []
|
|
|
|
def fetch(self, url: str) -> str:
|
|
self.urls.append(url)
|
|
return self.html
|
|
|
|
|
|
class FakeParser:
|
|
def __init__(self, product: Product) -> None:
|
|
self.product = product
|
|
self.received: list[str] = []
|
|
|
|
def parse(self, html: str) -> Product:
|
|
self.received.append(html)
|
|
return self.product
|
|
|
|
|
|
def test_read_product_fetches_url_and_returns_parsed_product() -> None:
|
|
expected = Product(title="Pot", price=9.5)
|
|
fetcher = FakeFetcher("<html>page</html>")
|
|
parser = FakeParser(expected)
|
|
|
|
result = read_product(fetcher, parser, "https://example.test/p")
|
|
|
|
assert result == expected
|
|
assert fetcher.urls == ["https://example.test/p"]
|
|
assert parser.received == ["<html>page</html>"]
|