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:
2026-05-14 06:38:21 +00:00
parent 3bb3ff547d
commit f4663f202e
2 changed files with 72 additions and 0 deletions
+47
View File
@@ -13,6 +13,7 @@ from rich.panel import Panel
from rich.table import Table
from app.core.errors import FeatureNotReadyError
from app.domain.models import Daemon
from app.schemas.auth import AuthTokenResponse
from app.schemas.portfolio import CommentResponse, PortfolioDetailResponse, PortfolioListItem
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:
table = Table(title="Service Tokens", expand=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:
console.print(empty_message)
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):
print_portfolio_table(console, payload)
return
@@ -257,6 +300,10 @@ def render_output(console: Console, output_format: str, payload: Any, *, empty_m
print_user_detail(console, payload)
return
if isinstance(payload, Daemon):
print_daemon_detail(console, payload)
return
if isinstance(payload, CommentResponse):
print_comment_detail(console, payload)
return
+25
View File
@@ -903,6 +903,31 @@ def test_cli_help_exposes_bot_token_commands() -> None:
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:
repo = FakeRepo(_portfolio())
user = repo.create_user("alice", permission="read")