from __future__ import annotations import json from pathlib import Path import pytest import typer from typer.core import TyperCommand from wf_api import WorkflowApi from wf_cli.context import ( CliTyperState, config_path_from_context, force_local_from_context, load_cli_context, rpc_timeout_from_context, rpc_url_from_context, ) from wf_server.config import build_workflow_server_from_workflow_config from .conftest import write_python_source_config def _typer_context(obj: object | None) -> typer.Context: ctx = typer.Context(TyperCommand(name="wf")) ctx.obj = obj return ctx def test_cli_typer_state_reads_typed_context_object() -> None: ctx = _typer_context( CliTyperState( config_path="remote.json", force_local=True, rpc_url="http://127.0.0.1:8000/rpc", rpc_timeout_seconds=2.5, ) ) assert config_path_from_context(ctx) == "remote.json" assert force_local_from_context(ctx) is True assert rpc_url_from_context(ctx) == "http://127.0.0.1:8000/rpc" assert rpc_timeout_from_context(ctx) == 2.5 def test_cli_typer_state_accepts_legacy_dict_context_object() -> None: ctx = _typer_context( { "config_path": "legacy.json", "force_local": True, "rpc_url": "http://localhost:9000/rpc", "rpc_timeout_seconds": 3, } ) assert config_path_from_context(ctx) == "legacy.json" assert force_local_from_context(ctx) is True assert rpc_url_from_context(ctx) == "http://localhost:9000/rpc" assert rpc_timeout_from_context(ctx) == 3.0 def test_cli_typer_state_defaults_for_missing_context_object() -> None: ctx = _typer_context(None) assert config_path_from_context(ctx) == "wf.config.json" assert force_local_from_context(ctx) is False assert rpc_url_from_context(ctx) is None assert rpc_timeout_from_context(ctx) is None def test_load_cli_context_builds_service_and_handlers(tmp_path: Path) -> None: root = tmp_path / "wf_cli_context" root.mkdir() config_path = root / "wf_mcp.config.json" config_path.write_text( json.dumps( { "store_root": ".wf_mcp_store", "connections": [ { "id": "demo.personal", "server": "demo", "account": "personal", } ], } ), encoding="utf-8", ) context = load_cli_context(config_path) service = context.service assert service is not None assert isinstance(context.handlers, WorkflowApi) assert context.config_path == config_path assert service.connections.list_all()[0].id == "demo.personal" assert context.handlers.context.artifact_store is service.artifact_store assert ( context.handlers.context.draft_workspace_store is service.draft_workspace_store ) assert context.handlers.context.run_store is service.run_store def test_load_cli_context_local_uses_workflow_store_override( tmp_path: Path, monkeypatch, ) -> None: config_path = tmp_path / "wf.json" config_path.write_text( json.dumps( { "version": 1, "client": {"target": {"kind": "local"}}, "server": { "store": {"kind": "filesystem", "root": ".default"}, "stores": { "workflow": { "kind": "filesystem", "root": ".workflow", } }, }, } ), encoding="utf-8", ) captured: dict[str, object] = {} def fake_build_workflow_server_from_workflow_config(config): captured["store_root"] = config.server.workflow_store.root return build_workflow_server_from_workflow_config(config) monkeypatch.setattr( "wf_cli.context.build_workflow_server_from_workflow_config", fake_build_workflow_server_from_workflow_config, ) context = load_cli_context(config_path) assert context.service is None assert captured["store_root"] == (tmp_path / ".workflow").resolve() @pytest.mark.asyncio async def test_load_cli_context_local_composes_configured_python_sources( tmp_path: Path, ) -> None: config_path = write_python_source_config( tmp_path, target={ "kind": "rpc_http", "url": "http://127.0.0.1:8765/rpc", }, ) context = load_cli_context(config_path, force_local=True) listed = await context.handlers.list_capabilities( source_id="local.ops", limit=100, ) assert {capability["name"] for capability in listed["capabilities"]} == { "local.ops.echo" }