# ----------------------------------------------------------------------------- # Forma-Ka — Agrégateur de formations (province de Québec) # Auteur : Simon-Pierre Boucher — contact@spboucher.ai # tests/test_normalize.py : couche de normalisation commune # ----------------------------------------------------------------------------- from formaka.normalize import ( extract_details, normalize_language, normalize_mode, normalize_type, parse_date_fr, parse_duration_hours, parse_price, ) from formaka.schema import Formation def test_parse_price(): assert parse_price("1 295,00 $ + tx") == 1295.0 assert parse_price("795 $") == 795.0 assert parse_price("$1,250.00") == 1250.0 assert parse_price("Gratuit") == 0.0 assert parse_price("Sans frais") == 0.0 assert parse_price("") is None assert parse_price("Sur demande") is None def test_parse_duration_hours(): assert parse_duration_hours("2 jours") == 14.0 assert parse_duration_hours("45 heures") == 45.0 assert parse_duration_hours("3,5 h") == 3.5 assert parse_duration_hours("90 minutes") == 1.5 assert parse_duration_hours("demi-journée") == 3.5 assert parse_duration_hours("") is None def test_normalize_mode(): assert normalize_mode("Classe virtuelle") == "en ligne" assert normalize_mode("À distance") == "en ligne" assert normalize_mode("En salle") == "présentiel" assert normalize_mode("Comodal") == "hybride" assert normalize_mode("À votre rythme") == "asynchrone" def test_normalize_type(): assert normalize_type("Séminaire de 2 jours") == "Séminaire" assert normalize_type("Cours de 1er cycle") == "Cours universitaire" assert normalize_type("AEC en bureautique") == "Cours collégial" assert normalize_type("Webinaire gratuit") == "Webinaire" assert normalize_type("Formation continue") == "Formation continue" def test_normalize_language(): assert normalize_language("Français") == "fr" assert normalize_language("English") == "en" assert normalize_language("FR et EN") == "fr/en" def test_parse_date_fr(): assert parse_date_fr("Débute le 14 octobre 2026") == "2026-10-14" assert parse_date_fr("1er décembre 2026") == "2026-12-01" assert parse_date_fr("2026-09-08") == "2026-09-08" assert parse_date_fr("14/10/2026") == "2026-10-14" assert parse_date_fr("aucune date") is None def test_extract_details(): d = extract_details("Ce cours de 3 crédits (1,4 UEC), niveau débutant, 45 heures") assert d["credits"] == 3.0 assert d["uec"] == 1.4 assert d["level"] == "débutant" assert d["duration_hours"] == 45.0 def test_finalize_prix_optionnel(): """Un cours universitaire sans prix est parfaitement valide.""" f = Formation(source="t", external_id="1", url="u", title=" Cours X ", description="Cours de 3 crédits offert à distance.").finalize() assert f.price is None assert f.is_free is None assert f.credits == "3 crédits" assert f.title == "Cours X" def test_finalize_complet(): f = Formation(source="t", external_id="2", url="u", title="Atelier", training_type="atelier", duration="2 jours", price_label="À partir de 795 $", schedule_label="Prochaine séance : 14 octobre 2026").finalize() assert f.training_type == "Atelier" assert f.price == 795.0 assert f.is_free is False assert f.duration_hours == 14.0 assert f.start_date == "2026-10-14" assert f.details.get("price_from") is True