Files
LangBot/tests/README.md

184 lines
5.3 KiB
Markdown
Raw Permalink Normal View History

feat: add comprehensive unit tests for pipeline stages (#1701) * feat: add comprehensive unit tests for pipeline stages * fix: deps install in ci * ci: use venv * ci: run run_tests.sh * fix: resolve circular import issues in pipeline tests Update all test files to use lazy imports via importlib.import_module() to avoid circular dependency errors. Fix mock_conversation fixture to properly mock list.copy() method. Changes: - Use lazy import pattern in all test files - Fix conftest.py fixture for conversation messages - Add integration test file for full import tests - Update documentation with known issues and workarounds Tests now successfully avoid circular import errors while maintaining full test coverage of pipeline stages. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * docs: add comprehensive testing summary Document implementation details, challenges, solutions, and future improvements for the pipeline unit test suite. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: rewrite unit tests to test actual pipeline stage code Rewrote unit tests to properly test real stage implementations instead of mock logic: - Test actual BanSessionCheckStage with 7 test cases (100% coverage) - Test actual RateLimit stage with 3 test cases (70% coverage) - Test actual PipelineManager with 5 test cases - Use lazy imports via import_module to avoid circular dependencies - Import pipelinemgr first to ensure proper stage registration - Use Query.model_construct() to bypass Pydantic validation in tests - Remove obsolete pure unit tests that didn't test real code - All 20 tests passing with 48% overall pipeline coverage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * test: add unit tests for GroupRespondRuleCheckStage Added comprehensive unit tests for resprule stage: - Test person message skips rule check - Test group message with no matching rules (INTERRUPT) - Test group message with matching rule (CONTINUE) - Test AtBotRule removes At component correctly - Test AtBotRule when no At component present Coverage: 100% on resprule.py and atbot.py All 25 tests passing with 51% overall pipeline coverage 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * refactor: restructure tests to tests/unit_tests/pipeline Reorganized test directory structure to support multiple test categories: - Move tests/pipeline → tests/unit_tests/pipeline - Rename .github/workflows/pipeline-tests.yml → run-tests.yml - Update run_tests.sh to run all unit tests (not just pipeline) - Update workflow to trigger on all pkg/** and tests/** changes - Coverage now tracks entire pkg/ module instead of just pipeline This structure allows for easy addition of more unit tests for other modules in the future. All 25 tests passing with 21% overall pkg coverage. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com> * ci: upload codecov report * ci: codecov file * ci: coverage.xml --------- Co-authored-by: Claude <noreply@anthropic.com>
2025-10-01 10:56:59 +08:00
# LangBot Test Suite
This directory contains the test suite for LangBot, with a focus on comprehensive unit testing of pipeline stages.
## Important Note
Due to circular import dependencies in the pipeline module structure, the test files use **lazy imports** via `importlib.import_module()` instead of direct imports. This ensures tests can run without triggering circular import errors.
## Structure
```
tests/
├── pipeline/ # Pipeline stage tests
│ ├── conftest.py # Shared fixtures and test infrastructure
│ ├── test_simple.py # Basic infrastructure tests (always pass)
│ ├── test_bansess.py # BanSessionCheckStage tests
│ ├── test_ratelimit.py # RateLimit stage tests
│ ├── test_preproc.py # PreProcessor stage tests
│ ├── test_respback.py # SendResponseBackStage tests
│ ├── test_resprule.py # GroupRespondRuleCheckStage tests
│ ├── test_pipelinemgr.py # PipelineManager tests
│ └── test_stages_integration.py # Integration tests
└── README.md # This file
```
## Test Architecture
### Fixtures (`conftest.py`)
The test suite uses a centralized fixture system that provides:
- **MockApplication**: Comprehensive mock of the Application object with all dependencies
- **Mock objects**: Pre-configured mocks for Session, Conversation, Model, Adapter
- **Sample data**: Ready-to-use Query objects, message chains, and configurations
- **Helper functions**: Utilities for creating results and common assertions
### Design Principles
1. **Isolation**: Each test is independent and doesn't rely on external systems
2. **Mocking**: All external dependencies are mocked to ensure fast, reliable tests
3. **Coverage**: Tests cover happy paths, edge cases, and error conditions
4. **Extensibility**: Easy to add new tests by reusing existing fixtures
## Running Tests
### Using the test runner script (recommended)
```bash
bash run_tests.sh
```
This script automatically:
- Activates the virtual environment
- Installs test dependencies if needed
- Runs tests with coverage
- Generates HTML coverage report
### Manual test execution
#### Run all tests
```bash
pytest tests/pipeline/
```
#### Run only simple tests (no imports, always pass)
```bash
pytest tests/pipeline/test_simple.py -v
```
#### Run specific test file
```bash
pytest tests/pipeline/test_bansess.py -v
```
#### Run with coverage
```bash
pytest tests/pipeline/ --cov=pkg/pipeline --cov-report=html
```
#### Run specific test
```bash
pytest tests/pipeline/test_bansess.py::test_bansess_whitelist_allow -v
```
### Known Issues
Some tests may encounter circular import errors. This is a known issue with the current module structure. The test infrastructure is designed to work around this using lazy imports, but if you encounter issues:
1. Make sure you're running from the project root directory
2. Ensure the virtual environment is activated
3. Try running `test_simple.py` first to verify the test infrastructure works
## CI/CD Integration
Tests are automatically run on:
- Pull request opened
- Pull request marked ready for review
- Push to PR branch
- Push to master/develop branches
The workflow runs tests on Python 3.10, 3.11, and 3.12 to ensure compatibility.
## Adding New Tests
### 1. For a new pipeline stage
Create a new test file `test_<stage_name>.py`:
```python
"""
<StageName> stage unit tests
"""
import pytest
from pkg.pipeline.<module>.<stage> import <StageClass>
from pkg.pipeline import entities as pipeline_entities
@pytest.mark.asyncio
async def test_stage_basic_flow(mock_app, sample_query):
"""Test basic flow"""
stage = <StageClass>(mock_app)
await stage.initialize({})
result = await stage.process(sample_query, '<StageName>')
assert result.result_type == pipeline_entities.ResultType.CONTINUE
```
### 2. For additional fixtures
Add new fixtures to `conftest.py`:
```python
@pytest.fixture
def my_custom_fixture():
"""Description of fixture"""
return create_test_data()
```
### 3. For test data
Use the helper functions in `conftest.py`:
```python
from tests.pipeline.conftest import create_stage_result, assert_result_continue
result = create_stage_result(
result_type=pipeline_entities.ResultType.CONTINUE,
query=sample_query
)
assert_result_continue(result)
```
## Best Practices
1. **Test naming**: Use descriptive names that explain what's being tested
2. **Arrange-Act-Assert**: Structure tests clearly with setup, execution, and verification
3. **One assertion per test**: Focus each test on a single behavior
4. **Mock appropriately**: Mock external dependencies, not the code under test
5. **Use fixtures**: Reuse common test data through fixtures
6. **Document tests**: Add docstrings explaining what each test validates
## Troubleshooting
### Import errors
Make sure you've installed the package in development mode:
```bash
uv pip install -e .
```
### Async test failures
Ensure you're using `@pytest.mark.asyncio` decorator for async tests.
### Mock not working
Check that you're mocking at the right level and using `AsyncMock` for async functions.
## Future Enhancements
- [ ] Add integration tests for full pipeline execution
- [ ] Add performance benchmarks
- [ ] Add mutation testing for better coverage quality
- [ ] Add property-based testing with Hypothesis