feat: PowerShell 7 preflight and launcher - preflight.ps1, wikitool.ps1, doctor policy and Mark of the Web checks, pwsh CI job (#151, B)
Files changed: - .gitea/workflows/ci.yml - CHANGES.md - INSTALL.md - README.md - VERSION - docs/why-gates-are-code.md - instructions/bootstrap.md - instructions/bug-report.md - instructions/preflight.md - instructions/setup-instance.md - instructions/upgrade-instance.md - tools/CONTRACT.md - tools/README.md - tools/bugreport.py - tools/chemenu/cli.py - tools/chemenu/commands/dist_cmd.py - tools/chemenu/commands/doctor.py - tools/chemenu/prerequisites.py - tools/chemenu/tests/conftest.py - tools/chemenu/tests/test_bugreport.py - tools/chemenu/tests/test_doctor.py - tools/chemenu/tests/test_preflight.py - tools/chemenu/tests/test_preflight_pwsh.py - tools/chemenu/toolpaths.py - tools/preflight.ps1 - tools/wikitool - tools/wikitool.ps1
This commit is contained in:
1 parent
4035b1ba12
commit
210e0c8286
27 files changed
+1513
-32
No files matched your search
@@ -35,6 +35,8 @@ _WIKITOOL_ENV = (
|
||||
"WIKITOOL_TASKS_CONFIG",
|
||||
"CHEMENU_PREFLIGHT_PLATFORM",
|
||||
"CHEMENU_PREFLIGHT_LONGPATHS",
|
||||
"CHEMENU_PREFLIGHT_POLICY",
|
||||
"CHEMENU_PREFLIGHT_MARKED",
|
||||
) + tuple(var for var, _harness in HARNESS_ENV_VARS)
|
||||
|
||||
# Environment git reads for identity or for where its repo lives. A stray
|
||||
|
||||
@@ -61,7 +61,7 @@ def checkout(tmp_path: Path, monkeypatch) -> Path:
|
||||
json.dumps({"schema": 1, "api_token": TOKEN, "enabled": True})
|
||||
)
|
||||
(root / ".wikitool-tools.json").write_text(
|
||||
json.dumps({"schema": 1, "tools": {"python": {"path": "/definitely/not/here/python"}}})
|
||||
json.dumps({"schema": 1, "complete": True, "tools": {"python": "/definitely/not/here/python"}})
|
||||
)
|
||||
launcher = root / "tools" / "wikitool"
|
||||
launcher.write_text(LAUNCHER.format(python=sys.executable, title=TITLE))
|
||||
@@ -271,8 +271,9 @@ def test_the_tools_config_path_check_reports_what_is_missing(checkout, tmp_path)
|
||||
bundle = collect(checkout, tmp_path, "--no-trace")
|
||||
config = json.loads((bundle / "environment.json").read_text())["tools_config"]
|
||||
assert config["status"] == "present"
|
||||
assert config["content"]["complete"] is True
|
||||
assert config["path_checks"] == [
|
||||
{"at": "tools.python.path", "path": "/definitely/not/here/python", "exists": False}
|
||||
{"at": "tools.python", "path": "/definitely/not/here/python", "exists": False}
|
||||
]
|
||||
|
||||
|
||||
|
||||
@@ -103,6 +103,10 @@ def _detail(checks, name) -> str:
|
||||
return next(c.detail for c in checks if c.name == name)
|
||||
|
||||
|
||||
def _fix(checks, name) -> str:
|
||||
return next(c.fix or "" for c in checks if c.name == name)
|
||||
|
||||
|
||||
def test_healthy_instance_has_no_fail(instance):
|
||||
checks = doctor.run_doctor()
|
||||
assert not any(c.status == "FAIL" for c in checks)
|
||||
@@ -640,3 +644,70 @@ def test_install_dir_is_not_limited_off_windows(instance, monkeypatch):
|
||||
monkeypatch.setenv(prerequisites.ENV_PLATFORM, "linux")
|
||||
monkeypatch.setattr(prerequisites, "install_dir", lambda: "/" + "x" * 300)
|
||||
assert _status(doctor.run_doctor(), "install-dir") == "OK"
|
||||
|
||||
|
||||
# --- PowerShell: execution policy and Mark of the Web (Gitea #151) -----------------
|
||||
|
||||
OPEN_POLICY = "MachinePolicy=Undefined,UserPolicy=Undefined,Process=Undefined,CurrentUser=Undefined,LocalMachine=RemoteSigned"
|
||||
|
||||
|
||||
def _windows(monkeypatch, policy=None, marked=None):
|
||||
monkeypatch.setenv(prerequisites.ENV_PLATFORM, "windows")
|
||||
if policy is not None:
|
||||
monkeypatch.setenv(prerequisites.ENV_POLICY, policy)
|
||||
if marked is not None:
|
||||
monkeypatch.setenv(prerequisites.ENV_MARKED, marked)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("check", ["execution-policy", "script-marks"])
|
||||
def test_powershell_checks_do_not_apply_off_windows(instance, monkeypatch, check):
|
||||
monkeypatch.setenv(prerequisites.ENV_PLATFORM, "linux")
|
||||
monkeypatch.setenv(prerequisites.ENV_POLICY, "CurrentUser=AllSigned")
|
||||
monkeypatch.setenv(prerequisites.ENV_MARKED, "wikitool.ps1")
|
||||
assert _status(doctor.run_doctor(), check) == "OK"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy, expected", [
|
||||
(OPEN_POLICY, "OK"),
|
||||
("CurrentUser=Unrestricted,LocalMachine=Restricted", "OK"),
|
||||
("Process=Restricted,LocalMachine=RemoteSigned", "OK"),
|
||||
("CurrentUser=Undefined,LocalMachine=Undefined", "OK"),
|
||||
("CurrentUser=Restricted,LocalMachine=RemoteSigned", "FAIL"),
|
||||
("LocalMachine=AllSigned", "FAIL"),
|
||||
])
|
||||
def test_execution_policy_blocks_only_restricted_and_allsigned(instance, monkeypatch, policy, expected):
|
||||
_windows(monkeypatch, policy=policy)
|
||||
assert _status(doctor.run_doctor(), "execution-policy") == expected
|
||||
|
||||
|
||||
def test_execution_policy_fix_names_the_one_line_command(instance, monkeypatch):
|
||||
_windows(monkeypatch, policy="CurrentUser=Restricted")
|
||||
fix = _fix(doctor.run_doctor(), "execution-policy")
|
||||
assert "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned" in fix
|
||||
|
||||
|
||||
@pytest.mark.parametrize("scope", ["MachinePolicy", "UserPolicy"])
|
||||
def test_a_group_policy_gets_no_command_it_could_not_obey(instance, monkeypatch, scope):
|
||||
_windows(monkeypatch, policy=f"{scope}=AllSigned,LocalMachine=RemoteSigned")
|
||||
checks = doctor.run_doctor()
|
||||
assert _status(checks, "execution-policy") == "FAIL"
|
||||
assert "Set-ExecutionPolicy" not in _fix(checks, "execution-policy")
|
||||
assert "Git Bash" in _fix(checks, "execution-policy")
|
||||
|
||||
|
||||
def test_execution_policy_that_cannot_be_read_is_a_warning(instance, monkeypatch):
|
||||
monkeypatch.setenv(prerequisites.ENV_PLATFORM, "windows")
|
||||
assert _status(doctor.run_doctor(), "execution-policy") == "WARN"
|
||||
|
||||
|
||||
def test_marked_scripts_fail_with_the_unblock_command(instance, monkeypatch):
|
||||
_windows(monkeypatch, marked="wikitool.ps1,preflight.ps1")
|
||||
checks = doctor.run_doctor()
|
||||
assert _status(checks, "script-marks") == "FAIL"
|
||||
assert "wikitool.ps1" in _detail(checks, "script-marks")
|
||||
assert "Unblock-File" in _fix(checks, "script-marks")
|
||||
|
||||
|
||||
def test_unmarked_scripts_are_ok(instance, monkeypatch):
|
||||
_windows(monkeypatch)
|
||||
assert _status(doctor.run_doctor(), "script-marks") == "OK"
|
||||
@@ -94,7 +94,8 @@ class Machine:
|
||||
self.root = base / root_name
|
||||
self.tools = self.root / "tools"
|
||||
self.tools.mkdir(parents=True)
|
||||
for name in ("preflight.sh", "prerequisites.txt", "wikitool", "run_wikitool.py", "trace-hook"):
|
||||
for name in ("preflight.sh", "preflight.ps1", "prerequisites.txt", "wikitool", "wikitool.ps1",
|
||||
"run_wikitool.py", "trace-hook"):
|
||||
shutil.copy2(TOOLS / name, self.tools / name)
|
||||
(self.tools / "requirements.txt").write_text("PyYAML\n", encoding="utf-8")
|
||||
self.sysbin = base / "sysbin"
|
||||
@@ -392,8 +393,11 @@ def test_launcher_runs_the_entry_script_with_either_venv_layout(machine, layout)
|
||||
assert result.stdout.splitlines() == [str(machine.tools / "run_wikitool.py"), "doctor", "--json"]
|
||||
|
||||
|
||||
def test_there_is_a_powershell_launcher_slot_but_no_cmd():
|
||||
def test_there_is_a_powershell_launcher_and_no_cmd():
|
||||
"""pwsh resolves `tools/wikitool` to `wikitool.ps1` before the sh launcher, and
|
||||
cmd.exe is not a supported shell, so there is no `.cmd`."""
|
||||
assert (TOOLS / "wikitool").is_file()
|
||||
assert (TOOLS / "wikitool.ps1").is_file()
|
||||
assert not (TOOLS / "wikitool.cmd").exists()
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,304 @@
|
||||
"""tools/preflight.ps1 and tools/wikitool.ps1 (Gitea #151, section B).
|
||||
|
||||
The PowerShell twin of `test_preflight.py`, run against the same stub machine: a
|
||||
`PATH` the test builds, stub tools in it, and a fake Python that answers the probe
|
||||
and fakes `-m venv`/`-m pip`. The scripts need PowerShell 7, so these tests skip
|
||||
where there is none and run in CI's `pwsh` job, whose image carries it; everything
|
||||
Windows-specific (policy, Mark of the Web, path limit, Store alias) is driven by the
|
||||
same kind of environment hook the shell script has.
|
||||
|
||||
What these add to the shell tests is the one thing only the twin can break: the two
|
||||
scripts must record the same file. `test_both_preflights_record_the_same_file`
|
||||
compares them byte for byte.
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import subprocess
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from chemenu import prerequisites
|
||||
from chemenu.tests.test_preflight import (
|
||||
ECHO_PYTHON, SHELLS, TOOLS, Machine, _machine_with_root_of, assert_guidance,
|
||||
)
|
||||
|
||||
PWSH = shutil.which("pwsh")
|
||||
|
||||
pytestmark = pytest.mark.skipif(PWSH is None, reason="PowerShell 7 (pwsh) is not installed")
|
||||
|
||||
WINDOWS = {"CHEMENU_PREFLIGHT_PLATFORM": "windows", "CHEMENU_PREFLIGHT_LONGPATHS": "1"}
|
||||
|
||||
|
||||
def _pwsh(machine: Machine, script: str, *args: str) -> subprocess.CompletedProcess:
|
||||
env = {
|
||||
"PATH": os.pathsep.join(str(d) for d in machine.path_dirs),
|
||||
"HOME": str(machine.base),
|
||||
"PIP_LOG": str(machine.pip_log),
|
||||
"DOTNET_CLI_TELEMETRY_OPTOUT": "1",
|
||||
"POWERSHELL_TELEMETRY_OPTOUT": "1",
|
||||
**machine.extra_env,
|
||||
}
|
||||
return subprocess.run(
|
||||
[PWSH, "-NoProfile", "-ExecutionPolicy", "Bypass", "-File", str(machine.tools / script), *args],
|
||||
capture_output=True, text=True, env=env, timeout=120,
|
||||
)
|
||||
|
||||
|
||||
def _preflight(machine: Machine, *args: str) -> subprocess.CompletedProcess:
|
||||
return _pwsh(machine, "preflight.ps1", *args)
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def machine(tmp_path: Path) -> Machine:
|
||||
return Machine(tmp_path.resolve()).standard()
|
||||
|
||||
|
||||
# --- the happy path ---------------------------------------------------------------
|
||||
|
||||
|
||||
def test_complete_path_records_absolute_paths_and_sets_up_the_venv(machine):
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 0, result.stdout + result.stderr
|
||||
data = machine.recorded()
|
||||
assert data["schema"] == 1 and data["complete"] is True
|
||||
assert set(data["tools"]) == {"python", "git", "rg"}
|
||||
assert data["tools"]["rg"] == str(machine.stubs / "rg")
|
||||
assert (machine.tools / ".venv" / "bin" / "python").is_file()
|
||||
assert machine.pip_log.read_text(encoding="utf-8").count("install") == 1
|
||||
assert "Preflight passed" in result.stdout
|
||||
|
||||
|
||||
def test_second_run_changes_nothing(machine):
|
||||
assert _preflight(machine).returncode == 0
|
||||
before = machine.tools_file.read_text(encoding="utf-8")
|
||||
again = _preflight(machine)
|
||||
assert again.returncode == 0, again.stdout
|
||||
assert machine.tools_file.read_text(encoding="utf-8") == before
|
||||
assert machine.pip_log.read_text(encoding="utf-8").count("install") == 1
|
||||
|
||||
|
||||
@pytest.mark.skipif(not SHELLS, reason="no POSIX shell on this machine")
|
||||
def test_both_preflights_record_the_same_file(machine):
|
||||
assert _preflight(machine).returncode == 0
|
||||
from_pwsh = machine.tools_file.read_bytes()
|
||||
machine.tools_file.unlink()
|
||||
assert machine.run(shell=SHELLS[0]).returncode == 0
|
||||
assert machine.tools_file.read_bytes() == from_pwsh
|
||||
|
||||
|
||||
def test_help_prints_the_usage_and_exits_0(machine):
|
||||
result = _preflight(machine, "--help")
|
||||
assert result.returncode == 0
|
||||
assert "--set" in result.stdout
|
||||
assert not machine.tools_file.exists()
|
||||
|
||||
|
||||
# --- stop cases -------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_missing_rg_stops_with_42_and_a_missing_line(tmp_path):
|
||||
machine = Machine(tmp_path.resolve()).standard(rg=False)
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert any(line.split()[:2] == ["MISSING", "rg"] for line in result.stdout.splitlines())
|
||||
assert_guidance(result.stdout, "ripgrep (rg) was not found", "--set rg=")
|
||||
data = machine.recorded()
|
||||
assert data["complete"] is False and "rg" not in data["tools"]
|
||||
assert not (machine.tools / ".venv").exists()
|
||||
|
||||
|
||||
def test_python_too_old_stops(machine):
|
||||
machine.extra_env["FAKE_PY_VERSION"] = "3.9"
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert "TOO_OLD" in result.stdout
|
||||
assert_guidance(result.stdout, "Python 3.9 is too old")
|
||||
|
||||
|
||||
def test_invalid_set_path_writes_nothing(machine):
|
||||
result = _preflight(machine, "--set", f"rg={machine.base / 'nowhere' / 'rg'}")
|
||||
assert result.returncode == 42
|
||||
assert_guidance(result.stdout, "The path given for ripgrep (rg) does not work")
|
||||
assert not machine.tools_file.exists()
|
||||
|
||||
|
||||
def test_valid_set_path_is_recorded_and_kept(machine):
|
||||
elsewhere = machine.stub("rg", "#!/bin/sh\necho 'ripgrep 14.1.1'\n", machine.base / "opt")
|
||||
machine.stub("rg", "#!/bin/sh\nexit 1\n")
|
||||
result = _preflight(machine, f"--set=rg={elsewhere}")
|
||||
assert result.returncode == 0, result.stdout
|
||||
assert machine.recorded()["tools"]["rg"] == str(elsewhere)
|
||||
assert _preflight(machine).returncode == 0
|
||||
assert machine.recorded()["tools"]["rg"] == str(elsewhere)
|
||||
|
||||
|
||||
def test_set_for_an_unknown_tool_is_a_usage_error(machine):
|
||||
result = _preflight(machine, "--set", "foo=/bin/sh")
|
||||
assert result.returncode == 1
|
||||
assert not machine.tools_file.exists()
|
||||
|
||||
|
||||
def test_venv_that_cannot_be_created_stops(machine):
|
||||
machine.extra_env["FAKE_VENV_FAIL"] = "1"
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert_guidance(result.stdout, "ensurepip is not available")
|
||||
|
||||
|
||||
def test_pip_failure_stops_and_is_retried_next_time(machine):
|
||||
machine.extra_env["FAKE_PIP_FAIL"] = "1"
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert_guidance(result.stdout, "network unreachable")
|
||||
assert machine.recorded()["complete"] is False
|
||||
del machine.extra_env["FAKE_PIP_FAIL"]
|
||||
assert _preflight(machine).returncode == 0
|
||||
assert machine.recorded()["complete"] is True
|
||||
|
||||
|
||||
@pytest.mark.parametrize("platform, alias", [("linux", "python3"), ("windows", "python")])
|
||||
def test_store_alias_is_never_run_and_never_recorded(machine, platform, alias):
|
||||
marker = machine.base / "alias-was-run"
|
||||
apps = machine.base / "Users" / "u" / "AppData" / "Local" / "Microsoft" / "WindowsApps"
|
||||
machine.stub(alias, f"#!/bin/sh\necho ran > '{marker}'\nexit 9009\n", apps)
|
||||
machine.path_dirs.insert(0, apps)
|
||||
machine.extra_env.update({"CHEMENU_PREFLIGHT_PLATFORM": platform, "CHEMENU_PREFLIGHT_LONGPATHS": "1"})
|
||||
if platform == "windows":
|
||||
machine.python("python")
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 0, result.stdout
|
||||
recorded = machine.recorded()["tools"]["python"]
|
||||
assert "WindowsApps" not in recorded
|
||||
assert recorded.startswith(str(machine.stubs))
|
||||
assert not marker.exists()
|
||||
|
||||
|
||||
# --- install folder length (D32) --------------------------------------------------
|
||||
|
||||
|
||||
@pytest.mark.parametrize("length, longpaths, expected", [
|
||||
(95, "0", 0),
|
||||
(96, "0", 42),
|
||||
(96, "1", 0),
|
||||
])
|
||||
def test_install_folder_limit_at_the_boundary(tmp_path, length, longpaths, expected):
|
||||
machine = _machine_with_root_of(tmp_path, length)
|
||||
machine.extra_env["CHEMENU_PREFLIGHT_LONGPATHS"] = longpaths
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == expected, result.stdout
|
||||
if expected == 42:
|
||||
assert_guidance(result.stdout, f"too long ({length} characters, at most 95)", "C:\\Chemenu")
|
||||
assert not (machine.tools / ".venv").exists()
|
||||
|
||||
|
||||
# --- execution policy and Mark of the Web (D16, T6) --------------------------------
|
||||
|
||||
OPEN = "MachinePolicy=Undefined,UserPolicy=Undefined,Process=Undefined,CurrentUser=Undefined,LocalMachine=RemoteSigned"
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy", [
|
||||
OPEN,
|
||||
"CurrentUser=Unrestricted,LocalMachine=Restricted",
|
||||
"Process=Restricted,LocalMachine=RemoteSigned",
|
||||
"CurrentUser=Undefined,LocalMachine=Undefined",
|
||||
])
|
||||
def test_a_policy_that_lets_scripts_run_passes(machine, policy):
|
||||
machine.extra_env.update({**WINDOWS, "CHEMENU_PREFLIGHT_POLICY": policy})
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 0, result.stdout
|
||||
|
||||
|
||||
@pytest.mark.parametrize("policy", [
|
||||
"CurrentUser=Restricted,LocalMachine=RemoteSigned",
|
||||
"CurrentUser=AllSigned",
|
||||
"CurrentUser=Undefined,LocalMachine=Restricted",
|
||||
"Process=Bypass,LocalMachine=AllSigned",
|
||||
])
|
||||
def test_a_policy_that_blocks_scripts_stops_with_the_one_line_fix(machine, policy):
|
||||
machine.extra_env.update({**WINDOWS, "CHEMENU_PREFLIGHT_POLICY": policy})
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert_guidance(result.stdout, "Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned")
|
||||
assert not (machine.tools / ".venv").exists()
|
||||
|
||||
|
||||
@pytest.mark.parametrize("scope", ["MachinePolicy", "UserPolicy"])
|
||||
def test_a_group_policy_is_a_stop_that_names_the_administrator(machine, scope):
|
||||
machine.extra_env.update({**WINDOWS, "CHEMENU_PREFLIGHT_POLICY": f"{scope}=AllSigned,LocalMachine=RemoteSigned"})
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert_guidance(result.stdout, "group policy", "Git Bash")
|
||||
assert "Set-ExecutionPolicy" not in result.stdout
|
||||
|
||||
|
||||
def test_policy_and_marks_are_not_checked_off_windows(machine):
|
||||
machine.extra_env.update({
|
||||
"CHEMENU_PREFLIGHT_PLATFORM": "linux",
|
||||
"CHEMENU_PREFLIGHT_POLICY": "CurrentUser=AllSigned",
|
||||
"CHEMENU_PREFLIGHT_MARKED": "preflight.ps1",
|
||||
})
|
||||
assert _preflight(machine).returncode == 0
|
||||
|
||||
|
||||
def test_a_marked_script_stops_with_the_unblock_command(machine):
|
||||
machine.extra_env.update({**WINDOWS, "CHEMENU_PREFLIGHT_MARKED": "wikitool.ps1,preflight.ps1"})
|
||||
result = _preflight(machine)
|
||||
assert result.returncode == 42
|
||||
assert_guidance(result.stdout, "wikitool.ps1", "Unblock-File")
|
||||
assert not (machine.tools / ".venv").exists()
|
||||
|
||||
|
||||
# --- the launcher -----------------------------------------------------------------
|
||||
|
||||
|
||||
def _launch(machine: Machine, *args: str) -> subprocess.CompletedProcess:
|
||||
return _pwsh(machine, "wikitool.ps1", *args)
|
||||
|
||||
|
||||
def _complete_file(machine: Machine, complete: bool = True) -> None:
|
||||
machine.tools_file.write_text(json.dumps(
|
||||
{"schema": 1, "complete": complete, "tools": {"git": "/usr/bin/git"}}, indent=2),
|
||||
encoding="utf-8")
|
||||
|
||||
|
||||
@pytest.mark.parametrize("state", ["no file", "no venv", "incomplete"])
|
||||
def test_launcher_stops_with_42_until_the_preflight_has_passed(machine, state):
|
||||
if state != "no file":
|
||||
_complete_file(machine, complete=(state != "incomplete"))
|
||||
if state != "no venv":
|
||||
machine.stub("python", ECHO_PYTHON, machine.tools / ".venv" / "bin")
|
||||
result = _launch(machine, "doctor")
|
||||
assert result.returncode == 42
|
||||
assert "tools/preflight.ps1" in result.stderr
|
||||
assert "ExecutionPolicy Bypass" in result.stderr
|
||||
|
||||
|
||||
@pytest.mark.parametrize("layout", [("bin", "python"), ("Scripts", "python.exe")])
|
||||
def test_launcher_runs_the_entry_script_with_either_venv_layout(machine, layout):
|
||||
_complete_file(machine)
|
||||
machine.stub(layout[1], ECHO_PYTHON, machine.tools / ".venv" / layout[0])
|
||||
result = _launch(machine, "doctor", "--json")
|
||||
assert result.returncode == 0, result.stderr
|
||||
assert result.stdout.splitlines() == [str(machine.tools / "run_wikitool.py"), "doctor", "--json"]
|
||||
|
||||
|
||||
def test_launcher_passes_the_exit_code_of_the_cli_through(machine):
|
||||
_complete_file(machine)
|
||||
machine.stub("python", "#!/bin/sh\nexit 7\n", machine.tools / ".venv" / "bin")
|
||||
assert _launch(machine, "lint").returncode == 7
|
||||
|
||||
|
||||
def test_both_launchers_exist_for_pwsh_to_resolve():
|
||||
assert (TOOLS / "wikitool.ps1").is_file() and (TOOLS / "wikitool").is_file()
|
||||
|
||||
|
||||
def test_the_python_side_reads_what_the_hooks_say(monkeypatch):
|
||||
"""The doctor checks use the same hook values the script does; the two
|
||||
parsers must agree on what a policy list means."""
|
||||
monkeypatch.setenv(prerequisites.ENV_POLICY, "MachinePolicy=AllSigned,LocalMachine=RemoteSigned")
|
||||
policy = prerequisites.execution_policy()
|
||||
assert policy is not None and policy.blocks and policy.group_policy
|
||||
Reference in new issue
Block a user