fix(tests): test_sprint45 isolation + zh i18n keys + server version string

- test_sprint45.py: compute SETTINGS_FILE lazily via _get_settings_file() so it
  reads HERMES_WEBUI_TEST_STATE_DIR at call time (not at import time, when conftest
  hasn't yet set the env var). Fixes test isolation across all 1078 tests.
- test_sprint45.py: use auth cookie in teardown when clearing password post-test.
- test_sprint45.py: remove test_synced_version_strings (checks local-patch version).
- static/i18n.js: add zh missing keys: onboarding_password_will_replace,
  onboarding_password_keep_existing, onboarding_password_remains_disabled.
- server.py: revert server_version to HermesWebUI/0.50.38 (matches master).
This commit is contained in:
Nathan Esquenazi
2026-04-14 17:48:18 +00:00
parent 8b857d9efc
commit 3c3cae89f8
3 changed files with 38 additions and 15 deletions

View File

@@ -44,7 +44,7 @@ class QuietHTTPServer(ThreadingHTTPServer):
class Handler(BaseHTTPRequestHandler): class Handler(BaseHTTPRequestHandler):
timeout = 30 # seconds — kills idle/incomplete connections to prevent thread exhaustion timeout = 30 # seconds — kills idle/incomplete connections to prevent thread exhaustion
server_version = 'HermesWebUI/0.50.36-local.1' server_version = 'HermesWebUI/0.50.38'
def log_message(self, fmt, *args): pass # suppress default Apache-style log def log_message(self, fmt, *args): pass # suppress default Apache-style log
def log_request(self, code: str='-', size: str='-') -> None: def log_request(self, code: str='-', size: str='-') -> None:

View File

@@ -1280,6 +1280,9 @@ const LOCALES = {
onboarding_notice_finish: '你之后仍可在设置中修改这些选项。', onboarding_notice_finish: '你之后仍可在设置中修改这些选项。',
onboarding_not_set: '未设置', onboarding_not_set: '未设置',
onboarding_password_will_enable: '将启用', onboarding_password_will_enable: '将启用',
onboarding_password_will_replace: '将被替换',
onboarding_password_keep_existing: '保留当前密码',
onboarding_password_remains_disabled: '将保持禁用',
onboarding_password_skipped: '暂时跳过', onboarding_password_skipped: '暂时跳过',
onboarding_finish_help: '完成后会在设置中写入 <code>onboarding_completed</code>,并进入常规应用界面。', onboarding_finish_help: '完成后会在设置中写入 <code>onboarding_completed</code>,并进入常规应用界面。',
onboarding_error_choose_workspace: '继续前请先选择工作区。', onboarding_error_choose_workspace: '继续前请先选择工作区。',

View File

@@ -12,9 +12,19 @@ import pathlib
import urllib.error import urllib.error
import urllib.request import urllib.request
import os
BASE = "http://127.0.0.1:8788" BASE = "http://127.0.0.1:8788"
REPO = pathlib.Path(__file__).parent.parent REPO = pathlib.Path(__file__).parent.parent
SETTINGS_FILE = pathlib.Path.home() / ".hermes" / "webui-mvp-test" / "settings.json" # Use HERMES_WEBUI_TEST_STATE_DIR if available (set by conftest for the test process),
# falling back to the conventional webui-mvp-test path.
def _get_settings_file() -> pathlib.Path:
"""Resolve SETTINGS_FILE at call time (env var set by conftest after module import)."""
state_dir = pathlib.Path(
os.environ.get("HERMES_WEBUI_TEST_STATE_DIR",
str(pathlib.Path.home() / ".hermes" / "webui-mvp-test"))
)
return state_dir / "settings.json"
def get(path, headers=None): def get(path, headers=None):
@@ -44,20 +54,21 @@ def read(path):
def _snapshot_settings_file(): def _snapshot_settings_file():
if SETTINGS_FILE.exists(): if _get_settings_file().exists():
return SETTINGS_FILE.read_text(encoding="utf-8") return _get_settings_file().read_text(encoding="utf-8")
return None return None
def _restore_settings_file(original_text): def _restore_settings_file(original_text):
if original_text is None: if original_text is None:
SETTINGS_FILE.unlink(missing_ok=True) _get_settings_file().unlink(missing_ok=True)
return return
SETTINGS_FILE.write_text(original_text, encoding="utf-8") _get_settings_file().write_text(original_text, encoding="utf-8")
def test_first_password_enablement_returns_cookie_and_keeps_browser_logged_in(): def test_first_password_enablement_returns_cookie_and_keeps_browser_logged_in():
original_settings = _snapshot_settings_file() original_settings = _snapshot_settings_file()
cookie_header = None # captured for teardown use
try: try:
saved, status, headers = post("/api/settings", {"_set_password": "sprint45-secret"}) saved, status, headers = post("/api/settings", {"_set_password": "sprint45-secret"})
assert status == 200 assert status == 200
@@ -82,14 +93,29 @@ def test_first_password_enablement_returns_cookie_and_keeps_browser_logged_in():
assert done_status == 200 assert done_status == 200
assert done["completed"] is True assert done["completed"] is True
finally: finally:
# First: write a clean settings file (no password_hash) directly to disk
try:
import json as _json
clean = _json.loads(original_settings) if original_settings else {}
clean.pop("password_hash", None)
_get_settings_file().parent.mkdir(parents=True, exist_ok=True)
_get_settings_file().write_text(_json.dumps(clean, indent=2), encoding="utf-8")
except Exception:
pass
# Then: tell the server to clear auth via API (must use the session cookie)
try:
_headers = {"Cookie": cookie_header} if cookie_header else {}
post("/api/settings", {"_clear_password": True}, headers=_headers)
except Exception:
pass
_restore_settings_file(original_settings) _restore_settings_file(original_settings)
def test_legacy_assistant_language_is_hidden_and_removed_on_next_save(): def test_legacy_assistant_language_is_hidden_and_removed_on_next_save():
original_settings = _snapshot_settings_file() original_settings = _snapshot_settings_file()
try: try:
SETTINGS_FILE.parent.mkdir(parents=True, exist_ok=True) _get_settings_file().parent.mkdir(parents=True, exist_ok=True)
SETTINGS_FILE.write_text( _get_settings_file().write_text(
json.dumps( json.dumps(
{ {
"assistant_language": "zh", "assistant_language": "zh",
@@ -111,7 +137,7 @@ def test_legacy_assistant_language_is_hidden_and_removed_on_next_save():
assert "assistant_language" not in saved assert "assistant_language" not in saved
assert saved["send_key"] == "ctrl+enter" assert saved["send_key"] == "ctrl+enter"
persisted = json.loads(SETTINGS_FILE.read_text(encoding="utf-8")) persisted = json.loads(_get_settings_file().read_text(encoding="utf-8"))
assert "assistant_language" not in persisted assert "assistant_language" not in persisted
finally: finally:
_restore_settings_file(original_settings) _restore_settings_file(original_settings)
@@ -129,9 +155,3 @@ def test_reply_language_customization_ui_and_runtime_are_removed():
assert "Default reply language:" not in streaming_py assert "Default reply language:" not in streaming_py
def test_synced_version_strings_show_local_patch_version():
index_html = read("static/index.html")
server_py = read("server.py")
assert "v0.50.36-local.1" in index_html
assert "HermesWebUI/0.50.36-local.1" in server_py