Creado Tests

This commit is contained in:
Miguel 2025-03-04 10:38:19 +01:00
parent 79194e0f61
commit b48058e495
20 changed files with 1190 additions and 605 deletions

48
generate_test_report.py Normal file
View File

@ -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())

View File

@ -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

View File

@ -0,0 +1 @@
{}

View File

@ -0,0 +1,4 @@
{
"max_project_id": 0,
"max_document_id": 0
}

View File

@ -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
}
]
}
}

View File

@ -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"
}
}

View File

@ -0,0 +1,2 @@
# This file makes the tests directory a Python package
# allowing the json_reporter module to be imported

View File

@ -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)

15
tests/helpers.py Normal file
View File

@ -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

View File

@ -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")

View File

@ -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

View File

@ -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()
)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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