"""KI-278 (2026-05-16) — eligibility filtering + profile-fit ranking. Pins the bug reported for the failing session: USER PROFILE: 29, metro, income 25L+, FIRST policy (existing_cover_inr=0), no pre-existing conditions, wants 15L sum insured, prefers ZERO co-pay. WHAT WAS WRONGLY RECOMMENDED: 1. Royal Sundaram "Multiplier" — 20% co-payment (user wants 0%) 2. Royal Sundaram "Advanced Top Up" — super-top-up; needs a base policy the first-time buyer does NOT have 3. generally B/C-grade plans whose metrics contradict stated needs Two logic defects, both fixed in backend/retrieval_filters.py: (a) ELIGIBILITY: top-up / super-top-up plans must be excluded when the user has no existing base cover (existing_cover_inr falsy / first buy). (b) PROFILE-FIT RANKING: high co-pay plans must not survive an explicit zero-copay preference; the SI floor (15L) must gate; better-fit plans must rank above worse-fit ones. Run: .venv/bin/python -m unittest tests.test_eligibility_ranking -v """ from __future__ import annotations import unittest from backend.retrieval_filters import ( apply_profile_filter, apply_eligibility_filter, rank_by_profile_fit, filter_pipeline, ) # --------------------------------------------------------------------------- # The exact failing-session profile. # --------------------------------------------------------------------------- FAILING_PROFILE = { "age": 29, "location_tier": "metro", "income_band": "25L+", "primary_goal": "first_buy", "existing_cover_inr": 0, # FIRST policy — no base cover "health_conditions": ["none"], "desired_sum_insured_inr": 1_500_000, # ₹15 lakh "copay_pct": 0, # explicit ZERO co-pay preference "dependents": "self", } def _chunk( policy_id: str, policy_name: str, *, score: float = 0.5, policy_type: str | None = None, deductible_amount: int | None = None, copay_pct: int | None = None, sum_insured_options: list[int] | None = None, grade: str | None = None, overall_score: int | None = None, doc_type: str = "policy", ) -> dict: """Build a chunk dict shaped like brain_tools.retrieve_policies output AFTER the new policy_facts enrichment step.""" return { "policy_id": policy_id, "policy_name": policy_name, "insurer_slug": policy_id.split("__")[0], "doc_type": doc_type, "score": score, "chunk_text": "", # Enriched structured facts (added by brain_tools before filtering): "policy_type_indemnity_or_fixed": policy_type, "deductible_amount": deductible_amount, "co_payment_pct": copay_pct, "sum_insured_options": sum_insured_options, "_grade": grade, "_overall_score": overall_score, } # Realistic catalog slice mirroring the failing session. MULTIPLIER = _chunk( "royal-sundaram__multiplier", "Multiplier Health Insurance Plan", score=0.71, policy_type="family_floater", deductible_amount=None, copay_pct=20, sum_insured_options=[500000, 1000000, 1500000, 2000000, 2500000], grade="B", overall_score=72, ) ADVANCED_TOP_UP = _chunk( "royal-sundaram__advanced-top-up", "Advanced Top Up Health Insurance Plan", score=0.69, policy_type="super_top_up", deductible_amount=500000, copay_pct=0, sum_insured_options=[1000000, 1500000, 2000000], grade="B", overall_score=70, ) GOOD_FIT_A = _chunk( "niva-bupa__reassure-2", "ReAssure 2.0", score=0.62, policy_type="indemnity", deductible_amount=None, copay_pct=0, sum_insured_options=[1000000, 1500000, 2000000, 5000000], grade="A", overall_score=88, ) GOOD_FIT_B = _chunk( "hdfc-ergo__optima-secure", "Optima Secure", score=0.58, policy_type="indemnity", deductible_amount=None, copay_pct=0, sum_insured_options=[500000, 1000000, 1500000, 2000000], grade="A", overall_score=85, ) LOW_SI = _chunk( "star-health__medi-classic", "Medi Classic", score=0.66, policy_type="indemnity", deductible_amount=None, copay_pct=0, sum_insured_options=[200000, 300000, 500000], # cannot offer 15L grade="C", overall_score=60, ) class TestEligibilityFilter(unittest.TestCase): """Defect (a) — top-up / super-top-up exclusion for first-time buyers.""" def test_super_top_up_dropped_when_no_base_cover(self): kept = apply_eligibility_filter( [ADVANCED_TOP_UP, GOOD_FIT_A], FAILING_PROFILE ) ids = {c["policy_id"] for c in kept} self.assertNotIn( "royal-sundaram__advanced-top-up", ids, "Super-top-up must be excluded for a first-time buyer with no " "existing base cover.", ) self.assertIn("niva-bupa__reassure-2", ids) def test_top_up_dropped_by_name_when_facts_missing(self): # No structured policy_type, but the NAME says "Top Up" / "Super Top Up". top_up_by_name = _chunk( "sbi-general__super-top-up", "SBI Super Top-up Health Insurance", policy_type=None, deductible_amount=None, ) kept = apply_eligibility_filter([top_up_by_name], FAILING_PROFILE) self.assertEqual( kept, [], "A plan named 'Super Top-up' must be excluded for a no-base-cover " "user even when structured policy_type is missing.", ) def test_top_up_dropped_by_deductible_signal(self): # No policy_type, name doesn't say top-up, but it carries a large # aggregate deductible — that IS the base-cover requirement. deductible_only = _chunk( "acko__platinum-super", "Platinum Plus Plan", policy_type=None, deductible_amount=500000, ) kept = apply_eligibility_filter([deductible_only], FAILING_PROFILE) self.assertEqual( kept, [], "A plan with a ₹5L aggregate deductible is a top-up in disguise " "and must be excluded for a no-base-cover user.", ) def test_top_up_kept_when_user_has_base_cover(self): has_base = dict(FAILING_PROFILE, existing_cover_inr=500000) kept = apply_eligibility_filter([ADVANCED_TOP_UP], has_base) self.assertEqual( len(kept), 1, "Top-up IS appropriate when the user already has base cover.", ) def test_si_floor_drops_plans_that_cannot_offer_requested_si(self): kept = apply_eligibility_filter([LOW_SI, GOOD_FIT_A], FAILING_PROFILE) ids = {c["policy_id"] for c in kept} self.assertNotIn( "star-health__medi-classic", ids, "A plan whose max SI is ₹5L cannot satisfy a ₹15L requirement.", ) self.assertIn("niva-bupa__reassure-2", ids) def test_zero_copay_preference_drops_high_copay_plan(self): kept = apply_eligibility_filter([MULTIPLIER, GOOD_FIT_A], FAILING_PROFILE) ids = {c["policy_id"] for c in kept} self.assertNotIn( "royal-sundaram__multiplier", ids, "A 20% co-pay plan must be excluded when the user explicitly " "wants ZERO co-pay and can afford full cover.", ) def test_non_policy_chunks_never_dropped(self): reg = _chunk("irdai__circular", "IRDAI Master Circular", doc_type="regulatory", policy_type="super_top_up", deductible_amount=500000) kept = apply_eligibility_filter([reg], FAILING_PROFILE) self.assertEqual(len(kept), 1, "Regulatory/review/profile chunks are never policies.") class TestProfileFitRanking(unittest.TestCase): """Defect (b) — better-fit plans must outrank worse-fit ones.""" def test_a_grade_zero_copay_ranks_above_b_grade(self): ranked = rank_by_profile_fit( [MULTIPLIER, GOOD_FIT_B, GOOD_FIT_A], FAILING_PROFILE ) order = [c["policy_id"] for c in ranked] self.assertLess( order.index("niva-bupa__reassure-2"), order.index("royal-sundaram__multiplier"), "A-grade zero-copay plan must rank above a B-grade 20%-copay plan " "even though the B-grade plan had higher raw cosine.", ) def test_ranking_stable_for_equally_good_plans(self): ranked = rank_by_profile_fit([GOOD_FIT_A, GOOD_FIT_B], FAILING_PROFILE) self.assertEqual(len(ranked), 2) class TestFilterPipelineIntegration(unittest.TestCase): """End-to-end: the exact failing catalog through filter_pipeline must NOT surface Multiplier or Advanced Top Up, and the top result must be a well-fitting A-grade plan.""" def test_failing_session_catalog(self): catalog = [ MULTIPLIER, ADVANCED_TOP_UP, LOW_SI, GOOD_FIT_A, GOOD_FIT_B, ] filtered, guard = filter_pipeline( catalog, profile=FAILING_PROFILE, query="first health policy metro 15 lakh sum insured zero co-pay", intent="recommendation", ) ids = [c["policy_id"] for c in filtered] self.assertNotIn("royal-sundaram__advanced-top-up", ids, "super-top-up must not reach the brain") self.assertNotIn("royal-sundaram__multiplier", ids, "20%-copay plan must not reach a zero-copay user") self.assertNotIn("star-health__medi-classic", ids, "₹5L-max plan must not reach a ₹15L requirement") self.assertTrue(ids, "good-fit plans must still survive") # Best-fit (A-grade, zero-copay, offers 15L) should be ranked first. self.assertEqual( ids[0], "niva-bupa__reassure-2", "Top recommendation must be the best profile-fit plan.", ) def test_demographic_filter_still_runs(self): # apply_profile_filter (age/senior/maternity) must still be composed. senior = _chunk("star-health__red-carpet", "Senior Citizens Red Carpet", policy_type="indemnity", sum_insured_options=[1500000]) senior["min_entry_age"] = 60 filtered, _ = filter_pipeline( [senior, GOOD_FIT_A], profile=FAILING_PROFILE, query="x", intent="recommendation", ) ids = [c["policy_id"] for c in filtered] self.assertNotIn("star-health__red-carpet", ids, "29yo must not see a senior-only plan") if __name__ == "__main__": unittest.main()