Skip to content

Commit 6d3ebc8

Browse files
committed
fix(cli): tailor runtime startup remediation
Signed-off-by: duang777 <duangjl007@gmail.com>
1 parent 877b372 commit 6d3ebc8

2 files changed

Lines changed: 74 additions & 9 deletions

File tree

‎loopx/cli_commands/start_goal.py‎

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,46 @@
3131
)
3232
_FINE_GRAINED_PREFIX = re.compile(r"\A--fine-grained(?P<remainder>(?:\s[\s\S]*)?)\Z")
3333

34+
_EFFECT_RUNTIME_STARTUP_REMEDIATION_BY_CODE = {
35+
"node_unavailable": (
36+
f"Install or activate Node.js {MINIMUM_NODE_VERSION_TEXT} or newer "
37+
"on PATH, then run `loopx doctor --deep` and retry "
38+
"`loopx start-goal --guided`."
39+
),
40+
"startup_lock_timeout": (
41+
"Another LoopX TypeScript control-plane runtime may be starting. Run "
42+
"`loopx doctor --deep`, wait for the active startup to settle, and retry "
43+
"`loopx start-goal --guided`."
44+
),
45+
"runtime_launch_failed": (
46+
"Run `loopx doctor --deep` and retry `loopx start-goal --guided`. If the "
47+
"runtime still cannot start, repair or reinstall LoopX."
48+
),
49+
"runtime_exited_before_ready": (
50+
"Run `loopx doctor --deep` and retry `loopx start-goal --guided`. If the "
51+
"runtime exits again, repair or reinstall LoopX."
52+
),
53+
"runtime_startup_timeout": (
54+
"Run `loopx doctor --deep` and retry `loopx start-goal --guided`. If "
55+
"startup continues to time out, repair or reinstall LoopX."
56+
),
57+
"runtime_request_failed": (
58+
"Run `loopx doctor --deep` and retry `loopx start-goal --guided`. If "
59+
"requests continue to fail, repair or reinstall LoopX."
60+
),
61+
}
62+
_EFFECT_RUNTIME_STARTUP_DEFAULT_REMEDIATION = (
63+
"Run `loopx doctor --deep` and retry `loopx start-goal --guided`. If the "
64+
"TypeScript control-plane runtime remains unavailable, repair or reinstall LoopX."
65+
)
66+
67+
68+
def _effect_runtime_startup_recommended_action(diagnostic_code: str) -> str:
69+
return _EFFECT_RUNTIME_STARTUP_REMEDIATION_BY_CODE.get(
70+
diagnostic_code,
71+
_EFFECT_RUNTIME_STARTUP_DEFAULT_REMEDIATION,
72+
)
73+
3474

3575
def _effect_runtime_startup_failure_payload(
3676
exc: EffectRuntimeStartupError,
@@ -46,10 +86,8 @@ def _effect_runtime_startup_failure_payload(
4686
"minimum_node_version": MINIMUM_NODE_VERSION_TEXT,
4787
"required_for": ["start-goal", "control_plane"],
4888
},
49-
"recommended_action": (
50-
f"Install or activate Node.js {MINIMUM_NODE_VERSION_TEXT} or newer "
51-
"on PATH, then run `loopx doctor --deep` and retry "
52-
"`loopx start-goal --guided`."
89+
"recommended_action": _effect_runtime_startup_recommended_action(
90+
diagnostic_code
5391
),
5492
}
5593

‎tests/control_plane/test_start_goal_compact_projection.py‎

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -149,11 +149,15 @@ def _invoke_ark_start_goal_cli(
149149
return exit_code, payload
150150

151151

152-
def _block_effect_runtime_startup(monkeypatch: pytest.MonkeyPatch) -> None:
152+
def _block_effect_runtime_startup(
153+
monkeypatch: pytest.MonkeyPatch,
154+
*,
155+
diagnostic_code: str = "node_unavailable",
156+
) -> None:
153157
def unavailable(*_args: object, **_kwargs: object) -> object:
154158
raise effect_runtime.EffectRuntimeStartupError(
155-
"LoopX Effect runtime requires Node.js 22.6.0 or newer",
156-
diagnostic_code="node_unavailable",
159+
"TypeScript Effect runtime could not serve the request",
160+
diagnostic_code=diagnostic_code,
157161
)
158162

159163
monkeypatch.setattr(
@@ -163,12 +167,31 @@ def unavailable(*_args: object, **_kwargs: object) -> object:
163167
)
164168

165169

170+
@pytest.mark.parametrize(
171+
("diagnostic_code", "expected_action_fragment", "mentions_node_installation"),
172+
[
173+
("node_unavailable", "Install or activate Node.js", True),
174+
(
175+
"startup_lock_timeout",
176+
"wait for the active startup to settle",
177+
False,
178+
),
179+
("runtime_launch_failed", "runtime still cannot start", False),
180+
("runtime_exited_before_ready", "runtime exits again", False),
181+
("runtime_startup_timeout", "startup continues to time out", False),
182+
("runtime_request_failed", "requests continue to fail", False),
183+
("future_runtime_diagnostic", "runtime remains unavailable", False),
184+
],
185+
)
166186
def test_cli_reports_effect_runtime_startup_failure_without_traceback(
167187
tmp_path: Path,
168188
monkeypatch: pytest.MonkeyPatch,
189+
diagnostic_code: str,
190+
expected_action_fragment: str,
191+
mentions_node_installation: bool,
169192
) -> None:
170193
project = _write_connected_project(tmp_path)
171-
_block_effect_runtime_startup(monkeypatch)
194+
_block_effect_runtime_startup(monkeypatch, diagnostic_code=diagnostic_code)
172195

173196
output = io.StringIO()
174197
with contextlib.redirect_stdout(output):
@@ -197,9 +220,13 @@ def test_cli_reports_effect_runtime_startup_failure_without_traceback(
197220
assert payload["ok"] is False
198221
assert payload["schema_version"] == "loopx_start_goal_guided_v0"
199222
assert payload["error"] == "LoopX TypeScript control-plane runtime is unavailable"
200-
assert payload["diagnostic_code"] == "node_unavailable"
223+
assert payload["diagnostic_code"] == diagnostic_code
201224
assert payload["runtime_requirement"]["minimum_node_version"] == "22.6.0"
202225
assert "loopx doctor --deep" in payload["recommended_action"]
226+
assert expected_action_fragment in payload["recommended_action"]
227+
assert ("Node.js" in payload["recommended_action"]) is mentions_node_installation
228+
if not mentions_node_installation:
229+
assert "Install or activate Node.js" not in payload["recommended_action"]
203230
assert "Traceback" not in raw_output
204231

205232

0 commit comments

Comments
 (0)