Delivers MIL-005 (lecture Step 4): the product URL comes from `PRODUCT_URL` (default: the lecture's Amazon page), price and title selectors and robot-check markers live in `constants.py`, and a robot-check page raises `RobotCheckError` with a clear message. The code does not try to solve or bypass the check. Outcome on the live page (2026-10-28): Amazon returned HTTP 200 with a robot-check page and no price; the page is kept (tokens redacted) as a test fixture and shown in the README. Two live requests were made in total, one to inspect the response and one for the screenshot; there are no retries or loops. Review: code against QC-PY-001 as RC-014 (Go, autonomous, not independent). 56 pytest tests (no network), ruff and mypy --strict pass. Task: MIL-005#1 Task: MIL-005#2 Task: MIL-005#3 Task: MIL-005#4 Task: MIL-005#5
65 lines
1.9 KiB
Python
65 lines
1.9 KiB
Python
"""Smoke tests for the constants module.
|
|
|
|
@file test_constants.py
|
|
@brief Checks the shape of the constants.
|
|
"""
|
|
|
|
from amazon_price_tracker import constants
|
|
|
|
ENV_NAMES: list[str] = [
|
|
constants.ENV_SMTP_ADDRESS,
|
|
constants.ENV_EMAIL_ADDRESS,
|
|
constants.ENV_EMAIL_PASSWORD,
|
|
constants.ENV_PRODUCT_URL,
|
|
]
|
|
|
|
|
|
def test_urls_use_https() -> None:
|
|
"""Both product URLs are https."""
|
|
assert constants.PRACTICE_URL.startswith("https://")
|
|
assert constants.AMAZON_URL.startswith("https://")
|
|
|
|
|
|
def test_target_price_is_positive_float() -> None:
|
|
"""The target price is a positive float."""
|
|
assert isinstance(constants.TARGET_PRICE, float)
|
|
assert constants.TARGET_PRICE > 0
|
|
|
|
|
|
def test_env_names_are_non_empty() -> None:
|
|
"""Every environment variable name has text."""
|
|
assert all(name.strip() for name in ENV_NAMES)
|
|
|
|
|
|
def test_env_names_are_unique() -> None:
|
|
"""No two environment variable names collide."""
|
|
assert len(set(ENV_NAMES)) == len(ENV_NAMES)
|
|
|
|
|
|
def test_default_headers_are_non_empty() -> None:
|
|
"""Every default header has a name and a value."""
|
|
assert constants.DEFAULT_HEADERS
|
|
assert all(k.strip() and v.strip() for k, v in constants.DEFAULT_HEADERS.items())
|
|
|
|
|
|
def test_selectors_and_markers_are_non_empty_tuples() -> None:
|
|
"""Selectors and robot-check markers are non-empty tuples of text."""
|
|
for group in (
|
|
constants.PRICE_SELECTORS,
|
|
constants.TITLE_SELECTORS,
|
|
constants.ROBOT_CHECK_MARKERS,
|
|
):
|
|
assert isinstance(group, tuple)
|
|
assert group
|
|
assert all(item.strip() for item in group)
|
|
|
|
|
|
def test_price_selectors_try_specific_before_generic() -> None:
|
|
"""The a-price selector comes before the bare a-offscreen one."""
|
|
assert constants.PRICE_SELECTORS == (".a-price .a-offscreen", ".a-offscreen")
|
|
|
|
|
|
def test_smtp_port_is_starttls_port() -> None:
|
|
"""The SMTP port is 587."""
|
|
assert constants.SMTP_PORT == 587
|