Arch/test/test_app.py

510 lines
19 KiB
Python

import os
import sys
import json
import pytest
import logging
import uuid
import time
from flask_testing import TestCase
from unittest import mock
# Add the project directory to sys.path
sys.path.append(os.path.abspath(os.path.join(os.path.dirname(__file__), "..")))
from app import create_app, initialize_storage_structure
from services.project_service import (
create_project,
update_project,
get_project,
delete_project,
get_all_projects,
get_project_children,
get_project_document_count,
filter_projects,
find_project_directory,
archive_project,
)
class AppTestCase(TestCase):
def create_app(self):
# Configurar la aplicación para pruebas
os.environ["FLASK_ENV"] = "testing"
app = create_app("testing")
return app
def setUp(self):
# Inicializar la estructura de almacenamiento para pruebas
initialize_storage_structure(self.app)
# Create a test schema for projects
schemas_dir = os.path.join(self.app.config["STORAGE_PATH"], "schemas")
os.makedirs(schemas_dir, exist_ok=True)
with open(os.path.join(schemas_dir, "schema.json"), "w") as f:
json.dump(
{
"SCHEMA1": {
"name": "Test Schema",
"descripcion": "Schema for testing",
},
"SCHEMA2": {
"name": "Another Schema",
"descripcion": "Another schema for testing",
},
},
f,
)
# Generar un identificador único para esta sesión de prueba
self.test_session_id = str(uuid.uuid4())[:8]
def tearDown(self):
# Cerrar los manejadores de logging del app logger
for handler in self.app.logger.handlers[:]:
self.app.logger.removeHandler(handler)
handler.close()
# Limpiar después de cada prueba
storage_path = self.app.config["STORAGE_PATH"]
if os.path.exists(storage_path):
import shutil
import time
# Retry logic to handle file in use errors
for _ in range(5):
try:
shutil.rmtree(storage_path)
break
except PermissionError:
time.sleep(1)
def get_unique_name(self, base_name):
"""Genera un nombre único para evitar conflictos en pruebas"""
timestamp = int(time.time() * 1000) % 10000
return f"{base_name}_{self.test_session_id}_{timestamp}"
def clean_existing_projects(self):
"""Limpia proyectos existentes que puedan interferir con las pruebas"""
with self.app.app_context():
projects = get_all_projects(include_inactive=True)
for project in projects:
project_id = int(project["codigo"].replace("PROJ", ""))
delete_project(project_id)
def test_logging_setup(self):
# Verificar que el sistema de logging se haya configurado correctamente
log_path = os.path.join(self.app.config["STORAGE_PATH"], "logs")
assert os.path.exists(log_path), "El directorio de logs no existe"
def test_project_creation(self):
# Verificar la creación de un proyecto de prueba
project_path = os.path.join(
self.app.config["STORAGE_PATH"], "projects", "test_project"
)
os.makedirs(project_path, exist_ok=True)
assert os.path.exists(
project_path
), "El proyecto de prueba no se creó correctamente"
def test_project_service_creation(self):
"""Test creating a project using project service"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
project_data = {
"descripcion": self.get_unique_name("Test Project Service"),
"cliente": "Test Client",
"esquema": "SCHEMA1",
"destinacion": "Test Destination",
}
success, message, project_id = create_project(project_data, "test_user")
# Verify project creation success
assert success, f"Project creation failed: {message}"
assert project_id is not None
assert message == "Proyecto creado correctamente."
# Verify project directory was created with correct format
project_dir = find_project_directory(project_id)
assert project_dir is not None
assert os.path.exists(project_dir)
# Verify project metadata file was created
meta_file = os.path.join(project_dir, "project_meta.json")
assert os.path.exists(meta_file)
# Verify project metadata content
with open(meta_file, "r") as f:
metadata = json.load(f)
assert metadata["descripcion"] == "Test Project Service"
assert metadata["cliente"] == "Test Client"
assert metadata["esquema"] == "SCHEMA1"
assert metadata["creado_por"] == "test_user"
assert metadata["estado"] == "activo"
def test_project_retrieval(self):
"""Test retrieving project information"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# First create a project
project_data = {
"descripcion": self.get_unique_name("Retrieval Test Project"),
"cliente": "Retrieval Client",
"esquema": "SCHEMA1",
}
success, _, project_id = create_project(project_data, "test_user")
assert success
# Now retrieve it
project = get_project(project_id)
# Verify retrieval
assert project is not None
assert project["descripcion"] == "Retrieval Test Project"
assert project["cliente"] == "Retrieval Client"
assert "directory" in project, "Directory information should be included"
def test_project_update(self):
"""Test updating project information"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# First create a project
project_data = {
"descripcion": self.get_unique_name("Original Project"),
"cliente": "Original Client",
"esquema": "SCHEMA1",
}
success, _, project_id = create_project(project_data, "test_user")
assert success
# Update the project
updated_data = {
"descripcion": "Updated Project",
"cliente": "Updated Client",
"destinacion": "Updated Destination",
}
update_success, update_message = update_project(
project_id, updated_data, "modifier_user"
)
# Verify update success
assert update_success, f"Update failed: {update_message}"
assert update_message == "Proyecto actualizado correctamente."
# Verify the changes were applied
updated_project = get_project(project_id)
assert updated_project["descripcion"] == "Updated Project"
assert updated_project["cliente"] == "Updated Client"
assert updated_project["destinacion"] == "Updated Destination"
assert updated_project["modificado_por"] == "modifier_user"
# Schema should remain unchanged
assert updated_project["esquema"] == "SCHEMA1"
def test_project_deletion(self):
"""Test project soft deletion"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# First create a project
project_data = {
"descripcion": self.get_unique_name("Project to Delete"),
"cliente": "Delete Client",
"esquema": "SCHEMA1",
}
success, _, project_id = create_project(project_data, "test_user")
assert success
# Delete the project (mark as inactive)
delete_success, delete_message = delete_project(project_id)
# Verify deletion success
assert delete_success, f"Deletion failed: {delete_message}"
assert delete_message == "Proyecto marcado como inactivo."
# Verify the project is marked as inactive
inactive_project = get_project(project_id)
assert inactive_project["estado"] == "inactivo"
# Verify the project directory still exists (soft delete)
project_dir = find_project_directory(project_id)
assert os.path.exists(project_dir)
def test_get_all_projects(self):
"""Test retrieving all projects"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# Create multiple projects
projects_data = [
{
"descripcion": self.get_unique_name("Project 1"),
"cliente": "Client 1",
"esquema": "SCHEMA1",
},
{
"descripcion": self.get_unique_name("Project 2"),
"cliente": "Client 2",
"esquema": "SCHEMA1",
},
{
"descripcion": self.get_unique_name("Project 3"),
"cliente": "Client 1",
"esquema": "SCHEMA2",
},
]
project_ids = []
for data in projects_data:
success, _, project_id = create_project(data, "test_user")
assert success
project_ids.append(project_id)
# Mark one project as inactive
delete_project(project_ids[1])
# Get all active projects
all_active_projects = get_all_projects(include_inactive=False)
# Should only return 2 active projects
assert len(all_active_projects) == 2
# Get all projects including inactive
all_projects = get_all_projects(include_inactive=True)
# Should return all 3 projects
assert len(all_projects) == 3
def test_project_filtering(self):
"""Test project filtering functionality"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# Create projects with different characteristics
projects_data = [
{
"descripcion": self.get_unique_name("Web Development"),
"cliente": "Client A",
"esquema": "SCHEMA1",
},
{
"descripcion": self.get_unique_name("Mobile App"),
"cliente": "Client B",
"esquema": "SCHEMA1",
},
{
"descripcion": self.get_unique_name("Desktop Application"),
"cliente": "Client A",
"esquema": "SCHEMA2",
},
]
for data in projects_data:
success, _, _ = create_project(data, "test_user")
assert success
# Test filtering by client
client_filter = {"cliente": "Client A"}
client_results = filter_projects(client_filter)
assert len(client_results) == 2
assert all(p["cliente"] == "Client A" for p in client_results)
# Test filtering by description
desc_filter = {"descripcion": "Web"}
desc_results = filter_projects(desc_filter)
assert len(desc_results) == 1
assert desc_results[0]["descripcion"] == "Web Development"
def test_project_hierarchy(self):
"""Test parent-child project relationships"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# Create parent project
parent_data = {
"descripcion": self.get_unique_name("Parent Project"),
"cliente": "Parent Client",
"esquema": "SCHEMA1",
}
success, _, parent_id = create_project(parent_data, "test_user")
assert success
# Get the parent project code
parent_project = get_project(parent_id)
parent_code = parent_project["codigo"]
# Create child projects
child_data = [
{
"descripcion": "Child Project 1",
"cliente": "Child Client",
"esquema": "SCHEMA1",
"proyecto_padre": parent_code,
},
{
"descripcion": "Child Project 2",
"cliente": "Child Client",
"esquema": "SCHEMA1",
"proyecto_padre": parent_code,
},
]
for data in child_data:
success, _, _ = create_project(data, "test_user")
assert success
# Get children of parent project
children = get_project_children(parent_id)
# Verify children
assert len(children) == 2
child_descriptions = [child["descripcion"] for child in children]
assert "Child Project 1" in child_descriptions
assert "Child Project 2" in child_descriptions
def test_document_count(self):
"""Test counting documents in a project"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# Create a test project
project_data = {
"descripcion": self.get_unique_name("Document Test Project"),
"cliente": "Document Client",
"esquema": "SCHEMA1",
}
success, _, project_id = create_project(project_data, "test_user")
assert success
# Find project directory
project_dir = find_project_directory(project_id)
documents_dir = os.path.join(project_dir, "documents")
# Initially should have no documents
initial_count = get_project_document_count(project_id)
assert initial_count == 0
# Create some mock document directories
os.makedirs(os.path.join(documents_dir, "@001_doc1"), exist_ok=True)
os.makedirs(os.path.join(documents_dir, "@002_doc2"), exist_ok=True)
# Create a non-document directory (should be ignored)
os.makedirs(os.path.join(documents_dir, "not_a_document"), exist_ok=True)
# Count should now be 2
updated_count = get_project_document_count(project_id)
assert updated_count == 2
def test_project_routes(self):
"""Test project routes and API endpoints"""
with self.app.app_context():
# Test project list route
response = self.client.get("/projects/")
self.assertStatus(
response, 302
) # Cambiar a esperar redirección en lugar de 200
# Test project API list route
api_response = self.client.get("/projects/api/list")
self.assert200(api_response)
data = json.loads(api_response.data)
assert isinstance(data, list)
# Test project creation route (GET - form)
create_response = self.client.get("/projects/create")
self.assert200(create_response)
# Setup mock for testing POST with CSRF token
with mock.patch("flask_wtf.csrf.validate_csrf", return_value=True):
# Test project creation route (POST)
post_data = {
"descripcion": "Route Test Project",
"cliente": "Route Test Client",
"esquema": "SCHEMA1",
"destinacion": "Route Test Destination",
"proyecto_padre": "",
}
# Mock project_service.create_project to return success
with mock.patch(
"routes.project_routes.create_project",
return_value=(True, "Proyecto creado correctamente.", 1),
):
post_response = self.client.post("/projects/create", data=post_data)
self.assertStatus(post_response, 302) # Redirect on success
# Test project view route
# First create a real project to view
project_data = {
"descripcion": "View Test Project",
"cliente": "View Client",
"esquema": "SCHEMA1",
}
success, _, project_id = create_project(project_data, "test_user")
assert success
view_response = self.client.get(f"/projects/{project_id}")
self.assert200(view_response)
def test_project_archival(self):
"""Test project archival functionality"""
with self.app.app_context():
# Limpiar proyectos existentes primero
self.clean_existing_projects()
# First create a project
project_data = {
"descripcion": self.get_unique_name("Project to Archive"),
"cliente": "Archive Client",
"esquema": "SCHEMA1",
}
success, _, project_id = create_project(project_data, "test_user")
assert success
# Archive the project
archive_success, archive_message = archive_project(
project_id, "archiver_user"
)
# Verify archival success
assert archive_success, f"Archival failed: {archive_message}"
assert archive_message == "Proyecto archivado correctamente."
# Verify the project is marked as archived
archived_project = get_project(project_id)
assert archived_project["estado"] == "archivado"
# Verify the project directory still exists (soft archive)
project_dir = find_project_directory(project_id)
assert os.path.exists(project_dir)
if __name__ == "__main__":
# Ejecutar pruebas y exportar resultados a resultados_test.json
result = pytest.main(
[
"-v",
"--tb=short",
"--disable-warnings",
"--json-report",
"--json-report-file=resultados_test.json",
]
)