feat: add daemon CLI rendering functions and tests
- Add print_daemon_table() function to display daemon list in rich table format - Add print_daemon_detail() function to display individual daemon information - Update render_output() to support daemon rendering with json/table formats - Add test for daemon create and list CLI commands with output validation - Integrate daemon output rendering into CLI workflow
This commit is contained in:
@@ -13,6 +13,7 @@ from rich.panel import Panel
|
|||||||
from rich.table import Table
|
from rich.table import Table
|
||||||
|
|
||||||
from app.core.errors import FeatureNotReadyError
|
from app.core.errors import FeatureNotReadyError
|
||||||
|
from app.domain.models import Daemon
|
||||||
from app.schemas.auth import AuthTokenResponse
|
from app.schemas.auth import AuthTokenResponse
|
||||||
from app.schemas.portfolio import CommentResponse, PortfolioDetailResponse, PortfolioListItem
|
from app.schemas.portfolio import CommentResponse, PortfolioDetailResponse, PortfolioListItem
|
||||||
from app.schemas.user import UserResponse
|
from app.schemas.user import UserResponse
|
||||||
@@ -163,6 +164,45 @@ def print_user_detail(console: Console, user: UserResponse) -> None:
|
|||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def print_daemon_table(console: Console, daemons: list[Daemon]) -> None:
|
||||||
|
table = Table(title="Daemons", expand=True)
|
||||||
|
table.add_column("ID", style="cyan", no_wrap=True)
|
||||||
|
table.add_column("Slug", no_wrap=True)
|
||||||
|
table.add_column("Display Name")
|
||||||
|
table.add_column("Active", no_wrap=True)
|
||||||
|
table.add_column("Created At", no_wrap=True)
|
||||||
|
table.add_column("Updated At", no_wrap=True)
|
||||||
|
for daemon in daemons:
|
||||||
|
table.add_row(
|
||||||
|
daemon.id,
|
||||||
|
daemon.daemon_slug,
|
||||||
|
daemon.display_name or "-",
|
||||||
|
"yes" if daemon.is_active else "no",
|
||||||
|
daemon.created_at.isoformat(timespec="seconds") if daemon.created_at else "N/A",
|
||||||
|
daemon.updated_at.isoformat(timespec="seconds") if daemon.updated_at else "N/A",
|
||||||
|
)
|
||||||
|
console.print(table)
|
||||||
|
|
||||||
|
|
||||||
|
def print_daemon_detail(console: Console, daemon: Daemon) -> None:
|
||||||
|
console.print(
|
||||||
|
Panel.fit(
|
||||||
|
"\n".join(
|
||||||
|
[
|
||||||
|
f"id: {daemon.id}",
|
||||||
|
f"owner_user_id: {daemon.owner_user_id}",
|
||||||
|
f"daemon_slug: {daemon.daemon_slug}",
|
||||||
|
f"display_name: {daemon.display_name or '-'}",
|
||||||
|
f"is_active: {daemon.is_active}",
|
||||||
|
f"created_at: {daemon.created_at.isoformat(timespec='seconds') if daemon.created_at else 'N/A'}",
|
||||||
|
f"updated_at: {daemon.updated_at.isoformat(timespec='seconds') if daemon.updated_at else 'N/A'}",
|
||||||
|
]
|
||||||
|
),
|
||||||
|
title="Daemon",
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def print_service_token_table(console: Console, tokens: list[AuthTokenResponse]) -> None:
|
def print_service_token_table(console: Console, tokens: list[AuthTokenResponse]) -> None:
|
||||||
table = Table(title="Service Tokens", expand=True)
|
table = Table(title="Service Tokens", expand=True)
|
||||||
table.add_column("Name", style="cyan", no_wrap=True)
|
table.add_column("Name", style="cyan", no_wrap=True)
|
||||||
@@ -235,6 +275,9 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m
|
|||||||
if not payload and empty_message:
|
if not payload and empty_message:
|
||||||
console.print(empty_message)
|
console.print(empty_message)
|
||||||
return
|
return
|
||||||
|
if all(isinstance(item, Daemon) for item in payload):
|
||||||
|
print_daemon_table(console, payload)
|
||||||
|
return
|
||||||
if all(isinstance(item, PortfolioListItem) for item in payload):
|
if all(isinstance(item, PortfolioListItem) for item in payload):
|
||||||
print_portfolio_table(console, payload)
|
print_portfolio_table(console, payload)
|
||||||
return
|
return
|
||||||
@@ -257,6 +300,10 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m
|
|||||||
print_user_detail(console, payload)
|
print_user_detail(console, payload)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if isinstance(payload, Daemon):
|
||||||
|
print_daemon_detail(console, payload)
|
||||||
|
return
|
||||||
|
|
||||||
if isinstance(payload, CommentResponse):
|
if isinstance(payload, CommentResponse):
|
||||||
print_comment_detail(console, payload)
|
print_comment_detail(console, payload)
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -903,6 +903,31 @@ def test_cli_help_exposes_bot_token_commands() -> None:
|
|||||||
assert "revoke" in result.stdout
|
assert "revoke" in result.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def test_cli_daemon_create_and_list_render_entries() -> None:
|
||||||
|
repo = FakeRepo(_portfolio())
|
||||||
|
repo.create_user("alice", permission="read")
|
||||||
|
runner = build_runner(repo)
|
||||||
|
|
||||||
|
create_result = runner.invoke(
|
||||||
|
runner.app,
|
||||||
|
["daemon", "create", "alpha-bot", "--user", "alice", "--display-name", "Alpha Bot"],
|
||||||
|
)
|
||||||
|
list_result = runner.invoke(
|
||||||
|
runner.app,
|
||||||
|
["daemon", "list", "--user", "alice"],
|
||||||
|
)
|
||||||
|
|
||||||
|
assert create_result.exit_code == 0
|
||||||
|
assert "Daemon" in create_result.stdout
|
||||||
|
assert "alpha-bot" in create_result.stdout
|
||||||
|
assert "Alpha Bot" in create_result.stdout
|
||||||
|
|
||||||
|
assert list_result.exit_code == 0
|
||||||
|
assert "Daemons" in list_result.stdout
|
||||||
|
assert "alpha-bot" in list_result.stdout
|
||||||
|
assert "Alpha Bot" in list_result.stdout
|
||||||
|
|
||||||
|
|
||||||
def test_cli_service_token_issue_supports_json() -> None:
|
def test_cli_service_token_issue_supports_json() -> None:
|
||||||
repo = FakeRepo(_portfolio())
|
repo = FakeRepo(_portfolio())
|
||||||
user = repo.create_user("alice", permission="read")
|
user = repo.create_user("alice", permission="read")
|
||||||
|
|||||||
Reference in New Issue
Block a user