gmao/tests/integration/test_room_profiles_checkpoint_b.py
root 31434c5ca2
Some checks are pending
CI - Tests et Syntax / lint-and-test (push) Waiting to run
Validate checkpoint B UI workflows
2026-08-23 18:10:40 +00:00

169 lines
9.2 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Couverture métier du checkpoint B : profils sans création implicite."""
from app_new import db
from app_new.core.models.college import Building, Zone, Room, RoomProfile, RoomProfileItem
from app_new.core.models.equipment import Equipment
from app_new.core.services.equipment_creation import create_equipment
from app_new.core.models.equipment import EquipmentCategory
from app_new.core.models.planning import WorkSchedule
from time import perf_counter
import re
def test_room_profile_is_editable_and_application_uses_shared_service(authenticated_client, app):
with app.app_context():
building = Building(name="TEST_UI_020_B_BUILDING")
db.session.add(building)
db.session.flush()
zone = Zone(name="TEST_UI_020_B_ZONE", building_id=building.id)
category = EquipmentCategory(name="TEST_UI_020_B_LUMINAIRE")
db.session.add_all([zone, category])
db.session.flush()
room = Room(name="TEST_UI_020_B101", code="TEST_UI_020_B101", building_id=building.id, zone_id=zone.id)
db.session.add(room)
db.session.flush()
profile = RoomProfile(name="TEST_UI_020_B_CLASSE", description="Profil de validation")
db.session.add(profile)
db.session.flush()
item = RoomProfileItem(profile_id=profile.id, label="Luminaire", category_id=category.id, quantity=10, is_group=True)
db.session.add(item)
db.session.commit()
profile_id, room_id, item_id = profile.id, room.id, item.id
assert RoomProfile.query.get(profile_id) is not None
assert Equipment.query.filter_by(room_id=room_id).count() == 0
with app.app_context():
item = RoomProfileItem.query.get(item_id)
created = create_equipment(name=item.label, category_id=item.category_id,
room_id=room_id, quantity=4, is_group=item.is_group,
create_schedule=False)
db.session.commit()
assert created.id is not None
assert Equipment.query.filter_by(room_id=room_id).count() == 1
assert Equipment.query.filter_by(room_id=room_id).one().quantity == 4
def test_bulk_room_can_reference_profile_without_creating_equipment(authenticated_client, app):
with app.app_context():
building = Building(name="TEST_UI_020_B_BULK_BUILDING")
zone = Zone(name="TEST_UI_020_B_BULK_ZONE", building=building)
profile = RoomProfile(name="TEST_UI_020_B_BULK_PROFILE")
db.session.add_all([building, zone, profile])
db.session.commit()
building_id, zone_id, profile_id = building.id, zone.id, profile.id
with app.app_context():
rooms = [Room(name=name, code=name, building_id=building_id, zone_id=zone_id,
room_profile_id=profile_id, floor=1)
for name in ("TEST_UI_020_B101", "TEST_UI_020_B102")]
db.session.add_all(rooms)
db.session.commit()
saved = Room.query.filter(Room.building_id == building_id, Room.room_profile_id == profile_id).all()
assert len(saved) == 2
assert Equipment.query.filter(Equipment.room_id.in_([room.id for room in saved])).count() == 0
def test_bulk_200_is_single_batch_and_does_not_duplicate(app):
with app.app_context():
building = Building(name="TEST_UI_020_BENCH_BUILDING")
db.session.add(building); db.session.flush()
names = [f"B{i:03d}" for i in range(200)]
started = perf_counter()
db.session.add_all([Room(name=name, code=name, building_id=building.id, floor=i % 4) for i, name in enumerate(names)])
db.session.commit()
elapsed = perf_counter() - started
assert Room.query.filter(Room.building_id == building.id).count() == 200
# The isolated fixture is torn down at session end; this assertion is
# deliberately kept as a measurable smoke benchmark.
assert elapsed < 10
def test_profile_preview_route_after_ui_creation(authenticated_client, app):
created = authenticated_client.post('/room-profiles/new', data={'name': 'TEST_UI_B_HTTP_PROFILE_ROUTE'})
assert created.status_code == 302
profile_id = int(re.search(r'/room-profiles/(\d+)/edit', created.headers['Location']).group(1))
item_response = authenticated_client.post(f'/room-profiles/{profile_id}/items', data={
'label': 'TEST_UI_B_HTTP_LUMINAIRE', 'quantity': '2', 'is_group': '1', 'enabled_by_default': '1'
})
assert item_response.status_code == 302
building_response = authenticated_client.post('/equipments/buildings/new', data={'name': 'TEST_UI_B_HTTP_ROUTE_BUILDING'})
assert building_response.status_code == 303
with app.app_context():
db.session.remove(); building_id = Building.query.filter_by(name='TEST_UI_B_HTTP_ROUTE_BUILDING').one().id
zone_response = authenticated_client.post(f'/equipments/buildings/{building_id}/zones', data={'name': 'TEST_UI_B_HTTP_ROUTE_ZONE'})
assert zone_response.status_code == 303
with app.app_context():
db.session.remove(); zone_id = Zone.query.filter_by(name='TEST_UI_B_HTTP_ROUTE_ZONE').one().id
room_response = authenticated_client.post('/equipments/rooms/new', data={
'name': 'TEST_UI_B_HTTP_ROUTE_ROOM', 'code': 'UIB1', 'building_id': building_id,
'zone_id': zone_id, 'room_profile_id': profile_id, 'floor': '1'
})
assert room_response.status_code == 302
with app.app_context():
db.session.remove()
room = Room.query.filter_by(code='UIB1').first()
assert room is not None
room_id = room.id
db.session.remove()
preview = authenticated_client.get(f'/room-profiles/{profile_id}/preview?room_id={room_id}')
assert preview.status_code == 200
assert b'TEST_UI_B_HTTP_LUMINAIRE' in preview.data
with app.app_context():
db.session.remove()
item_id = RoomProfileItem.query.filter_by(profile_id=profile_id).one().id
applied = authenticated_client.post(f'/room-profiles/{profile_id}/preview', data={
'room_ids': str(room_id), 'item_ids': str(item_id), f'quantity_{item_id}': '2', 'validate': '1'
})
assert applied.status_code == 302
with app.app_context():
db.session.remove()
equipment = Equipment.query.filter_by(room_id=room_id, room_profile_item_id=item_id).one()
assert equipment.quantity == 2
# Une seconde application est idempotente.
authenticated_client.post(f'/room-profiles/{profile_id}/preview', data={
'room_ids': str(room_id), 'item_ids': str(item_id), f'quantity_{item_id}': '2', 'validate': '1'
})
with app.app_context():
db.session.remove()
assert Equipment.query.filter_by(room_id=room_id, room_profile_item_id=item_id).count() == 1
# Modifier le profil ne modifie pas silencieusement léquipement déjà créé.
changed = authenticated_client.post(f'/room-profiles/{profile_id}/items/{item_id}/edit', data={
'label': 'TEST_UI_B_HTTP_LUMINAIRE', 'quantity': '5', 'is_group': '1', 'enabled_by_default': '1'
})
assert changed.status_code == 302
with app.app_context():
db.session.remove()
assert Equipment.query.filter_by(room_id=room_id, room_profile_item_id=item_id).one().quantity == 2
def test_work_schedule_ui_isolates_two_technicians(authenticated_client, app):
def create_user(username, full_name):
response = authenticated_client.post('/auth/users/new', data={
'username': username, 'email': f'{username}@example.com', 'full_name': full_name,
'password': 'Test-password-strong-123!', 'role': 'demandeur', 'role_ids': 'demandeur', 'is_active': 'y',
})
assert response.status_code == 302
with app.app_context():
db.session.remove(); return User.query.filter_by(username=username).one().id
from app_new.core.models.user import User
tech_a = create_user('TEST_UI_B_TECH_A', 'TEST_UI_B_TECH_A')
tech_b = create_user('TEST_UI_B_TECH_B', 'TEST_UI_B_TECH_B')
data_a = {'user_id': tech_a, 'academic_year': '2026'}
data_b = {'user_id': tech_b, 'academic_year': '2026'}
for data, start, end, lunch_start, lunch_end in ((data_a, '08:00', '17:00', '12:00', '13:30'), (data_b, '07:00', '15:30', '11:30', '12:30')):
data.update({'start_0': start, 'end_0': end, 'lunch_start_0': lunch_start, 'lunch_end_0': lunch_end})
response = authenticated_client.post('/planning/work-schedules', data=data)
assert response.status_code == 200
with app.app_context():
db.session.remove()
a = WorkSchedule.query.filter_by(user_id=tech_a, day_of_week=0, academic_year=2026, is_active=True).one()
b = WorkSchedule.query.filter_by(user_id=tech_b, day_of_week=0, academic_year=2026, is_active=True).one()
assert (a.start_time.hour, a.end_time.hour, a.lunch_start.hour, a.lunch_end.hour) == (8, 17, 12, 13)
assert (b.start_time.hour, b.end_time.hour, b.lunch_start.hour, b.lunch_end.hour) == (7, 15, 11, 12)
def test_configuration_center_renders_optional_states(authenticated_client):
response = authenticated_client.get('/configuration/')
assert response.status_code == 200
assert b'ESSENTIEL' in response.data
assert b'COMPTEURS' in response.data
assert b'CARTOGRAPHIE TECHNIQUE' in response.data
assert b'Facultatif' in response.data