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
85 lines
2.6 KiB
Python
85 lines
2.6 KiB
Python
import pytest
|
|
|
|
from amazon_price_tracker.constants import (
|
|
AMAZON_URL,
|
|
ENV_EMAIL_ADDRESS,
|
|
ENV_EMAIL_PASSWORD,
|
|
ENV_PRODUCT_URL,
|
|
ENV_SMTP_ADDRESS,
|
|
PRACTICE_URL,
|
|
)
|
|
from amazon_price_tracker.domain.errors import InvalidSettingError, MissingSettingError
|
|
from amazon_price_tracker.infrastructure.settings import (
|
|
load_mail_settings,
|
|
load_product_url,
|
|
)
|
|
|
|
FULL = {
|
|
ENV_SMTP_ADDRESS: "smtp.example.test",
|
|
ENV_EMAIL_ADDRESS: "me@example.test",
|
|
ENV_EMAIL_PASSWORD: "s3cret-pw",
|
|
}
|
|
|
|
|
|
def test_load_mail_settings_reads_all_values() -> None:
|
|
settings = load_mail_settings(FULL)
|
|
|
|
assert settings.smtp_address == "smtp.example.test"
|
|
assert settings.email_address == "me@example.test"
|
|
assert settings.email_password == "s3cret-pw"
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
"name", [ENV_SMTP_ADDRESS, ENV_EMAIL_ADDRESS, ENV_EMAIL_PASSWORD]
|
|
)
|
|
def test_load_mail_settings_raises_naming_missing_variable(name: str) -> None:
|
|
environ = {key: value for key, value in FULL.items() if key != name}
|
|
|
|
with pytest.raises(MissingSettingError, match=name):
|
|
load_mail_settings(environ)
|
|
|
|
|
|
def test_load_mail_settings_raises_on_empty_value() -> None:
|
|
with pytest.raises(MissingSettingError, match=ENV_EMAIL_ADDRESS):
|
|
load_mail_settings({**FULL, ENV_EMAIL_ADDRESS: ""})
|
|
|
|
|
|
def test_load_mail_settings_names_first_missing_variable() -> None:
|
|
with pytest.raises(MissingSettingError, match=ENV_SMTP_ADDRESS):
|
|
load_mail_settings({})
|
|
|
|
|
|
def test_load_product_url_defaults_to_amazon() -> None:
|
|
assert load_product_url({}) == AMAZON_URL
|
|
|
|
|
|
def test_load_product_url_defaults_when_blank() -> None:
|
|
assert load_product_url({ENV_PRODUCT_URL: " "}) == AMAZON_URL
|
|
|
|
|
|
def test_load_product_url_uses_override() -> None:
|
|
assert load_product_url({ENV_PRODUCT_URL: PRACTICE_URL}) == PRACTICE_URL
|
|
|
|
|
|
def test_load_product_url_strips_whitespace() -> None:
|
|
assert load_product_url({ENV_PRODUCT_URL: f" {PRACTICE_URL}\n"}) == PRACTICE_URL
|
|
|
|
|
|
@pytest.mark.parametrize("value", ["http://example.test/", "ftp://x", "example.test"])
|
|
def test_load_product_url_rejects_non_https(value: str) -> None:
|
|
with pytest.raises(InvalidSettingError, match=ENV_PRODUCT_URL) as info:
|
|
load_product_url({ENV_PRODUCT_URL: value})
|
|
|
|
assert value not in str(info.value)
|
|
|
|
|
|
def test_mail_settings_repr_hides_password() -> None:
|
|
assert "s3cret-pw" not in repr(load_mail_settings(FULL))
|
|
|
|
|
|
def test_missing_setting_error_does_not_contain_password() -> None:
|
|
with pytest.raises(MissingSettingError) as info:
|
|
load_mail_settings({**FULL, ENV_SMTP_ADDRESS: ""})
|
|
|
|
assert "s3cret-pw" not in str(info.value)
|