Creado Tests
This commit is contained in:
parent
79194e0f61
commit
b48058e495
|
@ -0,0 +1,48 @@
|
||||||
|
#!/usr/bin/env python
|
||||||
|
"""
|
||||||
|
Simple script to generate a test report.
|
||||||
|
This is a simplified version of run_tests.py focused on generating the JSON report.
|
||||||
|
"""
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
|
def main():
|
||||||
|
"""Run tests and generate JSON report."""
|
||||||
|
# Create directories if they don't exist
|
||||||
|
os.makedirs("test_reports", exist_ok=True)
|
||||||
|
|
||||||
|
# Generate timestamp for current run
|
||||||
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
|
||||||
|
print(f"Running tests and generating JSON report with timestamp: {timestamp}")
|
||||||
|
|
||||||
|
# Run pytest with the JSON reporter plugin loaded
|
||||||
|
cmd = [
|
||||||
|
"pytest",
|
||||||
|
"-v",
|
||||||
|
"--tb=short",
|
||||||
|
f"--junitxml=test_reports/junit_{timestamp}.xml",
|
||||||
|
"-p",
|
||||||
|
"tests.json_reporter",
|
||||||
|
"tests/",
|
||||||
|
]
|
||||||
|
|
||||||
|
result = subprocess.run(cmd, capture_output=False)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
print("\nTests completed successfully!")
|
||||||
|
else:
|
||||||
|
print(f"\nTests completed with some failures (exit code: {result.returncode})")
|
||||||
|
|
||||||
|
print(
|
||||||
|
f"JSON report should be available at: test_reports/test_results_{timestamp}.json"
|
||||||
|
)
|
||||||
|
|
||||||
|
return result.returncode
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
sys.exit(main())
|
66
run_tests.py
66
run_tests.py
|
@ -11,63 +11,73 @@ import json
|
||||||
import shutil
|
import shutil
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
|
||||||
def run_tests(args):
|
def run_tests(args):
|
||||||
"""Run pytest with specified arguments and generate reports."""
|
"""Run pytest with specified arguments and generate reports."""
|
||||||
# Create test reports directory if needed
|
# Create test reports directory if needed
|
||||||
os.makedirs('test_reports', exist_ok=True)
|
os.makedirs("test_reports", exist_ok=True)
|
||||||
|
|
||||||
# Generate timestamp for report files
|
# Generate timestamp for report files
|
||||||
timestamp = datetime.now().strftime('%Y%m%d_%H%M%S')
|
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
|
|
||||||
# Base pytest arguments
|
# Base pytest arguments
|
||||||
pytest_args = [
|
pytest_args = [
|
||||||
'-v', # Verbose output
|
"-v", # Verbose output
|
||||||
'--no-header', # No header in the output
|
"--no-header", # No header in the output
|
||||||
'--tb=short', # Short traceback
|
"--tb=short", # Short traceback
|
||||||
f'--junitxml=test_reports/junit_{timestamp}.xml', # JUnit XML report
|
f"--junitxml=test_reports/junit_{timestamp}.xml", # JUnit XML report
|
||||||
'--cov=app', # Coverage for app module
|
"--cov=app", # Coverage for app module
|
||||||
'--cov=routes', # Coverage for routes
|
"--cov=routes", # Coverage for routes
|
||||||
'--cov=services', # Coverage for services
|
"--cov=services", # Coverage for services
|
||||||
'--cov=utils', # Coverage for utils
|
"--cov=utils", # Coverage for utils
|
||||||
'--cov-report=html:test_reports/coverage', # HTML coverage report
|
"--cov-report=html:test_reports/coverage", # HTML coverage report
|
||||||
|
"-p",
|
||||||
|
"tests.json_reporter", # Load the JSON reporter plugin explicitly
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add test files/directories
|
# Add test files/directories
|
||||||
if args.tests:
|
if args.tests:
|
||||||
pytest_args.extend(args.tests)
|
pytest_args.extend(args.tests)
|
||||||
else:
|
else:
|
||||||
pytest_args.append('tests/')
|
pytest_args.append("tests/")
|
||||||
|
|
||||||
# Execute tests
|
# Execute tests
|
||||||
print(f"\n{'='*80}")
|
print(f"\n{'='*80}")
|
||||||
print(f"Running tests at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
print(f"Running tests at {datetime.now().strftime('%Y-%m-%d %H:%M:%S')}")
|
||||||
print(f"{'='*80}")
|
print(f"{'='*80}")
|
||||||
|
|
||||||
result = pytest.main(pytest_args)
|
result = pytest.main(pytest_args)
|
||||||
|
|
||||||
# Generate JSON report
|
# Generate JSON report
|
||||||
print(f"\n{'='*80}")
|
print(f"\n{'='*80}")
|
||||||
print(f"Test report available at: test_reports/test_results_{timestamp}.json")
|
print(f"Test report available at: test_reports/test_results_{timestamp}.json")
|
||||||
print(f"Coverage report available at: test_reports/coverage/index.html")
|
print(f"Coverage report available at: test_reports/coverage/index.html")
|
||||||
print(f"{'='*80}\n")
|
print(f"{'='*80}\n")
|
||||||
|
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
parser = argparse.ArgumentParser(description="Run tests for ARCH application")
|
parser = argparse.ArgumentParser(description="Run tests for ARCH application")
|
||||||
parser.add_argument('tests', nargs='*', help='Specific test files or directories to run')
|
parser.add_argument(
|
||||||
parser.add_argument('--clean', action='store_true', help='Clean test storage and results before running')
|
"tests", nargs="*", help="Specific test files or directories to run"
|
||||||
|
)
|
||||||
|
parser.add_argument(
|
||||||
|
"--clean",
|
||||||
|
action="store_true",
|
||||||
|
help="Clean test storage and results before running",
|
||||||
|
)
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
if args.clean:
|
if args.clean:
|
||||||
# Clean temporary test storage
|
# Clean temporary test storage
|
||||||
if os.path.exists('test_storage'):
|
if os.path.exists("test_storage"):
|
||||||
shutil.rmtree('test_storage')
|
shutil.rmtree("test_storage")
|
||||||
# Clean previous test reports
|
# Clean previous test reports
|
||||||
if os.path.exists('test_reports'):
|
if os.path.exists("test_reports"):
|
||||||
shutil.rmtree('test_reports')
|
shutil.rmtree("test_reports")
|
||||||
print("Cleaned test storage and reports.")
|
print("Cleaned test storage and reports.")
|
||||||
|
|
||||||
# Run tests and exit with the pytest result code
|
# Run tests and exit with the pytest result code
|
||||||
sys.exit(run_tests(args))
|
sys.exit(run_tests(args))
|
||||||
|
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -0,0 +1 @@
|
||||||
|
{}
|
|
@ -0,0 +1,4 @@
|
||||||
|
{
|
||||||
|
"max_project_id": 0,
|
||||||
|
"max_document_id": 0
|
||||||
|
}
|
|
@ -0,0 +1,28 @@
|
||||||
|
{
|
||||||
|
"ESQ001": {
|
||||||
|
"codigo": "ESQ001",
|
||||||
|
"descripcion": "Proyecto estándar",
|
||||||
|
"fecha_creacion": "2025-03-04T10:17:09.979764+00:00",
|
||||||
|
"creado_por": "admin",
|
||||||
|
"documentos": [
|
||||||
|
{
|
||||||
|
"tipo": "pdf",
|
||||||
|
"nombre": "Manual de Usuario",
|
||||||
|
"nivel_ver": 0,
|
||||||
|
"nivel_editar": 5000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tipo": "dwg",
|
||||||
|
"nombre": "Planos Técnicos",
|
||||||
|
"nivel_ver": 0,
|
||||||
|
"nivel_editar": 5000
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"tipo": "zip",
|
||||||
|
"nombre": "Archivos Fuente",
|
||||||
|
"nivel_ver": 1000,
|
||||||
|
"nivel_editar": 5000
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,14 @@
|
||||||
|
{
|
||||||
|
"admin": {
|
||||||
|
"nombre": "Administrador",
|
||||||
|
"username": "admin",
|
||||||
|
"email": "admin@example.com",
|
||||||
|
"password_hash": "$2b$04$MDmmt/Wr5K0vEpL0I2gfVOQb6af8xHtympw8lbDUjuTA8mY4wEJ7K",
|
||||||
|
"nivel": 9999,
|
||||||
|
"idioma": "es",
|
||||||
|
"fecha_caducidad": null,
|
||||||
|
"empresa": "",
|
||||||
|
"estado": "activo",
|
||||||
|
"ultimo_acceso": "2025-03-04T10:17:09.978287+00:00"
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,2 @@
|
||||||
|
# This file makes the tests directory a Python package
|
||||||
|
# allowing the json_reporter module to be imported
|
|
@ -2,68 +2,132 @@ import pytest
|
||||||
import os
|
import os
|
||||||
import json
|
import json
|
||||||
import shutil
|
import shutil
|
||||||
|
import time
|
||||||
|
import tempfile
|
||||||
from app import create_app
|
from app import create_app
|
||||||
|
|
||||||
|
|
||||||
|
def init_test_environment(storage_path):
|
||||||
|
"""Initialize the test environment with required directories."""
|
||||||
|
if os.path.exists(storage_path):
|
||||||
|
try:
|
||||||
|
shutil.rmtree(storage_path)
|
||||||
|
except PermissionError:
|
||||||
|
# Try to handle Windows file locking issues
|
||||||
|
print(f"Warning: Could not remove {storage_path} directory, retrying...")
|
||||||
|
time.sleep(1)
|
||||||
|
try:
|
||||||
|
shutil.rmtree(storage_path)
|
||||||
|
except Exception as e:
|
||||||
|
print(f"Failed to remove directory: {e}")
|
||||||
|
|
||||||
|
# Create main storage directory
|
||||||
|
os.makedirs(storage_path, exist_ok=True)
|
||||||
|
|
||||||
|
# Create required subdirectories
|
||||||
|
for dir_name in ["users", "schemas", "filetypes", "projects", "logs"]:
|
||||||
|
os.makedirs(os.path.join(storage_path, dir_name), exist_ok=True)
|
||||||
|
|
||||||
|
# Initialize test data
|
||||||
|
initialize_test_data(storage_path)
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def app():
|
def app():
|
||||||
"""Create a Flask application instance for testing."""
|
# Use a unique temporary directory for each test session
|
||||||
# Test configuration
|
storage_path = tempfile.mkdtemp(prefix="arch_test_")
|
||||||
test_config = {
|
|
||||||
'TESTING': True,
|
# Set up the test environment
|
||||||
'STORAGE_PATH': 'test_storage',
|
init_test_environment(storage_path)
|
||||||
'SECRET_KEY': 'test_key',
|
|
||||||
'WTF_CSRF_ENABLED': False
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create test storage directory
|
|
||||||
if not os.path.exists('test_storage'):
|
|
||||||
os.makedirs('test_storage')
|
|
||||||
|
|
||||||
# Create basic directory structure
|
|
||||||
for dir_name in ['users', 'schemas', 'filetypes', 'projects', 'logs', 'exports']:
|
|
||||||
if not os.path.exists(f'test_storage/{dir_name}'):
|
|
||||||
os.makedirs(f'test_storage/{dir_name}')
|
|
||||||
|
|
||||||
# Create app with test configuration
|
# Create app with test configuration
|
||||||
app = create_app('testing')
|
app = create_app("testing")
|
||||||
|
|
||||||
# Create context
|
# Update the app's config with our test settings
|
||||||
|
app.config.update(
|
||||||
|
{
|
||||||
|
"TESTING": True,
|
||||||
|
"STORAGE_PATH": storage_path,
|
||||||
|
"WTF_CSRF_ENABLED": False, # Disable CSRF protection for testing
|
||||||
|
"SERVER_NAME": "localhost.localdomain", # Set server name to ensure correct URLs
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
# Create app context
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Set up test data
|
yield app
|
||||||
initialize_test_data()
|
|
||||||
|
# Clean up after tests - with retry mechanism for Windows
|
||||||
yield app
|
max_retries = 3
|
||||||
|
retry_delay = 1
|
||||||
# Clean up after tests
|
|
||||||
shutil.rmtree('test_storage', ignore_errors=True)
|
for attempt in range(max_retries):
|
||||||
|
try:
|
||||||
|
if os.path.exists(storage_path):
|
||||||
|
shutil.rmtree(storage_path)
|
||||||
|
break
|
||||||
|
except PermissionError:
|
||||||
|
if attempt < max_retries - 1:
|
||||||
|
print(
|
||||||
|
f"Cleanup attempt {attempt+1} failed, retrying in {retry_delay}s..."
|
||||||
|
)
|
||||||
|
time.sleep(retry_delay)
|
||||||
|
else:
|
||||||
|
print(f"Warning: Could not clean up {storage_path}")
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def client(app):
|
def client(app):
|
||||||
"""Create a test client for the application."""
|
"""Create a test client for the application."""
|
||||||
return app.test_client()
|
with app.test_client() as client:
|
||||||
|
# Enable cookies and sessions for the test client
|
||||||
|
client.testing = True
|
||||||
|
yield client
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def auth(client):
|
def auth(client):
|
||||||
"""Helper for authentication tests."""
|
"""Helper for authentication tests."""
|
||||||
|
|
||||||
class AuthActions:
|
class AuthActions:
|
||||||
def login(self, username='admin', password='admin123'):
|
def login(self, username="admin", password="admin123"):
|
||||||
return client.post('/auth/login', data={
|
# Use session for better cookie handling
|
||||||
'username': username,
|
with client.session_transaction() as session:
|
||||||
'password': password
|
# Pre-clear any existing session data
|
||||||
}, follow_redirects=True)
|
session.clear()
|
||||||
|
|
||||||
|
response = client.post(
|
||||||
|
"/auth/login",
|
||||||
|
data={"username": username, "password": password},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
return response
|
||||||
|
|
||||||
def logout(self):
|
def logout(self):
|
||||||
return client.get('/auth/logout', follow_redirects=True)
|
return client.get("/auth/logout", follow_redirects=True)
|
||||||
|
|
||||||
return AuthActions()
|
return AuthActions()
|
||||||
|
|
||||||
|
|
||||||
@pytest.fixture
|
@pytest.fixture
|
||||||
def logged_in_client(client, auth):
|
def logged_in_client(client, auth):
|
||||||
"""Client that's already logged in as admin."""
|
"""Client that's already logged in as admin."""
|
||||||
auth.login()
|
response = auth.login()
|
||||||
|
|
||||||
|
# Verify login was successful
|
||||||
|
assert response.status_code == 200
|
||||||
|
|
||||||
|
# Check for expected content in response to confirm logged in state
|
||||||
|
assert (
|
||||||
|
b"Cerrar" in response.data
|
||||||
|
or b"Logout" in response.data
|
||||||
|
or b"Panel" in response.data
|
||||||
|
)
|
||||||
|
|
||||||
return client
|
return client
|
||||||
|
|
||||||
def initialize_test_data():
|
|
||||||
|
def initialize_test_data(storage_path):
|
||||||
"""Initialize test data files."""
|
"""Initialize test data files."""
|
||||||
# Create test users
|
# Create test users
|
||||||
users_data = {
|
users_data = {
|
||||||
|
@ -76,7 +140,7 @@ def initialize_test_data():
|
||||||
"idioma": "es",
|
"idioma": "es",
|
||||||
"fecha_caducidad": None,
|
"fecha_caducidad": None,
|
||||||
"empresa": "ARCH",
|
"empresa": "ARCH",
|
||||||
"estado": "activo"
|
"estado": "activo",
|
||||||
},
|
},
|
||||||
"user1": {
|
"user1": {
|
||||||
"nombre": "Usuario Normal",
|
"nombre": "Usuario Normal",
|
||||||
|
@ -87,32 +151,32 @@ def initialize_test_data():
|
||||||
"idioma": "es",
|
"idioma": "es",
|
||||||
"fecha_caducidad": None,
|
"fecha_caducidad": None,
|
||||||
"empresa": "ARCH",
|
"empresa": "ARCH",
|
||||||
"estado": "activo"
|
"estado": "activo",
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
with open('test_storage/users/users.json', 'w', encoding='utf-8') as f:
|
with open(f"{storage_path}/users/users.json", "w", encoding="utf-8") as f:
|
||||||
json.dump(users_data, f, ensure_ascii=False, indent=2)
|
json.dump(users_data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
# Create test file types
|
# Create test file types
|
||||||
filetypes_data = {
|
filetypes_data = {
|
||||||
"pdf": {
|
"pdf": {
|
||||||
"extension": "pdf",
|
"extension": "pdf",
|
||||||
"descripcion": "Documento PDF",
|
"descripcion": "Documento PDF",
|
||||||
"mime_type": "application/pdf",
|
"mime_type": "application/pdf",
|
||||||
"tamano_maximo": 20971520
|
"tamano_maximo": 20971520,
|
||||||
},
|
},
|
||||||
"txt": {
|
"txt": {
|
||||||
"extension": "txt",
|
"extension": "txt",
|
||||||
"descripcion": "Documento de texto",
|
"descripcion": "Documento de texto",
|
||||||
"mime_type": "text/plain",
|
"mime_type": "text/plain",
|
||||||
"tamano_maximo": 5242880
|
"tamano_maximo": 5242880,
|
||||||
}
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
with open('test_storage/filetypes/filetypes.json', 'w', encoding='utf-8') as f:
|
with open(f"{storage_path}/filetypes/filetypes.json", "w", encoding="utf-8") as f:
|
||||||
json.dump(filetypes_data, f, ensure_ascii=False, indent=2)
|
json.dump(filetypes_data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
# Create test schemas
|
# Create test schemas
|
||||||
schemas_data = {
|
schemas_data = {
|
||||||
"TEST001": {
|
"TEST001": {
|
||||||
|
@ -125,20 +189,17 @@ def initialize_test_data():
|
||||||
"tipo": "pdf",
|
"tipo": "pdf",
|
||||||
"nombre": "Documento de Prueba",
|
"nombre": "Documento de Prueba",
|
||||||
"nivel_ver": 0,
|
"nivel_ver": 0,
|
||||||
"nivel_editar": 1000
|
"nivel_editar": 1000,
|
||||||
}
|
}
|
||||||
]
|
],
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
with open('test_storage/schemas/schema.json', 'w', encoding='utf-8') as f:
|
with open(f"{storage_path}/schemas/schema.json", "w", encoding="utf-8") as f:
|
||||||
json.dump(schemas_data, f, ensure_ascii=False, indent=2)
|
json.dump(schemas_data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
||||||
# Create indices file
|
# Create indices file
|
||||||
indices_data = {
|
indices_data = {"max_project_id": 0, "max_document_id": 0}
|
||||||
"max_project_id": 0,
|
|
||||||
"max_document_id": 0
|
with open(f"{storage_path}/indices.json", "w", encoding="utf-8") as f:
|
||||||
}
|
|
||||||
|
|
||||||
with open('test_storage/indices.json', 'w', encoding='utf-8') as f:
|
|
||||||
json.dump(indices_data, f, ensure_ascii=False, indent=2)
|
json.dump(indices_data, f, ensure_ascii=False, indent=2)
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
"""Helper functions for tests."""
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def ensure_clean_session(client):
|
||||||
|
"""Ensure we have a clean session before each test."""
|
||||||
|
# Get the root URL to reset application state
|
||||||
|
with client.session_transaction() as session:
|
||||||
|
session.clear()
|
||||||
|
|
||||||
|
# Make a request to reset client state
|
||||||
|
client.get("/")
|
||||||
|
|
||||||
|
return client
|
|
@ -5,97 +5,104 @@ import os
|
||||||
import time
|
import time
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
class JSONReporter:
|
class JSONReporter:
|
||||||
"""
|
"""
|
||||||
Custom pytest plugin to generate JSON test reports.
|
Custom pytest plugin to generate JSON test reports.
|
||||||
Based on the specification in section 9.4 of descripcion.md.
|
Based on the specification in section 9.4 of descripcion.md.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, config):
|
def __init__(self, config):
|
||||||
self.config = config
|
self.config = config
|
||||||
self.start_time = time.time()
|
self.start_time = time.time()
|
||||||
self.results = {
|
self.results = {
|
||||||
'summary': {
|
"summary": {
|
||||||
'total': 0,
|
"total": 0,
|
||||||
'passed': 0,
|
"passed": 0,
|
||||||
'failed': 0,
|
"failed": 0,
|
||||||
'skipped': 0,
|
"skipped": 0,
|
||||||
'error': 0,
|
"error": 0,
|
||||||
'duration': 0,
|
"duration": 0,
|
||||||
'timestamp': datetime.datetime.now().isoformat()
|
"timestamp": datetime.datetime.now().isoformat(),
|
||||||
},
|
},
|
||||||
'tests': []
|
"tests": [],
|
||||||
}
|
}
|
||||||
|
|
||||||
def pytest_runtest_logreport(self, report):
|
def pytest_runtest_logreport(self, report):
|
||||||
"""Handle the reporting of a test run."""
|
"""Handle the reporting of a test run."""
|
||||||
if report.when == 'call' or (report.when == 'setup' and report.skipped):
|
if report.when == "call" or (report.when == "setup" and report.skipped):
|
||||||
self.results['summary']['total'] += 1
|
self.results["summary"]["total"] += 1
|
||||||
|
|
||||||
if report.passed:
|
if report.passed:
|
||||||
result = 'passed'
|
result = "passed"
|
||||||
self.results['summary']['passed'] += 1
|
self.results["summary"]["passed"] += 1
|
||||||
elif report.failed:
|
elif report.failed:
|
||||||
if report.when != 'call':
|
if report.when != "call":
|
||||||
result = 'error'
|
result = "error"
|
||||||
self.results['summary']['error'] += 1
|
self.results["summary"]["error"] += 1
|
||||||
else:
|
else:
|
||||||
result = 'failed'
|
result = "failed"
|
||||||
self.results['summary']['failed'] += 1
|
self.results["summary"]["failed"] += 1
|
||||||
elif report.skipped:
|
elif report.skipped:
|
||||||
result = 'skipped'
|
result = "skipped"
|
||||||
self.results['summary']['skipped'] += 1
|
self.results["summary"]["skipped"] += 1
|
||||||
|
|
||||||
# Extract test metadata
|
# Extract test metadata
|
||||||
test_module = report.nodeid.split('::')[0]
|
test_module = report.nodeid.split("::")[0]
|
||||||
test_class = report.nodeid.split('::')[1] if '::' in report.nodeid else None
|
test_class = report.nodeid.split("::")[1] if "::" in report.nodeid else None
|
||||||
test_name = report.nodeid.split('::')[-1]
|
test_name = report.nodeid.split("::")[-1]
|
||||||
|
|
||||||
# Extract error details if present
|
# Extract error details if present
|
||||||
error_message = None
|
error_message = None
|
||||||
error_trace = None
|
error_trace = None
|
||||||
if hasattr(report, 'longrepr') and report.longrepr:
|
if hasattr(report, "longrepr") and report.longrepr:
|
||||||
if hasattr(report.longrepr, 'reprcrash') and report.longrepr.reprcrash:
|
if hasattr(report.longrepr, "reprcrash") and report.longrepr.reprcrash:
|
||||||
error_message = report.longrepr.reprcrash.message
|
error_message = report.longrepr.reprcrash.message
|
||||||
error_trace = str(report.longrepr)
|
error_trace = str(report.longrepr)
|
||||||
|
|
||||||
# Add test result to list
|
# Add test result to list
|
||||||
self.results['tests'].append({
|
self.results["tests"].append(
|
||||||
'id': report.nodeid,
|
{
|
||||||
'module': test_module,
|
"id": report.nodeid,
|
||||||
'class': test_class,
|
"module": test_module,
|
||||||
'name': test_name,
|
"class": test_class,
|
||||||
'result': result,
|
"name": test_name,
|
||||||
'duration': report.duration,
|
"result": result,
|
||||||
'error_message': error_message,
|
"duration": report.duration,
|
||||||
'error_trace': error_trace
|
"error_message": error_message,
|
||||||
})
|
"error_trace": error_trace,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
def pytest_sessionfinish(self, session):
|
def pytest_sessionfinish(self, session):
|
||||||
"""Generate report at end of test session."""
|
"""Generate report at end of test session."""
|
||||||
self.results['summary']['duration'] = time.time() - self.start_time
|
self.results["summary"]["duration"] = time.time() - self.start_time
|
||||||
|
|
||||||
# Create output directory if it doesn't exist
|
# Create output directory if it doesn't exist
|
||||||
output_dir = Path('test_reports')
|
output_dir = Path("test_reports")
|
||||||
output_dir.mkdir(exist_ok=True)
|
output_dir.mkdir(exist_ok=True)
|
||||||
|
|
||||||
# Generate timestamp for file name
|
# Generate timestamp for file name
|
||||||
timestamp = datetime.datetime.now().strftime('%Y%m%d_%H%M%S')
|
timestamp = datetime.datetime.now().strftime("%Y%m%d_%H%M%S")
|
||||||
output_path = output_dir / f'test_results_{timestamp}.json'
|
output_path = output_dir / f"test_results_{timestamp}.json"
|
||||||
|
|
||||||
# Write results to file
|
# Write results to file
|
||||||
with open(output_path, 'w', encoding='utf-8') as f:
|
with open(output_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(self.results, f, indent=2, ensure_ascii=False)
|
json.dump(self.results, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
# Also create a symlink/copy to latest results
|
# Also create a symlink/copy to latest results
|
||||||
latest_path = output_dir / 'test_results_latest.json'
|
latest_path = output_dir / "test_results_latest.json"
|
||||||
if os.path.exists(latest_path):
|
if os.path.exists(latest_path):
|
||||||
os.remove(latest_path)
|
os.remove(latest_path)
|
||||||
with open(latest_path, 'w', encoding='utf-8') as f:
|
with open(latest_path, "w", encoding="utf-8") as f:
|
||||||
json.dump(self.results, f, indent=2, ensure_ascii=False)
|
json.dump(self.results, f, indent=2, ensure_ascii=False)
|
||||||
|
|
||||||
print(f"\nJSON test report generated: {output_path}")
|
print(f"\nJSON test report generated: {output_path}")
|
||||||
|
|
||||||
@pytest.hookimpl(tryfirst=True)
|
|
||||||
|
# Register the plugin
|
||||||
|
@pytest.hookimpl(trylast=True)
|
||||||
def pytest_configure(config):
|
def pytest_configure(config):
|
||||||
"""Register the JSON reporter plugin."""
|
"""Register the JSON reporter plugin."""
|
||||||
config.pluginmanager.register(JSONReporter(config), 'json_reporter')
|
config._json_reporter = JSONReporter(config)
|
||||||
|
config.pluginmanager.register(config._json_reporter, "json_reporter")
|
||||||
|
|
|
@ -1,99 +1,132 @@
|
||||||
import pytest
|
import pytest
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
from .helpers import ensure_clean_session
|
||||||
|
|
||||||
|
|
||||||
class TestAdminFunctions:
|
class TestAdminFunctions:
|
||||||
"""Test administrative functions."""
|
"""Test administrative functions."""
|
||||||
|
|
||||||
def test_admin_dashboard(self, logged_in_client):
|
def test_admin_dashboard(self, logged_in_client):
|
||||||
"""Test access to admin dashboard."""
|
"""Test accessing admin dashboard."""
|
||||||
response = logged_in_client.get('/admin/')
|
# Ensure we have a clean, authenticated session
|
||||||
|
logged_in_client = ensure_clean_session(logged_in_client)
|
||||||
|
|
||||||
|
# Access the admin dashboard
|
||||||
|
response = logged_in_client.get("/admin/dashboard")
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Panel de Administraci' in response.data # "Panel de Administración"
|
assert b"Administraci" in response.data or b"Dashboard" in response.data
|
||||||
|
|
||||||
def test_filetypes_management(self, logged_in_client):
|
def test_filetypes_management(self, logged_in_client):
|
||||||
"""Test file types management page."""
|
"""Test file types management page."""
|
||||||
response = logged_in_client.get('/admin/filetypes')
|
response = logged_in_client.get("/admin/filetypes")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Tipos de Archivo' in response.data
|
assert b"Tipos de Archivo" in response.data
|
||||||
|
|
||||||
def test_system_status(self, logged_in_client):
|
def test_system_status(self, logged_in_client):
|
||||||
"""Test system status page."""
|
"""Test system status page."""
|
||||||
response = logged_in_client.get('/admin/system')
|
response = logged_in_client.get("/admin/system")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Estado del Sistema' in response.data
|
assert b"Estado del Sistema" in response.data
|
||||||
|
|
||||||
def test_add_filetype(self, logged_in_client, app):
|
def test_add_filetype(self, logged_in_client, app):
|
||||||
"""Test adding a new file type."""
|
"""Test adding a new file type."""
|
||||||
response = logged_in_client.post('/admin/filetypes/add', data={
|
response = logged_in_client.post(
|
||||||
'extension': 'docx',
|
"/admin/filetypes/add",
|
||||||
'descripcion': 'Documento Word',
|
data={
|
||||||
'mime_type': 'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
|
"extension": "docx",
|
||||||
'tamano_maximo': 15728640 # 15MB in bytes
|
"descripcion": "Documento Word",
|
||||||
}, follow_redirects=True)
|
"mime_type": "application/vnd.openxmlformats-officedocument.wordprocessingml.document",
|
||||||
|
"tamano_maximo": 15728640, # 15MB in bytes
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify file type was added
|
# Verify file type was added
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
filetypes_path = os.path.join(app.config['STORAGE_PATH'], 'filetypes', 'filetypes.json')
|
filetypes_path = os.path.join(
|
||||||
with open(filetypes_path, 'r') as f:
|
app.config["STORAGE_PATH"], "filetypes", "filetypes.json"
|
||||||
|
)
|
||||||
|
with open(filetypes_path, "r") as f:
|
||||||
filetypes = json.load(f)
|
filetypes = json.load(f)
|
||||||
assert 'docx' in filetypes
|
assert "docx" in filetypes
|
||||||
assert filetypes['docx']['descripcion'] == 'Documento Word'
|
assert filetypes["docx"]["descripcion"] == "Documento Word"
|
||||||
|
|
||||||
def test_delete_filetype(self, logged_in_client, app):
|
def test_delete_filetype(self, logged_in_client, app):
|
||||||
"""Test deleting a file type."""
|
"""Test deleting a file type."""
|
||||||
# First add a file type to delete
|
# First add a file type to delete
|
||||||
logged_in_client.post('/admin/filetypes/add', data={
|
logged_in_client.post(
|
||||||
'extension': 'xlsx',
|
"/admin/filetypes/add",
|
||||||
'descripcion': 'Hoja de cálculo Excel',
|
data={
|
||||||
'mime_type': 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
"extension": "xlsx",
|
||||||
'tamano_maximo': 15728640
|
"descripcion": "Hoja de cálculo Excel",
|
||||||
}, follow_redirects=True)
|
"mime_type": "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||||
|
"tamano_maximo": 15728640,
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Verify it was added
|
# Verify it was added
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
filetypes_path = os.path.join(app.config['STORAGE_PATH'], 'filetypes', 'filetypes.json')
|
filetypes_path = os.path.join(
|
||||||
with open(filetypes_path, 'r') as f:
|
app.config["STORAGE_PATH"], "filetypes", "filetypes.json"
|
||||||
|
)
|
||||||
|
with open(filetypes_path, "r") as f:
|
||||||
filetypes = json.load(f)
|
filetypes = json.load(f)
|
||||||
assert 'xlsx' in filetypes
|
assert "xlsx" in filetypes
|
||||||
|
|
||||||
# Now delete it
|
# Now delete it
|
||||||
response = logged_in_client.post('/admin/filetypes/xlsx/delete', follow_redirects=True)
|
response = logged_in_client.post(
|
||||||
|
"/admin/filetypes/xlsx/delete", follow_redirects=True
|
||||||
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify it was deleted
|
# Verify it was deleted
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
filetypes_path = os.path.join(app.config['STORAGE_PATH'], 'filetypes', 'filetypes.json')
|
filetypes_path = os.path.join(
|
||||||
with open(filetypes_path, 'r') as f:
|
app.config["STORAGE_PATH"], "filetypes", "filetypes.json"
|
||||||
|
)
|
||||||
|
with open(filetypes_path, "r") as f:
|
||||||
filetypes = json.load(f)
|
filetypes = json.load(f)
|
||||||
assert 'xlsx' not in filetypes
|
assert "xlsx" not in filetypes
|
||||||
|
|
||||||
def test_update_filetype(self, logged_in_client, app):
|
def test_update_filetype(self, logged_in_client, app):
|
||||||
"""Test updating a file type."""
|
"""Test updating a file type."""
|
||||||
# First add a file type to update
|
# First add a file type to update
|
||||||
logged_in_client.post('/admin/filetypes/add', data={
|
logged_in_client.post(
|
||||||
'extension': 'png',
|
"/admin/filetypes/add",
|
||||||
'descripcion': 'Imagen PNG',
|
data={
|
||||||
'mime_type': 'image/png',
|
"extension": "png",
|
||||||
'tamano_maximo': 5242880 # 5MB
|
"descripcion": "Imagen PNG",
|
||||||
}, follow_redirects=True)
|
"mime_type": "image/png",
|
||||||
|
"tamano_maximo": 5242880, # 5MB
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Now update it
|
# Now update it
|
||||||
response = logged_in_client.post('/admin/filetypes/png/update', data={
|
response = logged_in_client.post(
|
||||||
'descripcion': 'Imagen PNG Actualizada',
|
"/admin/filetypes/png/update",
|
||||||
'mime_type': 'image/png',
|
data={
|
||||||
'tamano_maximo': 10485760 # 10MB (increased)
|
"descripcion": "Imagen PNG Actualizada",
|
||||||
}, follow_redirects=True)
|
"mime_type": "image/png",
|
||||||
|
"tamano_maximo": 10485760, # 10MB (increased)
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify changes
|
# Verify changes
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
filetypes_path = os.path.join(app.config['STORAGE_PATH'], 'filetypes', 'filetypes.json')
|
filetypes_path = os.path.join(
|
||||||
with open(filetypes_path, 'r') as f:
|
app.config["STORAGE_PATH"], "filetypes", "filetypes.json"
|
||||||
|
)
|
||||||
|
with open(filetypes_path, "r") as f:
|
||||||
filetypes = json.load(f)
|
filetypes = json.load(f)
|
||||||
assert 'png' in filetypes
|
assert "png" in filetypes
|
||||||
assert filetypes['png']['descripcion'] == 'Imagen PNG Actualizada'
|
assert filetypes["png"]["descripcion"] == "Imagen PNG Actualizada"
|
||||||
assert filetypes['png']['tamano_maximo'] == 10485760
|
assert filetypes["png"]["tamano_maximo"] == 10485760
|
||||||
|
|
|
@ -1,67 +1,72 @@
|
||||||
import pytest
|
import pytest
|
||||||
from flask import session, g
|
from flask import session, g
|
||||||
|
|
||||||
|
|
||||||
class TestAuth:
|
class TestAuth:
|
||||||
"""Test authentication functionality."""
|
"""Test authentication functionality."""
|
||||||
|
|
||||||
def test_login_page(self, client):
|
def test_login_page(self, client):
|
||||||
"""Test that login page loads correctly."""
|
"""Test that login page loads correctly."""
|
||||||
response = client.get('/auth/login')
|
response = client.get("/auth/login")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Iniciar sesi' in response.data # 'Iniciar sesión' in Spanish
|
assert b"Iniciar sesi" in response.data # 'Iniciar sesión' in Spanish
|
||||||
|
|
||||||
def test_login_success(self, client):
|
def test_login_success(self, client):
|
||||||
"""Test successful login with correct credentials."""
|
"""Test successful login with correct credentials."""
|
||||||
response = client.post(
|
response = client.post(
|
||||||
'/auth/login',
|
"/auth/login",
|
||||||
data={'username': 'admin', 'password': 'admin123'},
|
data={"username": "admin", "password": "admin123"},
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
# Check that we're redirected to the right page after login
|
# Check that we're redirected to the right page after login
|
||||||
assert b'Panel' in response.data or b'Proyectos' in response.data
|
assert b"Panel" in response.data or b"Proyectos" in response.data
|
||||||
|
|
||||||
def test_login_invalid_credentials(self, client):
|
def test_login_invalid_credentials(self, client):
|
||||||
"""Test login with invalid credentials."""
|
"""Test login with invalid credentials."""
|
||||||
response = client.post(
|
response = client.post(
|
||||||
'/auth/login',
|
"/auth/login",
|
||||||
data={'username': 'admin', 'password': 'wrongpassword'},
|
data={"username": "admin", "password": "wrongpassword"},
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'credenciales' in response.data.lower() # Error message about credentials
|
assert (
|
||||||
|
b"credenciales" in response.data.lower()
|
||||||
|
) # Error message about credentials
|
||||||
|
|
||||||
def test_logout(self, auth, client):
|
def test_logout(self, auth, client):
|
||||||
"""Test logout functionality."""
|
"""Test logout functionality."""
|
||||||
# First login
|
# First login
|
||||||
auth.login()
|
auth.login()
|
||||||
|
|
||||||
# Then logout
|
# Then logout
|
||||||
response = auth.logout()
|
response = auth.logout()
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Check if logged out - try to access a protected page
|
# Check if logged out - try to access a protected page
|
||||||
response = client.get('/users/', follow_redirects=True)
|
response = client.get("/users/", follow_redirects=True)
|
||||||
assert b'iniciar sesi' in response.data.lower() # Should see login page
|
assert b"iniciar sesi" in response.data.lower() # Should see login page
|
||||||
|
|
||||||
def test_access_protected_route(self, client):
|
def test_access_protected_route(self, client):
|
||||||
"""Test accessing a protected route without login."""
|
"""Test accessing a protected route without login."""
|
||||||
# Try to access users list without login
|
# Try to access users list without login
|
||||||
response = client.get('/users/', follow_redirects=True)
|
response = client.get("/users/", follow_redirects=True)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'iniciar sesi' in response.data.lower() # Should be redirected to login
|
assert b"iniciar sesi" in response.data.lower() # Should be redirected to login
|
||||||
|
|
||||||
def test_access_protected_route_with_login(self, logged_in_client):
|
def test_access_protected_route_with_login(self, logged_in_client):
|
||||||
"""Test accessing a protected route with login."""
|
"""Test accessing a protected route with login."""
|
||||||
# Admin should be able to access users list
|
# Admin should be able to access users list
|
||||||
response = logged_in_client.get('/admin/dashboard')
|
response = logged_in_client.get("/admin/dashboard")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
def test_permission_levels(self, client, auth):
|
def test_permission_levels(self, client, auth):
|
||||||
"""Test different permission levels."""
|
"""Test different permission levels."""
|
||||||
# Login as regular user
|
# Login as regular user
|
||||||
auth.login(username='user1', password='admin123')
|
auth.login(username="user1", password="admin123")
|
||||||
|
|
||||||
# Try to access admin-only page
|
# Try to access admin-only page
|
||||||
response = client.get('/admin/dashboard', follow_redirects=True)
|
response = client.get("/admin/dashboard", follow_redirects=True)
|
||||||
assert response.status_code == 403 or b'acceso denegado' in response.data.lower()
|
assert (
|
||||||
|
response.status_code == 403 or b"acceso denegado" in response.data.lower()
|
||||||
|
)
|
||||||
|
|
|
@ -4,151 +4,153 @@ import io
|
||||||
import json
|
import json
|
||||||
from werkzeug.datastructures import FileStorage
|
from werkzeug.datastructures import FileStorage
|
||||||
|
|
||||||
|
|
||||||
class TestDocuments:
|
class TestDocuments:
|
||||||
"""Test document management functionality."""
|
"""Test document management functionality."""
|
||||||
|
|
||||||
def setup_project(self, logged_in_client, app):
|
def setup_project(self, logged_in_client, app):
|
||||||
"""Helper to create a test project."""
|
"""Helper to create a test project."""
|
||||||
# Create a project
|
# Create a project
|
||||||
logged_in_client.post('/projects/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'TESTDOC',
|
"/projects/create",
|
||||||
'descripcion': 'Proyecto para documentos',
|
data={
|
||||||
'cliente': 'Cliente Test',
|
"codigo": "TESTDOC",
|
||||||
'esquema': 'TEST001',
|
"descripcion": "Proyecto para documentos",
|
||||||
'destinacion': 'Pruebas',
|
"cliente": "Cliente Test",
|
||||||
'notas': 'Proyecto para pruebas de documentos'
|
"esquema": "TEST001",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas",
|
||||||
|
"notas": "Proyecto para pruebas de documentos",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Find the project ID
|
# Find the project ID
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
project_dirs = [d for d in os.listdir(projects_dir) if 'TESTDOC' in d]
|
project_dirs = [d for d in os.listdir(projects_dir) if "TESTDOC" in d]
|
||||||
assert len(project_dirs) > 0
|
assert len(project_dirs) > 0
|
||||||
|
|
||||||
project_dir = project_dirs[0]
|
project_dir = project_dirs[0]
|
||||||
return project_dir.split('_')[0].replace('@', '')
|
return project_dir.split("_")[0].replace("@", "")
|
||||||
|
|
||||||
def test_upload_document(self, logged_in_client, app):
|
def test_upload_document(self, logged_in_client, app):
|
||||||
"""Test uploading a document to a project."""
|
"""Test uploading a document to a project."""
|
||||||
# Create a project and get its ID
|
# Create a project and get its ID
|
||||||
project_id = self.setup_project(logged_in_client, app)
|
project_id = self.setup_project(logged_in_client, app)
|
||||||
|
|
||||||
# Create a test file
|
# Create a test file
|
||||||
test_file = FileStorage(
|
test_file = FileStorage(
|
||||||
stream=io.BytesIO(b'This is a test document content.'),
|
stream=io.BytesIO(b"This is a test document content."),
|
||||||
filename='test_document.txt',
|
filename="test_document.txt",
|
||||||
content_type='text/plain',
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
|
|
||||||
# Upload the document
|
# Upload the document
|
||||||
response = logged_in_client.post(
|
response = logged_in_client.post(
|
||||||
f'/projects/{project_id}/documents/upload',
|
f"/projects/{project_id}/documents/upload",
|
||||||
data={
|
data={
|
||||||
'tipo_doc': 'Documento de Prueba', # Match schema type from TEST001
|
"tipo_doc": "Documento de Prueba", # Match schema type from TEST001
|
||||||
'descripcion': 'Documento de prueba para tests',
|
"descripcion": "Documento de prueba para tests",
|
||||||
'file': test_file
|
"file": test_file,
|
||||||
},
|
},
|
||||||
content_type='multipart/form-data',
|
content_type="multipart/form-data",
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify document was created
|
# Verify document was created
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
project_path = None
|
project_path = None
|
||||||
|
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
for dir_name in os.listdir(projects_dir):
|
for dir_name in os.listdir(projects_dir):
|
||||||
if dir_name.startswith(f'@{project_id}_'):
|
if dir_name.startswith(f"@{project_id}_"):
|
||||||
project_path = os.path.join(projects_dir, dir_name)
|
project_path = os.path.join(projects_dir, dir_name)
|
||||||
break
|
break
|
||||||
|
|
||||||
assert project_path is not None
|
assert project_path is not None
|
||||||
|
|
||||||
# Check for documents directory with content
|
# Check for documents directory with content
|
||||||
docs_path = os.path.join(project_path, 'documents')
|
docs_path = os.path.join(project_path, "documents")
|
||||||
assert os.path.exists(docs_path)
|
assert os.path.exists(docs_path)
|
||||||
|
|
||||||
# Should have at least one document folder
|
# Should have at least one document folder
|
||||||
assert len(os.listdir(docs_path)) > 0
|
assert len(os.listdir(docs_path)) > 0
|
||||||
|
|
||||||
def test_document_versions(self, logged_in_client, app):
|
def test_document_versions(self, logged_in_client, app):
|
||||||
"""Test document versioning functionality."""
|
"""Test document versioning functionality."""
|
||||||
# Create a project and upload first version
|
# Create a project and upload first version
|
||||||
project_id = self.setup_project(logged_in_client, app)
|
project_id = self.setup_project(logged_in_client, app)
|
||||||
|
|
||||||
# Upload first version
|
# Upload first version
|
||||||
test_file1 = FileStorage(
|
test_file1 = FileStorage(
|
||||||
stream=io.BytesIO(b'Document content version 1'),
|
stream=io.BytesIO(b"Document content version 1"),
|
||||||
filename='test_versioning.txt',
|
filename="test_versioning.txt",
|
||||||
content_type='text/plain',
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
|
|
||||||
logged_in_client.post(
|
logged_in_client.post(
|
||||||
f'/projects/{project_id}/documents/upload',
|
f"/projects/{project_id}/documents/upload",
|
||||||
data={
|
data={
|
||||||
'tipo_doc': 'Documento de Prueba',
|
"tipo_doc": "Documento de Prueba",
|
||||||
'descripcion': 'Documento para pruebas de versiones',
|
"descripcion": "Documento para pruebas de versiones",
|
||||||
'file': test_file1
|
"file": test_file1,
|
||||||
},
|
},
|
||||||
content_type='multipart/form-data',
|
content_type="multipart/form-data",
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Find the document ID
|
# Find the document ID
|
||||||
doc_id = None
|
doc_id = None
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
for dir_name in os.listdir(projects_dir):
|
for dir_name in os.listdir(projects_dir):
|
||||||
if dir_name.startswith(f'@{project_id}_'):
|
if dir_name.startswith(f"@{project_id}_"):
|
||||||
project_path = os.path.join(projects_dir, dir_name)
|
project_path = os.path.join(projects_dir, dir_name)
|
||||||
docs_path = os.path.join(project_path, 'documents')
|
docs_path = os.path.join(project_path, "documents")
|
||||||
|
|
||||||
# Get first document directory
|
# Get first document directory
|
||||||
doc_dirs = os.listdir(docs_path)
|
doc_dirs = os.listdir(docs_path)
|
||||||
if doc_dirs:
|
if doc_dirs:
|
||||||
doc_id = doc_dirs[0].split('_')[0].replace('@', '')
|
doc_id = doc_dirs[0].split("_")[0].replace("@", "")
|
||||||
break
|
break
|
||||||
|
|
||||||
assert doc_id is not None
|
assert doc_id is not None
|
||||||
|
|
||||||
# Upload second version of the same document
|
# Upload second version of the same document
|
||||||
test_file2 = FileStorage(
|
test_file2 = FileStorage(
|
||||||
stream=io.BytesIO(b'Document content version 2 - UPDATED'),
|
stream=io.BytesIO(b"Document content version 2 - UPDATED"),
|
||||||
filename='test_versioning_v2.txt',
|
filename="test_versioning_v2.txt",
|
||||||
content_type='text/plain',
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
|
|
||||||
response = logged_in_client.post(
|
response = logged_in_client.post(
|
||||||
f'/projects/{project_id}/documents/{doc_id}/upload',
|
f"/projects/{project_id}/documents/{doc_id}/upload",
|
||||||
data={
|
data={"descripcion": "Segunda versión del documento", "file": test_file2},
|
||||||
'descripcion': 'Segunda versión del documento',
|
content_type="multipart/form-data",
|
||||||
'file': test_file2
|
follow_redirects=True,
|
||||||
},
|
|
||||||
content_type='multipart/form-data',
|
|
||||||
follow_redirects=True
|
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Check for multiple versions in metadata
|
# Check for multiple versions in metadata
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
for dir_name in os.listdir(projects_dir):
|
for dir_name in os.listdir(projects_dir):
|
||||||
if dir_name.startswith(f'@{project_id}_'):
|
if dir_name.startswith(f"@{project_id}_"):
|
||||||
project_path = os.path.join(projects_dir, dir_name)
|
project_path = os.path.join(projects_dir, dir_name)
|
||||||
docs_path = os.path.join(project_path, 'documents')
|
docs_path = os.path.join(project_path, "documents")
|
||||||
|
|
||||||
# Find document directory
|
# Find document directory
|
||||||
for doc_dir in os.listdir(docs_path):
|
for doc_dir in os.listdir(docs_path):
|
||||||
if doc_dir.startswith(f'@{doc_id}_'):
|
if doc_dir.startswith(f"@{doc_id}_"):
|
||||||
doc_path = os.path.join(docs_path, doc_dir)
|
doc_path = os.path.join(docs_path, doc_dir)
|
||||||
meta_path = os.path.join(doc_path, 'meta.json')
|
meta_path = os.path.join(doc_path, "meta.json")
|
||||||
|
|
||||||
# Check metadata for versions
|
# Check metadata for versions
|
||||||
if os.path.exists(meta_path):
|
if os.path.exists(meta_path):
|
||||||
with open(meta_path, 'r') as f:
|
with open(meta_path, "r") as f:
|
||||||
metadata = json.load(f)
|
metadata = json.load(f)
|
||||||
assert 'versions' in metadata
|
assert "versions" in metadata
|
||||||
assert len(metadata['versions']) >= 2
|
assert len(metadata["versions"]) >= 2
|
||||||
|
|
|
@ -3,160 +3,177 @@ import os
|
||||||
import io
|
import io
|
||||||
from werkzeug.datastructures import FileStorage
|
from werkzeug.datastructures import FileStorage
|
||||||
|
|
||||||
|
|
||||||
class TestIntegration:
|
class TestIntegration:
|
||||||
"""Test integrations between different components."""
|
"""Test integrations between different components."""
|
||||||
|
|
||||||
def test_complete_workflow(self, logged_in_client, app):
|
def test_complete_workflow(self, logged_in_client, app):
|
||||||
"""Test a complete workflow from project creation to document download."""
|
"""Test a complete workflow from project creation to document download."""
|
||||||
# 1. Create a new project
|
# 1. Create a new project
|
||||||
logged_in_client.post('/projects/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'WORKFLOW',
|
"/projects/create",
|
||||||
'descripcion': 'Proyecto de flujo completo',
|
data={
|
||||||
'cliente': 'Cliente Integración',
|
"codigo": "WORKFLOW",
|
||||||
'esquema': 'TEST001',
|
"descripcion": "Proyecto de flujo completo",
|
||||||
'destinacion': 'Pruebas de integración',
|
"cliente": "Cliente Integración",
|
||||||
'notas': 'Notas de proyecto de prueba'
|
"esquema": "TEST001",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas de integración",
|
||||||
|
"notas": "Notas de proyecto de prueba",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# 2. Find the project ID
|
# 2. Find the project ID
|
||||||
project_id = None
|
project_id = None
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
project_dirs = [d for d in os.listdir(projects_dir) if 'WORKFLOW' in d]
|
project_dirs = [d for d in os.listdir(projects_dir) if "WORKFLOW" in d]
|
||||||
assert len(project_dirs) > 0
|
assert len(project_dirs) > 0
|
||||||
project_dir = project_dirs[0]
|
project_dir = project_dirs[0]
|
||||||
project_id = project_dir.split('_')[0].replace('@', '')
|
project_id = project_dir.split("_")[0].replace("@", "")
|
||||||
|
|
||||||
assert project_id is not None
|
assert project_id is not None
|
||||||
|
|
||||||
# 3. Upload a document to the project
|
# 3. Upload a document to the project
|
||||||
test_file = FileStorage(
|
test_file = FileStorage(
|
||||||
stream=io.BytesIO(b'Content for integration test document'),
|
stream=io.BytesIO(b"Content for integration test document"),
|
||||||
filename='integration_doc.txt',
|
filename="integration_doc.txt",
|
||||||
content_type='text/plain',
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
|
|
||||||
response = logged_in_client.post(
|
response = logged_in_client.post(
|
||||||
f'/projects/{project_id}/documents/upload',
|
f"/projects/{project_id}/documents/upload",
|
||||||
data={
|
data={
|
||||||
'tipo_doc': 'Documento de Prueba',
|
"tipo_doc": "Documento de Prueba",
|
||||||
'descripcion': 'Documento de flujo de integración',
|
"descripcion": "Documento de flujo de integración",
|
||||||
'file': test_file
|
"file": test_file,
|
||||||
},
|
},
|
||||||
content_type='multipart/form-data',
|
content_type="multipart/form-data",
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# 4. Find the document ID
|
# 4. Find the document ID
|
||||||
doc_id = None
|
doc_id = None
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
for dir_name in os.listdir(projects_dir):
|
for dir_name in os.listdir(projects_dir):
|
||||||
if dir_name.startswith(f'@{project_id}_'):
|
if dir_name.startswith(f"@{project_id}_"):
|
||||||
project_path = os.path.join(projects_dir, dir_name)
|
project_path = os.path.join(projects_dir, dir_name)
|
||||||
docs_path = os.path.join(project_path, 'documents')
|
docs_path = os.path.join(project_path, "documents")
|
||||||
|
|
||||||
doc_dirs = os.listdir(docs_path)
|
doc_dirs = os.listdir(docs_path)
|
||||||
if doc_dirs:
|
if doc_dirs:
|
||||||
doc_id = doc_dirs[0].split('_')[0].replace('@', '')
|
doc_id = doc_dirs[0].split("_")[0].replace("@", "")
|
||||||
break
|
break
|
||||||
|
|
||||||
assert doc_id is not None
|
assert doc_id is not None
|
||||||
|
|
||||||
# 5. View document details
|
# 5. View document details
|
||||||
response = logged_in_client.get(f'/projects/{project_id}/documents/{doc_id}')
|
response = logged_in_client.get(f"/projects/{project_id}/documents/{doc_id}")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Documento de flujo de integraci' in response.data
|
assert b"Documento de flujo de integraci" in response.data
|
||||||
|
|
||||||
# 6. Upload a new version of the document
|
# 6. Upload a new version of the document
|
||||||
test_file2 = FileStorage(
|
test_file2 = FileStorage(
|
||||||
stream=io.BytesIO(b'Updated content for version 2'),
|
stream=io.BytesIO(b"Updated content for version 2"),
|
||||||
filename='integration_doc_v2.txt',
|
filename="integration_doc_v2.txt",
|
||||||
content_type='text/plain',
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
|
|
||||||
response = logged_in_client.post(
|
response = logged_in_client.post(
|
||||||
f'/projects/{project_id}/documents/{doc_id}/upload',
|
f"/projects/{project_id}/documents/{doc_id}/upload",
|
||||||
data={
|
data={
|
||||||
'descripcion': 'Segunda versión del documento de integración',
|
"descripcion": "Segunda versión del documento de integración",
|
||||||
'file': test_file2
|
"file": test_file2,
|
||||||
},
|
},
|
||||||
content_type='multipart/form-data',
|
content_type="multipart/form-data",
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# 7. Download the document
|
# 7. Download the document
|
||||||
response = logged_in_client.get(f'/projects/{project_id}/documents/{doc_id}/download/latest')
|
response = logged_in_client.get(
|
||||||
|
f"/projects/{project_id}/documents/{doc_id}/download/latest"
|
||||||
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Updated content for version 2' in response.data
|
assert b"Updated content for version 2" in response.data
|
||||||
|
|
||||||
# 8. Download a specific version (the first one)
|
# 8. Download a specific version (the first one)
|
||||||
response = logged_in_client.get(f'/projects/{project_id}/documents/{doc_id}/download/1')
|
response = logged_in_client.get(
|
||||||
|
f"/projects/{project_id}/documents/{doc_id}/download/1"
|
||||||
|
)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Content for integration test document' in response.data
|
assert b"Content for integration test document" in response.data
|
||||||
|
|
||||||
def test_schema_project_document_integration(self, logged_in_client, app):
|
def test_schema_project_document_integration(self, logged_in_client, app):
|
||||||
"""Test integration between schemas, projects and documents."""
|
"""Test integration between schemas, projects and documents."""
|
||||||
# 1. Create a custom schema
|
# 1. Create a custom schema
|
||||||
logged_in_client.post('/schemas/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'CUSTOM',
|
"/schemas/create",
|
||||||
'descripcion': 'Esquema personalizado para integración',
|
data={
|
||||||
'documentos-0-tipo': 'pdf',
|
"codigo": "CUSTOM",
|
||||||
'documentos-0-nombre': 'Informe Principal',
|
"descripcion": "Esquema personalizado para integración",
|
||||||
'documentos-0-nivel_ver': 0,
|
"documentos-0-tipo": "pdf",
|
||||||
'documentos-0-nivel_editar': 5000,
|
"documentos-0-nombre": "Informe Principal",
|
||||||
'documentos-1-tipo': 'txt',
|
"documentos-0-nivel_ver": 0,
|
||||||
'documentos-1-nombre': 'Notas Adicionales',
|
"documentos-0-nivel_editar": 5000,
|
||||||
'documentos-1-nivel_ver': 0,
|
"documentos-1-tipo": "txt",
|
||||||
'documentos-1-nivel_editar': 1000
|
"documentos-1-nombre": "Notas Adicionales",
|
||||||
}, follow_redirects=True)
|
"documentos-1-nivel_ver": 0,
|
||||||
|
"documentos-1-nivel_editar": 1000,
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# 2. Create a project with the custom schema
|
# 2. Create a project with the custom schema
|
||||||
logged_in_client.post('/projects/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'PROJ_SCHEMA',
|
"/projects/create",
|
||||||
'descripcion': 'Proyecto con esquema personalizado',
|
data={
|
||||||
'cliente': 'Cliente Test',
|
"codigo": "PROJ_SCHEMA",
|
||||||
'esquema': 'CUSTOM',
|
"descripcion": "Proyecto con esquema personalizado",
|
||||||
'destinacion': 'Pruebas',
|
"cliente": "Cliente Test",
|
||||||
'notas': 'Proyecto para probar integración de esquemas'
|
"esquema": "CUSTOM",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas",
|
||||||
|
"notas": "Proyecto para probar integración de esquemas",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# 3. Find the project ID
|
# 3. Find the project ID
|
||||||
project_id = None
|
project_id = None
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
project_dirs = [d for d in os.listdir(projects_dir) if 'PROJ_SCHEMA' in d]
|
project_dirs = [d for d in os.listdir(projects_dir) if "PROJ_SCHEMA" in d]
|
||||||
assert len(project_dirs) > 0
|
assert len(project_dirs) > 0
|
||||||
project_dir = project_dirs[0]
|
project_dir = project_dirs[0]
|
||||||
project_id = project_dir.split('_')[0].replace('@', '')
|
project_id = project_dir.split("_")[0].replace("@", "")
|
||||||
|
|
||||||
# 4. Verify project uses the custom schema
|
# 4. Verify project uses the custom schema
|
||||||
response = logged_in_client.get(f'/projects/{project_id}')
|
response = logged_in_client.get(f"/projects/{project_id}")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'CUSTOM' in response.data
|
assert b"CUSTOM" in response.data
|
||||||
assert b'Informe Principal' in response.data
|
assert b"Informe Principal" in response.data
|
||||||
assert b'Notas Adicionales' in response.data
|
assert b"Notas Adicionales" in response.data
|
||||||
|
|
||||||
# 5. Upload a document of the specified type
|
# 5. Upload a document of the specified type
|
||||||
test_file = FileStorage(
|
test_file = FileStorage(
|
||||||
stream=io.BytesIO(b'Notes content for schema integration'),
|
stream=io.BytesIO(b"Notes content for schema integration"),
|
||||||
filename='notes.txt',
|
filename="notes.txt",
|
||||||
content_type='text/plain',
|
content_type="text/plain",
|
||||||
)
|
)
|
||||||
|
|
||||||
response = logged_in_client.post(
|
response = logged_in_client.post(
|
||||||
f'/projects/{project_id}/documents/upload',
|
f"/projects/{project_id}/documents/upload",
|
||||||
data={
|
data={
|
||||||
'tipo_doc': 'Notas Adicionales', # This should match schema document type
|
"tipo_doc": "Notas Adicionales", # This should match schema document type
|
||||||
'descripcion': 'Notas para prueba de esquema',
|
"descripcion": "Notas para prueba de esquema",
|
||||||
'file': test_file
|
"file": test_file,
|
||||||
},
|
},
|
||||||
content_type='multipart/form-data',
|
content_type="multipart/form-data",
|
||||||
follow_redirects=True
|
follow_redirects=True,
|
||||||
)
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
|
@ -2,92 +2,109 @@ import pytest
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
class TestProjects:
|
class TestProjects:
|
||||||
"""Test project-related functionality."""
|
"""Test project-related functionality."""
|
||||||
|
|
||||||
def test_project_list(self, logged_in_client):
|
def test_project_list(self, logged_in_client):
|
||||||
"""Test listing projects."""
|
"""Test listing projects."""
|
||||||
response = logged_in_client.get('/projects/')
|
response = logged_in_client.get("/projects/")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
def test_create_project(self, logged_in_client, app):
|
def test_create_project(self, logged_in_client, app):
|
||||||
"""Test creating a new project."""
|
"""Test creating a new project."""
|
||||||
response = logged_in_client.post('/projects/create', data={
|
response = logged_in_client.post(
|
||||||
'codigo': 'TEST123',
|
"/projects/create",
|
||||||
'descripcion': 'Proyecto de Prueba',
|
data={
|
||||||
'cliente': 'Cliente Test',
|
"codigo": "TEST123",
|
||||||
'esquema': 'TEST001',
|
"descripcion": "Proyecto de Prueba",
|
||||||
'destinacion': 'Pruebas',
|
"cliente": "Cliente Test",
|
||||||
'notas': 'Notas de prueba'
|
"esquema": "TEST001",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas",
|
||||||
|
"notas": "Notas de prueba",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Check if project was created in storage
|
# Check if project was created in storage
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
# Check indices
|
# Check indices
|
||||||
indices_path = os.path.join(app.config['STORAGE_PATH'], 'indices.json')
|
indices_path = os.path.join(app.config["STORAGE_PATH"], "indices.json")
|
||||||
with open(indices_path, 'r') as f:
|
with open(indices_path, "r") as f:
|
||||||
indices = json.load(f)
|
indices = json.load(f)
|
||||||
assert indices['max_project_id'] > 0
|
assert indices["max_project_id"] > 0
|
||||||
|
|
||||||
def test_view_project(self, logged_in_client, app):
|
def test_view_project(self, logged_in_client, app):
|
||||||
"""Test viewing a project (requires creating one first)."""
|
"""Test viewing a project (requires creating one first)."""
|
||||||
# Create a project first
|
# Create a project first
|
||||||
logged_in_client.post('/projects/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'TEST456',
|
"/projects/create",
|
||||||
'descripcion': 'Proyecto para visualizar',
|
data={
|
||||||
'cliente': 'Cliente Test',
|
"codigo": "TEST456",
|
||||||
'esquema': 'TEST001',
|
"descripcion": "Proyecto para visualizar",
|
||||||
'destinacion': 'Pruebas',
|
"cliente": "Cliente Test",
|
||||||
'notas': 'Notas de prueba'
|
"esquema": "TEST001",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas",
|
||||||
|
"notas": "Notas de prueba",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Now find the project ID
|
# Now find the project ID
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
project_dirs = os.listdir(projects_dir)
|
project_dirs = os.listdir(projects_dir)
|
||||||
assert len(project_dirs) > 0
|
assert len(project_dirs) > 0
|
||||||
|
|
||||||
# Get first project directory
|
# Get first project directory
|
||||||
project_dir = project_dirs[0]
|
project_dir = project_dirs[0]
|
||||||
project_id = project_dir.split('_')[0].replace('@', '')
|
project_id = project_dir.split("_")[0].replace("@", "")
|
||||||
|
|
||||||
# Try to view it
|
# Try to view it
|
||||||
response = logged_in_client.get(f'/projects/{project_id}')
|
response = logged_in_client.get(f"/projects/{project_id}")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Proyecto para visualizar' in response.data
|
assert b"Proyecto para visualizar" in response.data
|
||||||
|
|
||||||
def test_edit_project(self, logged_in_client, app):
|
def test_edit_project(self, logged_in_client, app):
|
||||||
"""Test editing a project."""
|
"""Test editing a project."""
|
||||||
# Create a project first
|
# Create a project first
|
||||||
logged_in_client.post('/projects/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'TESTEDIT',
|
"/projects/create",
|
||||||
'descripcion': 'Proyecto para editar',
|
data={
|
||||||
'cliente': 'Cliente Test',
|
"codigo": "TESTEDIT",
|
||||||
'esquema': 'TEST001',
|
"descripcion": "Proyecto para editar",
|
||||||
'destinacion': 'Pruebas',
|
"cliente": "Cliente Test",
|
||||||
'notas': 'Notas originales'
|
"esquema": "TEST001",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas",
|
||||||
|
"notas": "Notas originales",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Find the project ID
|
# Find the project ID
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
projects_dir = os.path.join(app.config['STORAGE_PATH'], 'projects')
|
projects_dir = os.path.join(app.config["STORAGE_PATH"], "projects")
|
||||||
project_dirs = [d for d in os.listdir(projects_dir) if 'TESTEDIT' in d]
|
project_dirs = [d for d in os.listdir(projects_dir) if "TESTEDIT" in d]
|
||||||
assert len(project_dirs) > 0
|
assert len(project_dirs) > 0
|
||||||
|
|
||||||
project_dir = project_dirs[0]
|
project_dir = project_dirs[0]
|
||||||
project_id = project_dir.split('_')[0].replace('@', '')
|
project_id = project_dir.split("_")[0].replace("@", "")
|
||||||
|
|
||||||
# Edit the project
|
# Edit the project
|
||||||
response = logged_in_client.post(f'/projects/{project_id}/edit', data={
|
response = logged_in_client.post(
|
||||||
'codigo': 'TESTEDIT',
|
f"/projects/{project_id}/edit",
|
||||||
'descripcion': 'Proyecto editado',
|
data={
|
||||||
'cliente': 'Cliente Test Modificado',
|
"codigo": "TESTEDIT",
|
||||||
'esquema': 'TEST001',
|
"descripcion": "Proyecto editado",
|
||||||
'destinacion': 'Pruebas Modificadas',
|
"cliente": "Cliente Test Modificado",
|
||||||
'notas': 'Notas modificadas'
|
"esquema": "TEST001",
|
||||||
}, follow_redirects=True)
|
"destinacion": "Pruebas Modificadas",
|
||||||
|
"notas": "Notas modificadas",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Proyecto editado' in response.data
|
assert b"Proyecto editado" in response.data
|
||||||
|
|
|
@ -2,77 +2,97 @@ import pytest
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
class TestSchemas:
|
class TestSchemas:
|
||||||
"""Test schema management functionality."""
|
"""Test schema management functionality."""
|
||||||
|
|
||||||
def test_list_schemas(self, logged_in_client):
|
def test_list_schemas(self, logged_in_client):
|
||||||
"""Test listing schemas."""
|
"""Test listing schemas."""
|
||||||
response = logged_in_client.get('/schemas/')
|
response = logged_in_client.get("/schemas/")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Esquemas de Proyecto' in response.data
|
assert b"Esquemas de Proyecto" in response.data
|
||||||
|
|
||||||
def test_view_schema(self, logged_in_client):
|
def test_view_schema(self, logged_in_client):
|
||||||
"""Test viewing a schema."""
|
"""Test viewing a schema."""
|
||||||
response = logged_in_client.get('/schemas/view/TEST001')
|
response = logged_in_client.get("/schemas/view/TEST001")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Esquema de prueba' in response.data
|
assert b"Esquema de prueba" in response.data
|
||||||
|
|
||||||
def test_create_schema(self, logged_in_client, app):
|
def test_create_schema(self, logged_in_client, app):
|
||||||
"""Test creating a new schema."""
|
"""Test creating a new schema."""
|
||||||
response = logged_in_client.post('/schemas/create', data={
|
response = logged_in_client.post(
|
||||||
'codigo': 'TEST002',
|
"/schemas/create",
|
||||||
'descripcion': 'Nuevo esquema de prueba',
|
data={
|
||||||
'documentos-0-tipo': 'pdf',
|
"codigo": "TEST002",
|
||||||
'documentos-0-nombre': 'Manual de Usuario',
|
"descripcion": "Nuevo esquema de prueba",
|
||||||
'documentos-0-nivel_ver': 0,
|
"documentos-0-tipo": "pdf",
|
||||||
'documentos-0-nivel_editar': 5000,
|
"documentos-0-nombre": "Manual de Usuario",
|
||||||
'documentos-1-tipo': 'txt',
|
"documentos-0-nivel_ver": 0,
|
||||||
'documentos-1-nombre': 'Notas de Proyecto',
|
"documentos-0-nivel_editar": 5000,
|
||||||
'documentos-1-nivel_ver': 0,
|
"documentos-1-tipo": "txt",
|
||||||
'documentos-1-nivel_editar': 1000
|
"documentos-1-nombre": "Notas de Proyecto",
|
||||||
}, follow_redirects=True)
|
"documentos-1-nivel_ver": 0,
|
||||||
|
"documentos-1-nivel_editar": 1000,
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Check if schema was created
|
# Check if schema was created
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
schemas_path = os.path.join(app.config['STORAGE_PATH'], 'schemas', 'schema.json')
|
schemas_path = os.path.join(
|
||||||
with open(schemas_path, 'r') as f:
|
app.config["STORAGE_PATH"], "schemas", "schema.json"
|
||||||
|
)
|
||||||
|
with open(schemas_path, "r") as f:
|
||||||
schemas = json.load(f)
|
schemas = json.load(f)
|
||||||
assert 'TEST002' in schemas
|
assert "TEST002" in schemas
|
||||||
assert schemas['TEST002']['descripcion'] == 'Nuevo esquema de prueba'
|
assert schemas["TEST002"]["descripcion"] == "Nuevo esquema de prueba"
|
||||||
assert len(schemas['TEST002']['documentos']) == 2
|
assert len(schemas["TEST002"]["documentos"]) == 2
|
||||||
|
|
||||||
def test_edit_schema(self, logged_in_client, app):
|
def test_edit_schema(self, logged_in_client, app):
|
||||||
"""Test editing a schema."""
|
"""Test editing a schema."""
|
||||||
# First create a schema to edit
|
# First create a schema to edit
|
||||||
logged_in_client.post('/schemas/create', data={
|
logged_in_client.post(
|
||||||
'codigo': 'TESTEDIT',
|
"/schemas/create",
|
||||||
'descripcion': 'Esquema para editar',
|
data={
|
||||||
'documentos-0-tipo': 'pdf',
|
"codigo": "TESTEDIT",
|
||||||
'documentos-0-nombre': 'Documento Original',
|
"descripcion": "Esquema para editar",
|
||||||
'documentos-0-nivel_ver': 0,
|
"documentos-0-tipo": "pdf",
|
||||||
'documentos-0-nivel_editar': 5000
|
"documentos-0-nombre": "Documento Original",
|
||||||
}, follow_redirects=True)
|
"documentos-0-nivel_ver": 0,
|
||||||
|
"documentos-0-nivel_editar": 5000,
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Now edit it
|
# Now edit it
|
||||||
response = logged_in_client.post('/schemas/edit/TESTEDIT', data={
|
response = logged_in_client.post(
|
||||||
'codigo': 'TESTEDIT',
|
"/schemas/edit/TESTEDIT",
|
||||||
'descripcion': 'Esquema editado',
|
data={
|
||||||
'documentos-0-tipo': 'pdf',
|
"codigo": "TESTEDIT",
|
||||||
'documentos-0-nombre': 'Documento Modificado',
|
"descripcion": "Esquema editado",
|
||||||
'documentos-0-nivel_ver': 500,
|
"documentos-0-tipo": "pdf",
|
||||||
'documentos-0-nivel_editar': 6000
|
"documentos-0-nombre": "Documento Modificado",
|
||||||
}, follow_redirects=True)
|
"documentos-0-nivel_ver": 500,
|
||||||
|
"documentos-0-nivel_editar": 6000,
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify changes
|
# Verify changes
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
schemas_path = os.path.join(app.config['STORAGE_PATH'], 'schemas', 'schema.json')
|
schemas_path = os.path.join(
|
||||||
with open(schemas_path, 'r') as f:
|
app.config["STORAGE_PATH"], "schemas", "schema.json"
|
||||||
|
)
|
||||||
|
with open(schemas_path, "r") as f:
|
||||||
schemas = json.load(f)
|
schemas = json.load(f)
|
||||||
assert 'TESTEDIT' in schemas
|
assert "TESTEDIT" in schemas
|
||||||
assert schemas['TESTEDIT']['descripcion'] == 'Esquema editado'
|
assert schemas["TESTEDIT"]["descripcion"] == "Esquema editado"
|
||||||
assert schemas['TESTEDIT']['documentos'][0]['nombre'] == 'Documento Modificado'
|
assert (
|
||||||
assert schemas['TESTEDIT']['documentos'][0]['nivel_ver'] == 500
|
schemas["TESTEDIT"]["documentos"][0]["nombre"]
|
||||||
|
== "Documento Modificado"
|
||||||
|
)
|
||||||
|
assert schemas["TESTEDIT"]["documentos"][0]["nivel_ver"] == 500
|
||||||
|
|
|
@ -2,127 +2,146 @@ import pytest
|
||||||
import json
|
import json
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
|
||||||
class TestUserManagement:
|
class TestUserManagement:
|
||||||
"""Test user management functionality."""
|
"""Test user management functionality."""
|
||||||
|
|
||||||
def test_list_users(self, logged_in_client):
|
def test_list_users(self, logged_in_client):
|
||||||
"""Test listing users."""
|
"""Test listing users."""
|
||||||
response = logged_in_client.get('/users/')
|
response = logged_in_client.get("/users/")
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
assert b'Usuarios del Sistema' in response.data
|
assert b"Usuarios del Sistema" in response.data
|
||||||
# Check for existing users
|
# Check for existing users
|
||||||
assert b'admin' in response.data
|
assert b"admin" in response.data
|
||||||
assert b'user1' in response.data
|
assert b"user1" in response.data
|
||||||
|
|
||||||
def test_create_user(self, logged_in_client, app):
|
def test_create_user(self, logged_in_client, app):
|
||||||
"""Test creating a new user."""
|
"""Test creating a new user."""
|
||||||
response = logged_in_client.post('/users/create', data={
|
response = logged_in_client.post(
|
||||||
'nombre': 'Usuario de Prueba',
|
"/users/create",
|
||||||
'username': 'testuser',
|
data={
|
||||||
'email': 'test@example.com',
|
"nombre": "Usuario de Prueba",
|
||||||
'password': 'password123',
|
"username": "testuser",
|
||||||
'password_confirm': 'password123',
|
"email": "test@example.com",
|
||||||
'nivel': 1000,
|
"password": "password123",
|
||||||
'idioma': 'es',
|
"password_confirm": "password123",
|
||||||
'empresa': 'Empresa Test',
|
"nivel": 1000,
|
||||||
'estado': 'activo',
|
"idioma": "es",
|
||||||
'fecha_caducidad': ''
|
"empresa": "Empresa Test",
|
||||||
}, follow_redirects=True)
|
"estado": "activo",
|
||||||
|
"fecha_caducidad": "",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Check if user was created
|
# Check if user was created
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
users_path = os.path.join(app.config['STORAGE_PATH'], 'users', 'users.json')
|
users_path = os.path.join(app.config["STORAGE_PATH"], "users", "users.json")
|
||||||
with open(users_path, 'r') as f:
|
with open(users_path, "r") as f:
|
||||||
users = json.load(f)
|
users = json.load(f)
|
||||||
assert 'testuser' in users
|
assert "testuser" in users
|
||||||
assert users['testuser']['nombre'] == 'Usuario de Prueba'
|
assert users["testuser"]["nombre"] == "Usuario de Prueba"
|
||||||
assert users['testuser']['email'] == 'test@example.com'
|
assert users["testuser"]["email"] == "test@example.com"
|
||||||
assert users['testuser']['nivel'] == 1000
|
assert users["testuser"]["nivel"] == 1000
|
||||||
|
|
||||||
def test_edit_user(self, logged_in_client, app):
|
def test_edit_user(self, logged_in_client, app):
|
||||||
"""Test editing an existing user."""
|
"""Test editing an existing user."""
|
||||||
# First create a user to edit
|
# First create a user to edit
|
||||||
logged_in_client.post('/users/create', data={
|
logged_in_client.post(
|
||||||
'nombre': 'Usuario para Editar',
|
"/users/create",
|
||||||
'username': 'edit_user',
|
data={
|
||||||
'email': 'edit@example.com',
|
"nombre": "Usuario para Editar",
|
||||||
'password': 'password123',
|
"username": "edit_user",
|
||||||
'password_confirm': 'password123',
|
"email": "edit@example.com",
|
||||||
'nivel': 1000,
|
"password": "password123",
|
||||||
'idioma': 'es',
|
"password_confirm": "password123",
|
||||||
'empresa': 'Empresa Original',
|
"nivel": 1000,
|
||||||
'estado': 'activo',
|
"idioma": "es",
|
||||||
'fecha_caducidad': ''
|
"empresa": "Empresa Original",
|
||||||
}, follow_redirects=True)
|
"estado": "activo",
|
||||||
|
"fecha_caducidad": "",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Now edit the user
|
# Now edit the user
|
||||||
response = logged_in_client.post('/users/edit/edit_user', data={
|
response = logged_in_client.post(
|
||||||
'nombre': 'Usuario Editado',
|
"/users/edit/edit_user",
|
||||||
'email': 'edited@example.com',
|
data={
|
||||||
'password': '', # Empty password means no change
|
"nombre": "Usuario Editado",
|
||||||
'password_confirm': '',
|
"email": "edited@example.com",
|
||||||
'nivel': 5000, # Changed level
|
"password": "", # Empty password means no change
|
||||||
'idioma': 'en', # Changed language
|
"password_confirm": "",
|
||||||
'empresa': 'Empresa Modificada',
|
"nivel": 5000, # Changed level
|
||||||
'estado': 'activo',
|
"idioma": "en", # Changed language
|
||||||
'fecha_caducidad': ''
|
"empresa": "Empresa Modificada",
|
||||||
}, follow_redirects=True)
|
"estado": "activo",
|
||||||
|
"fecha_caducidad": "",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify changes
|
# Verify changes
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
users_path = os.path.join(app.config['STORAGE_PATH'], 'users', 'users.json')
|
users_path = os.path.join(app.config["STORAGE_PATH"], "users", "users.json")
|
||||||
with open(users_path, 'r') as f:
|
with open(users_path, "r") as f:
|
||||||
users = json.load(f)
|
users = json.load(f)
|
||||||
assert 'edit_user' in users
|
assert "edit_user" in users
|
||||||
assert users['edit_user']['nombre'] == 'Usuario Editado'
|
assert users["edit_user"]["nombre"] == "Usuario Editado"
|
||||||
assert users['edit_user']['email'] == 'edited@example.com'
|
assert users["edit_user"]["email"] == "edited@example.com"
|
||||||
assert users['edit_user']['nivel'] == 5000
|
assert users["edit_user"]["nivel"] == 5000
|
||||||
assert users['edit_user']['idioma'] == 'en'
|
assert users["edit_user"]["idioma"] == "en"
|
||||||
assert users['edit_user']['empresa'] == 'Empresa Modificada'
|
assert users["edit_user"]["empresa"] == "Empresa Modificada"
|
||||||
|
|
||||||
def test_delete_user(self, logged_in_client, app):
|
def test_delete_user(self, logged_in_client, app):
|
||||||
"""Test deleting a user."""
|
"""Test deleting a user."""
|
||||||
# First create a user to delete
|
# First create a user to delete
|
||||||
logged_in_client.post('/users/create', data={
|
logged_in_client.post(
|
||||||
'nombre': 'Usuario para Eliminar',
|
"/users/create",
|
||||||
'username': 'delete_user',
|
data={
|
||||||
'email': 'delete@example.com',
|
"nombre": "Usuario para Eliminar",
|
||||||
'password': 'password123',
|
"username": "delete_user",
|
||||||
'password_confirm': 'password123',
|
"email": "delete@example.com",
|
||||||
'nivel': 1000,
|
"password": "password123",
|
||||||
'idioma': 'es',
|
"password_confirm": "password123",
|
||||||
'empresa': 'Empresa Test',
|
"nivel": 1000,
|
||||||
'estado': 'activo',
|
"idioma": "es",
|
||||||
'fecha_caducidad': ''
|
"empresa": "Empresa Test",
|
||||||
}, follow_redirects=True)
|
"estado": "activo",
|
||||||
|
"fecha_caducidad": "",
|
||||||
|
},
|
||||||
|
follow_redirects=True,
|
||||||
|
)
|
||||||
|
|
||||||
# Verify user was created
|
# Verify user was created
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
users_path = os.path.join(app.config['STORAGE_PATH'], 'users', 'users.json')
|
users_path = os.path.join(app.config["STORAGE_PATH"], "users", "users.json")
|
||||||
with open(users_path, 'r') as f:
|
with open(users_path, "r") as f:
|
||||||
users = json.load(f)
|
users = json.load(f)
|
||||||
assert 'delete_user' in users
|
assert "delete_user" in users
|
||||||
|
|
||||||
# Now delete the user
|
# Now delete the user
|
||||||
response = logged_in_client.post('/users/delete/delete_user', follow_redirects=True)
|
response = logged_in_client.post(
|
||||||
|
"/users/delete/delete_user", follow_redirects=True
|
||||||
|
)
|
||||||
|
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Verify user was deleted
|
# Verify user was deleted
|
||||||
with app.app_context():
|
with app.app_context():
|
||||||
users_path = os.path.join(app.config['STORAGE_PATH'], 'users', 'users.json')
|
users_path = os.path.join(app.config["STORAGE_PATH"], "users", "users.json")
|
||||||
with open(users_path, 'r') as f:
|
with open(users_path, "r") as f:
|
||||||
users = json.load(f)
|
users = json.load(f)
|
||||||
assert 'delete_user' not in users
|
assert "delete_user" not in users
|
||||||
|
|
||||||
def test_cannot_delete_admin(self, logged_in_client):
|
def test_cannot_delete_admin(self, logged_in_client):
|
||||||
"""Test that admin user cannot be deleted."""
|
"""Test that admin user cannot be deleted."""
|
||||||
response = logged_in_client.post('/users/delete/admin', follow_redirects=True)
|
response = logged_in_client.post("/users/delete/admin", follow_redirects=True)
|
||||||
assert response.status_code == 200
|
assert response.status_code == 200
|
||||||
|
|
||||||
# Should see an error message
|
# Should see an error message
|
||||||
assert b'No se puede eliminar' in response.data or b'no puede' in response.data
|
assert b"No se puede eliminar" in response.data or b"no puede" in response.data
|
||||||
|
|
Loading…
Reference in New Issue