pbi-cli/tests/test_mcp_client.py
MinaSaad1 51a23668a7 feat: add REPL mode, test suite, CI/CD, and Claude Skills (Sprints 6-8)
Sprint 6 - REPL Mode + Polish:
- Error hierarchy (PbiCliError, McpToolError, etc.) for clean REPL error handling
- Interactive REPL with prompt-toolkit (persistent MCP connection, command completion, history)
- REPL-aware run_tool() and connection commands that reuse shared client
- README.md and CHANGELOG.md

Sprint 7 - Tests + CI/CD:
- 120 tests across unit, command, and e2e test files (79% coverage)
- MockPbiMcpClient with canned responses for test isolation
- GitHub Actions CI (lint + typecheck + test matrix: 3 OS x 3 Python)
- GitHub Actions release workflow for PyPI trusted publishing

Sprint 8 - Claude Skills + Installer:
- 5 bundled SKILL.md files (modeling, dax, deployment, security, docs)
- `pbi skills install/list/uninstall` command for Claude Code discovery
- Skills packaged with wheel via setuptools package-data
2026-03-26 13:54:24 +02:00

86 lines
2.3 KiB
Python

"""Tests for pbi_cli.core.mcp_client (unit-level, no real server)."""
from __future__ import annotations
import json
from pbi_cli.core.mcp_client import (
_extract_text,
_parse_content,
get_client,
get_shared_client,
)
# ---------------------------------------------------------------------------
# _parse_content tests
# ---------------------------------------------------------------------------
class FakeTextContent:
"""Mimics mcp TextContent blocks."""
def __init__(self, text: str) -> None:
self.text = text
def test_parse_content_single_json() -> None:
blocks = [FakeTextContent('{"name": "Sales"}')]
result = _parse_content(blocks)
assert result == {"name": "Sales"}
def test_parse_content_single_plain_text() -> None:
blocks = [FakeTextContent("just a string")]
result = _parse_content(blocks)
assert result == "just a string"
def test_parse_content_multiple_blocks() -> None:
blocks = [FakeTextContent("hello"), FakeTextContent(" world")]
result = _parse_content(blocks)
assert result == "hello\n world"
def test_parse_content_non_list() -> None:
result = _parse_content("raw value")
assert result == "raw value"
def test_parse_content_json_array() -> None:
blocks = [FakeTextContent('[{"a": 1}]')]
result = _parse_content(blocks)
assert result == [{"a": 1}]
# ---------------------------------------------------------------------------
# _extract_text tests
# ---------------------------------------------------------------------------
def test_extract_text_from_blocks() -> None:
blocks = [FakeTextContent("error occurred")]
result = _extract_text(blocks)
assert result == "error occurred"
def test_extract_text_non_list() -> None:
result = _extract_text("plain error")
assert result == "plain error"
# ---------------------------------------------------------------------------
# get_client / get_shared_client tests
# ---------------------------------------------------------------------------
def test_get_client_oneshot_returns_fresh() -> None:
c1 = get_client(repl_mode=False)
c2 = get_client(repl_mode=False)
assert c1 is not c2
def test_get_shared_client_returns_same_instance() -> None:
c1 = get_shared_client()
c2 = get_shared_client()
assert c1 is c2