# TISBackup Test Suite This directory contains the test suite for TISBackup using pytest. ## Running Tests ### Run all tests ```bash uv run pytest ``` ### Run tests for a specific module ```bash uv run pytest tests/test_ssh.py ``` ### Run with verbose output ```bash uv run pytest -v ``` ### Run tests matching a pattern ```bash uv run pytest -k "ssh" -v ``` ### Run with coverage (requires pytest-cov) ```bash uv run pytest --cov=libtisbackup --cov-report=html ``` ## Test Structure ### Current Test Modules - **[test_ssh.py](test_ssh.py)** - Tests for SSH operations module - `TestLoadSSHPrivateKey` - Tests for key loading with Ed25519, ECDSA, and RSA support - `TestSSHExec` - Tests for remote command execution via SSH - `TestSSHModuleIntegration` - Integration tests for SSH functionality ## Test Categories Tests are organized using pytest markers: - `@pytest.mark.unit` - Unit tests for individual functions - `@pytest.mark.integration` - Integration tests for multiple components - `@pytest.mark.ssh` - SSH-related tests - `@pytest.mark.slow` - Long-running tests ### Run only unit tests ```bash uv run pytest -m unit ``` ### Run only SSH tests ```bash uv run pytest -m ssh ``` ## Writing New Tests ### Test File Naming - Test files should be named `test_*.py` - Place them in the `tests/` directory ### Test Class Naming - Test classes should start with `Test` - Example: `TestMyModule` ### Test Function Naming - Test functions should start with `test_` - Use descriptive names: `test_load_ed25519_key_success` ### Example Test Structure ```python import pytest from libtisbackup.mymodule import my_function class TestMyFunction: """Test cases for my_function.""" def test_basic_functionality(self): """Test basic use case.""" result = my_function("input") assert result == "expected_output" def test_error_handling(self): """Test error handling.""" with pytest.raises(ValueError): my_function(None) ``` ## Mocking The test suite uses `pytest-mock` for mocking dependencies. Common patterns: ### Mocking with patch ```python from unittest.mock import patch, Mock def test_with_mock(): with patch('module.function') as mock_func: mock_func.return_value = "mocked" result = my_code() assert result == "mocked" ``` ### Using pytest fixtures ```python @pytest.fixture def mock_ssh_client(): return Mock(spec=paramiko.SSHClient) def test_with_fixture(mock_ssh_client): # Use the fixture pass ``` ## Coverage Goals Aim for: - **80%+** overall code coverage - **90%+** for critical modules (ssh, database, base_driver) - **100%** for utility functions ## Test Configuration Test configuration is in the `[tool.pytest.ini_options]` section of [pyproject.toml](../pyproject.toml): - Test discovery patterns - Output formatting - Markers definition - Minimum Python version ## Continuous Integration Tests should pass before merging: ```bash # Run linting uv run ruff check . # Run tests uv run pytest -v # Both must pass ```