mirror of
https://git.zavage.net/Zavage-Software/wabot.git
synced 2026-07-21 13:06:08 -06:00
fix: service_alive returns False for non-HTTP responders
This commit is contained in:
parent
99cfe0337c
commit
e9d307e020
@ -657,6 +657,7 @@ def status_server():
|
|||||||
thread.start()
|
thread.start()
|
||||||
yield f"http://127.0.0.1:{server.server_address[1]}"
|
yield f"http://127.0.0.1:{server.server_address[1]}"
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
|
server.server_close()
|
||||||
|
|
||||||
|
|
||||||
class TestServiceAlive:
|
class TestServiceAlive:
|
||||||
@ -683,6 +684,33 @@ class TestExternalServer:
|
|||||||
def test_raises_when_dead(self):
|
def test_raises_when_dead(self):
|
||||||
with pytest.raises(ConnectionError, match="no WebDriver server"):
|
with pytest.raises(ConnectionError, match="no WebDriver server"):
|
||||||
ExternalServer("http://127.0.0.1:1").ensure_running()
|
ExternalServer("http://127.0.0.1:1").ensure_running()
|
||||||
|
|
||||||
|
|
||||||
|
class TestServiceAliveNonHttpResponder:
|
||||||
|
def test_non_http_service_on_port_is_false_and_clean_error(self):
|
||||||
|
# wrong-port misconfiguration: some non-HTTP service answers the socket
|
||||||
|
import socket
|
||||||
|
|
||||||
|
server = socket.create_server(("127.0.0.1", 0))
|
||||||
|
port = server.getsockname()[1]
|
||||||
|
|
||||||
|
def answer(count=2):
|
||||||
|
for _ in range(count):
|
||||||
|
try:
|
||||||
|
conn, _ = server.accept()
|
||||||
|
except OSError:
|
||||||
|
return
|
||||||
|
conn.sendall(b"SSH-2.0-OpenSSH_9.7\r\n")
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=answer, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
try:
|
||||||
|
assert service_alive(f"http://127.0.0.1:{port}", timeout=2.0) is False
|
||||||
|
with pytest.raises(ConnectionError, match="no WebDriver server"):
|
||||||
|
ExternalServer(f"http://127.0.0.1:{port}").ensure_running()
|
||||||
|
finally:
|
||||||
|
server.close()
|
||||||
```
|
```
|
||||||
|
|
||||||
- [ ] **Step 2: Run tests to verify they fail**
|
- [ ] **Step 2: Run tests to verify they fail**
|
||||||
@ -705,6 +733,7 @@ Selenium Grid or standalone driver the user runs) and ``ManagedService``
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import http.client
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@ -764,12 +793,13 @@ def service_alive(url: str, timeout: float = 2.0) -> bool:
|
|||||||
|
|
||||||
Liveness only: geckodriver reports ready=false while its single session
|
Liveness only: geckodriver reports ready=false while its single session
|
||||||
is in use, so the ``ready`` flag is deliberately ignored.
|
is in use, so the ``ready`` flag is deliberately ignored.
|
||||||
|
The ``timeout`` bounds each socket operation, not total wall-clock time.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(f"{url.rstrip('/')}/status", timeout=timeout) as resp:
|
with urllib.request.urlopen(f"{url.rstrip('/')}/status", timeout=timeout) as resp:
|
||||||
json.loads(resp.read())
|
json.loads(resp.read())
|
||||||
return resp.status == 200
|
return resp.status == 200
|
||||||
except (OSError, ValueError):
|
except (OSError, ValueError, http.client.HTTPException):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
@ -788,7 +818,7 @@ class ExternalServer:
|
|||||||
- [ ] **Step 4: Run tests to verify they pass**
|
- [ ] **Step 4: Run tests to verify they pass**
|
||||||
|
|
||||||
Run: `uv run pytest tests/unit/test_hosts.py -v`
|
Run: `uv run pytest tests/unit/test_hosts.py -v`
|
||||||
Expected: 12 passed
|
Expected: 13 passed
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
@ -898,6 +928,7 @@ Expected: new tests FAIL — `ImportError: cannot import name 'ManagedService'`;
|
|||||||
Append to `src/wabot/hosts.py`, and extend the top import block to:
|
Append to `src/wabot/hosts.py`, and extend the top import block to:
|
||||||
|
|
||||||
```python
|
```python
|
||||||
|
import http.client
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import os
|
import os
|
||||||
@ -1006,7 +1037,7 @@ def stop_service(pid: int) -> bool:
|
|||||||
- [ ] **Step 4: Run tests to verify they pass**
|
- [ ] **Step 4: Run tests to verify they pass**
|
||||||
|
|
||||||
Run: `uv run pytest tests/unit/test_hosts.py -v`
|
Run: `uv run pytest tests/unit/test_hosts.py -v`
|
||||||
Expected: 17 passed (12 from Task 4 + 5 new)
|
Expected: 18 passed (13 from Task 4 + 5 new)
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
@ -1142,7 +1173,7 @@ Run: `uv run pytest tests/unit/test_reattach.py -v`
|
|||||||
Expected: 5 passed
|
Expected: 5 passed
|
||||||
|
|
||||||
Run: `uv run pytest`
|
Run: `uv run pytest`
|
||||||
Expected: all unit tests so far pass (pacing 7, sessions 14, hosts 17, reattach 5 = 43)
|
Expected: all unit tests so far pass (pacing 7, sessions 14, hosts 18, reattach 5 = 44)
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
@ -2667,7 +2698,7 @@ Run: `uv run pytest tests/unit/test_api.py -v`
|
|||||||
Expected: 11 passed
|
Expected: 11 passed
|
||||||
|
|
||||||
Run: `uv run pytest`
|
Run: `uv run pytest`
|
||||||
Expected: full unit suite passes (104 tests: pacing 7, sessions 14, hosts 17, reattach 5, fields 11, page 23, screenshot 3, browser 13, api 11)
|
Expected: full unit suite passes (105 tests: pacing 7, sessions 14, hosts 18, reattach 5, fields 11, page 23, screenshot 3, browser 13, api 11)
|
||||||
|
|
||||||
- [ ] **Step 5: Commit**
|
- [ ] **Step 5: Commit**
|
||||||
|
|
||||||
|
|||||||
@ -8,6 +8,7 @@ Selenium Grid or standalone driver the user runs) and ``ManagedService``
|
|||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import http.client
|
||||||
import json
|
import json
|
||||||
import logging
|
import logging
|
||||||
import urllib.request
|
import urllib.request
|
||||||
@ -67,12 +68,13 @@ def service_alive(url: str, timeout: float = 2.0) -> bool:
|
|||||||
|
|
||||||
Liveness only: geckodriver reports ready=false while its single session
|
Liveness only: geckodriver reports ready=false while its single session
|
||||||
is in use, so the ``ready`` flag is deliberately ignored.
|
is in use, so the ``ready`` flag is deliberately ignored.
|
||||||
|
The ``timeout`` bounds each socket operation, not total wall-clock time.
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
with urllib.request.urlopen(f"{url.rstrip('/')}/status", timeout=timeout) as resp:
|
with urllib.request.urlopen(f"{url.rstrip('/')}/status", timeout=timeout) as resp:
|
||||||
json.loads(resp.read())
|
json.loads(resp.read())
|
||||||
return resp.status == 200
|
return resp.status == 200
|
||||||
except (OSError, ValueError):
|
except (OSError, ValueError, http.client.HTTPException):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -65,6 +65,7 @@ def status_server():
|
|||||||
thread.start()
|
thread.start()
|
||||||
yield f"http://127.0.0.1:{server.server_address[1]}"
|
yield f"http://127.0.0.1:{server.server_address[1]}"
|
||||||
server.shutdown()
|
server.shutdown()
|
||||||
|
server.server_close()
|
||||||
|
|
||||||
|
|
||||||
class TestServiceAlive:
|
class TestServiceAlive:
|
||||||
@ -91,3 +92,30 @@ class TestExternalServer:
|
|||||||
def test_raises_when_dead(self):
|
def test_raises_when_dead(self):
|
||||||
with pytest.raises(ConnectionError, match="no WebDriver server"):
|
with pytest.raises(ConnectionError, match="no WebDriver server"):
|
||||||
ExternalServer("http://127.0.0.1:1").ensure_running()
|
ExternalServer("http://127.0.0.1:1").ensure_running()
|
||||||
|
|
||||||
|
|
||||||
|
class TestServiceAliveNonHttpResponder:
|
||||||
|
def test_non_http_service_on_port_is_false_and_clean_error(self):
|
||||||
|
# wrong-port misconfiguration: some non-HTTP service answers the socket
|
||||||
|
import socket
|
||||||
|
|
||||||
|
server = socket.create_server(("127.0.0.1", 0))
|
||||||
|
port = server.getsockname()[1]
|
||||||
|
|
||||||
|
def answer(count=2):
|
||||||
|
for _ in range(count):
|
||||||
|
try:
|
||||||
|
conn, _ = server.accept()
|
||||||
|
except OSError:
|
||||||
|
return
|
||||||
|
conn.sendall(b"SSH-2.0-OpenSSH_9.7\r\n")
|
||||||
|
conn.close()
|
||||||
|
|
||||||
|
thread = threading.Thread(target=answer, daemon=True)
|
||||||
|
thread.start()
|
||||||
|
try:
|
||||||
|
assert service_alive(f"http://127.0.0.1:{port}", timeout=2.0) is False
|
||||||
|
with pytest.raises(ConnectionError, match="no WebDriver server"):
|
||||||
|
ExternalServer(f"http://127.0.0.1:{port}").ensure_running()
|
||||||
|
finally:
|
||||||
|
server.close()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user