Skip to content

Commit 0290eb4

Browse files
committed
Retry transient Home Assistant resets in E2E
1 parent e8dd93f commit 0290eb4

2 files changed

Lines changed: 45 additions & 2 deletions

File tree

scripts/e2e_home_assistant.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,12 @@
33
from __future__ import annotations
44

55
import argparse
6+
import http.client
67
import json
78
import shutil
89
import subprocess
910
import tempfile
1011
import time
11-
import urllib.error
1212
import urllib.request
1313
import uuid
1414
from collections.abc import Mapping, Sequence
@@ -82,7 +82,7 @@ def wait_for_http(url: str, *, timeout: float) -> bytes:
8282
if not isinstance(body, bytes):
8383
raise TypeError("HTTP response body must be bytes")
8484
return body
85-
except (TimeoutError, urllib.error.URLError) as error:
85+
except (OSError, http.client.HTTPException) as error:
8686
last_error = error
8787
time.sleep(1)
8888
raise TimeoutError(f"{url} did not become ready within {timeout}s: {last_error}")

tests/test_e2e_home_assistant.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,38 @@
11
import json
22
from pathlib import Path
3+
from types import TracebackType
4+
from typing import Self
35

46
import pytest
57

68
from scripts.e2e_home_assistant import (
79
addon_options,
810
assetlink_sites,
11+
wait_for_http,
912
write_json,
1013
)
1114

1215

16+
class SuccessfulResponse:
17+
"""Minimal typed context manager matching the urllib response boundary."""
18+
19+
status = 200
20+
21+
def __enter__(self) -> Self:
22+
return self
23+
24+
def __exit__(
25+
self,
26+
exception_type: type[BaseException] | None,
27+
exception: BaseException | None,
28+
traceback: TracebackType | None,
29+
) -> None:
30+
return None
31+
32+
def read(self) -> bytes:
33+
return b"ready"
34+
35+
1336
def test_addon_options_constructs_independent_serializable_values() -> None:
1437
sites = ["https://ha.example.com"]
1538

@@ -49,3 +72,23 @@ def test_write_json_replaces_document_deterministically(tmp_path: Path) -> None:
4972

5073
assert destination.read_text(encoding="utf-8") == ('{\n "a": true,\n "z": 1\n}\n')
5174
assert not destination.with_suffix(".json.tmp").exists()
75+
76+
77+
def test_wait_for_http_retries_connection_reset(
78+
monkeypatch: pytest.MonkeyPatch,
79+
) -> None:
80+
attempts = 0
81+
82+
def open_with_startup_reset(url: str, *, timeout: float) -> SuccessfulResponse:
83+
nonlocal attempts
84+
del url, timeout
85+
attempts += 1
86+
if attempts == 1:
87+
raise ConnectionResetError("service is still starting")
88+
return SuccessfulResponse()
89+
90+
monkeypatch.setattr("urllib.request.urlopen", open_with_startup_reset)
91+
monkeypatch.setattr("time.sleep", lambda _seconds: None)
92+
93+
assert wait_for_http("http://home-assistant.test", timeout=1) == b"ready"
94+
assert attempts == 2

0 commit comments

Comments
 (0)