# ============================================================================= # Job·Ka — Groupe KA # Auteur : Simon-Pierre Boucher # Contact : contact@spboucher.ai # Fichier : tests/test_enrich.py # Rôle : Tests unitaires de l'enrichissement Phase 3 — exigences # structurées (expérience, scolarité, langues) et séniorité # Créé : 2026-08-25 Modifié : 2026-08-25 # ============================================================================= from jobka.normalize import extract_benefits, extract_requirements, parse_seniority from jobka.schema import JobPosting # --- exigences : années d'expérience ------------------------------------------ def test_experience_range_fr(): r = extract_requirements("Comptable", "Détenir 3 à 5 ans d'expérience en comptabilité.") assert r["experience_years"] == 3 def test_experience_plus_en(): r = extract_requirements("Developer", "5+ years of experience with Python required.") assert r["experience_years"] == 5 def test_experience_minimum_de(): r = extract_requirements("Technicien", "Expérience minimum de 2 ans dans un poste similaire.") assert r["experience_years"] == 2 def test_experience_absente(): r = extract_requirements("Cuisinier", "Préparer les plats du menu. Horaire de 35 heures.") assert "experience_years" not in r def test_experience_bornes(): # 45 ans d'expérience = non plausible, on ne retient rien r = extract_requirements("Poste", "45 ans d'expérience exigée") assert "experience_years" not in r # --- exigences : scolarité ------------------------------------------------------ def test_education_dec_avec_contexte(): r = extract_requirements("Technicien", "Diplôme d'études collégiales (DEC) en informatique requis.") assert r["education"] == "DEC" def test_education_dec_sans_contexte_ignore(): # « déc. » = décembre, pas un diplôme : sans contexte scolaire on ignore r = extract_requirements("Poste", "Entrée en fonction : déc. 2026. Aucun prérequis.") assert "education" not in r def test_education_bac(): r = extract_requirements("Analyste", "Baccalauréat en administration des affaires.") assert r["education"] == "baccalauréat" def test_education_bachelor_en(): r = extract_requirements("Analyst", "Bachelor's degree in Computer Science or equivalent.") assert r["education"] == "baccalauréat" def test_education_plus_bas_exige(): # « bac ou maîtrise » -> l'exigence minimale est le bac r = extract_requirements("Poste", "Baccalauréat ou maîtrise en génie exigé.") assert r["education"] == "baccalauréat" def test_education_dep(): r = extract_requirements("Mécanicien", "Détenir un DEP en mécanique automobile.") assert r["education"] == "DEP" # --- exigences : langues -------------------------------------------------------- def test_langues_bilingue(): r = extract_requirements("Poste", "Le candidat doit être bilingue (français/anglais).") assert r["languages"] == ["français", "anglais"] def test_langues_anglais_requis(): r = extract_requirements("Poste", "Anglais fonctionnel requis pour ce poste.") assert r["languages"] == ["anglais"] def test_langues_fluent_english(): r = extract_requirements("Role", "Fluent in English; French is an asset.") assert "anglais" in r["languages"] def test_langues_aucune_mention(): r = extract_requirements("Poste", "Assembler des pièces sur la chaîne de production.") assert "languages" not in r # --- séniorité ------------------------------------------------------------------- def test_seniority_titre_senior(): assert parse_seniority("Développeur senior") == "senior" def test_seniority_titre_junior(): assert parse_seniority("Analyste junior") == "junior" def test_seniority_titre_direction(): assert parse_seniority("Directeur des finances") == "direction" def test_seniority_stage_par_type(): assert parse_seniority("Analyste TI", None, "stage") == "stage" def test_seniority_label_ats(): assert parse_seniority("Ingénieur", {"experience": "Mid-Senior Level"}) == "senior" assert parse_seniority("Ingénieur", {"experience": "Entry level"}) == "junior" def test_seniority_annees_experience(): assert parse_seniority("Comptable", {"experience_years": 1}) == "junior" assert parse_seniority("Comptable", {"experience_years": 4}) == "intermediaire" assert parse_seniority("Comptable", {"experience_years": 8}) == "senior" def test_seniority_aucun_signal(): assert parse_seniority("Préposé à l'entretien") is None def test_seniority_not_applicable_ignore(): assert parse_seniority("Poste", {"experience": "Not Applicable"}) is None # --- avantages : en-têtes élargis ------------------------------------------------- def test_benefits_ce_quon_offre(): desc = ("Ce que nous vous offrons\n• Assurances collectives\n" "• REER avec cotisation de l'employeur\n• 4 semaines de vacances\n") assert len(extract_benefits(desc)) == 3 def test_benefits_whats_in_it_for_you(): desc = ("What's in it for you\n- Health insurance\n- RRSP matching\n\nApply now.") assert extract_benefits(desc) == ["Health insurance", "RRSP matching"] # --- intégration finalize() -------------------------------------------------------- def test_finalize_remplit_exigences_et_seniorite(): job = JobPosting( source="test", external_id="1", url="https://example.com/1", employer="Exemple inc.", title="Développeur senior", description=("Baccalauréat en informatique et 5 ans d'expérience " "exigés. Le candidat doit être bilingue."), ).finalize() assert job.requirements["experience_years"] == 5 assert job.requirements["education"] == "baccalauréat" assert job.requirements["languages"] == ["français", "anglais"] assert job.seniority == "senior" def test_finalize_ne_touche_pas_valeurs_ats(): job = JobPosting( source="test", external_id="2", url="https://example.com/2", employer="Exemple inc.", title="Analyste", description="2 ans d'expérience demandés.", requirements={"experience_years": 7}, seniority="junior", ).finalize() assert job.requirements["experience_years"] == 7 # valeur ATS conservée assert job.seniority == "junior"