Merge pull request 'Web UI: Test coverage and CI checks (#436)' (#457) from feat/issue-436-ci-coverage into master
This commit was merged in pull request #457.
This commit is contained in:
@@ -54,6 +54,25 @@ Use `-q` for a compact summary and `-v` to see individual test names.
|
||||
./venv/bin/python -m pytest tests/ -q
|
||||
```
|
||||
|
||||
### Web UI suite (#436)
|
||||
|
||||
Hermetic unittest modules matching `test_webui_*.py` cover route rendering,
|
||||
read-only guards, registry/prompt/queue loaders, and optional child-issue
|
||||
modules when present on the branch.
|
||||
|
||||
```bash
|
||||
./scripts/test-webui
|
||||
./scripts/ci-webui-check # skip unless the diff touches web UI paths
|
||||
WEBUI_CI_FORCE=1 ./scripts/ci-webui-check
|
||||
```
|
||||
|
||||
`scripts/test-webui` defaults `WEBUI_TEST_OFFLINE=1` so route coverage never
|
||||
needs Gitea credentials or MCP daemon credential access. Set
|
||||
`WEBUI_TEST_OFFLINE=0` only for explicit operator live-fetch checks.
|
||||
|
||||
Wire `scripts/ci-webui-check` into Jenkins (or equivalent) for PRs that touch
|
||||
`webui/`, `tests/test_webui_*`, or `docs/webui*`.
|
||||
|
||||
### Run targeted tests
|
||||
|
||||
```bash
|
||||
|
||||
+51
-1
@@ -155,4 +155,54 @@ pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/t
|
||||
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_lease_visibility.py tests/test_webui_runtime_health.py -q
|
||||
|
||||
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_deployment_boundary.py -q
|
||||
```
|
||||
|
||||
## Tests (#436)
|
||||
|
||||
Run the full hermetic web UI suite (all `test_webui_*.py` modules):
|
||||
|
||||
```bash
|
||||
./scripts/test-webui
|
||||
```
|
||||
|
||||
CI / Jenkins multibranch can call the path-filtered gate (runs only when the
|
||||
diff touches `webui/`, `tests/test_webui_*`, or web UI docs/scripts):
|
||||
|
||||
```bash
|
||||
./scripts/ci-webui-check
|
||||
WEBUI_CI_FORCE=1 ./scripts/ci-webui-check # always run
|
||||
```
|
||||
|
||||
`scripts/test-webui` sets `WEBUI_TEST_OFFLINE=1` by default. In that mode the
|
||||
queue, lease, and runtime routes use empty offline snapshots instead of Gitea
|
||||
credentials, so CI can run without MCP daemon credential access. Set
|
||||
`WEBUI_TEST_OFFLINE=0` only when deliberately validating live fetch behavior.
|
||||
|
||||
Or invoke unittest directly:
|
||||
|
||||
```bash
|
||||
python3 -m unittest discover -s tests -p 'test_webui_*.py' -q
|
||||
```
|
||||
|
||||
## Lease visibility (#433)
|
||||
|
||||
`/leases` surfaces read-only lease and collision state: local issue lock file,
|
||||
in-progress claim inventory (#268), reviewer PR lease comments when present
|
||||
(`<!-- mcp-review-lease:v1 -->`, #407), duplicate open PRs per issue (#400),
|
||||
and duplicate local branches per issue. Links to collision-history backend
|
||||
issues (#267, #268, #400, #407) are included. No lease acquire/release from UI.
|
||||
|
||||
## Runtime health (#430)
|
||||
|
||||
`/runtime` surfaces read-only MCP/runtime diagnostics for the default registry
|
||||
project: active profile and role kind, authenticated identity (when credentials
|
||||
are available), config model/mode, local vs remote `master` SHA sync, shell
|
||||
health, workflow/schema SHA-256 hashes, and stale-runtime warnings when the
|
||||
checkout is behind merged safety-gate changes. Restart guidance links to #420;
|
||||
no tokens or MCP restart actions are exposed.
|
||||
|
||||
## Tests
|
||||
|
||||
```bash
|
||||
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_audit.py tests/test_webui_worktree_hygiene.py -q
|
||||
pytest tests/test_webui_skeleton.py tests/test_webui_project_registry.py tests/test_webui_prompt_library.py tests/test_webui_queue_dashboard.py tests/test_webui_lease_visibility.py tests/test_webui_runtime_health.py -q
|
||||
```
|
||||
|
||||
Executable
+53
@@ -0,0 +1,53 @@
|
||||
#!/usr/bin/env bash
|
||||
# Path-filtered CI gate for the internal web UI (#436).
|
||||
# Runs the hermetic web UI unittest suite when a change touches UI surfaces.
|
||||
set -euo pipefail
|
||||
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "$script_dir/.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
if [[ "${WEBUI_CI_FORCE:-}" == "1" ]]; then
|
||||
exec "$script_dir/test-webui"
|
||||
fi
|
||||
|
||||
base_ref="${WEBUI_CI_BASE_REF:-}"
|
||||
if [[ -z "$base_ref" ]]; then
|
||||
if git rev-parse --verify prgs/master >/dev/null 2>&1; then
|
||||
base_ref="$(git merge-base HEAD prgs/master 2>/dev/null || true)"
|
||||
fi
|
||||
if [[ -z "$base_ref" ]]; then
|
||||
base_ref="${GITHUB_BASE_REF:-${CHANGE_TARGET:-master}}"
|
||||
if git rev-parse --verify "origin/$base_ref" >/dev/null 2>&1; then
|
||||
base_ref="$(git merge-base HEAD "origin/$base_ref")"
|
||||
elif git rev-parse --verify "$base_ref" >/dev/null 2>&1; then
|
||||
base_ref="$(git merge-base HEAD "$base_ref")"
|
||||
else
|
||||
base_ref="HEAD~1"
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
|
||||
changed="$(git diff --name-only "$base_ref" HEAD 2>/dev/null || true)"
|
||||
if [[ -z "$changed" ]]; then
|
||||
echo "ci-webui-check: no changed files vs $base_ref; skipping web UI suite"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
python_bin="${WEBUI_TEST_PYTHON:-python3}"
|
||||
if [[ -x "$repo_root/venv/bin/python" ]]; then
|
||||
python_bin="$repo_root/venv/bin/python"
|
||||
fi
|
||||
|
||||
if printf '%s\n' "$changed" | "$python_bin" -c "
|
||||
import sys
|
||||
from webui.ci_paths import should_run_webui_ci
|
||||
paths = [line.strip() for line in sys.stdin if line.strip()]
|
||||
raise SystemExit(0 if should_run_webui_ci(paths) else 1)
|
||||
"; then
|
||||
echo "ci-webui-check: web UI surface changed; running suite"
|
||||
exec "$script_dir/test-webui"
|
||||
fi
|
||||
|
||||
echo "ci-webui-check: no web UI paths in diff; skipping suite"
|
||||
exit 0
|
||||
Executable
+14
@@ -0,0 +1,14 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
repo_root="$(cd "$script_dir/.." && pwd)"
|
||||
cd "$repo_root"
|
||||
|
||||
python_bin="${WEBUI_TEST_PYTHON:-python3}"
|
||||
if [[ -x "$repo_root/venv/bin/python" ]]; then
|
||||
python_bin="$repo_root/venv/bin/python"
|
||||
fi
|
||||
|
||||
pattern="${WEBUI_TEST_PATTERN:-test_webui_*.py}"
|
||||
export WEBUI_TEST_OFFLINE="${WEBUI_TEST_OFFLINE:-1}"
|
||||
exec "$python_bin" -m unittest discover -s tests -p "$pattern" "$@"
|
||||
@@ -0,0 +1,90 @@
|
||||
"""Tests for web UI CI path-filter gate (#436)."""
|
||||
import os
|
||||
import subprocess
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
from unittest import mock
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from webui.ci_paths import should_run_webui_ci, touches_webui_surface
|
||||
from webui.lease_loader import load_lease_snapshot
|
||||
from webui.queue_loader import load_queue_snapshot
|
||||
from webui.runtime_health import load_runtime_snapshot
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
|
||||
class TestCiPathMatching(unittest.TestCase):
|
||||
def test_positive_paths(self):
|
||||
for path in (
|
||||
"webui/app.py",
|
||||
"tests/test_webui_skeleton.py",
|
||||
"docs/webui-local-dev.md",
|
||||
"scripts/test-webui",
|
||||
):
|
||||
with self.subTest(path=path):
|
||||
self.assertTrue(touches_webui_surface(path))
|
||||
|
||||
def test_negative_paths(self):
|
||||
for path in ("mcp_server.py", "tests/test_mcp_server.py", "README.md"):
|
||||
with self.subTest(path=path):
|
||||
self.assertFalse(touches_webui_surface(path))
|
||||
|
||||
def test_should_run_aggregate(self):
|
||||
self.assertTrue(should_run_webui_ci(["README.md", "webui/layout.py"]))
|
||||
self.assertFalse(should_run_webui_ci(["mcp_server.py", "gitea_auth.py"]))
|
||||
|
||||
|
||||
class TestCiRunnerScript(unittest.TestCase):
|
||||
def test_test_webui_smoke(self):
|
||||
script = _REPO_ROOT / "scripts" / "test-webui"
|
||||
result = subprocess.run(
|
||||
["bash", str(script), "-q"],
|
||||
cwd=_REPO_ROOT,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=60,
|
||||
env={
|
||||
**os.environ,
|
||||
"WEBUI_TEST_PATTERN": "test_webui_skeleton.py",
|
||||
},
|
||||
)
|
||||
self.assertEqual(result.returncode, 0, result.stderr or result.stdout)
|
||||
|
||||
|
||||
class TestOfflineWebuiCiMode(unittest.TestCase):
|
||||
def test_offline_mode_avoids_live_queue_auth(self):
|
||||
with mock.patch.dict(os.environ, {"WEBUI_TEST_OFFLINE": "1"}):
|
||||
with mock.patch(
|
||||
"webui.queue_loader.get_auth_header",
|
||||
side_effect=AssertionError("live auth should not run"),
|
||||
):
|
||||
snapshot = load_queue_snapshot()
|
||||
self.assertIsNone(snapshot.fetch_error)
|
||||
self.assertEqual(snapshot.prs, ())
|
||||
self.assertEqual(snapshot.issues, ())
|
||||
|
||||
def test_offline_mode_avoids_live_lease_auth(self):
|
||||
with mock.patch.dict(os.environ, {"WEBUI_TEST_OFFLINE": "1"}):
|
||||
with mock.patch(
|
||||
"webui.lease_loader.get_auth_header",
|
||||
side_effect=AssertionError("live auth should not run"),
|
||||
):
|
||||
snapshot = load_lease_snapshot()
|
||||
self.assertIsNone(snapshot.fetch_error)
|
||||
self.assertEqual(snapshot.reviewer_leases, ())
|
||||
|
||||
def test_offline_mode_avoids_live_runtime_identity(self):
|
||||
with mock.patch.dict(os.environ, {"WEBUI_TEST_OFFLINE": "1"}):
|
||||
with mock.patch(
|
||||
"webui.runtime_health.get_auth_header",
|
||||
side_effect=AssertionError("live auth should not run"),
|
||||
):
|
||||
snapshot = load_runtime_snapshot()
|
||||
self.assertEqual(snapshot.identity_error, "offline web UI test mode")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -0,0 +1,156 @@
|
||||
"""Consolidated MVP coverage for the internal web UI (#436)."""
|
||||
from __future__ import annotations
|
||||
|
||||
import importlib
|
||||
import os
|
||||
import sys
|
||||
import unittest
|
||||
from pathlib import Path
|
||||
|
||||
sys.path.insert(0, str(Path(__file__).resolve().parent.parent))
|
||||
|
||||
from starlette.testclient import TestClient
|
||||
from starlette.routing import Route
|
||||
|
||||
from webui.app import create_app
|
||||
|
||||
_REPO_ROOT = Path(__file__).resolve().parent.parent
|
||||
|
||||
# HTML pages and JSON exports registered on master MVP.
|
||||
MVP_GET_ROUTES: tuple[tuple[str, str | tuple[str, ...]], ...] = (
|
||||
("/", "Operator console"),
|
||||
("/health", "mcp-control-plane-webui"),
|
||||
("/queue", "Live queue"),
|
||||
("/api/queue", ("prs", "issues")),
|
||||
("/projects", "Gitea-Tools"),
|
||||
("/api/projects", "projects"),
|
||||
("/prompts", "Prompt library"),
|
||||
("/api/prompts", "prompts"),
|
||||
("/runtime", "Runtime"),
|
||||
("/audit", "Audit"),
|
||||
("/worktrees", "Worktrees"),
|
||||
("/leases", "Leases"),
|
||||
)
|
||||
|
||||
MUTATION_METHODS = ("POST", "PUT", "PATCH", "DELETE")
|
||||
|
||||
|
||||
def _import_optional(module_name: str):
|
||||
try:
|
||||
return importlib.import_module(module_name)
|
||||
except ImportError:
|
||||
return None
|
||||
|
||||
|
||||
class TestWebuiServerStartup(unittest.TestCase):
|
||||
def test_create_app_imports_cleanly(self):
|
||||
app = create_app()
|
||||
self.assertIsNotNone(app)
|
||||
|
||||
def test_main_module_imports(self):
|
||||
import webui.__main__ as main_mod
|
||||
|
||||
self.assertTrue(callable(main_mod.main))
|
||||
|
||||
|
||||
class TestWebuiRouteRendering(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client = TestClient(create_app())
|
||||
|
||||
def test_all_mvp_get_routes_render(self):
|
||||
for path, needle in MVP_GET_ROUTES:
|
||||
with self.subTest(path=path):
|
||||
response = self.client.get(path)
|
||||
self.assertEqual(response.status_code, 200, path)
|
||||
if path == "/health":
|
||||
assert isinstance(needle, str)
|
||||
self.assertEqual(response.json()["service"], needle)
|
||||
elif path.startswith("/api/"):
|
||||
data = response.json()
|
||||
if isinstance(needle, tuple):
|
||||
for key in needle:
|
||||
self.assertIn(key, data)
|
||||
else:
|
||||
self.assertIn(needle, data)
|
||||
else:
|
||||
assert isinstance(needle, str)
|
||||
self.assertIn(needle, response.text)
|
||||
|
||||
def test_registered_routes_include_mvp_paths(self):
|
||||
app = create_app()
|
||||
get_paths = {
|
||||
route.path
|
||||
for route in app.routes
|
||||
if isinstance(route, Route) and "GET" in route.methods
|
||||
}
|
||||
for path, _ in MVP_GET_ROUTES:
|
||||
with self.subTest(path=path):
|
||||
self.assertIn(path, get_paths)
|
||||
|
||||
|
||||
class TestWebuiReadOnlyGuards(unittest.TestCase):
|
||||
def setUp(self):
|
||||
self.client = TestClient(create_app())
|
||||
|
||||
def test_mutation_methods_rejected_on_core_paths(self):
|
||||
paths = ("/health", "/queue", "/projects", "/prompts", "/api/queue")
|
||||
for path in paths:
|
||||
for method in MUTATION_METHODS:
|
||||
with self.subTest(path=path, method=method):
|
||||
response = self.client.request(method, path)
|
||||
self.assertEqual(response.status_code, 405)
|
||||
self.assertEqual(response.json()["error"], "read-only-mvp")
|
||||
|
||||
|
||||
class TestWebuiOptionalModules(unittest.TestCase):
|
||||
"""Exercise child-issue modules when present on the branch under test."""
|
||||
|
||||
def test_audit_validator_basics_when_present(self):
|
||||
mod = _import_optional("webui.audit_validator")
|
||||
if mod is None:
|
||||
self.skipTest("webui.audit_validator not merged on this branch")
|
||||
report = "## Controller Handoff\n- Task: review PR #1\n"
|
||||
self.assertEqual(mod.infer_task_kind(report), "review_pr")
|
||||
result = mod.audit_report(report)
|
||||
self.assertIn("grade", result)
|
||||
|
||||
def test_gated_actions_fail_closed_when_present(self):
|
||||
mod = _import_optional("webui.gated_actions")
|
||||
if mod is None:
|
||||
self.skipTest("webui.gated_actions not merged on this branch")
|
||||
registry = mod.build_action_registry()
|
||||
for action in registry.actions:
|
||||
with self.subTest(action=action.action_id):
|
||||
self.assertFalse(action.enabled)
|
||||
result = mod.attempt_action("merge_pr", pr_number=1)
|
||||
self.assertFalse(result["success"])
|
||||
|
||||
def test_deployment_boundary_when_present(self):
|
||||
mod = _import_optional("webui.deployment_boundary")
|
||||
if mod is None:
|
||||
self.skipTest("webui.deployment_boundary not merged on this branch")
|
||||
assessment = mod.assess_bind_host("0.0.0.0")
|
||||
self.assertEqual(assessment.disposition, "refuse")
|
||||
|
||||
|
||||
class TestWebuiTestInventory(unittest.TestCase):
|
||||
def test_webui_test_modules_exist(self):
|
||||
modules = sorted(_REPO_ROOT.glob("tests/test_webui_*.py"))
|
||||
names = [path.name for path in modules]
|
||||
self.assertIn("test_webui_skeleton.py", names)
|
||||
self.assertIn("test_webui_project_registry.py", names)
|
||||
self.assertIn("test_webui_prompt_library.py", names)
|
||||
self.assertIn("test_webui_queue_dashboard.py", names)
|
||||
self.assertGreaterEqual(len(names), 5)
|
||||
|
||||
|
||||
class TestWebuiCiScripts(unittest.TestCase):
|
||||
def test_runner_scripts_exist(self):
|
||||
for name in ("test-webui", "ci-webui-check"):
|
||||
script = _REPO_ROOT / "scripts" / name
|
||||
self.assertTrue(script.is_file(), name)
|
||||
self.assertTrue(os.access(script, os.X_OK), name)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
@@ -206,8 +206,9 @@ class TestQueueRoutes(unittest.TestCase):
|
||||
self.assertEqual(data["pagination"]["issues"]["returned_count"], 3)
|
||||
|
||||
def test_queue_fail_closed_without_credentials(self):
|
||||
with mock.patch("webui.queue_loader.get_auth_header", return_value=None):
|
||||
snapshot = load_queue_snapshot()
|
||||
with mock.patch.dict("os.environ", {"WEBUI_TEST_OFFLINE": "0"}):
|
||||
with mock.patch("webui.queue_loader.get_auth_header", return_value=None):
|
||||
snapshot = load_queue_snapshot()
|
||||
self.assertIsNotNone(snapshot.fetch_error)
|
||||
self.assertEqual(len(snapshot.prs), 0)
|
||||
|
||||
@@ -285,4 +286,4 @@ class TestQueueFailClosedUx(unittest.TestCase):
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
unittest.main()
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
"""Path filters for web UI CI gating (#436)."""
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import re
|
||||
from collections.abc import Iterable
|
||||
|
||||
_WEBUI_TOUCH_RE = re.compile(
|
||||
r"^(?:webui/|tests/test_webui_|docs/webui|scripts/(?:run-webui|test-webui|ci-webui-check)$)"
|
||||
)
|
||||
|
||||
|
||||
def touches_webui_surface(path: str) -> bool:
|
||||
"""Return True when *path* is a web UI surface file."""
|
||||
return bool(_WEBUI_TOUCH_RE.match(path.strip()))
|
||||
|
||||
|
||||
def should_run_webui_ci(changed_paths: Iterable[str]) -> bool:
|
||||
"""Return True when any changed path should trigger the web UI test suite."""
|
||||
return any(touches_webui_surface(path) for path in changed_paths)
|
||||
+24
-5
@@ -228,10 +228,29 @@ def load_lease_snapshot(
|
||||
)
|
||||
|
||||
host = _host_from_url(project.remote_host)
|
||||
pr_fetch = fetch_prs or _fetch_prs
|
||||
issue_fetch = fetch_issues or _fetch_issues
|
||||
comment_fetch = fetch_comments or _fetch_comments
|
||||
using_live = fetch_prs is None or fetch_issues is None
|
||||
offline_test = (os.environ.get("WEBUI_TEST_OFFLINE") or "").strip() in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
}
|
||||
|
||||
def _empty_pr_fetch(*_args, **_kwargs):
|
||||
return [], None
|
||||
|
||||
def _empty_issue_fetch(*_args, **_kwargs):
|
||||
return [], None
|
||||
|
||||
def _empty_comment_fetch(*_args, **_kwargs):
|
||||
return []
|
||||
|
||||
pr_fetch = fetch_prs or (_empty_pr_fetch if offline_test else _fetch_prs)
|
||||
issue_fetch = fetch_issues or (
|
||||
_empty_issue_fetch if offline_test else _fetch_issues
|
||||
)
|
||||
comment_fetch = fetch_comments or (
|
||||
_empty_comment_fetch if offline_test else _fetch_comments
|
||||
)
|
||||
using_live = not offline_test and (fetch_prs is None or fetch_issues is None)
|
||||
auth = get_auth_header(host) if using_live else "test-auth"
|
||||
|
||||
if using_live and not auth:
|
||||
@@ -328,4 +347,4 @@ def snapshot_to_dict(snapshot: LeaseSnapshot) -> dict[str, Any]:
|
||||
],
|
||||
"collision_history": list(snapshot.collision_history),
|
||||
"fetch_error": snapshot.fetch_error,
|
||||
}
|
||||
}
|
||||
|
||||
+19
-4
@@ -2,6 +2,7 @@
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import os
|
||||
import re
|
||||
from dataclasses import dataclass
|
||||
from datetime import datetime, timezone
|
||||
@@ -268,9 +269,23 @@ def load_queue_snapshot(
|
||||
)
|
||||
|
||||
host = _host_from_url(project.remote_host)
|
||||
pr_fetch = fetch_prs or _fetch_prs
|
||||
issue_fetch = fetch_issues or _fetch_issues
|
||||
using_live_fetch = fetch_prs is None or fetch_issues is None
|
||||
offline_test = (os.environ.get("WEBUI_TEST_OFFLINE") or "").strip() in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
}
|
||||
|
||||
def _empty_fetch(*_args, **_kwargs):
|
||||
return [], _pagination_from_pages(
|
||||
per_page=50,
|
||||
pages_fetched=1,
|
||||
returned_count=0,
|
||||
is_final_page=True,
|
||||
)
|
||||
|
||||
pr_fetch = fetch_prs or (_empty_fetch if offline_test else _fetch_prs)
|
||||
issue_fetch = fetch_issues or (_empty_fetch if offline_test else _fetch_issues)
|
||||
using_live_fetch = not offline_test and (fetch_prs is None or fetch_issues is None)
|
||||
auth = get_auth_header(host) if using_live_fetch else "test-auth"
|
||||
if using_live_fetch and not auth:
|
||||
return QueueSnapshot(
|
||||
@@ -382,4 +397,4 @@ def snapshot_to_dict(snapshot: QueueSnapshot) -> dict[str, Any]:
|
||||
"prs": _page(snapshot.pr_pagination),
|
||||
"issues": _page(snapshot.issue_pagination),
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
+18
-3
@@ -217,6 +217,11 @@ def load_runtime_snapshot(
|
||||
repo = _repo_root()
|
||||
host = _host_from_url(project.remote_host)
|
||||
remote = _remote_name_for_host(host)
|
||||
offline_test = (os.environ.get("WEBUI_TEST_OFFLINE") or "").strip() in {
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
}
|
||||
profile = get_profile()
|
||||
allowed = profile.get("allowed_operations") or []
|
||||
forbidden = profile.get("forbidden_operations") or []
|
||||
@@ -247,10 +252,20 @@ def load_runtime_snapshot(
|
||||
fetch_error=str(exc),
|
||||
)
|
||||
|
||||
identity_fn = resolve_username or _resolve_username
|
||||
identity_fn = resolve_username
|
||||
if identity_fn is None:
|
||||
if offline_test:
|
||||
identity_fn = lambda _host: (None, "offline web UI test mode")
|
||||
else:
|
||||
identity_fn = _resolve_username
|
||||
username, identity_error = identity_fn(host)
|
||||
|
||||
git_fn = git_sync or _git_sync_status
|
||||
git_fn = git_sync
|
||||
if git_fn is None:
|
||||
if offline_test:
|
||||
git_fn = lambda _repo, _remote, _branch: (None, None, None)
|
||||
else:
|
||||
git_fn = _git_sync_status
|
||||
remote_ref = "prgs" if remote == "prgs" else "origin"
|
||||
local_sha, remote_sha, behind = git_fn(repo, remote_ref, project.default_branch)
|
||||
|
||||
@@ -317,4 +332,4 @@ def snapshot_to_dict(snapshot: RuntimeSnapshot) -> dict[str, Any]:
|
||||
"schema_hashes": [_hash_row(item) for item in snapshot.schema_hashes],
|
||||
"restart_guidance": snapshot.restart_guidance,
|
||||
"fetch_error": snapshot.fetch_error,
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user