Add CLI source code and update .gitignore
This commit is contained in:
307
cli_anything/codimd/tests/test_full_e2e.py
Normal file
307
cli_anything/codimd/tests/test_full_e2e.py
Normal file
@@ -0,0 +1,307 @@
|
||||
"""
|
||||
End-to-end tests for CodiMD CLI.
|
||||
|
||||
Tests use real files and simulate full CLI workflows.
|
||||
"""
|
||||
|
||||
import pytest
|
||||
import json
|
||||
import subprocess
|
||||
import tempfile
|
||||
from pathlib import Path
|
||||
from unittest.mock import Mock, patch
|
||||
import sys
|
||||
|
||||
# Add parent directory to path
|
||||
sys.path.insert(0, str(Path(__file__).parent.parent.parent))
|
||||
|
||||
from cli_anything.codimd.core import Config, Session, CodiMDClient
|
||||
from cli_anything.codimd.codimd_cli import cli
|
||||
from click.testing import CliRunner
|
||||
|
||||
|
||||
class TestCLISubprocess:
|
||||
"""Tests for installed CLI via subprocess."""
|
||||
|
||||
@staticmethod
|
||||
def _resolve_cli(cli_name: str) -> str:
|
||||
"""Resolve CLI command path.
|
||||
|
||||
If CLI_ANYTHING_FORCE_INSTALLED is set, use the command name directly.
|
||||
Otherwise, use the Python module path.
|
||||
"""
|
||||
if "CLI_ANYTHING_FORCE_INSTALLED" in str(__import__("os").environ):
|
||||
return cli_name
|
||||
# For testing, use python -m to run the module
|
||||
return f"python -m cli_anything.codimd.codimd_cli"
|
||||
|
||||
def test_cli_help(self):
|
||||
"""Test CLI help command."""
|
||||
result = subprocess.run(
|
||||
["python", "-m", "cli_anything.codimd.codimd_cli", "--help"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
assert result.returncode == 0
|
||||
assert "CodiMD CLI" in result.stdout
|
||||
|
||||
def test_cli_version(self):
|
||||
"""Test CLI version command."""
|
||||
result = subprocess.run(
|
||||
["python", "-m", "cli_anything.codimd.codimd_cli", "--version"],
|
||||
capture_output=True,
|
||||
text=True
|
||||
)
|
||||
assert result.returncode == 0
|
||||
assert "0.1.0" in result.stdout
|
||||
|
||||
|
||||
class TestE2EClickRunner:
|
||||
"""End-to-end tests using Click CliRunner."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment."""
|
||||
self.runner = CliRunner()
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment."""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
def test_config_get_command(self, mock_config_class):
|
||||
"""Test config get command."""
|
||||
mock_config = Mock()
|
||||
mock_config.server = "http://localhost:3000"
|
||||
mock_config.timeout = 30
|
||||
mock_config.verify_ssl = True
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
result = self.runner.invoke(cli, ["config", "get"])
|
||||
assert result.exit_code == 0
|
||||
assert "Server:" in result.output or "localhost:3000" in result.output
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
def test_config_set_command(self, mock_config_class):
|
||||
"""Test config set command."""
|
||||
mock_config = Mock()
|
||||
mock_config.server = "http://localhost:3000"
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
result = self.runner.invoke(cli, ["config", "set", "timeout", "60"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
def test_help_command(self):
|
||||
"""Test help command."""
|
||||
result = self.runner.invoke(cli, ["--help"])
|
||||
assert result.exit_code == 0
|
||||
assert "CodiMD CLI" in result.output
|
||||
assert "note" in result.output
|
||||
assert "user" in result.output
|
||||
assert "export" in result.output
|
||||
|
||||
def test_version_command(self):
|
||||
"""Test version command."""
|
||||
result = self.runner.invoke(cli, ["--version"])
|
||||
assert result.exit_code == 0
|
||||
assert "0.1.0" in result.output
|
||||
|
||||
def test_note_help(self):
|
||||
"""Test note group help."""
|
||||
result = self.runner.invoke(cli, ["note", "--help"])
|
||||
assert result.exit_code == 0
|
||||
assert "list" in result.output
|
||||
assert "get" in result.output
|
||||
assert "create" in result.output
|
||||
|
||||
def test_user_help(self):
|
||||
"""Test user group help."""
|
||||
result = self.runner.invoke(cli, ["user", "--help"])
|
||||
assert result.exit_code == 0
|
||||
assert "me" in result.output
|
||||
assert "login" in result.output
|
||||
assert "logout" in result.output
|
||||
|
||||
def test_export_help(self):
|
||||
"""Test export group help."""
|
||||
result = self.runner.invoke(cli, ["export", "--help"])
|
||||
assert result.exit_code == 0
|
||||
assert "markdown" in result.output
|
||||
assert "pdf" in result.output
|
||||
assert "html" in result.output
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
@patch("cli_anything.codimd.codimd_cli.Session")
|
||||
def test_json_output_mode(self, mock_session_class, mock_config_class):
|
||||
"""Test JSON output mode."""
|
||||
mock_config = Mock()
|
||||
mock_config.config_dir = Path(self.temp_dir)
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.is_authenticated.return_value = False
|
||||
mock_session_class.return_value = mock_session
|
||||
|
||||
result = self.runner.invoke(cli, ["--json", "config", "get"])
|
||||
# Should output JSON
|
||||
assert '"status"' in result.output or '{"server"' in result.output or '"timeout"' in result.output
|
||||
|
||||
|
||||
class TestE2ENoteCommands:
|
||||
"""End-to-end tests for note commands."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment."""
|
||||
self.runner = CliRunner()
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment."""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.get_client")
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
@patch("cli_anything.codimd.codimd_cli.Session")
|
||||
def test_note_list_authenticated(self, mock_session_class, mock_config_class, mock_get_client):
|
||||
"""Test listing notes when authenticated."""
|
||||
mock_config = Mock()
|
||||
mock_config.config_dir = Path(self.temp_dir)
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.is_authenticated.return_value = True
|
||||
mock_session_class.return_value = mock_session
|
||||
|
||||
mock_client = Mock()
|
||||
mock_note_manager = Mock()
|
||||
mock_note_manager.list_my_notes.return_value = [
|
||||
{"id": "abc123", "text": "Test Note"}
|
||||
]
|
||||
mock_get_client.return_value = (mock_client, mock_session)
|
||||
|
||||
with patch("cli_anything.codimd.codimd_cli.NoteManager", return_value=mock_note_manager):
|
||||
result = self.runner.invoke(cli, ["note", "list"])
|
||||
assert result.exit_code == 0
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.get_client")
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
@patch("cli_anything.codimd.codimd_cli.Session")
|
||||
def test_note_create(self, mock_session_class, mock_config_class, mock_get_client):
|
||||
"""Test creating a note."""
|
||||
mock_config = Mock()
|
||||
mock_config.config_dir = Path(self.temp_dir)
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session_class.return_value = mock_session
|
||||
|
||||
mock_client = Mock()
|
||||
mock_note_manager = Mock()
|
||||
mock_note_manager.create_note.return_value = {
|
||||
"id": "new-id",
|
||||
"url": "http://example.com/new-id",
|
||||
"status": "created"
|
||||
}
|
||||
mock_get_client.return_value = (mock_client, mock_session)
|
||||
|
||||
with patch("cli_anything.codimd.codimd_cli.NoteManager", return_value=mock_note_manager):
|
||||
result = self.runner.invoke(cli, ["note", "create", "--content", "Test content"])
|
||||
assert result.exit_code == 0
|
||||
assert "created" in result.output
|
||||
|
||||
|
||||
class TestE2EExportCommands:
|
||||
"""End-to-end tests for export commands."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment."""
|
||||
self.runner = CliRunner()
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment."""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.get_client")
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
@patch("cli_anything.codimd.codimd_cli.Session")
|
||||
def test_export_markdown_to_file(self, mock_session_class, mock_config_class, mock_get_client):
|
||||
"""Test exporting markdown to file."""
|
||||
mock_config = Mock()
|
||||
mock_config.config_dir = Path(self.temp_dir)
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session_class.return_value = mock_session
|
||||
|
||||
mock_client = Mock()
|
||||
mock_export_manager = Mock()
|
||||
mock_export_manager.export_markdown.return_value = "# Test Note"
|
||||
mock_get_client.return_value = (mock_client, mock_session)
|
||||
|
||||
output_file = Path(self.temp_dir) / "output.md"
|
||||
|
||||
with patch("cli_anything.codimd.codimd_cli.ExportManager", return_value=mock_export_manager):
|
||||
result = self.runner.invoke(
|
||||
cli,
|
||||
["export", "markdown", "test-id", "--output", str(output_file)]
|
||||
)
|
||||
assert result.exit_code == 0
|
||||
|
||||
|
||||
class TestE2EUserCommands:
|
||||
"""End-to-end tests for user commands."""
|
||||
|
||||
def setup_method(self):
|
||||
"""Set up test environment."""
|
||||
self.runner = CliRunner()
|
||||
self.temp_dir = tempfile.mkdtemp()
|
||||
|
||||
def teardown_method(self):
|
||||
"""Clean up test environment."""
|
||||
import shutil
|
||||
shutil.rmtree(self.temp_dir, ignore_errors=True)
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.get_client")
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
@patch("cli_anything.codimd.codimd_cli.Session")
|
||||
def test_user_logout(self, mock_session_class, mock_config_class, mock_get_client):
|
||||
"""Test user logout."""
|
||||
mock_config = Mock()
|
||||
mock_config.config_dir = Path(self.temp_dir)
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session_class.return_value = mock_session
|
||||
|
||||
mock_get_client.return_value = (Mock(), mock_session)
|
||||
|
||||
result = self.runner.invoke(cli, ["user", "logout"])
|
||||
assert result.exit_code == 0
|
||||
assert "logged out" in result.output.lower()
|
||||
|
||||
@patch("cli_anything.codimd.codimd_cli.get_client")
|
||||
@patch("cli_anything.codimd.codimd_cli.Config")
|
||||
@patch("cli_anything.codimd.codimd_cli.Session")
|
||||
def test_user_me_unauthenticated(self, mock_session_class, mock_config_class, mock_get_client):
|
||||
"""Test getting user info when not authenticated."""
|
||||
mock_config = Mock()
|
||||
mock_config.config_dir = Path(self.temp_dir)
|
||||
mock_config_class.return_value = mock_config
|
||||
|
||||
mock_session = Mock()
|
||||
mock_session.is_authenticated.return_value = False
|
||||
mock_session_class.return_value = mock_session
|
||||
|
||||
mock_get_client.return_value = (Mock(), mock_session)
|
||||
|
||||
result = self.runner.invoke(cli, ["user", "me"])
|
||||
assert result.exit_code == 1
|
||||
assert "authenticated" in result.output.lower()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
pytest.main([__file__, "-v"])
|
||||
Reference in New Issue
Block a user