feat(auth): CLI users subcommand
This commit is contained in:
@@ -0,0 +1,128 @@
|
||||
"""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
|
||||
Reference in New Issue
Block a user