93 lines
3.1 KiB
Python
93 lines
3.1 KiB
Python
"""The Google Workspace setup script must not install packages itself.
|
|
|
|
The Google SDKs ship in the [google] extra, which [all] contains, so each
|
|
supported install path already has them. A stripped environment is a broken
|
|
install, and the repair is `hermes update`, not a pip run from inside a
|
|
skill script that writes to whatever interpreter it happens to be under.
|
|
|
|
These tests hold that line: the script reports the repair, and it exits
|
|
rather than continuing into an API call that would fail with an import
|
|
error further down.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import builtins
|
|
import importlib.util
|
|
from pathlib import Path
|
|
|
|
import pytest
|
|
|
|
|
|
SETUP_PATH = (
|
|
Path(__file__).resolve().parents[2]
|
|
/ "skills/productivity/google-workspace/scripts/setup.py"
|
|
)
|
|
|
|
|
|
@pytest.fixture()
|
|
def setup_module():
|
|
spec = importlib.util.spec_from_file_location(
|
|
"test_google_workspace_setup_module",
|
|
SETUP_PATH,
|
|
)
|
|
assert spec is not None and spec.loader is not None
|
|
module = importlib.util.module_from_spec(spec)
|
|
spec.loader.exec_module(module)
|
|
return module
|
|
|
|
|
|
def _break_google_imports(monkeypatch, setup_module) -> None:
|
|
"""Make the three Google SDK imports fail, and leave the rest alone."""
|
|
real_import = builtins.__import__
|
|
broken = {"googleapiclient", "google.auth", "google_auth_oauthlib"}
|
|
|
|
def fake_import(name, *args, **kwargs):
|
|
if name in broken:
|
|
raise ImportError(f"No module named {name!r}")
|
|
return real_import(name, *args, **kwargs)
|
|
|
|
monkeypatch.setattr(builtins, "__import__", fake_import)
|
|
|
|
|
|
def test_missing_sdks_stop_the_script(setup_module, monkeypatch, capsys):
|
|
"""Exit rather than run on into an API call that cannot work."""
|
|
_break_google_imports(monkeypatch, setup_module)
|
|
with pytest.raises(SystemExit) as exc:
|
|
setup_module._require_google_libs()
|
|
assert exc.value.code == 1
|
|
|
|
|
|
def test_the_message_names_the_repair(setup_module, monkeypatch, capsys):
|
|
"""A user needs the command that fixes this, not the import error.
|
|
|
|
The packages come from an extra, so the fix is to repair the install.
|
|
A pip command here would write to whichever interpreter the script runs
|
|
under, which is not always the one Hermes uses.
|
|
"""
|
|
_break_google_imports(monkeypatch, setup_module)
|
|
with pytest.raises(SystemExit):
|
|
setup_module._require_google_libs()
|
|
err = capsys.readouterr().err
|
|
assert "hermes update" in err
|
|
assert "uv sync --extra all" in err
|
|
assert "[google]" in err
|
|
|
|
|
|
def test_present_sdks_are_not_an_error(setup_module):
|
|
"""The venv running the tests has the [google] extra, so this passes."""
|
|
pytest.importorskip("googleapiclient")
|
|
pytest.importorskip("google.auth")
|
|
pytest.importorskip("google_auth_oauthlib")
|
|
setup_module._require_google_libs()
|
|
|
|
|
|
def test_the_script_installs_nothing(setup_module):
|
|
"""No install path may reappear here.
|
|
|
|
An earlier version pip-installed a hardcoded list of pins, which is a
|
|
second copy of what the [google] extra already declares.
|
|
"""
|
|
assert not hasattr(setup_module, "install_deps")
|
|
assert not hasattr(setup_module, "_missing_required_packages")
|