""" NAH-13 — weak-topic targeting: next_card biases toward low-average-grade topics. No model needed (pure scheduling logic). Run: python3 test_weak_topic.py """ import learning_engine as le from schema import new_card def _deck(): return [ new_card("strong q1", "a", topic="Algebra", card_id="alg1"), new_card("strong q2", "a", topic="Algebra", card_id="alg2"), new_card("weak q1", "a", topic="Geometry", card_id="geo1"), new_card("weak q2", "a", topic="Geometry", card_id="geo2"), ] def test_no_history_keeps_order(): s = le.init_session(_deck()) # No answers yet -> serve the queue head untouched. assert le.next_card(s)["id"] == "alg1" assert s["queue"][0] == "alg1" print("ok no history -> normal queue order") def test_weak_topic_surfaces_first(): s = le.init_session(_deck()) # Simulate: aced Algebra, bombed Geometry. s["history"] = [ {"card_id": "alg1", "topic": "Algebra", "grade": 5, "user_answer": ""}, {"card_id": "geo1", "topic": "Geometry", "grade": 1, "user_answer": ""}, ] # Queue still leads with an Algebra card, but a weak Geometry card is in reach. s["queue"] = ["alg2", "geo2", "alg1"] card = le.next_card(s) assert card["topic"] == "Geometry", f"expected weak topic first, got {card}" assert s["queue"][0] == card["id"], "chosen card must be rotated to the front" print("ok weak topic (Geometry) surfaced ahead of strong topic") def test_lookahead_window_respected(): s = le.init_session(_deck()) s["history"] = [{"card_id": "geo1", "topic": "Geometry", "grade": 0, "user_answer": ""}] # Weak Geometry card sits beyond the lookahead window -> not pulled forward. s["queue"] = ["alg1", "alg2", "alg2", "alg1", "geo2"] le.WEAK_LOOKAHEAD # window is 4; geo2 is at index 4 card = le.next_card(s) assert card["topic"] == "Algebra", "weak card outside window should not jump" print("ok weak card beyond lookahead window left in place") def test_apply_result_contract_holds(): # next_card rotates the served card to front; apply_result pops the front. s = le.init_session(_deck()) s["history"] = [{"card_id": "geo1", "topic": "Geometry", "grade": 1, "user_answer": ""}] s["queue"] = ["alg1", "geo2"] card = le.next_card(s) assert card["id"] == "geo2" before = len(s["queue"]) le.apply_result(s, card, {"score": 4, "correct": True, "explanation": "", "missed_concept": ""}) assert "geo2" not in s["queue"][:1], "served card should be popped from front" assert len(s["queue"]) == before, "popped one, re-inserted one" print("ok apply_result still pops the served card from the front") if __name__ == "__main__": test_no_history_keeps_order() test_weak_topic_surfaces_first() test_lookahead_window_respected() test_apply_result_contract_holds() print("\nAll NAH-13 weak-topic tests passed.")