Skip to content

Commit 9ecceb2

Browse files
committed
Address review comments: Final annotation, atexit tmp cleanup, parametrized tests
- Annotate DEFAULT_COANA_CLI_VERSION with typing.Final. - Register an atexit handler to remove the npm-install fallback's temp dirs. - Trim the over-long --force explanation in _spawn_coana's docstring and drop the inline comment that duplicated it. - Use try/finally in the cache-clearing test fixture. - Parametrize the spec-resolution, npx-version, and launcher-failure-heuristic tests.
1 parent 90f398d commit 9ecceb2

2 files changed

Lines changed: 62 additions & 54 deletions

File tree

socketsecurity/core/tools/reachability.py

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
from socketdev import socketdev
2-
from typing import List, Optional, Dict, Any
2+
from typing import List, Optional, Dict, Any, Final
3+
import atexit
34
import os
45
import platform
6+
import shutil
57
import subprocess
68
import json
79
import pathlib
@@ -16,13 +18,22 @@
1618
# Pinned @coana-tech/cli version. Bumped deliberately per Python CLI release so the
1719
# reachability engine version only changes through a standard pip upgrade (advance notice).
1820
# Pass --reach-version latest to opt into the newest published version instead.
19-
DEFAULT_COANA_CLI_VERSION = "15.3.24"
21+
DEFAULT_COANA_CLI_VERSION: Final = "15.3.24"
2022

2123
# Resolved @coana-tech/cli script paths from the npm-install fallback, keyed by version.
2224
# Lives for the process lifetime so repeated fallback invocations install only once
2325
# (mirrors the Node CLI's installedCoanaScriptPathsByVersion).
2426
_INSTALLED_COANA_SCRIPT_PATHS: Dict[str, str] = {}
2527

28+
# Temp dirs created by the npm-install fallback, removed at process exit.
29+
_COANA_INSTALL_DIRS: List[str] = []
30+
31+
32+
@atexit.register
33+
def _cleanup_coana_install_dirs() -> None:
34+
for install_dir in _COANA_INSTALL_DIRS:
35+
shutil.rmtree(install_dir, ignore_errors=True)
36+
2637

2738
def _build_caller_user_agent() -> str:
2839
"""Build the SOCKET_CALLER_USER_AGENT string forwarded to the coana CLI.
@@ -302,12 +313,9 @@ def _spawn_coana(
302313
) -> int:
303314
"""Run coana for the given args, returning the process exit code.
304315
305-
Primary path: ``npx --yes --force @coana-tech/cli@<version> ...`` — the exact flags
306-
the Socket Node CLI passes for coana. ``--yes`` skips npx's interactive install
307-
confirmation so non-interactive/CI runs don't hang. ``--force`` matches the Node CLI
308-
(it opts out of npm's prompts/protections and refreshes within-range specs); note it
309-
does NOT force a re-download of an already-cached pinned version — npx still reuses a
310-
cached pinned package, so this is parity with the Node CLI, not a cache bypass.
316+
Primary path: ``npx --yes --force @coana-tech/cli@<version> ...`` — the exact flags the
317+
Socket Node CLI passes for coana (``--yes`` skips npx's interactive install prompt so
318+
CI runs don't hang).
311319
312320
Fallback path: if npx is missing, or its launcher dies before coana starts, install
313321
@coana-tech/cli into a temp dir via ``npm install`` and run it directly via ``node``.
@@ -322,7 +330,6 @@ def _spawn_coana(
322330
return self._spawn_coana_via_npm_install(coana_args, effective_version, coana_env, cwd)
323331

324332
package_spec = f"@coana-tech/cli@{effective_version}"
325-
# --yes skips npx's install confirmation; --force matches the Node CLI's coana flags.
326333
npx_cmd = ["npx", "--yes", "--force", package_spec, *coana_args]
327334
log.debug(f"Reachability command: {' '.join(npx_cmd)}")
328335
try:
@@ -390,6 +397,7 @@ def _install_coana_to_tmpdir(self, version: str, env: Dict[str, str]) -> str:
390397
return cached
391398

392399
install_dir = tempfile.mkdtemp(prefix="socket-coana-")
400+
_COANA_INSTALL_DIRS.append(install_dir)
393401
npm_cmd = [
394402
"npm", "install",
395403
"--no-save", "--no-package-lock", "--no-audit", "--no-fund",

tests/unit/test_reachability.py

Lines changed: 45 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,10 @@ def analyzer():
2828
def _clear_coana_install_cache():
2929
"""The npm-install fallback caches resolved script paths in a module-level dict; isolate tests."""
3030
reachability._INSTALLED_COANA_SCRIPT_PATHS.clear()
31-
yield
32-
reachability._INSTALLED_COANA_SCRIPT_PATHS.clear()
31+
try:
32+
yield
33+
finally:
34+
reachability._INSTALLED_COANA_SCRIPT_PATHS.clear()
3335

3436

3537
def _spawn_mock(analyzer, mocker, returncode=0, **kwargs):
@@ -125,27 +127,21 @@ def test_repo_branch_env_absent_when_none(analyzer, mocker):
125127
# --- Coana package-spec resolution (pinned by default, latest is opt-in) ---
126128

127129

128-
def test_resolve_spec_defaults_to_pinned_version(analyzer):
129-
"""No --reach-version -> pinned DEFAULT_COANA_CLI_VERSION (no auto-update)."""
130-
assert (
131-
analyzer._resolve_coana_package_spec(None)
132-
== f"@coana-tech/cli@{DEFAULT_COANA_CLI_VERSION}"
133-
)
134-
135-
136-
def test_resolve_spec_pins_explicit_version(analyzer):
137-
assert analyzer._resolve_coana_package_spec("1.2.3") == "@coana-tech/cli@1.2.3"
138-
139-
140-
def test_resolve_spec_latest_opt_in(analyzer):
141-
"""'latest' opts into the newest published version."""
142-
assert analyzer._resolve_coana_package_spec("latest") == "@coana-tech/cli@latest"
143-
144-
145-
def test_resolve_spec_is_always_versioned(analyzer):
146-
"""Never the bare '@coana-tech/cli' (which would let npx pick a stray global version)."""
147-
for version in (None, "latest", "1.2.3", " 1.2.3 "):
148-
assert analyzer._resolve_coana_package_spec(version).startswith("@coana-tech/cli@")
130+
@pytest.mark.parametrize(
131+
("version", "expected"),
132+
[
133+
# No --reach-version -> pinned default (no auto-update).
134+
(None, f"@coana-tech/cli@{DEFAULT_COANA_CLI_VERSION}"),
135+
# Explicit version pinned through.
136+
("1.2.3", "@coana-tech/cli@1.2.3"),
137+
# 'latest' opts into the newest published version.
138+
("latest", "@coana-tech/cli@latest"),
139+
# Surrounding whitespace is stripped; always versioned (never bare '@coana-tech/cli').
140+
(" 1.2.3 ", "@coana-tech/cli@1.2.3"),
141+
],
142+
)
143+
def test_resolve_coana_package_spec(analyzer, version, expected):
144+
assert analyzer._resolve_coana_package_spec(version) == expected
149145

150146

151147
def _spec_in(cmd):
@@ -161,19 +157,17 @@ def test_npx_uses_yes_and_force_flags(analyzer, mocker):
161157
assert "--force" in cmd
162158

163159

164-
def test_npx_runs_pinned_version_by_default(analyzer, mocker):
165-
cmd, _ = _run(analyzer, mocker)
166-
assert _spec_in(cmd) == f"@coana-tech/cli@{DEFAULT_COANA_CLI_VERSION}"
167-
168-
169-
def test_npx_runs_explicit_version(analyzer, mocker):
170-
cmd, _ = _run(analyzer, mocker, version="9.9.9")
171-
assert _spec_in(cmd) == "@coana-tech/cli@9.9.9"
172-
173-
174-
def test_npx_runs_latest_when_opted_in(analyzer, mocker):
175-
cmd, _ = _run(analyzer, mocker, version="latest")
176-
assert _spec_in(cmd) == "@coana-tech/cli@latest"
160+
@pytest.mark.parametrize(
161+
("version", "expected_spec"),
162+
[
163+
(None, f"@coana-tech/cli@{DEFAULT_COANA_CLI_VERSION}"), # pinned default
164+
("9.9.9", "@coana-tech/cli@9.9.9"), # explicit pin
165+
("latest", "@coana-tech/cli@latest"), # opt-in to newest
166+
],
167+
)
168+
def test_npx_runs_resolved_version(analyzer, mocker, version, expected_spec):
169+
cmd, _ = _run(analyzer, mocker, version=version)
170+
assert _spec_in(cmd) == expected_spec
177171

178172

179173
def test_default_path_never_runs_npm_install(analyzer, mocker):
@@ -195,16 +189,22 @@ def test_env_strips_npm_package_vars(analyzer, mocker, monkeypatch):
195189
# --- npm-install + node fallback (when the npx launcher fails before coana starts) ---
196190

197191

198-
def test_launcher_failure_heuristic():
192+
@pytest.mark.parametrize(
193+
("returncode", "is_launcher_failure"),
194+
[
195+
# Signal kills / >=128 -> launcher failure -> retry.
196+
(-9, True), # killed by signal
197+
(137, True), # 128 + SIGKILL
198+
(249, True), # observed npx launcher failure
199+
# Small positive exit codes are ambiguous (coana's own codes) -> do NOT retry.
200+
(1, False),
201+
(2, False),
202+
(127, False),
203+
],
204+
)
205+
def test_launcher_failure_heuristic(returncode, is_launcher_failure):
199206
f = ReachabilityAnalyzer._npx_launcher_failed_before_coana
200-
# Signal kills / >=128 -> launcher failure -> retry.
201-
assert f(-9) is True
202-
assert f(137) is True
203-
assert f(249) is True
204-
# Small positive exit codes are ambiguous (coana's own codes) -> do NOT retry.
205-
assert f(1) is False
206-
assert f(2) is False
207-
assert f(127) is False
207+
assert f(returncode) is is_launcher_failure
208208

209209

210210
def _capture_spawns(analyzer, mocker, npx_behavior, **kwargs):

0 commit comments

Comments
 (0)