aboutsummaryrefslogtreecommitdiff
path: root/lib/project/tests/test_gui.py
diff options
context:
space:
mode:
Diffstat (limited to 'lib/project/tests/test_gui.py')
-rw-r--r--lib/project/tests/test_gui.py31
1 files changed, 31 insertions, 0 deletions
diff --git a/lib/project/tests/test_gui.py b/lib/project/tests/test_gui.py
index e490e99..2f17549 100644
--- a/lib/project/tests/test_gui.py
+++ b/lib/project/tests/test_gui.py
@@ -1,5 +1,6 @@
"""Optional GUI API, origin boundary, uploads, snapshots, and real sound previews."""
from dataclasses import asdict
+import socket
import hashlib
from pathlib import Path
import struct
@@ -13,6 +14,7 @@ pytest.importorskip("fastapi")
from fastapi.testclient import TestClient
from producer.config import Settings
+from producer import gui as gui_module
from producer.gui import create_app
from producer import gui_jobs
@@ -280,3 +282,32 @@ def test_waveform_buffers_bounded_and_scan_cancellable(wav, monkeypatch):
with pytest.raises(gui_jobs.Cancelled):
gui_jobs.waveform(source, cancel)
assert reads == [65536]
+
+
+def test_serve_listener_allows_quick_restart(monkeypatch):
+ """serve() must set SO_REUSEADDR: without it a restart right after Ctrl+C
+ fails while connections the server closed first sit in TIME_WAIT."""
+ captured = {}
+
+ class FakeServer:
+ def __init__(self, config):
+ pass
+ def run(self, sockets):
+ captured["sockets"] = sockets
+ (sock,) = sockets
+ # Sample the state while serve() still holds the socket open.
+ captured["reuseaddr"] = sock.getsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR)
+ captured["address"] = sock.getsockname()
+
+ class FakeConfig:
+ def __init__(self, *args, **kwargs):
+ pass
+
+ class FakeUvicorn:
+ Server, Config = FakeServer, FakeConfig
+
+ monkeypatch.setitem(__import__("sys").modules, "uvicorn", FakeUvicorn)
+ monkeypatch.setattr(gui_module, "create_app", lambda *a, **k: object())
+ gui_module.serve(18765)
+ assert captured["reuseaddr"] == 1
+ assert captured["address"] == ("127.0.0.1", 18765)