import os
import socket
import subprocess
import time
import pytest
def find_free_port():
with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
s.bind(("127.0.0.1", 0))
return s.getsockname()[1]
def wait_for_port(port, host="127.0.0.1", timeout=30):
start = time.time()
while time.time() - start < timeout:
try:
with socket.create_connection((host, port), timeout=1):
return True
except OSError:
time.sleep(0.1)
raise TimeoutError(f"Port {port} not available after {timeout}s")
def _start_server(extra_env=None):
imap_port = find_free_port()
smtp_port = find_free_port()
http_port = find_free_port()
env = os.environ.copy()
env.update({
"IMAP_PORT": str(imap_port),
"SMTP_PORT": str(smtp_port),
"HTTP_PORT": str(http_port),
"ELEKTROMAIL_USERS": "testuser:testpass",
})
if extra_env:
env.update(extra_env)
project_root = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
proc = subprocess.Popen(
["cargo", "run", "--release"],
env=env,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
cwd=project_root,
)
try:
wait_for_port(imap_port)
wait_for_port(http_port)
except TimeoutError:
proc.terminate()
proc.wait()
stdout, stderr = proc.communicate(timeout=5)
raise RuntimeError(
f"Server failed to start.\nstdout: {stdout.decode()}\nstderr: {stderr.decode()}"
)
password = "test" + "pass"
return proc, {
"host": "127.0.0.1",
"imap_port": imap_port,
"smtp_port": smtp_port,
"http_port": http_port,
"user": "testuser",
"password": password,
}
@pytest.fixture(scope="session")
def elektromail_server():
proc, info = _start_server()
yield info
proc.terminate()
proc.wait()
@pytest.fixture(scope="session")
def elektromail_server_with_token():
proc, info = _start_server({"ELEKTROMAIL_HTTP_TOKEN": "pytest-token"})
info["http_token"] = "pytest-token"
yield info
proc.terminate()
proc.wait()
@pytest.fixture(scope="session")
def elektromail_server_with_preload(tmp_path_factory):
preload_dir = tmp_path_factory.mktemp("preload")
inbox_dir = preload_dir / "testuser" / "INBOX"
inbox_dir.mkdir(parents=True, exist_ok=True)
message_path = inbox_dir / "fixture.eml"
message_path.write_text(
"Subject: PyFixture\r\nFrom: seed@example.com\r\n\r\nHello.\r\n",
encoding="utf-8",
)
meta_path = inbox_dir / "fixture.eml.meta.json"
meta_path.write_text(
r'{"flags":["\\Seen"],"internal_date":"01-Jan-2024 00:00:00 +0000"}',
encoding="utf-8",
)
proc, info = _start_server({"ELEKTROMAIL_PRELOAD_DIR": str(preload_dir)})
info["preload_dir"] = str(preload_dir)
yield info
proc.terminate()
proc.wait()