CBCFacil v8.0 - Refactored with AMD GPU support

This commit is contained in:
2026-01-09 13:05:46 -03:00
parent cb17136f21
commit b017504c52
54 changed files with 7251 additions and 3670 deletions

418
docs/DEPLOYMENT.md Normal file
View File

@@ -0,0 +1,418 @@
# Guia de Despliegue - CBCFacil
Esta guia describe las opciones y procedimientos para desplegar CBCFacil en diferentes entornos.
## Opciones de Despliegue
| Metodo | Complejidad | Recomendado para |
|--------|-------------|------------------|
| Docker Compose | Baja | Desarrollo, Produccion ligera |
| Docker Standalone | Media | Produccion con orquestacion |
| Virtual Environment | Baja | Desarrollo local |
| Kubernetes | Alta | Produccion a escala |
## Despliegue con Docker Compose
### Prerrequisitos
- Docker 24.0+
- Docker Compose 2.20+
- NVIDIA Container Toolkit (para GPU)
### Configuracion
1. Crear archivo de configuracion:
```bash
cp .env.example .env.production
nano .env.production
```
2. Configurar variables sensibles:
```bash
# .env.production
NEXTCLOUD_URL=https://nextcloud.example.com/remote.php/webdav
NEXTCLOUD_USER=tu_usuario
NEXTCLOUD_PASSWORD=tu_contrasena_segura
ANTHROPIC_AUTH_TOKEN=sk-ant-...
GEMINI_API_KEY=AIza...
TELEGRAM_TOKEN=bot_token
TELEGRAM_CHAT_ID=chat_id
CUDA_VISIBLE_DEVICES=all
LOG_LEVEL=INFO
```
3. Verificar docker-compose.yml:
```yaml
# docker-compose.yml
version: '3.8'
services:
cbcfacil:
build: .
container_name: cbcfacil
restart: unless-stopped
ports:
- "5000:5000"
volumes:
- ./downloads:/app/downloads
- ./resumenes_docx:/app/resumenes_docx
- ./logs:/app/logs
- ./data:/app/data
environment:
- NEXTCLOUD_URL=${NEXTCLOUD_URL}
- NEXTCLOUD_USER=${NEXTCLOUD_USER}
- NEXTCLOUD_PASSWORD=${NEXTCLOUD_PASSWORD}
- ANTHROPIC_AUTH_TOKEN=${ANTHROPIC_AUTH_TOKEN}
- GEMINI_API_KEY=${GEMINI_API_KEY}
- TELEGRAM_TOKEN=${TELEGRAM_TOKEN}
- TELEGRAM_CHAT_ID=${TELEGRAM_CHAT_ID}
- CUDA_VISIBLE_DEVICES=${CUDA_VISIBLE_DEVICES}
- LOG_LEVEL=${LOG_LEVEL}
deploy:
resources:
reservations:
devices:
- driver: nvidia
count: all
capabilities: [gpu]
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:5000/health"]
interval: 30s
timeout: 10s
retries: 3
```
### Despliegue
```bash
# Construir y levantar
docker compose up -d --build
# Ver logs
docker compose logs -f cbcfacil
# Ver estado
docker compose ps
# Reiniciar
docker compose restart cbcfacil
# Detener
docker compose down
```
### Actualizacion
```bash
# Hacer backup de datos
docker cp cbcfacil:/app/data ./backup/data
# Actualizar imagen
docker compose pull
docker compose up -d --build
# Verificar
docker compose logs -f cbcfacil
```
## Despliegue con Docker Standalone
### Construir Imagen
```bash
# Construir imagen
docker build -t cbcfacil:latest .
# Verificar imagen
docker images cbcfacil
```
### Ejecutar Contenedor
```bash
# Con GPU
docker run -d \
--name cbcfacil \
--gpus all \
-p 5000:5000 \
-v $(pwd)/downloads:/app/downloads \
-v $(pwd)/resumenes_docx:/app/resumenes_docx \
-v $(pwd)/logs:/app/logs \
-e NEXTCLOUD_URL=${NEXTCLOUD_URL} \
-e NEXTCLOUD_USER=${NEXTCLOUD_USER} \
-e NEXTCLOUD_PASSWORD=${NEXTCLOUD_PASSWORD} \
-e ANTHROPIC_AUTH_TOKEN=${ANTHROPIC_AUTH_TOKEN} \
-e GEMINI_API_KEY=${GEMINI_API_KEY} \
cbcfacil:latest
# Ver logs
docker logs -f cbcfacil
# Detener
docker stop cbcfacil && docker rm cbcfacil
```
## Despliegue Local (Virtual Environment)
### Prerrequisitos
- Python 3.10+
- NVIDIA drivers (opcional)
### Instalacion
```bash
# Clonar y entrar al directorio
git clone <repo_url>
cd cbcfacil
# Crear entorno virtual
python3 -m venv .venv
source .venv/bin/activate
# Instalar dependencias
pip install -r requirements.txt
# Configurar variables de entorno
cp .env.example .env.production
nano .env.production
# Crear directorios
mkdir -p downloads resumenes_docx logs data
# Ejecutar
python main.py
```
### Como Servicio Systemd
```ini
# /etc/systemd/system/cbcfacil.service
[Unit]
Description=CBCFacil AI Service
After=network.target
[Service]
Type=simple
User=cbcfacil
WorkingDirectory=/opt/cbcfacil
Environment="PATH=/opt/cbcfacil/.venv/bin"
EnvironmentFile=/opt/cbcfacil/.env.production
ExecStart=/opt/cbcfacil/.venv/bin/python main.py
Restart=always
RestartSec=10
# Logging
StandardOutput=journal
StandardError=journal
SyslogIdentifier=cbcfacil
[Install]
WantedBy=multi-user.target
```
```bash
# Instalar servicio
sudo cp cbcfacil.service /etc/systemd/system/
sudo systemctl daemon-reload
sudo systemctl enable cbcfacil
sudo systemctl start cbcfacil
# Verificar estado
sudo systemctl status cbcfacil
# Ver logs
journalctl -u cbcfacil -f
```
## Configuracion de Produccion
### Variables de Entorno Criticas
```bash
# Obligatorias
NEXTCLOUD_URL=...
NEXTCLOUD_USER=...
NEXTCLOUD_PASSWORD=...
# Recomendadas para produccion
DEBUG=false
LOG_LEVEL=WARNING
POLL_INTERVAL=10
# GPU
CUDA_VISIBLE_DEVICES=all
```
### Optimizaciones
```bash
# En .env.production
# Reducir polling para menor carga
POLL_INTERVAL=10
# Optimizar memoria GPU
PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512
# Limitar threads
OMP_NUM_THREADS=4
MKL_NUM_THREADS=4
```
### Seguridad
```bash
# Crear usuario dedicado
sudo useradd -r -s /bin/false cbcfacil
# Asignar permisos
sudo chown -R cbcfacil:cbcfacil /opt/cbcfacil
# Proteger archivo de variables
sudo chmod 600 /opt/cbcfacil/.env.production
```
## Monitoreo
### Health Check
```bash
# Endpoint de salud
curl http://localhost:5000/health
# Respuesta esperada
{"status": "healthy"}
```
### Logging
```bash
# Ver logs en tiempo real
docker logs -f cbcfacil
# O con journalctl
journalctl -u cbcfacil -f
# Logs estructurados en JSON (produccion)
LOG_LEVEL=WARNING
```
### Metricas
El sistema expone metricas via API:
```bash
# Estado del servicio
curl http://localhost:5000/api/status
```
## Respaldo y Recuperacion
### Respaldo de Datos
```bash
# Directorios a respaldar
# - downloads/ (archivos procesados)
# - resumenes_docx/ (documentos generados)
# - data/ (registros y estados)
# - logs/ (logs del sistema)
# Script de backup
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR=/backup/cbcfacil
mkdir -p $BACKUP_DIR
tar -czf $BACKUP_DIR/cbcfacil_$DATE.tar.gz \
/opt/cbcfacil/downloads \
/opt/cbcfacil/resumenes_docx \
/opt/cbcfacil/data
# Limpiar backups antiguos (mantener ultimos 7 dias)
find $BACKUP_DIR -name "*.tar.gz" -mtime +7 -delete
```
### Recuperacion
```bash
# Detener servicio
docker compose down
# Restaurar datos
tar -xzf backup_*.tar.gz -C /opt/cbcfacil/
# Verificar permisos
chown -R cbcfacil:cbcfacil /opt/cbcfacil
# Reiniciar servicio
docker compose up -d
```
## Troubleshooting de Produccion
### Contenedor no Inicia
```bash
# Verificar logs
docker logs cbcfacil
# Verificar configuracion
docker exec -it cbcfacil python -c "from config import settings; print(settings.has_webdav_config)"
```
### Error de Memoria GPU
```bash
# Verificar GPUs disponibles
nvidia-smi
# Liberar memoria
sudo nvidia-smi --gpu-reset
# O limitar GPU
CUDA_VISIBLE_DEVICES=0
```
### WebDAV Connection Failed
```bash
# Verificar conectividad
curl -u $NEXTCLOUD_USER:$NEXTCLOUD_PASSWORD $NEXTCLOUD_URL
# Verificar credenciales
echo "URL: $NEXTCLOUD_URL"
echo "User: $NEXTCLOUD_USER"
```
### High CPU Usage
```bash
# Reducir threads
OMP_NUM_THREADS=2
MKL_NUM_THREADS=2
# Aumentar intervalo de polling
POLL_INTERVAL=15
```
## Checklist de Produccion
- [ ] Variables de entorno configuradas
- [ ] Credenciales seguras (.env.production)
- [ ] Usuario dedicado creado
- [ ] Permisos correctos asignados
- [ ] Logs configurados
- [ ] Health check funcionando
- [ ] Backup automatizado configurado
- [ ] Monitoreo activo
- [ ] SSL/TLS configurado (si aplica)
- [ ] Firewall configurado
## Recursos Adicionales
- `docs/SETUP.md` - Guia de configuracion inicial
- `docs/TESTING.md` - Guia de testing
- `ARCHITECTURE.md` - Documentacion arquitectonica

337
docs/SETUP.md Normal file
View File

@@ -0,0 +1,337 @@
# Guia de Configuracion - CBCFacil
Esta guia describe los pasos para configurar el entorno de desarrollo de CBCFacil.
## Requisitos Previos
### Software Requerido
| Componente | Version Minima | Recomendada |
|------------|----------------|-------------|
| Python | 3.10 | 3.11+ |
| Git | 2.0 | Latest |
| NVIDIA Driver | 535+ | 550+ (para CUDA 12.1) |
| Docker | 24.0 | 25.0+ (opcional) |
| Docker Compose | 2.20 | Latest (opcional) |
### Hardware Recomendado
- **CPU**: 4+ nucleos
- **RAM**: 8GB minimum (16GB+ recomendado)
- **GPU**: NVIDIA con 4GB+ VRAM (opcional, soporta CPU)
- **Almacenamiento**: 10GB+ libres
## Instalacion Paso a Paso
### 1. Clonar el Repositorio
```bash
git clone <repo_url>
cd cbcfacil
```
### 2. Crear Entorno Virtual
```bash
# Crear venv
python3 -m venv .venv
# Activar (Linux/macOS)
source .venv/bin/activate
# Activar (Windows)
.venv\Scripts\activate
```
### 3. Instalar Dependencias
```bash
# Actualizar pip
pip install --upgrade pip
# Instalar dependencias de produccion
pip install -r requirements.txt
# Instalar dependencias de desarrollo (opcional)
pip install -r requirements-dev.txt
```
### 4. Configurar Variables de Entorno
```bash
# Copiar template de configuracion
cp .env.example .env.secrets
# Editar con tus credenciales
nano .env.secrets
```
### 5. Estructura de Archivos Necesaria
```bash
# Crear directorios requeridos
mkdir -p downloads resumenes_docx logs
# Verificar estructura
ls -la
```
## Configuracion de Credenciales
### Nextcloud/WebDAV
```bash
# Obligatorio para sincronizacion de archivos
NEXTCLOUD_URL=https://tu-nextcloud.com/remote.php/webdav
NEXTCLOUD_USER=tu_usuario
NEXTCLOUD_PASSWORD=tu_contrasena
```
### AI Providers (Opcional)
```bash
# Claude via Z.ai
ANTHROPIC_AUTH_TOKEN=sk-ant-...
# Google Gemini
GEMINI_API_KEY=AIza...
# Gemini CLI (opcional)
GEMINI_CLI_PATH=/usr/local/bin/gemini
```
### Telegram (Opcional)
```bash
TELEGRAM_TOKEN=bot_token
TELEGRAM_CHAT_ID=chat_id
```
### GPU Configuration (Opcional)
```bash
# Usar GPU especifica
CUDA_VISIBLE_DEVICES=0
# Usar todas las GPUs
CUDA_VISIBLE_DEVICES=all
# Forzar CPU
CUDA_VISIBLE_DEVICES=
```
## Verificacion de Instalacion
### 1. Verificar Python
```bash
python --version
# Debe mostrar Python 3.10+
```
### 2. Verificar Dependencias
```bash
python -c "import torch; print(f'PyTorch: {torch.__version__}')"
python -c "import flask; print(f'Flask: {flask.__version__}')"
python -c "import whisper; print('Whisper instalado correctamente')"
```
### 3. Verificar Configuracion
```bash
python -c "from config import settings; print(f'WebDAV configurado: {settings.has_webdav_config}')"
python -c "from config import settings; print(f'AI configurado: {settings.has_ai_config}')"
```
### 4. Test Rapido
```bash
# Ejecutar tests basicos
pytest tests/test_config.py -v
```
## Configuracion de Desarrollo
### IDE Recomendado
#### VS Code
```json
// .vscode/settings.json
{
"python.defaultInterpreterPath": ".venv/bin/python",
"python.linting.enabled": true,
"python.linting.pylintEnabled": true,
"editor.formatOnSave": true,
"python.formatting.provider": "black"
}
```
#### PyCharm
1. Open Settings > Project > Python Interpreter
2. Add Interpreter > Existing Environment
3. Select `.venv/bin/python`
### Git Hooks (Opcional)
```bash
# Instalar pre-commit
pip install pre-commit
pre-commit install
# Verificar hooks
pre-commit run --all-files
```
### Formateo de Codigo
```bash
# Instalar formateadores
pip install black isort
# Formatear codigo
black .
isort .
# Verificar estilo
black --check .
isort --check-only .
```
## Ejecucion del Servicio
### Modo Desarrollo
```bash
# Activar entorno virtual
source .venv/bin/activate
# Ejecutar servicio completo
python main.py
# Con logging verbose
LOG_LEVEL=DEBUG python main.py
```
### Comandos CLI Disponibles
```bash
# Transcribir audio
python main.py whisper <archivo_audio> <directorio_output>
# Procesar PDF
python main.py pdf <archivo_pdf> <directorio_output>
```
### Dashboard
El dashboard estara disponible en:
- URL: http://localhost:5000
- API: http://localhost:5000/api/
## Solucion de Problemas Comunes
### Error: "Module not found"
```bash
# Verificar que el venv esta activado
which python
# Debe mostrar path hacia .venv/bin/python
# Reinstalar dependencias
pip install -r requirements.txt
```
### Error: "CUDA out of memory"
```bash
# Reducir uso de GPU
export CUDA_VISIBLE_DEVICES=
# O usar solo una GPU
export CUDA_VISIBLE_DEVICES=0
```
### Error: "WebDAV connection failed"
```bash
# Verificar credenciales
echo $NEXTCLOUD_URL
echo $NEXTCLOUD_USER
# Probar conexion manualmente
curl -u $NEXTCLOUD_USER:$NEXTCLOUD_PASSWORD $NEXTCLOUD_URL
```
### Error: "Telegram token invalid"
```bash
# Verificar token con BotFather
# https://t.me/BotFather
# Verificar variables de entorno
echo $TELEGRAM_TOKEN
echo $TELEGRAM_CHAT_ID
```
### Error: "Whisper model not found"
```bash
# El modelo se descarga automaticamente la primera vez
# Para forzar recarga:
rm -rf ~/.cache/whisper
```
## Configuracion de GPU (Opcional)
### Verificar Instalacion de CUDA
```bash
# Verificar drivers NVIDIA
nvidia-smi
# Verificar CUDA Toolkit
nvcc --version
# Verificar PyTorch CUDA
python -c "import torch; print(f'CUDA available: {torch.cuda.is_available()}')"
```
### Configurar Memoria GPU
```bash
# En .env.secrets
PYTORCH_CUDA_ALLOC_CONF=max_split_size_mb:512
```
## Variables de Entorno Completa
| Variable | Requerido | Default | Descripcion |
|----------|-----------|---------|-------------|
| NEXTCLOUD_URL | Si | - | URL WebDAV de Nextcloud |
| NEXTCLOUD_USER | Si | - | Usuario Nextcloud |
| NEXTCLOUD_PASSWORD | Si | - | Contrasena Nextcloud |
| ANTHROPIC_AUTH_TOKEN | No | - | Token Claude/Z.ai |
| GEMINI_API_KEY | No | - | API Key Gemini |
| TELEGRAM_TOKEN | No | - | Token Bot Telegram |
| TELEGRAM_CHAT_ID | No | - | Chat ID Telegram |
| CUDA_VISIBLE_DEVICES | No | "all" | GPUs a usar |
| POLL_INTERVAL | No | 5 | Segundos entre polls |
| LOG_LEVEL | No | "INFO" | Nivel de logging |
| DEBUG | No | false | Modo debug |
| DASHBOARD_PORT | No | 5000 | Puerto del dashboard |
| DASHBOARD_HOST | No | "0.0.0.0" | Host del dashboard |
## Siguientes Pasos
1. Verificar instalacion con tests
2. Configurar integracion con Nextcloud
3. Probar procesamiento de archivos
4. Configurar notificaciones Telegram (opcional)
Ver juga:
- `docs/TESTING.md` - Guia de testing
- `docs/DEPLOYMENT.md` - Guia de despliegue
- `ARCHITECTURE.md` - Documentacion arquitectonica

482
docs/TESTING.md Normal file
View File

@@ -0,0 +1,482 @@
# Guia de Testing - CBCFacil
Esta guia describe como ejecutar y escribir tests para CBCFacil.
## Estructura de Tests
```
tests/
├── conftest.py # Fixtures compartidos
├── test_config.py # Tests de configuracion
├── test_storage.py # Tests de almacenamiento
├── test_webdav.py # Tests de WebDAV
├── test_processors.py # Tests de procesadores
├── test_ai_providers.py # Tests de AI providers
├── test_vram_manager.py # Tests de VRAM manager
└── test_main_integration.py # Tests de integracion
```
## Instalacion de Dependencias de Test
```bash
# Activar entorno virtual
source .venv/bin/activate
# Instalar dependencias de desarrollo
pip install -r requirements-dev.txt
# Verificar instalacion
pytest --version
```
## Ejecutar Tests
### Todos los Tests
```bash
# Ejecutar todos los tests
pytest tests/
# Con output detallado
pytest tests/ -v
```
### Tests Especificos
```bash
# Tests de configuracion
pytest tests/test_config.py -v
# Tests de almacenamiento
pytest tests/test_storage.py -v
# Tests de WebDAV
pytest tests/test_webdav.py -v
# Tests de procesadores
pytest tests/test_processors.py -v
# Tests de AI providers
pytest tests/test_ai_providers.py -v
# Tests de VRAM manager
pytest tests/test_vram_manager.py -v
# Tests de integracion
pytest tests/test_main_integration.py -v
```
### Tests con Coverage
```bash
# Coverage basico
pytest tests/ --cov=cbcfacil
# Coverage con reporte HTML
pytest tests/ --cov=cbcfacil --cov-report=html
# Coverage con reporte term-missing
pytest tests/ --cov=cbcfacil --cov-report=term-missing
# Coverage por modulo
pytest tests/ --cov=cbcfacil --cov-report=term-missing --cov-report=annotate
```
### Tests en Modo Watch
```bash
# Recargar automaticamente al detectar cambios
pytest-watch tests/
```
### Tests Parallelos
```bash
# Ejecutar tests en paralelo
pytest tests/ -n auto
# Con numero fijo de workers
pytest tests/ -n 4
```
## Escribir Nuevos Tests
### Estructura Basica
```python
# tests/test_ejemplo.py
import pytest
from pathlib import Path
class TestEjemplo:
"""Clase de tests para un modulo"""
def setup_method(self):
"""Setup antes de cada test"""
pass
def teardown_method(self):
"""Cleanup despues de cada test"""
pass
def test_funcion_basica(self):
"""Test de una funcion basica"""
# Arrange
input_value = "test"
# Act
result = mi_funcion(input_value)
# Assert
assert result is not None
assert result == "expected"
```
### Usar Fixtures
```python
# tests/conftest.py
import pytest
from pathlib import Path
@pytest.fixture
def temp_directory(tmp_path):
"""Fixture para directorio temporal"""
dir_path = tmp_path / "test_files"
dir_path.mkdir()
return dir_path
@pytest.fixture
def mock_settings():
"""Fixture con settings de prueba"""
class MockSettings:
NEXTCLOUD_URL = "https://test.example.com"
NEXTCLOUD_USER = "test_user"
NEXTCLOUD_PASSWORD = "test_pass"
return MockSettings()
# En tu test
def test_con_fixture(temp_directory, mock_settings):
"""Test usando fixtures"""
assert temp_directory.exists()
assert mock_settings.NEXTCLOUD_URL == "https://test.example.com"
```
### Tests de Configuracion
```python
# tests/test_config.py
import pytest
from config import settings
class TestSettings:
"""Tests para configuracion"""
def test_has_webdav_config_true(self):
"""Test con WebDAV configurado"""
# Verificar que las properties funcionan
assert hasattr(settings, 'has_webdav_config')
assert hasattr(settings, 'has_ai_config')
def test_processed_files_path(self):
"""Test del path de archivos procesados"""
path = settings.processed_files_path
assert isinstance(path, Path)
assert path.suffix == ".txt"
```
### Tests de WebDAV
```python
# tests/test_webdav.py
import pytest
from unittest.mock import Mock, patch
class TestWebDAVService:
"""Tests para WebDAV Service"""
@pytest.fixture
def webdav_service(self):
"""Crear instancia del servicio"""
from services.webdav_service import webdav_service
return webdav_service
def test_list_remote_path(self, webdav_service):
"""Test de listado de archivos remotos"""
# Mock del cliente WebDAV
with patch('services.webdav_service.WebDAVClient') as mock_client:
mock_instance = Mock()
mock_instance.list.return_value = ['file1.pdf', 'file2.mp3']
mock_client.return_value = mock_instance
# Inicializar servicio
webdav_service.initialize()
# Test
files = webdav_service.list("TestFolder")
assert len(files) == 2
assert "file1.pdf" in files
```
### Tests de Procesadores
```python
# tests/test_processors.py
import pytest
from unittest.mock import Mock, patch
class TestAudioProcessor:
"""Tests para Audio Processor"""
@pytest.fixture
def processor(self):
"""Crear procesador"""
from processors.audio_processor import AudioProcessor
return AudioProcessor()
def test_process_audio_file(self, processor, tmp_path):
"""Test de procesamiento de audio"""
# Crear archivo de prueba
audio_file = tmp_path / "test.mp3"
audio_file.write_bytes(b"fake audio content")
# Mock de Whisper
with patch('processors.audio_processor.whisper') as mock_whisper:
mock_whisper.load_model.return_value.transcribe.return_value = {
"text": "Texto transcrito de prueba"
}
# Ejecutar
result = processor.process(str(audio_file))
# Verificar
assert result is not None
```
### Tests de AI Providers
```python
# tests/test_ai_providers.py
import pytest
from unittest.mock import Mock, patch
class TestClaudeProvider:
"""Tests para Claude Provider"""
def test_summarize_text(self):
"""Test de resumen con Claude"""
from services.ai.claude_provider import ClaudeProvider
provider = ClaudeProvider()
test_text = "Texto largo para resumir..."
# Mock de la llamada API
with patch.object(provider, '_call_api') as mock_call:
mock_call.return_value = "Texto resumido"
result = provider.summarize(test_text)
assert result == "Texto resumido"
mock_call.assert_called_once()
```
### Tests de Integracion
```python
# tests/test_main_integration.py
import pytest
from unittest.mock import patch
class TestMainIntegration:
"""Tests de integracion del main"""
def test_main_loop_no_files(self):
"""Test del loop principal sin archivos nuevos"""
with patch('main.webdav_service') as mock_webdav:
with patch('main.processed_registry') as mock_registry:
mock_webdav.list.return_value = []
mock_registry.is_processed.return_value = True
# El loop no debe procesar nada
# Verificar que no se llama a procesadores
```
## Configuracion de pytest
```ini
# pytest.ini o pyproject.toml
[tool.pytest.ini_options]
testpaths = ["tests"]
python_files = ["test_*.py"]
python_classes = ["Test*"]
python_functions = ["test_*"]
addopts = [
"-v",
"--tb=short",
"--strict-markers",
]
filterwarnings = [
"ignore::DeprecationWarning",
]
```
## Mejores Practicas
### 1. Nombrado de Tests
```python
# BIEN
def test_webdav_service_list_returns_files():
...
def test_processed_registry_is_processed_true_for_processed_file():
...
# MAL
def test_list():
...
def test_check():
...
```
### 2. Estructura AAA
```python
def test_ejemplo_aaa():
# Arrange
input_data = {"key": "value"}
expected = "result"
# Act
actual = function_under_test(input_data)
# Assert
assert actual == expected
```
### 3. Tests Aislados
```python
# Cada test debe ser independiente
def test_independent():
# No depender de estado de otros tests
# Usar fixtures para setup/cleanup
pass
```
### 4. Evitar TestLego
```python
# BIEN - Test del comportamiento, no la implementacion
def test_registry_returns_true_for_processed_file():
registry = ProcessedRegistry()
registry.save("file.txt")
assert registry.is_processed("file.txt") is True
# MAL - Test de implementacion
def test_registry_uses_set_internally():
# No testar detalles de implementacion
registry = ProcessedRegistry()
assert hasattr(registry, '_processed_files')
```
### 5. Mocks Appropriados
```python
# Usar mocks para dependencias externas
from unittest.mock import Mock, patch, MagicMock
def test_with_mocked_api():
with patch('requests.get') as mock_get:
mock_response = Mock()
mock_response.json.return_value = {"key": "value"}
mock_get.return_value = mock_response
result = my_api_function()
assert result == {"key": "value"}
```
## Coverage Objetivo
| Componente | Coverage Minimo |
|------------|-----------------|
| config/ | 90% |
| core/ | 90% |
| services/ | 70% |
| processors/ | 60% |
| storage/ | 90% |
| api/ | 80% |
## Integracion con CI/CD
```yaml
# .github/workflows/tests.yml
name: Tests
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install -r requirements-dev.txt
- name: Run tests
run: |
pytest tests/ --cov=cbcfacil --cov-report=xml
- name: Upload coverage
uses: codecov/codecov-action@v3
```
## Troubleshooting
### Tests Fallan por Imports
```bash
# Verificar que el venv esta activado
source .venv/bin/activate
# Reinstalar el paquete en modo desarrollo
pip install -e .
```
### Tests Muy Lentos
```bash
# Ejecutar en paralelo
pytest tests/ -n auto
# O ejecutar solo tests rapidos
pytest tests/ -m "not slow"
```
### Memory Errors
```bash
# Reducir workers
pytest tests/ -n 2
# O ejecutar secuencial
pytest tests/ -n 0
```
## Recursos Adicionales
- [Documentacion de pytest](https://docs.pytest.org/)
- [Documentacion de unittest.mock](https://docs.python.org/3/library/unittest.mock.html)
- [pytest-cov](https://pytest-cov.readthedocs.io/)