generated from coulomb/repo-seed
237 lines
9.3 KiB
Python
237 lines
9.3 KiB
Python
"""
|
|
Tests for CLI init parameter resolution and command behaviour.
|
|
|
|
cmd_init resolution order: flag > config > system derivation.
|
|
All three identity fields (username, fullname, email) follow this pattern.
|
|
"""
|
|
|
|
import argparse
|
|
from unittest.mock import patch
|
|
|
|
import pytest
|
|
|
|
from local_identity.cli import (
|
|
_oidc_bootstrap_payload,
|
|
_resolve_init_params,
|
|
cmd_bootstrap_oidc,
|
|
cmd_init,
|
|
)
|
|
from local_identity.store import init_dirs, list_users, read_config, read_user
|
|
|
|
|
|
def _args(username=None, fullname=None, email=None, force=False):
|
|
"""Build a minimal Namespace for _resolve_init_params / cmd_init."""
|
|
ns = argparse.Namespace()
|
|
ns.username = username
|
|
ns.fullname = fullname
|
|
ns.email = email
|
|
ns.force = force
|
|
ns.func = cmd_init
|
|
return ns
|
|
|
|
|
|
def _oidc_args(
|
|
client_id="local-dev",
|
|
redirect_uri="http://127.0.0.1:3000/callback",
|
|
port=8443,
|
|
scheme="https",
|
|
scope="openid profile email",
|
|
output="env",
|
|
):
|
|
ns = argparse.Namespace()
|
|
ns.client_id = client_id
|
|
ns.redirect_uri = redirect_uri
|
|
ns.port = port
|
|
ns.scheme = scheme
|
|
ns.scope = scope
|
|
ns.output = output
|
|
ns.func = cmd_bootstrap_oidc
|
|
return ns
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
# _resolve_init_params #
|
|
# ------------------------------------------------------------------ #
|
|
|
|
class TestResolveInitParams:
|
|
def test_flags_take_priority_over_config_and_system(self):
|
|
config = {"username": "from_config", "fullname": "Config Name", "email": "config@x.com"}
|
|
with (
|
|
patch("local_identity.cli.current_username", return_value="system_user"),
|
|
patch("local_identity.cli.get_gecos_fullname", return_value="System Name"),
|
|
):
|
|
u, f, e = _resolve_init_params(
|
|
_args(username="tegwick", fullname="Bernd Worsch", email="bernd@example.com"),
|
|
config,
|
|
)
|
|
assert u == "tegwick"
|
|
assert f == "Bernd Worsch"
|
|
assert e == "bernd@example.com"
|
|
|
|
def test_config_takes_priority_over_system(self):
|
|
config = {"username": "tegwick", "fullname": "Bernd Worsch", "email": "bernd@example.com"}
|
|
with (
|
|
patch("local_identity.cli.current_username", return_value="worsch"),
|
|
patch("local_identity.cli.get_gecos_fullname", return_value="worsch"),
|
|
):
|
|
u, f, e = _resolve_init_params(_args(), config)
|
|
assert u == "tegwick"
|
|
assert f == "Bernd Worsch"
|
|
assert e == "bernd@example.com"
|
|
|
|
def test_system_derivation_when_no_flags_or_config(self):
|
|
config = {}
|
|
with (
|
|
patch("local_identity.cli.current_username", return_value="worsch"),
|
|
patch("local_identity.cli.get_gecos_fullname", return_value="Bernd Worsch"),
|
|
):
|
|
u, f, e = _resolve_init_params(_args(), config)
|
|
assert u == "worsch"
|
|
assert f == "Bernd Worsch"
|
|
assert e == "" # no default; caller must prompt
|
|
|
|
def test_flag_username_overrides_config_username(self):
|
|
config = {"username": "wrong", "fullname": "X", "email": "x@x.com"}
|
|
with patch("local_identity.cli.current_username", return_value="system"):
|
|
u, _, _ = _resolve_init_params(_args(username="tegwick"), config)
|
|
assert u == "tegwick"
|
|
|
|
def test_flag_fullname_overrides_config_fullname(self):
|
|
config = {"username": "u", "fullname": "Wrong Name", "email": "x@x.com"}
|
|
with patch("local_identity.cli.get_gecos_fullname", return_value="Gecos Name"):
|
|
_, f, _ = _resolve_init_params(_args(fullname="Bernd Worsch"), config)
|
|
assert f == "Bernd Worsch"
|
|
|
|
def test_gecos_used_when_no_fullname_flag_or_config(self):
|
|
config = {"username": "tegwick", "email": "x@x.com"}
|
|
with patch("local_identity.cli.get_gecos_fullname", return_value="Bernd Worsch"):
|
|
_, f, _ = _resolve_init_params(_args(), config)
|
|
assert f == "Bernd Worsch"
|
|
|
|
def test_empty_email_when_absent_everywhere(self):
|
|
config = {"username": "u", "fullname": "N"}
|
|
_, _, e = _resolve_init_params(_args(), config)
|
|
assert e == ""
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
# cmd_init integration (uses tmp_store fixture) #
|
|
# ------------------------------------------------------------------ #
|
|
|
|
class TestCmdInit:
|
|
def test_init_with_all_flags(self, tmp_store):
|
|
with (
|
|
patch("local_identity.cli.current_username", return_value="worsch"),
|
|
patch("local_identity.cli.get_gecos_fullname", return_value="worsch"),
|
|
):
|
|
cmd_init(_args(username="tegwick", fullname="Bernd Worsch", email="bernd@example.com"))
|
|
|
|
primary = read_user("tegwick")
|
|
assert primary.username == "tegwick"
|
|
assert primary.fullname == "Bernd Worsch"
|
|
assert primary.email == "bernd@example.com"
|
|
|
|
def test_test_users_derived_from_override_username(self, tmp_store):
|
|
with patch("local_identity.cli.current_username", return_value="worsch"):
|
|
cmd_init(_args(username="tegwick", fullname="Bernd Worsch", email="bernd@example.com"))
|
|
|
|
users = {u.username for u in list_users()}
|
|
assert users == {"tegwick", "tegwick1", "tegwick2"}
|
|
|
|
def test_test_user_email_uses_override_email(self, tmp_store):
|
|
with patch("local_identity.cli.current_username", return_value="worsch"):
|
|
cmd_init(_args(username="tegwick", fullname="Bernd Worsch", email="bernd@example.com"))
|
|
|
|
assert read_user("tegwick1").email == "bernd+test1@example.com"
|
|
assert read_user("tegwick2").email == "bernd+test2@example.com"
|
|
|
|
def test_overrides_persisted_to_config(self, tmp_store):
|
|
with patch("local_identity.cli.current_username", return_value="worsch"):
|
|
cmd_init(_args(username="tegwick", fullname="Bernd Worsch", email="bernd@example.com"))
|
|
|
|
cfg = read_config()
|
|
assert cfg["username"] == "tegwick"
|
|
assert cfg["fullname"] == "Bernd Worsch"
|
|
assert cfg["email"] == "bernd@example.com"
|
|
|
|
def test_force_reinit_reads_config_without_flags(self, tmp_store):
|
|
with patch("local_identity.cli.current_username", return_value="worsch"):
|
|
cmd_init(_args(username="tegwick", fullname="Bernd Worsch", email="bernd@example.com"))
|
|
# Second init with --force and no flags — should reuse config
|
|
cmd_init(_args(force=True))
|
|
|
|
primary = read_user("tegwick")
|
|
assert primary.username == "tegwick"
|
|
assert primary.fullname == "Bernd Worsch"
|
|
|
|
def test_fails_if_store_exists_without_force(self, tmp_store):
|
|
with (
|
|
patch("local_identity.cli.current_username", return_value="worsch"),
|
|
pytest.raises(SystemExit) as exc_info,
|
|
):
|
|
cmd_init(_args(email="a@b.com"))
|
|
cmd_init(_args(email="a@b.com")) # second call should fail
|
|
|
|
assert exc_info.value.code == 1
|
|
|
|
|
|
# ------------------------------------------------------------------ #
|
|
# cmd_bootstrap_oidc #
|
|
# ------------------------------------------------------------------ #
|
|
|
|
class TestCmdBootstrapOidc:
|
|
def test_payload_uses_local_issuer_and_client_settings(self):
|
|
payload = _oidc_bootstrap_payload(
|
|
_oidc_args(
|
|
client_id="example-app",
|
|
redirect_uri="http://localhost:8080/oidc/callback",
|
|
port=9443,
|
|
)
|
|
)
|
|
|
|
assert payload == {
|
|
"issuer": "https://127.0.0.1:9443",
|
|
"discovery_url": "https://127.0.0.1:9443/.well-known/openid-configuration",
|
|
"client_id": "example-app",
|
|
"redirect_uri": "http://localhost:8080/oidc/callback",
|
|
"scope": "openid profile email",
|
|
"token_endpoint_auth_method": "none",
|
|
}
|
|
|
|
def test_rejects_non_loopback_redirect_uri(self):
|
|
with pytest.raises(ValueError, match="loopback"):
|
|
_oidc_bootstrap_payload(
|
|
_oidc_args(redirect_uri="https://example.com/callback")
|
|
)
|
|
|
|
def test_persists_client_bootstrap_config(self, tmp_store, capsys):
|
|
with patch("local_identity.cli.current_username", return_value="worsch"):
|
|
cmd_init(_args(username="alice", fullname="Alice Smith", email="alice@example.com"))
|
|
|
|
cmd_bootstrap_oidc(
|
|
_oidc_args(
|
|
client_id="demo",
|
|
redirect_uri="http://127.0.0.1:5173/auth/callback",
|
|
port=9443,
|
|
)
|
|
)
|
|
|
|
cfg = read_config()
|
|
assert cfg["last_oidc_bootstrap"] == "demo"
|
|
assert cfg["oidc_clients"]["demo"]["issuer"] == "https://127.0.0.1:9443"
|
|
assert cfg["oidc_clients"]["demo"]["redirect_uri"] == "http://127.0.0.1:5173/auth/callback"
|
|
|
|
out = capsys.readouterr().out
|
|
assert "OIDC_ISSUER=https://127.0.0.1:9443" in out
|
|
assert "OIDC_TOKEN_ENDPOINT_AUTH_METHOD=none" in out
|
|
|
|
def test_json_output(self, tmp_store, capsys):
|
|
with patch("local_identity.cli.current_username", return_value="worsch"):
|
|
cmd_init(_args(username="alice", fullname="Alice Smith", email="alice@example.com"))
|
|
|
|
cmd_bootstrap_oidc(_oidc_args(client_id="json-app", output="json"))
|
|
|
|
data = capsys.readouterr().out
|
|
assert '"client_id": "json-app"' in data
|
|
assert '"token_endpoint_auth_method": "none"' in data
|