test: negative API-surface and configuration-interaction suites (Phase 18/19) - #483
Merged
Merged
Conversation
Phase 19 (negative API testing) and Phase 18 (configuration interaction testing) for the production-readiness program. tests/test_negative_api_surface.py feeds deliberately invalid input to the public entry points in ``freshdata.__all__`` (clean, clean_text, validate, validate_fields, suggest_plan, plan, apply_plan, clean_csv, clean_excel, profile, infer_roles, explain_clean, compile_context, run_suite). Every case asserts the exact exception class, a message that names the offending parameter and the expected form, that the caller's frame is untouched (digest over values, dtypes, index name and .attrs — assert_frame_equal ignores the last two), and that the same bad input raises identically twice. tests/test_config_interactions.py exercises the high-risk combinations rather than each option alone: domain + semantic_mode + context, profile + policy + memory, target_column + semantic_mode, id_columns + clean_text, strict + fallback_policy="error", streaming + semantic, engine="polars" + domain, contract + a non-pandas engine, and preserve_columns + a context-declared protected column. Tests that pin weaker-than-documented behaviour carry an S2/S3 comment naming the gap; they are regression anchors, not endorsements. The notable ones: domain= short-circuits the execution dispatch so engine=, output_format=, engine_config= and fallback_policy="error" are silently dropped; fd.plan accepts any engine name and reports a confident verdict for it; an unknown semantic_type in semantic_context is adopted at confidence 1.0 without a warning, even under strict=True; five entry points leak an internal AttributeError instead of a TypeError for non-frame input. No production code is changed.
Contributor
|
Important
This repository does not receive automatic reviews because it has fewer than 10 stars. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Advanced Run ID: Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
What
Two new test suites for the production-readiness program: Phase 19 — negative
API testing and Phase 18 — configuration interaction testing. No
production code changes.
tests/test_negative_api_surface.py(160 tests)Deliberately invalid input to the public entry points in
freshdata.__all__:clean,clean_text,validate,validate_fields,suggest_plan,plan,apply_plan,clean_csv,clean_excel,profile,infer_roles,explain_clean,compile_context,run_suite.Every case asserts four things, not just "something raised":
type(exc) is ..., never a bareException);the bar being the existing
impute must be one of (None, 'auto', 'mean', 'median', 'mode', 'missforest'), got 'knn';values, column labels, dtypes, the index and its name, and
.attrs—assert_frame_equalignores the last two, so it cannot catch a step thatstashed metadata on the input;
message twice in a row.
Covered: wrong frame type (list / dict /
None/ ndarray / scalar), duplicateand missing columns, out-of-range and mis-ordered thresholds, every enum-valued
CleanConfigoption, unknown domains and engines, contradictory configuration,corrupted policy / memory / suite / profile files, and unsupported
engine + operation combinations.
tests/test_config_interactions.py(28 tests)The high-risk combinations, not each option in isolation:
domain+semantic_mode+context;profile+policy+memory;target_column+semantic_mode;id_columns+clean_text;strict=True+fallback_policy="error";streaming+semantic;engine="polars"+domain;contract+ a non-pandas engine;preserve_columns+ acontext-declared protected column.
Findings pinned by these tests
Tests that pin weaker-than-documented behaviour carry an
S2:/S3:docstringnaming the gap. They are regression anchors, not endorsements — each is
expected to be rewritten when the gap is closed.
domain=short-circuits the execution dispatch.engine=,output_format=,engine_config=andfallback_policy="error"are silently discarded: the run executes on pandas, returns pandas, records no fallback event, and the documented "strict out-of-core guarantee" never fires. Every other pandas-only feature (contract=/memory=/profile=/context=) raisesTypeErroron a native engine instead.fd.plan(df, engine=...)never validates the backend name.fd.clean(engine="sqlite")raises;fd.planechoes any string back asplan.backendwith a plausiblefallback_reason, so a typo yields a confident, wrong execution verdict from the very function documented to answer that question up front.semantic_typeinsemantic_context["columns"]is adopted verbatim at confidence 1.0 — no warning, no report entry, not even understrict=True.semantic_backendswarns and raisesPolicyErrorunder strict for the same class of typo, and bothFieldSpecandconfig_for_fieldwarn.suggest_plan,plan,clean_text,validate_fieldsandcompile_contextleakAttributeError: 'list' object has no attribute 'columns'for non-frame input, whereclean/profile/infer_roles/explain_clean/validate/run_suiteraiseTypeErrornaming the accepted types.missing_threshold_low="big") surfaces'<' not supported between instances of 'float' and 'str'— neither parameter nor expected type named..jsonpolicy or.fdmemmemory loads as an empty object. A truncated or foreign memory file silently replays nothing and reports no problem.nameraises a bareKeyError: 'name'.run_suite(df, <not a suite>)leaksAttributeError: ... has no attribute 'to_contract';fd.validate(suite=...), the other front door onto the same engine, type-checks it properly.fd.clean_csv(df)— a natural slip — fails withargument of type 'method' is not iterablefrom the leading-zero pre-scan.fd.validate_fieldsrange-checks neitherrare_thresholdnoroutlier_fence;outlier_fence=-3.0silently classifies every value as astatistical_outlier.clean_text(config={...})/validate_fields(policy={...})are not type-checked and leakAttributeError.semantic_context["columns"]is dropped in silence, while the same unresolved reference incontext=raisesPolicyErrorunderstrict=True.fd.clean_texthas no id-column channel at all —id_columnsnever reaches it, andfield_typesis the only protection.string_caseis a layer-1 repair that ignoresid_columns/preserve_columns/sensitive_columns. Only a context policy stops it. Consistent with each option's narrow documented meaning, but the obvious way to say "leave my identifiers alone" does not work.Verified as not defects
preserve_columnsand a context-declared protected column are unioned,not overridden, and the policy protection additionally lowers to an immutable
semantic hint.
target_column,id_columnsandpreserve_columnseach stop the semanticlayer —
sensitive_columnsdeliberately does not (it redacts report text).StreamingCleanerruns the semantic layer, honours the column protections andreports an unknown backend exactly as
fd.cleandoes;semantic_modeis notdropped.
profile=adopts its embedded memory only when nomemory=was supplied.domain+semantic_mode+contextall stay active in one run.clean/profileaccept duplicate column labels becausecolumn_namesdeduplicates them as step 1 — not an inconsistency with the entry points that
reject them.
context=is refused before it is compiled, so the erroris the same whether the rule text resolves or not.
Verification
pytest -m "not online and not large" -p no:randomly— full suite green.ruff check .— clean over the whole repo.mypyskipped (numpy 2.5 stubs); CI covers it.