import pytest from app import create_app, db from app.models.user import User from app.models.role import Role from app.models.academic_term import AcademicTerm from app.models.classroom import Classroom from app.models.building import Building from app.models.subject import Subject, Commission from app.models.reservation import Reservation from app.services.jwt_service import JWTService from datetime import datetime, timedelta @pytest.fixture def app(): app = create_app() app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///:memory:' app.config['TESTING'] = True with app.app_context(): db.create_all() admin_role = Role.query.filter_by(name='ADMIN').first() if not admin_role: admin_role = Role(name='ADMIN', description='Administrator') db.session.add(admin_role) db.session.commit() user = User( email='admin_abm@unicaba.edu.ar', name='Admin ABM', first_name='Admin', last_name='ABM', role='ADMIN', role_id=admin_role.id, is_active=True ) user.set_password('Admin1234!') db.session.add(user) db.session.commit() yield app db.session.remove() db.drop_all() @pytest.fixture def client(app): return app.test_client() @pytest.fixture def auth_headers(app): with app.app_context(): user = User.query.filter_by(email='admin_abm@unicaba.edu.ar').first() token = JWTService.generate_tokens(user)['access_token'] return { 'Authorization': f'Bearer {token}', 'Content-Type': 'application/json' } def test_academic_term_abm(client, auth_headers): # 1. Crear término res = client.post('/api/v1/admin/academic-terms', headers=auth_headers, json={ 'name': '2026-Segundo Cuatrimestre Test', 'code': '2026-2C-TEST', 'year': 2026, 'period_type': 'Segundo Cuatrimestre', 'is_active': True, 'is_current': False }) assert res.status_code == 201 data = res.get_json() term_id = data['term']['id'] # 2. Toggle active res_toggle = client.post(f'/api/v1/admin/academic-terms/{term_id}/toggle', headers=auth_headers) assert res_toggle.status_code == 200 assert res_toggle.get_json()['is_active'] is False # 3. Delete término res_del = client.delete(f'/api/v1/admin/academic-terms/{term_id}', headers=auth_headers) assert res_del.status_code == 200 assert 'eliminado' in res_del.get_json()['message'] def test_classroom_toggle(client, auth_headers): # Crear edificio y aula building = Building(name='Sede Central Test', code='SC-TEST', address='Av Test 123') db.session.add(building) db.session.commit() classroom = Classroom( building=building.name, code='A-101-TEST', capacity=50, floor='1', building_id=building.id, is_active=True ) db.session.add(classroom) db.session.commit() # Toggle aula res = client.post(f'/api/v1/classrooms/{classroom.id}/toggle', headers=auth_headers) assert res.status_code == 200 assert res.get_json()['is_active'] is False def test_reservation_hard_delete(client, auth_headers): # Crear edificio, aula, materia, comisión y reserva building = Building(name='Sede Norte Test', code='SN-TEST', address='Av Norte 456') db.session.add(building) db.session.commit() classroom = Classroom( building=building.name, code='LAB-1-TEST', capacity=30, floor='PB', building_id=building.id, is_active=True ) db.session.add(classroom) subject = Subject(name='Base de Datos Test', code='BD-TEST', is_active=True) db.session.add(subject) db.session.commit() user = User.query.filter_by(email='admin_abm@unicaba.edu.ar').first() commission = Commission( subject_id=subject.id, code='COM-101', semester='2C', year=2026, teacher_id=user.id, max_students=30, active=True ) db.session.add(commission) db.session.commit() now = datetime.now() reservation = Reservation( classroom_id=classroom.id, commission_id=commission.id, user_id=user.id, purpose='Clase Regular de BD', expected_attendees=25, start_time=now, end_time=now + timedelta(hours=2), status='CONFIRMED' ) db.session.add(reservation) db.session.commit() res_id = reservation.id # Hard delete res_del = client.delete(f'/api/v1/reservations/{res_id}?hard=true', headers=auth_headers) assert res_del.status_code == 200 assert 'eliminada' in res_del.get_json()['message'] assert db.session.get(Reservation, res_id) is None def test_system_reset_validation(client, auth_headers): # 1. Sin contraseña -> 400 res_no_pass = client.post('/api/v1/admin/system/reset', headers=auth_headers, json={}) assert res_no_pass.status_code == 400 assert 'contraseña' in res_no_pass.get_json()['message'].lower() # 2. Con contraseña errónea -> 401 res_bad_pass = client.post('/api/v1/admin/system/reset', headers=auth_headers, json={'password': 'PasswordIncorrecto123'}) assert res_bad_pass.status_code == 401 assert 'incorrecta' in res_bad_pass.get_json()['message'].lower() def test_system_reset_success_preserves_users(client, auth_headers): from app.models.career import Career from app.models.audit_log import AuditLog # Crear datos operativos career = Career(name='Ingenieria en Software', code='ING-SOFT') db.session.add(career) building = Building(name='Pabellón Norte', code='PNORTE', is_active=True) db.session.add(building) db.session.flush() classroom = Classroom(code='Aula 501', floor='5', capacity=40, building_id=building.id, building=building.name, is_active=True) subject = Subject(name='Sistemas Operativos', code='SO-101', is_active=True, career_id=career.id) term = AcademicTerm(name='2026-1C', code='2026-1C', start_date=datetime.now().date(), end_date=(datetime.now() + timedelta(days=120)).date(), is_active=True) db.session.add_all([classroom, subject, term]) db.session.commit() user = User.query.filter_by(email='admin_abm@unicaba.edu.ar').first() comm = Commission(code='COM-SO-1', subject_id=subject.id, semester=1, year=2026, max_students=35, is_active=True) db.session.add(comm) db.session.commit() resv = Reservation(classroom_id=classroom.id, commission_id=comm.id, user_id=user.id, purpose='Clase', expected_attendees=25, start_time=datetime.now(), end_time=datetime.now() + timedelta(hours=2)) db.session.add(resv) db.session.commit() # Verificar que los datos existen antes del reset assert Classroom.query.count() >= 1 assert Building.query.count() >= 1 assert Subject.query.count() >= 1 assert Career.query.count() >= 1 assert Commission.query.count() >= 1 assert Reservation.query.count() >= 1 assert AcademicTerm.query.count() >= 1 # Ejecutar reset del sistema con la contraseña correcta del admin res_reset = client.post('/api/v1/admin/system/reset', headers=auth_headers, json={'password': 'Admin1234!'}) assert res_reset.status_code == 200 data = res_reset.get_json() assert data['status'] == 'success' assert 'reinicializado' in data['message'].lower() # Comprobar que los datos operativos fueron purgados assert Classroom.query.count() == 0 assert Building.query.count() == 0 assert Subject.query.count() == 0 assert Career.query.count() == 0 assert Commission.query.count() == 0 assert Reservation.query.count() == 0 assert AcademicTerm.query.count() == 0 # Comprobar que los usuarios y roles se PRESERVAN assert User.query.filter_by(email='admin_abm@unicaba.edu.ar').first() is not None assert Role.query.count() >= 1 # Comprobar que se generó un nuevo registro de auditoría del evento SYSTEM_RESET reset_logs = AuditLog.query.filter_by(action='SYSTEM_RESET').all() assert len(reset_logs) == 1 assert reset_logs[0].user_email == 'admin_abm@unicaba.edu.ar'