ComfyUI-Manager/tests/common/pip_util/README.md
Dr.Lt.Data 2866193baf ● feat: Draft pip package policy management system (not yet integrated)
Add comprehensive pip dependency conflict resolution framework as draft implementation. This is self-contained and does not affect existing
ComfyUI Manager functionality.

Key components:
- pip_util.py with PipBatch class for policy-driven package management
- Lazy-loaded policy system supporting base + user overrides
- Multi-stage policy execution (uninstall → apply_first_match → apply_all_matches → restore)
- Conditional policies based on platform, installed packages, and ComfyUI version
- Comprehensive test suite covering edge cases, workflows, and platform scenarios
- Design and implementation documentation

Policy capabilities (draft):
- Package replacement (e.g., PIL → Pillow, opencv-python → opencv-contrib-python)
- Version pinning to prevent dependency conflicts
- Dependency protection during installations
- Platform-specific handling (Linux/Windows, GPU detection)
- Pre-removal and post-restoration workflows

Testing infrastructure:
- Pytest-based test suite with isolated environments
- Dependency analysis tools for conflict detection
- Coverage for policy priority, edge cases, and environment recovery

Status: Draft implementation complete, integration with manager workflows pending.
2025-10-04 08:55:59 +09:00

306 lines
8.4 KiB
Markdown

# pip_util Integration Tests
Real integration tests for `pip_util.py` using actual PyPI packages and pip operations.
## Overview
These tests use a **real isolated venv** to verify pip_util behavior with actual package installations, deletions, and version changes. No mocks - real pip operations only.
## Quick Start
### 1. Setup Test Environment
```bash
cd tests/common/pip_util
./setup_test_env.sh
```
This creates `test_venv/` with base packages:
- urllib3==1.26.15
- certifi==2023.7.22
- charset-normalizer==3.2.0
- colorama==0.4.6
- six==1.16.0
- attrs==23.1.0
- packaging==23.1
- pytest (latest)
### 2. Run Tests
```bash
# Run all integration tests
pytest -v --override-ini="addopts="
# Run specific test
pytest test_dependency_protection.py -v --override-ini="addopts="
# Run with markers
pytest -m integration -v --override-ini="addopts="
```
## Test Architecture
### Real venv Integration
- **No subprocess mocking** - uses real pip install/uninstall
- **Isolated test venv** - prevents system contamination
- **Automatic cleanup** - `reset_test_venv` fixture restores state after each test
### Test Fixtures
**venv Management**:
- `test_venv_path` - Path to test venv (session scope)
- `test_pip_cmd` - pip command for test venv
- `reset_test_venv` - Restore venv to initial state after each test
**Helpers**:
- `get_installed_packages()` - Get current venv packages
- `install_packages(*packages)` - Install packages in test venv
- `uninstall_packages(*packages)` - Uninstall packages in test venv
**Policy Configuration**:
- `temp_policy_dir` - Temporary directory for base policies
- `temp_user_policy_dir` - Temporary directory for user policies
- `mock_manager_util` - Mock manager_util paths to use temp dirs
- `mock_context` - Mock context paths to use temp dirs
## Test Scenarios
### Scenario 1: Dependency Version Protection
**File**: `test_dependency_protection.py::test_dependency_version_protection_with_pin`
**Initial State**:
```python
urllib3==1.26.15
certifi==2023.7.22
charset-normalizer==3.2.0
```
**Action**: Install `requests` with pin_dependencies policy
**Expected Result**:
```python
# Dependencies stay at old versions (protected by pin)
urllib3==1.26.15 # NOT upgraded to 2.x
certifi==2023.7.22 # NOT upgraded
charset-normalizer==3.2.0 # NOT upgraded
requests==2.31.0 # newly installed
```
### Scenario 2: Click-Colorama Dependency Chain
**File**: `test_dependency_protection.py::test_dependency_chain_with_click_colorama`
**Initial State**:
```python
colorama==0.4.6
```
**Action**: Install `click` with force_version + pin_dependencies
**Expected Result**:
```python
colorama==0.4.6 # PINNED
click==8.1.3 # FORCED to specific version
```
### Scenario 3: Package Deletion and Restore
**File**: `test_environment_recovery.py::test_package_deletion_and_restore`
**Initial State**:
```python
six==1.16.0
attrs==23.1.0
packaging==23.1
```
**Action**: Delete `six` → call `batch.ensure_installed()`
**Expected Result**:
```python
six==1.16.0 # RESTORED to required version
```
### Scenario 4: Version Change and Restore
**File**: `test_environment_recovery.py::test_version_change_and_restore`
**Initial State**:
```python
urllib3==1.26.15
```
**Action**: Upgrade `urllib3` to 2.1.0 → call `batch.ensure_installed()`
**Expected Result**:
```python
urllib3==1.26.15 # RESTORED to required version (downgraded)
```
## Test Categories
### Priority 1 (Essential) ✅ ALL PASSING
- ✅ Dependency version protection (enhanced with exact versions)
- ✅ Package deletion and restore (enhanced with exact versions)
- ✅ Version change and restore (enhanced with downgrade verification)
- ✅ Pin only affects specified packages ✨ NEW
- ✅ Major version jump prevention ✨ NEW
### Priority 2 (Important)
- ✅ Complex dependency chains (python-dateutil + six)
- ⏳ Full workflow integration (TODO: update to real venv)
- ⏳ Pin failure retry (TODO: update to real venv)
### Priority 3 (Edge Cases)
- ⏳ Platform conditions (TODO: update to real venv)
- ⏳ Policy priority (TODO: update to real venv)
- ⏳ Unit tests (no venv needed)
- ⏳ Edge cases (no venv needed)
## Package Selection
All test packages are **real PyPI packages < 200KB**:
| Package | Size | Version | Purpose |
|---------|------|---------|---------|
| **urllib3** | ~100KB | 1.26.15 | Protected dependency (prevent 2.x upgrade) |
| **certifi** | ~10KB | 2023.7.22 | SSL certificates (pinned) |
| **charset-normalizer** | ~46KB | 3.2.0 | Charset detection (pinned) |
| **requests** | ~100KB | 2.31.0 | Main package to install |
| **colorama** | ~25KB | 0.4.6 | Terminal colors (pinned) |
| **click** | ~90KB | 8.1.3 | CLI framework (forced version) |
| **six** | ~11KB | 1.16.0 | Python 2/3 compatibility (restore) |
| **attrs** | ~61KB | 23.1.0 | Bystander package |
| **packaging** | ~48KB | 23.1 | Bystander package |
## Cleanup
### Manual Cleanup
```bash
# Remove test venv
rm -rf test_venv/
# Recreate fresh venv
./setup_test_env.sh
```
### Automatic Cleanup
The `reset_test_venv` fixture automatically:
1. Records initial package state
2. Runs test
3. Removes all packages (except pip/setuptools/wheel)
4. Reinstalls initial packages
## Troubleshooting
### Error: "Test venv not found"
**Solution**: Run `./setup_test_env.sh`
### Error: "Package not installed in initial state"
**Solution**: Check `requirements-test-base.txt` and recreate venv
### Tests are slow
**Reason**: Real pip operations take 2-3 seconds per test
**This is expected** - we're doing actual pip install/uninstall
## Implementation Details
### How reset_test_venv Works
```python
@pytest.fixture
def reset_test_venv(test_pip_cmd):
# 1. Record initial state
initial = subprocess.run(test_pip_cmd + ["freeze"], ...)
yield # Run test here
# 2. Remove all packages
current = subprocess.run(test_pip_cmd + ["freeze"], ...)
subprocess.run(test_pip_cmd + ["uninstall", "-y", ...], ...)
# 3. Restore initial state
subprocess.run(test_pip_cmd + ["install", "-r", initial], ...)
```
### How make_pip_cmd is Patched
```python
@pytest.fixture(autouse=True)
def setup_pip_util(monkeypatch, test_pip_cmd):
from comfyui_manager.common import pip_util
def make_test_pip_cmd(args: List[str]) -> List[str]:
return test_pip_cmd + args # Use test venv pip
monkeypatch.setattr(
pip_util.manager_util,
"make_pip_cmd",
make_test_pip_cmd
)
```
## Dependency Analysis Tool
Use `analyze_dependencies.py` to examine package dependencies before adding new tests:
```bash
# Analyze specific package
python analyze_dependencies.py requests
# Analyze all test packages
python analyze_dependencies.py --all
# Show current environment
python analyze_dependencies.py --env
```
**Output includes**:
- Latest available versions
- Dependencies that would be installed
- Version upgrades that would occur
- Impact of pin constraints
**Example output**:
```
📦 Latest version: 2.32.5
🔍 Scenario A: Install without constraints
Would install 5 packages:
• urllib3 1.26.15 → 2.5.0 ⚠️ UPGRADE
🔍 Scenario B: Install with pin constraints
Would install 5 packages:
• urllib3 1.26.15 (no change) 📌 PINNED
✅ Pin prevented 2 upgrade(s)
```
## Test Statistics
**Current Status**: 6 tests, 100% passing
```
test_dependency_version_protection_with_pin PASSED (2.28s)
test_dependency_chain_with_six_pin PASSED (2.00s)
test_pin_only_affects_specified_packages PASSED (2.25s) ✨ NEW
test_major_version_jump_prevention PASSED (3.53s) ✨ NEW
test_package_deletion_and_restore PASSED (2.25s)
test_version_change_and_restore PASSED (2.24s)
Total: 14.10s
```
**Test Improvements**:
- ✅ All tests verify exact version numbers
- ✅ All tests reference DEPENDENCY_TREE_CONTEXT.md
- ✅ Added 2 new critical tests (pin selectivity, major version prevention)
- ✅ Enhanced error messages with expected vs actual values
## Design Documents
- **TEST_IMPROVEMENTS.md** - Summary of test enhancements based on dependency context
- **DEPENDENCY_TREE_CONTEXT.md** - Verified dependency trees for all test packages
- **DEPENDENCY_ANALYSIS.md** - Dependency analysis methodology
- **CONTEXT_FILES_GUIDE.md** - Guide for using context files
- **TEST_SCENARIOS.md** - Detailed test scenario specifications
- **pip_util.test-design.md** - Test design and architecture
- **pip_util.design.en.md** - pip_util design documentation