Files
flan 0a5441779a
Validate / hassfest (push) Skipped
Validate / pytest (push) Successful in 11s
Validate / HACS (push) Skipped
Fix produce-box select cancelling config entry setup
async_added_to_hass listed the switchable boxes inline, one fetch per box
popup, so on a slower connection the platform setup ran past
SLOW_SETUP_MAX_WAIT and the whole config entry was cancelled into
setup_error — every entity unavailable despite valid credentials. Move the
listing to a background task so setup never blocks on it; the options fill in
once it returns.

Also bound every portal request to a 30s timeout so one hung request can no
longer drag a refresh, or a first setup, past Home Assistant's own limits and
fail it outright.

Regression covered in tests/test_setup_hygiene.py.
2026-08-04 16:04:27 +00:00

102 lines
3.7 KiB
Python

"""Tests for the subscription and vacation-hold parsers.
Written after a real bug: the first version matched `.account-item-multi-fields`,
which is the HEADING row, so an account with a live subscription reported zero.
A count of nothing looks exactly like an account with nothing.
"""
from __future__ import annotations
import importlib
import sys
import types
from pathlib import Path
COMPONENT = Path(__file__).parent.parent / "custom_components"
def _load():
if "aiohttp" not in sys.modules:
stub = types.ModuleType("aiohttp")
stub.ClientSession = object
stub.ClientError = Exception
stub.ClientTimeout = lambda *args, **kwargs: None
sys.modules["aiohttp"] = stub
sys.path.insert(0, str(COMPONENT))
pkg = types.ModuleType("fh_pkg")
pkg.__path__ = [str(COMPONENT / "freshharvest")]
sys.modules["fh_pkg"] = pkg
return importlib.import_module("fh_pkg.actions")
actions = _load()
SUBS_HTML = """
<div class='account'>
<div class='account-item'>
<div class='account-item-multi-fields'>
<div class='account-item-heading account-item-history-qty'>Qty</div>
<div class='account-item-heading account-item-description'>Item</div>
<div class='account-item-heading account-item-history'>Arriving</div>
<div class='account-item-heading account-item-history-vendor'>Partner</div>
<div class='account-item-heading account-item-history center'>Frequency</div>
</div>
</div>
<div class='account-item'>
<div class='account-item-container'>
<div class='account-item-text account-item-history-qty'><div class='center'>2</div></div>
<div class='account-item-text account-item-description'>Georgia Grown Small Box</div>
<div class='account-item-text account-item-history'>tomorrow</div>
<div class='account-item-text account-item-history-vendor'>Various Partners</div>
<div class='account-item-text account-item-history center'>Weekly</div>
<div class='account-item-action account-item-history-action right'>Change Basket</div>
</div>
</div>
</div>
"""
def test_heading_row_is_not_a_subscription():
"""The regression: the header row must not be counted, and must not hide the real one."""
subs = actions.parse_subscriptions(SUBS_HTML)
assert len(subs) == 1
assert subs[0].name == "Georgia Grown Small Box"
def test_subscription_fields():
sub = actions.parse_subscriptions(SUBS_HTML)[0]
assert (sub.quantity, sub.frequency) == (2, "Weekly")
assert sub.partner == "Various Partners"
assert sub.arriving == "tomorrow"
def test_no_subscriptions_is_empty_not_an_error():
assert actions.parse_subscriptions("<html><body>nothing</body></html>") == []
def test_vacation_hold_uses_the_sites_own_date_format():
"""The page writes "Tuesday, Dec 1 - Monday, Dec 7", never ISO.
The first version of this test asserted ISO dates, so it passed against a
format the site does not produce while the parser reported zero holds on an
account that had one.
"""
html = (
"<div class='account'><div class='account-item'>Upcoming Pauses"
"<div class='account-item-container'>Tuesday, Dec 1 - Monday, Dec 7"
"<a>Remove</a></div></div></div>"
)
holds = actions.parse_vacation_holds(html)
assert len(holds) == 1
assert (holds[0].start, holds[0].end) == ("Dec 1", "Dec 7")
def test_no_holds_parses_empty():
assert actions.parse_vacation_holds("<div class='account'>none</div>") == []
def test_frequency_names_map_to_site_values():
assert actions.FREQUENCIES["weekly"] == "1"
# id='FrequencyID' but the POST field is popup-toggle; posting the id does nothing.
assert actions.FREQUENCY_FIELD == "popup-toggle"