Spaces:
Sleeping
Sleeping
| """ | |
| test_grader.py β Unit tests for grader.py | |
| ========================================== | |
| Run with: pytest test_grader.py -v | |
| These tests cover every branch of grade_action() and the edge-cases of | |
| calculate_overall_score(). They also act as a regression guard β any | |
| change to a reward constant will immediately fail the assertion that was | |
| relying on it, forcing an intentional review. | |
| """ | |
| from __future__ import annotations | |
| import pytest | |
| from grader import ( | |
| HEALTH_DRAIN_THRESHOLD, | |
| PASS_THRESHOLD, | |
| R_BREACH, | |
| R_DISRUPTION, | |
| R_MALWARE_QUARANTINE, | |
| R_PARTIAL, | |
| R_PERFECT, | |
| R_PHISH_BEC_QUARANTINE, | |
| R_SPAM_BLOCK, | |
| R_SPAM_QUARANTINE, | |
| R_WRONG_PROCEDURE, | |
| calculate_overall_score, | |
| grade_action, | |
| ) | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CONSTANT SANITY CHECKS | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestRewardConstants: | |
| def test_all_rewards_in_open_interval(self): | |
| for r in [R_PERFECT, R_MALWARE_QUARANTINE, R_PHISH_BEC_QUARANTINE, | |
| R_SPAM_BLOCK, R_SPAM_QUARANTINE, R_WRONG_PROCEDURE, | |
| R_DISRUPTION, R_BREACH]: | |
| assert 0.0 < r < 1.0, f"Reward {r} violates open-interval contract" | |
| def test_reward_ordering(self): | |
| assert R_BREACH < R_DISRUPTION < R_WRONG_PROCEDURE | |
| assert R_WRONG_PROCEDURE < HEALTH_DRAIN_THRESHOLD | |
| assert HEALTH_DRAIN_THRESHOLD < R_SPAM_QUARANTINE | |
| assert R_SPAM_QUARANTINE < R_SPAM_BLOCK | |
| assert R_SPAM_BLOCK < R_PHISH_BEC_QUARANTINE | |
| assert R_PHISH_BEC_QUARANTINE < R_MALWARE_QUARANTINE | |
| assert R_MALWARE_QUARANTINE < R_PERFECT | |
| def test_partial_alias(self): | |
| assert R_PARTIAL == R_MALWARE_QUARANTINE | |
| def test_health_drain_covers_breach_disruption_wrong(self): | |
| assert R_BREACH < HEALTH_DRAIN_THRESHOLD | |
| assert R_DISRUPTION < HEALTH_DRAIN_THRESHOLD | |
| assert R_WRONG_PROCEDURE < HEALTH_DRAIN_THRESHOLD | |
| def test_cautious_scores_never_drain_health(self): | |
| assert R_SPAM_QUARANTINE >= HEALTH_DRAIN_THRESHOLD | |
| assert R_SPAM_BLOCK >= HEALTH_DRAIN_THRESHOLD | |
| assert R_PHISH_BEC_QUARANTINE >= HEALTH_DRAIN_THRESHOLD | |
| assert R_MALWARE_QUARANTINE >= HEALTH_DRAIN_THRESHOLD | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # GRADE_ACTION β PERFECT MATCH | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestPerfectTriage: | |
| def test_perfect_match(self, action, etype): | |
| reward, msg = grade_action(action, action, etype) | |
| assert reward == R_PERFECT | |
| assert "PERFECT_TRIAGE" in msg | |
| def test_case_insensitive_action(self): | |
| reward, _ = grade_action("move_to_spam", "MOVE_TO_SPAM", "SPAM") | |
| assert reward == R_PERFECT | |
| def test_case_insensitive_type(self): | |
| reward, _ = grade_action("MARK_SAFE", "MARK_SAFE", "safe") | |
| assert reward == R_PERFECT | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # GRADE_ACTION β SECURITY BREACH | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestSecurityBreach: | |
| def test_mark_safe_on_any_threat_is_breach(self, etype): | |
| """BUG FIX coverage: SPAM + MARK_SAFE must return R_BREACH (not R_WRONG_PROCEDURE).""" | |
| reward, msg = grade_action("MARK_SAFE", "BLOCK_DOMAIN", etype) | |
| assert reward == R_BREACH | |
| assert "SECURITY_BREACH" in msg | |
| def test_spam_mark_safe_is_breach_not_wrong_procedure(self): | |
| """Regression: the original code excluded SPAM from _THREAT_TYPES.""" | |
| reward, _ = grade_action("MARK_SAFE", "MOVE_TO_SPAM", "SPAM") | |
| assert reward == R_BREACH, ( | |
| "SPAM + MARK_SAFE must be R_BREACH β SPAM was excluded from " | |
| "_THREAT_TYPES in the v1.0.2 bug" | |
| ) | |
| assert reward < HEALTH_DRAIN_THRESHOLD # must drain health | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # GRADE_ACTION β BUSINESS DISRUPTION | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestBusinessDisruption: | |
| def test_blocking_safe_email_is_disruption(self, blocking_action): | |
| reward, msg = grade_action(blocking_action, "MARK_SAFE", "SAFE") | |
| assert reward == R_DISRUPTION | |
| assert "BUSINESS_DISRUPTION" in msg | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # GRADE_ACTION β PARTIAL CREDIT | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestPartialCredit: | |
| def test_malware_quarantine(self): | |
| reward, msg = grade_action("QUARANTINE", "BLOCK_DOMAIN", "MALWARE") | |
| assert reward == R_MALWARE_QUARANTINE | |
| assert "CAUTIOUS" in msg | |
| def test_phish_quarantine(self): | |
| reward, msg = grade_action("QUARANTINE", "BLOCK_DOMAIN", "PHISH") | |
| assert reward == R_PHISH_BEC_QUARANTINE | |
| assert "UNDER_RESPONSE" in msg | |
| def test_bec_quarantine(self): | |
| reward, msg = grade_action("QUARANTINE", "BLOCK_DOMAIN", "BEC") | |
| assert reward == R_PHISH_BEC_QUARANTINE | |
| def test_spam_block_domain(self): | |
| reward, msg = grade_action("BLOCK_DOMAIN", "MOVE_TO_SPAM", "SPAM") | |
| assert reward == R_SPAM_BLOCK | |
| assert "OVER_ESCALATION" in msg | |
| def test_spam_quarantine(self): | |
| reward, msg = grade_action("QUARANTINE", "MOVE_TO_SPAM", "SPAM") | |
| assert reward == R_SPAM_QUARANTINE | |
| assert "OVER_ESCALATION" in msg | |
| def test_partial_credits_never_drain_health(self, action): | |
| reward, _ = grade_action(action, "MOVE_TO_SPAM", "SPAM") | |
| assert reward >= HEALTH_DRAIN_THRESHOLD | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # GRADE_ACTION β WRONG PROCEDURE / INVALID | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestWrongProcedure: | |
| def test_malware_block_domain_is_wrong_procedure(self): | |
| """lv4 / lv8: expected QUARANTINE, agent picks BLOCK_DOMAIN.""" | |
| reward, msg = grade_action("BLOCK_DOMAIN", "QUARANTINE", "MALWARE") | |
| assert reward == R_WRONG_PROCEDURE | |
| assert "INCORRECT_PROCEDURE" in msg | |
| def test_invalid_action_token(self): | |
| reward, msg = grade_action("DELETE", "QUARANTINE", "MALWARE") | |
| assert reward == R_WRONG_PROCEDURE | |
| assert "INVALID_ACTION" in msg | |
| def test_invalid_action_drains_health(self): | |
| reward, _ = grade_action("NONSENSE", "QUARANTINE", "PHISH") | |
| assert reward < HEALTH_DRAIN_THRESHOLD | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # CALCULATE_OVERALL_SCORE | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestCalculateOverallScore: | |
| def test_empty_list_returns_r_breach(self): | |
| assert calculate_overall_score([]) == R_BREACH | |
| def test_all_perfect_returns_r_perfect(self): | |
| assert calculate_overall_score([R_PERFECT, R_PERFECT, R_PERFECT]) == R_PERFECT | |
| def test_result_never_exceeds_r_perfect(self): | |
| assert calculate_overall_score([1.0, 1.0]) == R_PERFECT | |
| def test_result_never_below_r_breach(self): | |
| assert calculate_overall_score([0.0, 0.0]) == R_BREACH | |
| def test_easy_all_perfect(self): | |
| scores = [R_PERFECT] * 3 | |
| assert calculate_overall_score(scores) == R_PERFECT | |
| def test_medium_mixed(self): | |
| scores = [R_PERFECT, R_BREACH, R_PERFECT, R_PHISH_BEC_QUARANTINE] | |
| result = calculate_overall_score(scores) | |
| expected = round((R_PERFECT + R_BREACH + R_PERFECT + R_PHISH_BEC_QUARANTINE) / 4, 4) | |
| assert result == expected | |
| def test_hard_mostly_bad(self): | |
| scores = [R_BREACH, R_DISRUPTION, R_PERFECT] | |
| result = calculate_overall_score(scores) | |
| assert result < PASS_THRESHOLD | |
| def test_returns_four_decimal_places(self): | |
| result = calculate_overall_score([R_PERFECT, R_BREACH]) | |
| assert result == round(result, 4) | |
| def test_single_perfect_step(self): | |
| assert calculate_overall_score([R_PERFECT]) == R_PERFECT | |
| def test_single_breach_step(self): | |
| assert calculate_overall_score([R_BREACH]) == R_BREACH | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| # EDGE CASES β INPUT NORMALIZATION | |
| # βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ | |
| class TestInputNormalization: | |
| def test_whitespace_padded_action(self): | |
| """Actions with leading/trailing spaces should still match.""" | |
| reward, msg = grade_action(" MARK_SAFE ", "MARK_SAFE", "SAFE") | |
| assert reward == R_PERFECT | |
| assert "PERFECT_TRIAGE" in msg | |
| def test_mixed_case_email_type_phish(self): | |
| reward, _ = grade_action("MARK_SAFE", "BLOCK_DOMAIN", "Phish") | |
| assert reward == R_BREACH | |
| def test_mixed_case_email_type_bec(self): | |
| reward, _ = grade_action("QUARANTINE", "BLOCK_DOMAIN", "bEc") | |
| assert reward == R_PHISH_BEC_QUARANTINE | |
| def test_tab_in_action(self): | |
| reward, _ = grade_action("\tQUARANTINE\t", "QUARANTINE", "MALWARE") | |
| assert reward == R_PERFECT | |
| def test_empty_action_string(self): | |
| reward, msg = grade_action("", "QUARANTINE", "MALWARE") | |
| assert reward == R_WRONG_PROCEDURE | |
| assert "INVALID_ACTION" in msg | |
| def test_calculate_overall_score_single_breach(self): | |
| assert calculate_overall_score([R_BREACH]) == R_BREACH | |
| def test_calculate_overall_score_all_disruption(self): | |
| result = calculate_overall_score([R_DISRUPTION] * 5) | |
| assert result == R_DISRUPTION | |