diff --git a/deploy/check_home_seo.py b/deploy/check_home_seo.py
index 07ba705f..c585b3cf 100644
--- a/deploy/check_home_seo.py
+++ b/deploy/check_home_seo.py
@@ -1,6 +1,5 @@
"""Verify homepage metadata without changing Markdown or non-home resources."""
-from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
-from pathlib import Path
+
import socket
import subprocess
import tempfile
@@ -8,9 +7,13 @@
import time
import urllib.error
import urllib.request
+from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer
+from pathlib import Path
HTML = b'
msgPublic home'
MARKDOWN = b'# msg\n\nAgent-readable public home.\n'
+
+
class Backend(BaseHTTPRequestHandler):
def do_GET(self):
browser = 'text/html' in self.headers.get('Accept', '')
@@ -19,41 +22,58 @@ def do_GET(self):
self.send_header('Content-Security-Policy', "default-src 'none'")
self.end_headers()
self.wfile.write(HTML if browser else MARKDOWN)
+
def log_message(self, *args):
pass
+
+
backend = ThreadingHTTPServer(('127.0.0.1', 0), Backend)
thread = threading.Thread(target=backend.serve_forever, daemon=True)
thread.start()
with socket.socket() as sock:
sock.bind(('127.0.0.1', 0))
port = sock.getsockname()[1]
+
+
def get(path, accept):
- with urllib.request.urlopen(urllib.request.Request(
- f'http://127.0.0.1:{port}{path}', headers={'Accept': accept}), timeout=3) as response:
+ with urllib.request.urlopen(
+ urllib.request.Request(f'http://127.0.0.1:{port}{path}', headers={'Accept': accept}),
+ timeout=3,
+ ) as response:
return response.headers, response.read()
+
+
try:
with tempfile.TemporaryDirectory(prefix='msg-home-seo-') as temp:
directory = Path(temp)
conf = directory / 'nginx.conf'
snippet = Path(__file__).with_name('nginx-home-seo.conf').read_text()
- conf.write_text(f'pid {directory}/nginx.pid; error_log {directory}/error.log; daemon off; master_process off; events {{}}\n'
- 'http { access_log off;\n'
- + ''.join(f'{kind}_temp_path {directory}/{kind};\n' for kind in ['client_body','proxy','fastcgi','uwsgi','scgi'])
- + f'upstream msg_lmm_best {{ server 127.0.0.1:{backend.server_port}; }}\n'
- 'map $uri $msg_browser_cache_control { default "private, no-cache"; }\n'
- 'map $uri $msg_browser_referrer_policy { default "no-referrer"; }\n'
- + f'server {{ listen 127.0.0.1:{port};\n' + snippet
- + 'location / { proxy_pass http://msg_lmm_best; }\n}}\n')
- process = subprocess.Popen(['nginx', '-p', temp, '-c', str(conf)], stdout=subprocess.PIPE, stderr=subprocess.PIPE)
+ conf.write_text(
+ f'pid {directory}/nginx.pid; error_log {directory}/error.log; daemon off; master_process off; events {{}}\n'
+ 'http { access_log off;\n'
+ + ''.join(
+ f'{kind}_temp_path {directory}/{kind};\n'
+ for kind in ['client_body', 'proxy', 'fastcgi', 'uwsgi', 'scgi']
+ )
+ + f'upstream msg_lmm_best {{ server 127.0.0.1:{backend.server_port}; }}\n'
+ 'map $uri $msg_browser_cache_control { default "private, no-cache"; }\n'
+ 'map $uri $msg_browser_referrer_policy { default "no-referrer"; }\n'
+ + f'server {{ listen 127.0.0.1:{port};\n'
+ + snippet
+ + 'location / { proxy_pass http://msg_lmm_best; }\n}}\n'
+ )
+ process = subprocess.Popen(
+ ['nginx', '-p', temp, '-c', str(conf)], stdout=subprocess.PIPE, stderr=subprocess.PIPE
+ )
try:
- for attempt in range(40):
+ for _attempt in range(40):
try:
get('/', 'text/html')
break
except urllib.error.URLError:
if process.poll() is not None:
- raise RuntimeError(process.communicate()[1].decode())
- time.sleep(.05)
+ raise RuntimeError(process.communicate()[1].decode()) from None
+ time.sleep(0.05)
else:
raise RuntimeError('Nginx did not start')
headers, body = get('/', 'text/html')
@@ -64,7 +84,9 @@ def get(path, accept):
headers, body = get('/', 'text/markdown')
assert body == MARKDOWN and headers['Content-Type'].startswith('text/markdown')
assert get('/another-resource', 'text/html')[1] == HTML
- print('HTML metadata, canonical header, CSP/cache, unchanged Markdown and other resources passed')
+ print(
+ 'HTML metadata, canonical header, CSP/cache, unchanged Markdown and other resources passed'
+ )
finally:
if process.poll() is None:
process.terminate()
diff --git a/pyproject.toml b/pyproject.toml
index 96b4316c..b5702fbd 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -66,6 +66,7 @@ testpaths = ["tests"]
pythonpath = ["src"]
markers = ["db: 需要真实 PostgreSQL,包含 fixture 依赖和测试内自建集群"]
asyncio_mode = "auto"
+asyncio_default_fixture_loop_scope = "function"
filterwarnings = ["error"]
[tool.ruff]
diff --git a/src/msg/admin/recovery_proof.py b/src/msg/admin/recovery_proof.py
index 2b81c8ce..3787043d 100644
--- a/src/msg/admin/recovery_proof.py
+++ b/src/msg/admin/recovery_proof.py
@@ -564,7 +564,7 @@ async def open_for_proof(config_dir):
decode(Certificate, trust['certificate']),
unb64(trust['public_key'], limit=32),
settings.service_url,
- app.clock,
+ lambda: app.clock(),
)
from msg.admin.backups import _regular_tree
diff --git a/src/msg/application.py b/src/msg/application.py
index 78c8656f..760e79a4 100644
--- a/src/msg/application.py
+++ b/src/msg/application.py
@@ -117,7 +117,11 @@ async def load(self):
recovery_window=lambda: self.settings.credential_delivery_recovery_window,
)
self.certificates = CertificateValidator(
- self.registry, root_certificate, root_public, self.settings.service_url, self.clock
+ self.registry,
+ root_certificate,
+ root_public,
+ self.settings.service_url,
+ lambda: self.clock(),
)
async with self.metadata.transaction(write=False) as tx:
self.runtime_generation.require_current(tx)
diff --git a/tests/conftest.py b/tests/conftest.py
index 5090732f..f5214c84 100644
--- a/tests/conftest.py
+++ b/tests/conftest.py
@@ -6,6 +6,7 @@
import psycopg
import pytest
+import pytest_asyncio
from psycopg import sql
from msg.admin.root import _approve_csr, _provision
@@ -39,7 +40,7 @@ def _drop_database(cluster, name):
connection.execute(sql.SQL('DROP DATABASE {} WITH (FORCE)').format(sql.Identifier(name)))
-@pytest.fixture(scope='session')
+@pytest_asyncio.fixture(scope='session', loop_scope='session')
async def installation_seed(tmp_path_factory, pg_cluster):
directory = tmp_path_factory.mktemp('msg-seed')
database = 'msg_seed_' + uuid.uuid4().hex