e4dcfc49aa
Tests / Import Check (Python 3.13) (push) Has been cancelled
Tests / Import Check (Python 3.14) (push) Has been cancelled
Tests / Python Tests (Python 3.11) (push) Has been cancelled
Tests / Python Tests (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.14) (push) Has been cancelled
Tests / Test Summary (push) Has been cancelled
Tests / Lint and Format (push) Has been cancelled
Tests / Web Node Tests (push) Has been cancelled
Tests / Import Check (Python 3.11) (push) Has been cancelled
Tests / Import Check (Python 3.12) (push) Has been cancelled
Tests / Python Tests (Python 3.13) (push) Has been cancelled
216 lines
7.1 KiB
Python
216 lines
7.1 KiB
Python
"""CLI smoke tests for the standalone ``deeptutor-cli`` package."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
from typing import Any
|
|
|
|
import pytest
|
|
from typer.testing import CliRunner
|
|
|
|
from deeptutor.app import DeepTutorApp, TurnRequest
|
|
from deeptutor.runtime.bootstrap.builtin_capabilities import BUILTIN_CAPABILITY_CLASSES
|
|
from deeptutor_cli.main import app
|
|
|
|
runner = CliRunner()
|
|
|
|
|
|
def _install_fake_runtime(monkeypatch, captured_requests: list[TurnRequest]) -> None:
|
|
async def _start_turn(self, request): # noqa: ANN001
|
|
if isinstance(request, dict):
|
|
request = TurnRequest(**request)
|
|
captured_requests.append(request)
|
|
return {"id": request.session_id or "session-1"}, {"id": "turn-1"}
|
|
|
|
async def _stream_turn(self, turn_id: str, after_seq: int = 0): # noqa: ANN001
|
|
yield {"type": "session", "turn_id": turn_id, "seq": after_seq}
|
|
yield {"type": "stage_start", "stage": "responding"}
|
|
yield {"type": "content", "content": "response body"}
|
|
yield {"type": "result", "metadata": {"response": "response body"}}
|
|
yield {"type": "done"}
|
|
|
|
monkeypatch.setattr("deeptutor.app.facade.DeepTutorApp.start_turn", _start_turn)
|
|
monkeypatch.setattr("deeptutor.app.facade.DeepTutorApp.stream_turn", _stream_turn)
|
|
|
|
|
|
def test_run_command_json_mode(monkeypatch) -> None:
|
|
captured_requests: list[TurnRequest] = []
|
|
_install_fake_runtime(monkeypatch, captured_requests)
|
|
|
|
capabilities = list(BUILTIN_CAPABILITY_CLASSES)
|
|
|
|
for cap in capabilities:
|
|
result = runner.invoke(
|
|
app,
|
|
[
|
|
"run",
|
|
cap,
|
|
"hello world",
|
|
"--format",
|
|
"json",
|
|
"--tool",
|
|
"rag",
|
|
"--kb",
|
|
"demo-kb",
|
|
"--history-ref",
|
|
"session-old",
|
|
"--notebook-ref",
|
|
"nb1:rec1,rec2",
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
lines = [json.loads(line) for line in result.output.splitlines() if line.strip()]
|
|
assert any(line["type"] == "result" for line in lines)
|
|
|
|
assert len(captured_requests) == len(capabilities)
|
|
assert captured_requests[0].capability == "chat"
|
|
assert captured_requests[0].tools == ["rag"]
|
|
assert captured_requests[0].knowledge_bases == ["demo-kb"]
|
|
assert captured_requests[0].history_references == ["session-old"]
|
|
assert captured_requests[0].notebook_references == [
|
|
{"notebook_id": "nb1", "record_ids": ["rec1", "rec2"]}
|
|
]
|
|
assert {request.capability for request in captured_requests} == set(capabilities)
|
|
|
|
|
|
def test_builtin_capability_aliases_resolve_to_canonical_names() -> None:
|
|
runtime = DeepTutorApp()
|
|
|
|
assert runtime.resolve_capability("solve") == "deep_solve"
|
|
assert runtime.resolve_capability("quiz") == "deep_question"
|
|
assert runtime.resolve_capability("research") == "deep_research"
|
|
assert runtime.resolve_capability("viz") == "visualize"
|
|
assert runtime.resolve_capability("animate") == "math_animator"
|
|
assert runtime.resolve_capability("mastery") == "mastery_path"
|
|
with pytest.raises(ValueError, match="Unknown capability `auto`"):
|
|
runtime.resolve_capability("auto")
|
|
|
|
|
|
def test_run_command_rejects_removed_auto_capability() -> None:
|
|
result = runner.invoke(app, ["run", "auto", "hello"])
|
|
|
|
assert result.exit_code != 0
|
|
assert isinstance(result.exception, ValueError)
|
|
assert "Unknown capability `auto`" in str(result.exception)
|
|
|
|
|
|
def test_run_command_rich_mode(monkeypatch) -> None:
|
|
captured_requests: list[TurnRequest] = []
|
|
_install_fake_runtime(monkeypatch, captured_requests)
|
|
|
|
result = runner.invoke(app, ["run", "chat", "hello rich"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert "response body" in result.output
|
|
assert captured_requests[0].capability == "chat"
|
|
|
|
|
|
def test_run_command_with_config(monkeypatch) -> None:
|
|
captured_requests: list[TurnRequest] = []
|
|
_install_fake_runtime(monkeypatch, captured_requests)
|
|
|
|
result = runner.invoke(
|
|
app,
|
|
[
|
|
"run",
|
|
"deep_research",
|
|
"compare retrieval stacks",
|
|
"--config-json",
|
|
'{"mode":"report","depth":"deep"}',
|
|
],
|
|
)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
request = captured_requests[0]
|
|
assert request.capability == "deep_research"
|
|
assert request.config == {
|
|
"mode": "report",
|
|
"depth": "deep",
|
|
}
|
|
|
|
|
|
def test_chat_repl_config_commands_match_docs_syntax(monkeypatch) -> None:
|
|
captured_requests: list[TurnRequest] = []
|
|
_install_fake_runtime(monkeypatch, captured_requests)
|
|
|
|
result = runner.invoke(
|
|
app,
|
|
["chat", "--config", "initial=true"],
|
|
input=(
|
|
"/config set num_questions 5\n"
|
|
'/config set question_types \'["short_answer","mcq"]\'\n'
|
|
"/refs\n"
|
|
"Generate questions\n"
|
|
"/quit\n"
|
|
),
|
|
)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert '"num_questions": 5' in result.output
|
|
assert '"question_types"' in result.output
|
|
assert captured_requests[0].config == {
|
|
"initial": True,
|
|
"num_questions": 5,
|
|
"question_types": ["short_answer", "mcq"],
|
|
}
|
|
|
|
|
|
def test_chat_repl_backslash_continuation_sends_single_message(monkeypatch) -> None:
|
|
captured_requests: list[TurnRequest] = []
|
|
_install_fake_runtime(monkeypatch, captured_requests)
|
|
|
|
result = runner.invoke(
|
|
app,
|
|
["chat"],
|
|
input="Please review this code:\\\ndef fib(n): return n\n/quit\n",
|
|
)
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert captured_requests[0].content == "Please review this code:\ndef fib(n): return n"
|
|
|
|
|
|
def test_plugin_info_includes_capability_aliases_and_availability() -> None:
|
|
result = runner.invoke(app, ["plugin", "info", "deep_solve"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
payload = json.loads(result.output)
|
|
assert payload["name"] == "deep_solve"
|
|
assert payload["cli_aliases"] == ["solve"]
|
|
assert payload["availability"]["available"] is True
|
|
|
|
|
|
def test_session_list_command_uses_shared_store(monkeypatch) -> None:
|
|
async def _list_sessions(self, limit: int = 50, offset: int = 0): # noqa: ANN001
|
|
return [
|
|
{
|
|
"id": "session-1",
|
|
"title": "Algebra",
|
|
"capability": "chat",
|
|
"status": "completed",
|
|
"message_count": 4,
|
|
}
|
|
]
|
|
|
|
monkeypatch.setattr("deeptutor.app.facade.DeepTutorApp.list_sessions", _list_sessions)
|
|
|
|
result = runner.invoke(app, ["session", "list"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert "session-1" in result.output
|
|
assert "Algebra" in result.output
|
|
|
|
|
|
def test_start_command_delegates_to_runtime_launcher(monkeypatch) -> None:
|
|
calls: list[object] = []
|
|
|
|
def _fake_start(home=None): # noqa: ANN001
|
|
calls.append(home)
|
|
|
|
monkeypatch.setattr("deeptutor.runtime.launcher.start", _fake_start)
|
|
|
|
result = runner.invoke(app, ["start"])
|
|
|
|
assert result.exit_code == 0, result.output
|
|
assert calls == [None]
|