Files
2026-06-22 15:34:18 -06:00

129 lines
3.9 KiB
Python

"""CLI: python -m cyclone users {create,list,disable,reset-password,set-role}.
Uses Click's CliRunner (matches the existing parse-837/parse-835 CLI tests).
The full CLI group lives in ``cyclone.cli.main`` — we exercise the
``users`` subgroup through the top-level ``main`` so the wiring is real.
"""
from __future__ import annotations
import pytest
from sqlalchemy import delete, select
from cyclone.auth import users
from cyclone.cli import main as cli_main
from cyclone.db import Session as DbSession
from cyclone.db import SessionLocal, User
@pytest.fixture(autouse=True)
def _clear():
with SessionLocal()() as db:
db.execute(delete(DbSession))
db.execute(delete(User))
db.commit()
yield
with SessionLocal()() as db:
db.execute(delete(DbSession))
db.execute(delete(User))
db.commit()
def test_create_user_via_cli():
from click.testing import CliRunner
runner = CliRunner()
result = runner.invoke(
cli_main,
[
"users", "create", "cli-user",
"--role", "viewer",
"--password", "clipassword1",
],
input="", # don't prompt
)
assert result.exit_code == 0, result.output
with SessionLocal()() as db:
u = users.get_by_username(db, "cli-user")
assert u is not None
assert u.role == "viewer"
def test_list_users_via_cli():
from click.testing import CliRunner
with SessionLocal()() as db:
users.create(db, username="listed", password="hunter2hunter2", role="user")
runner = CliRunner()
result = runner.invoke(cli_main, ["users", "list"])
assert result.exit_code == 0, result.output
assert "listed" in result.output
def test_disable_user_via_cli():
from click.testing import CliRunner
with SessionLocal()() as db:
users.create(db, username="todie", password="hunter2hunter2", role="user")
runner = CliRunner()
result = runner.invoke(cli_main, ["users", "disable", "todie"])
assert result.exit_code == 0, result.output
with SessionLocal()() as db:
u = users.get_by_username(db, "todie")
assert u.disabled_at is not None
def test_reset_password_via_cli():
from click.testing import CliRunner
with SessionLocal()() as db:
users.create(db, username="pwchange", password="oldpassword1", role="user")
runner = CliRunner()
result = runner.invoke(
cli_main,
[
"users", "reset-password", "pwchange",
"--password", "newpassword1",
],
)
assert result.exit_code == 0, result.output
with SessionLocal()() as db:
u = users.get_by_username(db, "pwchange")
assert users.verify_password("newpassword1", u.password_hash)
def test_set_role_via_cli():
from click.testing import CliRunner
with SessionLocal()() as db:
users.create(db, username="promote", password="hunter2hunter2", role="viewer")
runner = CliRunner()
result = runner.invoke(
cli_main,
["users", "set-role", "promote", "--role", "admin"],
)
assert result.exit_code == 0, result.output
with SessionLocal()() as db:
u = users.get_by_username(db, "promote")
assert u.role == "admin"
def test_create_rejects_short_password():
from click.testing import CliRunner
runner = CliRunner()
result = runner.invoke(
cli_main,
[
"users", "create", "weak",
"--role", "viewer",
"--password", "short",
],
)
# Click surfaces validation failures with a non-zero exit code.
assert result.exit_code != 0, result.output
with SessionLocal()() as db:
rows = db.execute(select(User).where(User.username == "weak")).scalars().all()
assert rows == []
def test_disable_unknown_user_exits_nonzero():
from click.testing import CliRunner
runner = CliRunner()
result = runner.invoke(cli_main, ["users", "disable", "ghost"])
assert result.exit_code != 0, result.output