test(planning): cover Pronote handoff and document security
This commit is contained in:
parent
0e30470604
commit
8248ca0df3
1 changed files with 67 additions and 1 deletions
|
|
@ -1,4 +1,5 @@
|
||||||
from datetime import date, datetime, time
|
from datetime import date, datetime, time
|
||||||
|
import os
|
||||||
|
|
||||||
from app_new.extensions import db
|
from app_new.extensions import db
|
||||||
from app_new.core.models.college import Building, Room, RoomSchedule, Zone
|
from app_new.core.models.college import Building, Room, RoomSchedule, Zone
|
||||||
|
|
@ -6,7 +7,10 @@ from app_new.core.models.cleaning import CommercialProduct, ProductGeneric, Prod
|
||||||
from app_new.core.models.equipment import Equipment, EquipmentCategory, EquipmentDocument
|
from app_new.core.models.equipment import Equipment, EquipmentCategory, EquipmentDocument
|
||||||
from app_new.core.models.maintenance import Intervention, InterventionDocument
|
from app_new.core.models.maintenance import Intervention, InterventionDocument
|
||||||
from app_new.core.models.rbac import Permission, RolePermission
|
from app_new.core.models.rbac import Permission, RolePermission
|
||||||
from app_new.core.services.room_planning import active_room_schedules, room_is_available, sync_pronote_schedules
|
from app_new.core.services.room_planning import (
|
||||||
|
active_room_schedules, normalize_pronote_lessons, room_is_available,
|
||||||
|
sync_pronote_schedules,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def test_document_center_aggregates_sources_and_download_handles_missing_file(authenticated_client, app):
|
def test_document_center_aggregates_sources_and_download_handles_missing_file(authenticated_client, app):
|
||||||
|
|
@ -52,6 +56,21 @@ def test_document_center_aggregates_sources_and_download_handles_missing_file(au
|
||||||
assert authenticated_client.get(f"/documents/download/intervention/{intervention_id}").status_code == 302
|
assert authenticated_client.get(f"/documents/download/intervention/{intervention_id}").status_code == 302
|
||||||
assert authenticated_client.get(f"/documents/download/equipment/{equipment_id}").status_code == 302
|
assert authenticated_client.get(f"/documents/download/equipment/{equipment_id}").status_code == 302
|
||||||
assert authenticated_client.get(f"/documents/download/product/{product_id}").status_code == 302
|
assert authenticated_client.get(f"/documents/download/product/{product_id}").status_code == 302
|
||||||
|
from flask import current_app
|
||||||
|
intervention_doc.filepath = os.path.join(current_app.config["UPLOAD_FOLDER"], "..", "..", "etc", "passwd")
|
||||||
|
db.session.commit()
|
||||||
|
assert authenticated_client.get(f"/documents/download/intervention/{intervention_id}").status_code == 302
|
||||||
|
assert authenticated_client.get("/documents/?source=intervention&document_type=rapport").status_code == 200
|
||||||
|
assert "intervention.pdf" in authenticated_client.get("/documents/?q=Phase1%20intervention").get_data(as_text=True)
|
||||||
|
assert "notice.pdf" not in authenticated_client.get("/documents/?source=intervention").get_data(as_text=True)
|
||||||
|
assert authenticated_client.get("/documents/download/unknown/1").status_code == 403
|
||||||
|
|
||||||
|
|
||||||
|
def test_document_center_requires_source_permission(app, monkeypatch):
|
||||||
|
from app_new.documents import service
|
||||||
|
monkeypatch.setattr(service, "has_permission", lambda permission, user=None: permission == "documents.view")
|
||||||
|
with app.app_context():
|
||||||
|
assert service.list_documents(user=object()) == []
|
||||||
|
|
||||||
|
|
||||||
def test_room_schedule_replaces_only_unprotected_manual_slot(app):
|
def test_room_schedule_replaces_only_unprotected_manual_slot(app):
|
||||||
|
|
@ -117,3 +136,50 @@ def test_pronote_sync_supersedes_initial_manual_and_preserves_protected(app):
|
||||||
assert initial.resolution_status == "superseded"
|
assert initial.resolution_status == "superseded"
|
||||||
assert protected.protected_from_sync is True
|
assert protected.protected_from_sync is True
|
||||||
assert any(row.external_id == "sync-conflict" and row.conflict_note for row in rows)
|
assert any(row.external_id == "sync-conflict" and row.conflict_note for row in rows)
|
||||||
|
|
||||||
|
|
||||||
|
def test_pronote_sync_change_removal_ambiguity_and_freshness(app):
|
||||||
|
with app.app_context():
|
||||||
|
building = Building(name="Phase15 edge building")
|
||||||
|
db.session.add(building)
|
||||||
|
db.session.flush()
|
||||||
|
room = Room(name="Phase15 edge room", building_id=building.id)
|
||||||
|
db.session.add(room)
|
||||||
|
db.session.flush()
|
||||||
|
week = date(2026, 9, 7)
|
||||||
|
# Deux manuels compatibles rendent le rapprochement ambigu.
|
||||||
|
manuals = [RoomSchedule(
|
||||||
|
room_id=room.id, week_start=week, day_of_week=0,
|
||||||
|
start_time=time(8), end_time=time(9), subject="Maths",
|
||||||
|
source="manual", protected_from_sync=False,
|
||||||
|
) for _ in range(2)]
|
||||||
|
db.session.add_all(manuals)
|
||||||
|
db.session.commit()
|
||||||
|
sync_at = datetime(2026, 9, 1, 8, 15)
|
||||||
|
sync_pronote_schedules(room.id, [{
|
||||||
|
"external_id": "edge-course", "week_start": week, "day_of_week": 0,
|
||||||
|
"start_time": time(8, 30), "end_time": time(9, 30), "subject": "Maths",
|
||||||
|
}], synced_at=sync_at)
|
||||||
|
row = RoomSchedule.query.filter_by(room_id=room.id, external_id="edge-course").one()
|
||||||
|
assert row.last_synced_at == sync_at
|
||||||
|
assert row.conflict_note and "ambiguë" in row.conflict_note
|
||||||
|
assert all(item.resolution_status == "active" for item in manuals)
|
||||||
|
|
||||||
|
# Une synchronisation réussie sans ce créneau le désactive sans DELETE.
|
||||||
|
sync_pronote_schedules(room.id, [], synced_at=datetime(2026, 9, 2, 8, 15))
|
||||||
|
db.session.refresh(row)
|
||||||
|
assert row.resolution_status == "disabled"
|
||||||
|
assert db.session.get(RoomSchedule, row.id) is not None
|
||||||
|
|
||||||
|
|
||||||
|
def test_normalize_pronote_lessons_is_stable_and_skips_invalid_entries():
|
||||||
|
week = date(2026, 9, 7)
|
||||||
|
lessons = [{
|
||||||
|
"day_of_week": 0, "start_time": "08:00", "end_time": "09:00",
|
||||||
|
"subject": "Maths", "teacher": "Mme Test", "class_name": "6e A",
|
||||||
|
}, {"day_of_week": 0, "start_time": "invalide", "end_time": "09:00"}]
|
||||||
|
first = normalize_pronote_lessons(lessons, week)
|
||||||
|
second = normalize_pronote_lessons(lessons, week)
|
||||||
|
assert len(first) == 1
|
||||||
|
assert first == second
|
||||||
|
assert len(first[0]["external_id"]) == 64
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue