test: reach 100% coverage and enforce it in CI - #194
Merged
SemyonSinchenko merged 3 commits intoAug 20, 2026
Conversation
SemyonSinchenko
approved these changes
Aug 19, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Brings the test suite to 100% statement and branch coverage, and adds the tooling to keep it there.
mainThe small drop in totals comes from the
flatten.pyimport change described below.Tests added
Two new files:
tests/test_chispa.py— theChispaclass had no direct coverage: default-format fallback, passing aFormattingConfigthrough untouched, converting a legacy dataclass, and both outcomes ofassert_df_equality.tests/test_number_helpers.py—nan_safe_equality/nan_safe_approx_equality, including theisnanTypeErrorpath and the boundary where the difference exactly equals the precision.Extended:
test_row_comparer.py— rewritten as parametrized cases with named ids. Adds missing-row handling, nan-vs-non-nan, and nested list/tuple comparison at differing lengths.test_schema_comparer.py—print_schema_diffin both output formats plus its unknown-format error, empty schemas, and tree-diff colouring for differing leaf types, nullability, and map key types.test_dataframe_comparer.py—_contains_map_typeacross nesting shapes, the zero-precision fallbacks inassert_approx_df_equality, and custom format objects reaching the output.test_rows_comparer.py—assert_generic_rows_equalitywith default and custom formats.test_deprecated.py— the two deprecated*_ignore_nullablehelpers now assert theirDeprecationWarning.test_formats.py/test_formatting_config.py— error paths for non-dict input, a list where a colour string is expected, and an invalid format type.Assertions target observable behaviour — the actual ANSI codes in the error message, the specific tree line — rather than just "it raised".
Source changes
Kept to the minimum needed to measure coverage — no logic is touched.
chispa/flatten.py: the pyspark type imports moved out of theTYPE_CHECKINGblock to runtime imports, so the lines execute under coverage.# pragma: no branchmarkers on finalelifarms whose fall-through is impossible (flatten.pymap handling,_compare_datatypesmap dispatch), each with the reason inline.schema_comparer.pyis otherwise byte-identical tomain— the marker is a comment; the branch it silences is unreachable because the enclosingifhas already established the type is one of the three complex kinds, and the two preceding arms rule out the other two.No behaviour changes.
Tooling
pyproject.toml([tool.coverage.run]withbranch = true,[tool.coverage.report]withfail_under = 100), so every entry point that passes--covgets the same configuration.make testnow reportsterm-missing, so a shortfall names the lines.coverageCI job runningmake test. It pinspyspark-version: 4.0.2— the composite action defaults to 3.5.1, which no test job covers, and 4.0.2 is both exercised by thetestmatrix and shares its cache key with the Python 3.11 leg.The nine-leg
testmatrix keeps running plainpytest tests. A pyspark-version quirk should surface as a test failure on the leg that has it, not as nine red coverage checks.For reviewers
One xfail is intentional.
it_throws_when_duplicate_column_names_hide_a_mismatchis markedxfail(strict=True)and documents a real bug:assert_basic_rows_equalityzips fields positionally but looks them up by name, so when a DataFrame has duplicate column names every lookup returns the first match and genuine differences go unreported. The test is written to pass once that is fixed. The same pattern exists inassert_generic_rows_equalityand is not yet marked.Two further bugs found but deliberately left alone — both reachable from the public API, both invisible to coverage, both better as their own PR:
assert_df_equality/assert_approx_df_equalitymutate the caller'stransformslist. Passing the same list to two calls withignore_column_order=Trueleaves it holding two sort transforms.assert_basic_rows_equalityraisesAttributeError: 'NoneType' object has no attribute 'split'instead ofDataFramesNotEqualErrorwhen rows have different field counts, becausezip_longestyields aNonefield name that is then used as a key.Three tests call private helpers with a
Noneside on purpose.describe_compare_array_types,describe_compare_map_types, and the both-missing case indescribe_are_fields_shallow_equalpass arguments no public call path produces — the callers pair two same-kind types, andzip_longestnever yields twoNones. Those guards are therefore dead defensive code. They are covered here rather than deleted to keep this PR to tests; deleting them instead would remove ~16 statements and ~10 branches and reach 100% by subtraction. Happy to do that here if reviewers prefer.Verification
Locally: 252 passed, 1 xfailed, 100% coverage;
pre-commit run -aandmypy chispaclean. Note the local run is Python 3.14 / pyspark 4.0.0