Spaces:
Running
Running
Download tests/test_term_selection.py from TransLegal/grading-answers: direct link, hf CLI and curl.
- Browser
- Download file 5.1 kB
-
https://huggingface.co/spaces/TransLegal/grading-answers/resolve/main/tests/test_term_selection.py
- Command line
-
hf download hf://spaces/TransLegal/grading-answers/tests/test_term_selection.py
-
curl -L -o test_term_selection.py https://huggingface.co/spaces/TransLegal/grading-answers/resolve/main/tests/test_term_selection.py
5.1 kB
| """The term cap must be even across concept classes, reproducible and nested.""" | |
| import term_selection as ts | |
| CONCEPTS = [f"Concept {i}" for i in range(8)] | |
| def _pool(indices: list[int], concepts: list[str] | None = None) -> dict[str, str]: | |
| """One term per (concept, index). Even indices only, so a later pool can | |
| interleave odd ones and expose a name-ordered implementation.""" | |
| concepts = concepts or CONCEPTS | |
| return { | |
| f"term_{c}_{i:03d}": concept | |
| for c, concept in enumerate(concepts) | |
| for i in indices | |
| } | |
| BASE = _pool(list(range(0, 40, 2))) # 8 concepts x 20 terms | |
| def test_cap_divides_evenly_across_concepts() -> None: | |
| selection = ts.select_terms_by_concept(BASE, max_terms=40) | |
| assert len(selection.terms) == 40 | |
| assert set(selection.per_concept.values()) == {5} | |
| def test_cap_that_does_not_divide_evenly_differs_by_one() -> None: | |
| selection = ts.select_terms_by_concept(BASE, max_terms=41) | |
| counts = selection.per_concept | |
| assert len(selection.terms) == 41 | |
| assert sum(counts.values()) == 41 | |
| assert max(counts.values()) - min(counts.values()) == 1 | |
| def test_a_small_concept_redistributes_its_slots() -> None: | |
| pool = dict(BASE) | |
| pool.update(_pool([0, 2], concepts=["Concept tiny"])) | |
| selection = ts.select_terms_by_concept(pool, max_terms=40) | |
| assert len(selection.terms) == 40 | |
| assert selection.per_concept["Concept tiny"] == 2 | |
| assert sum(selection.per_concept.values()) == 40 | |
| def test_shortfall_names_the_concept_that_ran_out() -> None: | |
| pool = dict(BASE) | |
| pool.update(_pool([0, 2], concepts=["Concept tiny"])) | |
| selection = ts.select_terms_by_concept(pool, max_terms=40) | |
| assert selection.shortfall_concepts() == ["Concept tiny"] | |
| def test_a_cap_larger_than_the_pool_takes_everything_and_names_nobody() -> None: | |
| selection = ts.select_terms_by_concept(BASE, max_terms=1000) | |
| assert len(selection.terms) == len(BASE) | |
| assert selection.shortfall_concepts() == [] | |
| def test_shortfall_is_empty_when_cap_exactly_matches_the_pool() -> None: | |
| """A cap equal to the pool size still took everything -- same as a cap | |
| larger than the pool, or no cap at all -- so nothing was left out.""" | |
| pool = {f"small_{i:03d}": "Small" for i in range(20)} | |
| pool.update({f"big_{i:03d}": "Big" for i in range(24)}) | |
| selection = ts.select_terms_by_concept(pool, max_terms=len(pool)) | |
| assert len(selection.terms) == len(pool) | |
| assert selection.shortfall_concepts() == [] | |
| def test_selection_is_nested_across_cap_sizes() -> None: | |
| """This is what keeps grader verdicts valid when a batch is widened.""" | |
| larger = ts.select_terms_by_concept(BASE, max_terms=80).terms | |
| for smaller_n in (8, 16, 40, 64): | |
| smaller = ts.select_terms_by_concept(BASE, max_terms=smaller_n).terms | |
| assert larger[:smaller_n] == smaller | |
| def test_selection_is_nested_with_a_non_multiple_cap_and_an_undersized_concept() -> ( | |
| None | |
| ): | |
| """Slack redistribution -- what a small concept's leftover slots do -- is | |
| where a refactor is most likely to break nesting, and that arithmetic is | |
| only reachable with a cap that doesn't divide the concept count, over a | |
| pool with an undersized concept.""" | |
| pool = dict(BASE) | |
| pool.update(_pool([0, 2], concepts=["Concept tiny"])) | |
| larger = ts.select_terms_by_concept(pool, max_terms=49).terms | |
| smaller = ts.select_terms_by_concept(pool, max_terms=41).terms | |
| assert larger[:41] == smaller | |
| def test_result_does_not_depend_on_insertion_order() -> None: | |
| shuffled = dict(reversed(list(BASE.items()))) | |
| assert ts.select_terms_by_concept(BASE, max_terms=40) == ts.select_terms_by_concept( | |
| shuffled, max_terms=40 | |
| ) | |
| def test_adding_terms_keeps_most_of_the_existing_prefix() -> None: | |
| """Terms are ranked by hash of the term, not by position or name, so new | |
| terms displace only their share. A name-ordered implementation would put | |
| every odd index near the front and move 16 of the 40.""" | |
| before = set(ts.select_terms_by_concept(BASE, max_terms=40).terms) | |
| grown = dict(BASE) | |
| grown.update(_pool([1, 3])) | |
| after = set(ts.select_terms_by_concept(grown, max_terms=40).terms) | |
| assert len(before & after) >= 32 | |
| def test_no_cap_selects_every_term() -> None: | |
| selection = ts.select_terms_by_concept(BASE) | |
| assert len(selection.terms) == len(BASE) | |
| assert selection.requested is None | |
| assert selection.shortfall_concepts() == [] | |
| def test_a_cap_of_zero_selects_nothing() -> None: | |
| selection = ts.select_terms_by_concept(BASE, max_terms=0) | |
| assert selection.terms == () | |
| assert selection.per_concept == {} | |
| def test_empty_pool() -> None: | |
| selection = ts.select_terms_by_concept({}, max_terms=10) | |
| assert selection.terms == () | |
| assert selection.available == {} | |
| assert selection.shortfall_concepts() == [] | |
| def test_available_counts_the_whole_pool_not_the_sample() -> None: | |
| selection = ts.select_terms_by_concept(BASE, max_terms=8) | |
| assert set(selection.available.values()) == {20} | |
| assert set(selection.per_concept.values()) == {1} | |