From 22aff48329f47cd40f9f3fc98049c8ebc76ff042 Mon Sep 17 00:00:00 2001 From: xiami762 <> Date: Fri, 3 Jul 2026 11:50:01 +0800 Subject: [PATCH] test: add manual branch upgrade flow --- scripts/dev.sh | 4 + tests/server/routes/test_update_routes.py | 119 ++++++++++++++++++++++ 2 files changed, 123 insertions(+) diff --git a/scripts/dev.sh b/scripts/dev.sh index 8a7554c24..80474efb6 100644 --- a/scripts/dev.sh +++ b/scripts/dev.sh @@ -217,6 +217,10 @@ start_backend() { } start_frontend() { + echo -e "${GREEN}πŸ—οΈ ζž„ε»Ίε‰η«―...${NC}" + cd "${PROJECT_ROOT}/webui" + npm run build + echo -e "${GREEN}🎨 ε―εŠ¨ε‰η«―ζœεŠ‘: http://${FRONTEND_HOST}:${FRONTEND_PORT}${NC}" cd "${PROJECT_ROOT}/webui" VITE_API_BASE_URL="${BACKEND_BASE_URL}" \ diff --git a/tests/server/routes/test_update_routes.py b/tests/server/routes/test_update_routes.py index f673fca72..8be953356 100644 --- a/tests/server/routes/test_update_routes.py +++ b/tests/server/routes/test_update_routes.py @@ -1,5 +1,8 @@ from __future__ import annotations +import os +from urllib.parse import quote, unquote, urlparse + import pytest from fastapi import HTTPException, status from starlette.requests import Request @@ -7,11 +10,80 @@ pytestmark = pytest.mark.asyncio +_MANUAL_REAL_UPGRADE_ENV = "FLOCKS_RUN_REAL_WEBUI_UPGRADE_TEST" +_MANUAL_REAL_UPGRADE_BRANCH_ENV = "FLOCKS_REAL_WEBUI_UPGRADE_BRANCH" +_MANUAL_REAL_UPGRADE_TARGET_BRANCH = "" + def _request() -> Request: return Request({"type": "http", "method": "GET", "path": "/api/update/check", "headers": []}) +def _manual_real_upgrade_branch() -> str: + branch_input = ( + os.environ.get(_MANUAL_REAL_UPGRADE_BRANCH_ENV, "").strip() + or _MANUAL_REAL_UPGRADE_TARGET_BRANCH.strip() + ) + try: + if not branch_input: + branch_input = input("Target branch for the real WebUI upgrade test: ").strip() + if not branch_input: + pytest.skip("No target branch was provided for the real WebUI upgrade test") + + confirmation = input( + "This will trigger a real upgrade and may replace the current install tree. " + f"Type the branch name again to confirm ({branch_input}): " + ).strip() + except OSError as exc: + pytest.skip(f"Interactive confirmation is required: {exc}") + + if confirmation != branch_input: + pytest.skip("Real WebUI upgrade test was not confirmed") + return _normalize_manual_branch_target(branch_input) + + +def _normalize_manual_branch_target(target: str) -> str: + branch = target.strip() + parsed = urlparse(branch) + if parsed.scheme in {"http", "https"}: + path = parsed.path + github_marker = "/archive/refs/heads/" + gitee_marker = "/repository/archive/" + if github_marker in path: + branch = path.split(github_marker, 1)[1] + elif gitee_marker in path: + branch = path.split(gitee_marker, 1)[1] + branch = branch.removesuffix(".tar.gz").removesuffix(".zip") + branch = unquote(branch) + + for prefix in ("refs/heads/",): + if branch.startswith(prefix): + branch = branch[len(prefix):] + break + + if not branch: + pytest.skip("No target branch was provided for the real WebUI upgrade test") + return branch + + +def _manual_branch_version_label(branch: str) -> str: + return "branch-" + branch.replace("/", "-") + + +def _github_branch_archive_url(branch: str, extension: str) -> str: + encoded_branch = quote(branch, safe="/") + return f"https://github.com/AgentFlocks/flocks/archive/refs/heads/{encoded_branch}.{extension}" + + +async def test_normalize_manual_branch_target_accepts_archive_urls(): + assert _normalize_manual_branch_target( + "https://gitee.com/flocks/flocks/repository/archive/refactor/supervisor-control-adapters.zip" + ) == "refactor/supervisor-control-adapters" + assert _normalize_manual_branch_target( + "https://github.com/AgentFlocks/flocks/archive/refs/heads/fix/session-mixed-parts-read-merge.zip" + ) == "fix/session-mixed-parts-read-merge" + + async def test_check_version_requires_admin_for_flockspro(monkeypatch: pytest.MonkeyPatch): from flocks.server.routes import update as update_routes @@ -52,3 +124,50 @@ async def _fake_check_update(**kwargs): info = await update_routes.check_version(_request(), locale="zh-CN", edition="flocks") assert info.current_version == "v2026.5.9" + + +@pytest.mark.skipif( + os.environ.get(_MANUAL_REAL_UPGRADE_ENV) != "1", + reason=f"manual real upgrade test; set {_MANUAL_REAL_UPGRADE_ENV}=1 to enable", +) +async def test_manual_webui_apply_update_upgrades_to_confirmed_branch( + client, + monkeypatch: pytest.MonkeyPatch, +): + from flocks.config.config import UpdaterConfig + from flocks.server.routes import update as update_routes + from flocks.updater import updater as updater_module + from flocks.updater.models import VersionInfo + + branch = _manual_real_upgrade_branch() + version_label = _manual_branch_version_label(branch) + + async def _manual_github_updater_config(): + return UpdaterConfig( + repo="AgentFlocks/flocks", + gitee_repo=None, + sources=["github"], + archive_format="zip", + ) + + async def _manual_branch_update_info(**kwargs): + assert kwargs["force_console_manifest"] is False + return VersionInfo( + current_version="manual-real-upgrade-test", + latest_version=version_label, + has_update=True, + release_url=f"https://github.com/AgentFlocks/flocks/tree/{quote(branch, safe='/')}", + zipball_url=_github_branch_archive_url(branch, "zip"), + tarball_url=_github_branch_archive_url(branch, "tar.gz"), + ) + + monkeypatch.setattr(update_routes, "check_update", _manual_branch_update_info) + monkeypatch.setattr(updater_module, "_get_updater_config", _manual_github_updater_config) + + response = await client.post( + "/api/update/apply", + params={"edition": "flocks"}, + ) + + assert response.status_code == 200, response.text + assert '"stage":"error"' not in response.text