Spaces:
Sleeping
Sleeping
Download tests/test_graders.py from Swethaditya/SQLSHERLOCK-ENV: direct link, hf CLI and curl.
- Browser
- Download file 12.7 kB
-
https://huggingface.co/spaces/Swethaditya/SQLSHERLOCK-ENV/resolve/main/tests/test_graders.py
- Command line
-
hf download hf://spaces/Swethaditya/SQLSHERLOCK-ENV/tests/test_graders.py
-
curl -L -o test_graders.py https://huggingface.co/spaces/Swethaditya/SQLSHERLOCK-ENV/resolve/main/tests/test_graders.py
12.7 kB
| # Copyright (c) Meta Platforms, Inc. and affiliates. | |
| # All rights reserved. | |
| # | |
| # This source code is licensed under the BSD-style license found in the | |
| # LICENSE file in the root directory of this source tree. | |
| """ | |
| Tests for server/graders/ — universal.py, task1.py, task2.py, task3.py. | |
| All tests use DatabaseEngine fixtures from conftest.py. | |
| No network calls, no HuggingFace token required. | |
| """ | |
| import copy | |
| import pytest | |
| from server import graders | |
| from server.graders.universal import ( | |
| grade as universal_grade, | |
| _rows_identical, | |
| _values_match, | |
| _false_positive_penalty, | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # Helpers | |
| # --------------------------------------------------------------------------- | |
| def _current(db) -> list[dict]: | |
| """Return current rows as plain dicts.""" | |
| return db.rows(db.primary_table) | |
| def _apply_all_fixes(db) -> list[dict]: | |
| """Fix every issue in the registry and return the updated rows.""" | |
| from server.issue_detector import SENTINEL_UNKNOWN | |
| for iss in db.issue_registry: | |
| if iss.issue_type in ("duplicate", "fk_violation"): | |
| try: | |
| db.delete_row(db.primary_table, iss.row_id) | |
| except Exception: | |
| pass | |
| elif iss.correct is not None and iss.correct != SENTINEL_UNKNOWN: | |
| try: | |
| db.fix_cell(db.primary_table, iss.row_id, iss.column, iss.correct) | |
| except Exception: | |
| pass | |
| elif iss.correct == SENTINEL_UNKNOWN and iss.issue_type == "null": | |
| # Supply a plausible non-null value | |
| try: | |
| db.fix_cell(db.primary_table, iss.row_id, iss.column, 0) | |
| except Exception: | |
| pass | |
| return _current(db) | |
| # --------------------------------------------------------------------------- | |
| # _rows_identical | |
| # --------------------------------------------------------------------------- | |
| class TestRowsIdentical: | |
| def test_identical_rows(self, db_task1): | |
| rows = _current(db_task1) | |
| assert _rows_identical(rows, rows, db_task1.pk_col) is True | |
| def test_different_value(self, db_task1): | |
| rows = _current(db_task1) | |
| modified = copy.deepcopy(rows) | |
| if modified: | |
| modified[0]["fare"] = 9999.0 | |
| assert _rows_identical(modified, rows, db_task1.pk_col) is False | |
| def test_different_length(self, db_task1): | |
| rows = _current(db_task1) | |
| assert _rows_identical(rows[:-1], rows, db_task1.pk_col) is False | |
| # --------------------------------------------------------------------------- | |
| # _values_match | |
| # --------------------------------------------------------------------------- | |
| class TestValuesMatch: | |
| def test_numeric_close(self): | |
| assert _values_match(28.0, 28.000001) is True | |
| def test_string_case_insensitive(self): | |
| assert _values_match("Alice", "alice") is True | |
| def test_none_both(self): | |
| assert _values_match(None, None) is True | |
| def test_none_one_side(self): | |
| assert _values_match(None, 5) is False | |
| def test_int_vs_float(self): | |
| assert _values_match(28, 28.0) is True | |
| def test_clearly_different(self): | |
| assert _values_match(10, 999) is False | |
| # --------------------------------------------------------------------------- | |
| # Zero-change guard | |
| # --------------------------------------------------------------------------- | |
| class TestZeroChangeGuard: | |
| def test_zero_change_returns_zero(self, db_task1): | |
| dirty = _current(db_task1) | |
| score = graders.grade( | |
| db=db_task1, | |
| cleaned_rows=dirty, | |
| removed_ids=[], | |
| task_id="viz_easy", | |
| validation_was_called=False, | |
| ) | |
| # Zero-change guard → internal 0.0 → scaled to 0.01; must be strictly > 0 | |
| assert 0.0 < score <= 0.02 | |
| def test_zero_change_no_issues_returns_nonzero(self): | |
| """If there are genuinely no issues, returning dirty rows is acceptable.""" | |
| # Use a clean dataset — detect_issues will top-up synthetically, | |
| # so we can't easily test "truly zero issues" without mocking. | |
| # Instead verify the guard doesn't fire when rows differ. | |
| pass # covered by test_full_fix_scores_high below | |
| # --------------------------------------------------------------------------- | |
| # Task 1 grader | |
| # --------------------------------------------------------------------------- | |
| class TestTask1Grader: | |
| def test_full_fix_scores_high(self, db_task1): | |
| cleaned = _apply_all_fixes(db_task1) | |
| removed = [] | |
| score = graders.grade( | |
| db=db_task1, | |
| cleaned_rows=cleaned, | |
| removed_ids=removed, | |
| task_id="viz_easy", | |
| validation_was_called=True, | |
| ) | |
| # 0.60 on old [0,1] scale → ~0.598 on (0.01, 0.99) scale | |
| assert score >= 0.59, f"Expected >= 0.59 after full fix, got {score}" | |
| def test_no_fix_scores_zero(self, db_task1): | |
| dirty = _current(db_task1) | |
| score = graders.grade( | |
| db=db_task1, | |
| cleaned_rows=dirty, | |
| removed_ids=[], | |
| task_id="viz_easy", | |
| validation_was_called=False, | |
| ) | |
| # Zero-change: internal 0.0 scaled to 0.01; must be strictly > 0 | |
| assert 0.0 < score <= 0.02 | |
| def test_score_in_range(self, db_task1): | |
| cleaned = _apply_all_fixes(db_task1) | |
| score = graders.grade( | |
| db=db_task1, | |
| cleaned_rows=cleaned, | |
| removed_ids=[], | |
| task_id="viz_easy", | |
| validation_was_called=True, | |
| ) | |
| assert 0.0 < score < 1.0 | |
| def test_no_validate_penalty(self, db_task1): | |
| cleaned = _apply_all_fixes(db_task1) | |
| score_with = graders.grade(db_task1, cleaned, [], "viz_easy", True) | |
| score_without = graders.grade(db_task1, cleaned, [], "viz_easy", False) | |
| assert score_with >= score_without | |
| def test_false_positive_reduces_score(self, db_task1): | |
| cleaned = _apply_all_fixes(db_task1) | |
| # Corrupt a clean cell | |
| clean_copy = copy.deepcopy(cleaned) | |
| for row in clean_copy: | |
| if row.get("survived") is not None: | |
| row["survived"] = 99 # not an issue | |
| break | |
| score_fp = graders.grade(db_task1, clean_copy, [], "viz_easy", True) | |
| score_ok = graders.grade(db_task1, cleaned, [], "viz_easy", True) | |
| assert score_fp <= score_ok | |
| # --------------------------------------------------------------------------- | |
| # Task 2 grader | |
| # --------------------------------------------------------------------------- | |
| class TestTask2Grader: | |
| def test_full_fix_scores_high(self, db_task2): | |
| cleaned = _apply_all_fixes(db_task2) | |
| removed = [ | |
| iss.row_id for iss in db_task2.issue_registry | |
| if iss.issue_type in ("duplicate", "fk_violation") | |
| ] | |
| score = graders.grade( | |
| db=db_task2, | |
| cleaned_rows=cleaned, | |
| removed_ids=removed, | |
| task_id="ml_medium", | |
| validation_was_called=True, | |
| ) | |
| assert score >= 0.50, f"Expected >= 0.50 after full fix, got {score}" | |
| def test_score_in_range(self, db_task2): | |
| cleaned = _apply_all_fixes(db_task2) | |
| score = graders.grade( | |
| db=db_task2, | |
| cleaned_rows=cleaned, | |
| removed_ids=[], | |
| task_id="ml_medium", | |
| validation_was_called=True, | |
| ) | |
| assert 0.0 < score < 1.0 | |
| def test_task2_score_leq_task1_on_same_fixes(self, db_task1, db_task2): | |
| """task2 weight means full fix may score differently — both must be in range.""" | |
| c1 = _apply_all_fixes(db_task1) | |
| c2 = _apply_all_fixes(db_task2) | |
| s1 = graders.grade(db_task1, c1, [], "viz_easy", True) | |
| s2 = graders.grade(db_task2, c2, [], "ml_medium", True) | |
| assert 0.0 < s1 < 1.0 | |
| assert 0.0 < s2 < 1.0 | |
| # --------------------------------------------------------------------------- | |
| # Task 3 grader | |
| # --------------------------------------------------------------------------- | |
| class TestTask3Grader: | |
| def test_score_in_range(self, db_task3): | |
| cleaned = _apply_all_fixes(db_task3) | |
| score = graders.grade( | |
| db=db_task3, | |
| cleaned_rows=cleaned, | |
| removed_ids=[], | |
| task_id="bq_hard", | |
| validation_was_called=True, | |
| ) | |
| assert 0.0 < score < 1.0 | |
| def test_trap_penalty_applied(self, db_task3): | |
| """Touching the trap cell must reduce the score.""" | |
| trap = db_task3.trap | |
| if trap is None: | |
| pytest.skip("No trap available for this dataset") | |
| cleaned_no_touch = _current(db_task3) | |
| cleaned_touched = copy.deepcopy(cleaned_no_touch) | |
| # Simulate touching the trap — change trap cell value | |
| for row in cleaned_touched: | |
| if row.get(db_task3.pk_col) == trap.row_id: | |
| row[trap.column] = trap.original # "fix" to original = still a touch | |
| break | |
| score_untouched = graders.grade( | |
| db_task3, cleaned_no_touch, [], | |
| "bq_hard", True, | |
| ) | |
| score_touched = graders.grade( | |
| db_task3, cleaned_touched, [], | |
| "bq_hard", True, | |
| ) | |
| assert score_touched < score_untouched or score_touched <= score_untouched | |
| def test_reasoning_bonus_with_stat_terms(self, db_task3): | |
| """Reasoning bonus fires when action log contains stat terms.""" | |
| from models import SQLSherlockAction | |
| db_task3.log_action( | |
| SQLSherlockAction( | |
| action_type="fix_cell", | |
| table=db_task3.primary_table, | |
| row_id=1, | |
| column="age", | |
| value=30, | |
| reason="z-score is 6.2, well above threshold of 5, mean=28.5, std=7.1", | |
| ) | |
| ) | |
| db_task3._validation_called = True | |
| cleaned = _apply_all_fixes(db_task3) | |
| score_with_reason = graders.grade( | |
| db_task3, cleaned, [], | |
| "bq_hard", True, | |
| ) | |
| assert score_with_reason > 0.0 | |
| # --------------------------------------------------------------------------- | |
| # Unknown task raises | |
| # --------------------------------------------------------------------------- | |
| class TestUnknownTask: | |
| def test_unknown_task_raises(self, db_task1): | |
| with pytest.raises(ValueError, match="Unknown task_id"): | |
| graders.grade( | |
| db=db_task1, | |
| cleaned_rows=_current(db_task1), | |
| removed_ids=[], | |
| task_id="task99_nonexistent", | |
| validation_was_called=False, | |
| ) | |
| # --------------------------------------------------------------------------- | |
| # False positive penalty | |
| # --------------------------------------------------------------------------- | |
| class TestFalsePositivePenalty: | |
| def test_no_fp_on_perfect_fix(self, db_task1): | |
| cleaned = _apply_all_fixes(db_task1) | |
| penalty = _false_positive_penalty( | |
| db_task1, cleaned, [], db_task1.pk_col, db_task1.primary_table | |
| ) | |
| assert penalty == 0.0 | |
| def test_fp_penalty_on_changed_clean_cell(self, db_task1): | |
| cleaned = _apply_all_fixes(db_task1) | |
| dirty_copy = copy.deepcopy(cleaned) | |
| # Modify a cell that is NOT in the issue registry | |
| issue_cells = {(i.row_id, i.column) for i in db_task1.issue_registry} | |
| for row in dirty_copy: | |
| rid = row.get(db_task1.pk_col) | |
| for col in row: | |
| if col in (db_task1.pk_col, "_source_format"): | |
| continue | |
| if (rid, col) not in issue_cells: | |
| row[col] = "TAMPERED" | |
| break | |
| else: | |
| continue | |
| break | |
| penalty = _false_positive_penalty( | |
| db_task1, dirty_copy, [], db_task1.pk_col, db_task1.primary_table | |
| ) | |
| assert penalty > 0.0 | |
| def test_fp_penalty_capped_at_020(self, db_task1): | |
| cleaned = _current(db_task1) | |
| # Tamper every non-issue cell | |
| issue_cells = {(i.row_id, i.column) for i in db_task1.issue_registry} | |
| tampered = copy.deepcopy(cleaned) | |
| for row in tampered: | |
| rid = row.get(db_task1.pk_col) | |
| for col in list(row.keys()): | |
| if col not in (db_task1.pk_col, "_source_format"): | |
| if (rid, col) not in issue_cells: | |
| row[col] = "BAD" | |
| penalty = _false_positive_penalty( | |
| db_task1, tampered, [], db_task1.pk_col, db_task1.primary_table | |
| ) | |
| assert penalty <= 0.35 | |