tests: add coverage for deferred audit findings + isolate test_settings from ambient env#1513
Conversation
…gs from ambient env Two test-suite hardening changes, all in tests/, no source-code changes: ## 1. Coverage additions for 3 audit-deferred findings (6 test functions) - tests/integration/test_dependencies.py TestLoadAllShortCircuit (2 tests) Guards the ~53ms/populate-key perf fix from PR datajoint#1499 by spying on Dependencies.load during a repeat load_all_upstream/load_all_downstream call. Verified: gating the early-return at dependencies.py:266-267, :300-301 with 'if False and ...' makes both tests fail; restored. - tests/integration/test_gc.py TestDeleteSchemaPathPruning (2 tests) Covers the parent-directory pruning loop in gc.py delete_schema_path. Inserts two rows in separate pk-dirs, deletes one, runs collect(), asserts the orphan's pk-dir is pruned, the sibling's is preserved, and the store root is untouched. Verified: removing the pruning loop at gc.py:293-302 makes test_pruning_removes_empty_pk_directory fail; restored. - tests/integration/test_object.py TestStoragePathGeneration (2 tests) Covers build_object_path with schema_prefix='' (the documented pre-2.3.1 legacy layout — not previously tested). Asserts the path is {schema}/{table}/pk=v/field_token.dat (no leading empty segment). Verified: removing the 'if prefix:' guard at storage.py:267-269 makes both tests fail; restored. ## 2. Env-isolation fixture for tests/unit/test_settings.py Four tests in TestStoreEnv and TestBackendConfiguration read DJ_HOST/ DJ_USER/DJ_PASS/DJ_PORT/DJ_BACKEND via pydantic-settings and fail if any of those vars is exported in the developer's shell — even when the test's own contract is 'ignore config file' or 'auto-detect port'. CI runs in a clean env so this only bites devs who source a local .env, but the tests' contract shouldn't depend on ambient env cleanliness. Adds a class-level @pytest.fixture(autouse=True) that monkeypatch.delenv's the 5 DJ_* vars at the top of each test in both classes. Individual tests that need to set DJ_* explicitly still work — the fixture runs first, so their setenv wins. Verified: pytest tests/unit/test_settings.py::TestStoreEnv tests/unit/test_settings.py::TestBackendConfiguration → 19/19 pass locally with the fixture.
|
Reviewed — verified all four files against the source; the tests are correct and well-targeted (test-only, no source changes). Each new test genuinely fails if its guarded behavior is regressed:
One suggestion — cite method names, not line numbers. The Minor: Substance looks good. |
dimitri-yatsenko
left a comment
There was a problem hiding this comment.
Approving — the tests are correct and well-targeted (re-verified against current origin/master @55f3beef).
Correction to my earlier comment: I'd checked the line refs against a stale local tree and got the drift backwards. Against current master:
gc.py:295(thewhile parent and parent != root …boundary) and the293-302range — exact.storage.py:267-269(if prefix:guard) — exact.dependencies.py— this is the drifted one: theif self._loaded and known_schemas <= self._loaded_schemas: returnshort-circuits are at 258 (downstream) / 292 (upstream) now, not the cited266-267/300-301(~8 lines off, shifted by the #1508 MultiDiGraph merge).
Which only reinforces the earlier suggestion: cite the method (the pruning loop in delete_schema_path, the short-circuit in load_all_downstream/load_all_upstream) rather than file:line — the numbers demonstrably rot; they even fooled me via a stale checkout. Non-blocking — fine as a quick follow-up or to leave.
Substance is solid. ✅
Two test-suite hardening changes, all in
tests/, no source-code changes.1. Coverage for 3 audit-deferred findings (6 test functions)
Adds targeted regression guards for behaviors that had no direct coverage. Each test's docstring cites the underlying code and describes what regression it catches.
tests/integration/test_dependencies.py::TestLoadAllShortCircuit(2 tests) — guards the ~53 ms/populate-key perf fix from #1499. Warm up the graph, then spy onDependencies.loadduring a second identicalload_all_upstream/load_all_downstreamcall; assertcall_count == 0. Regressing the early-return atdependencies.py:266-267and:300-301(e.g.if False and ...) makes both tests fail.tests/integration/test_gc.py::TestDeleteSchemaPathPruning(2 tests) — covers the parent-directory pruning loop indelete_schema_path. Inserts two rows into separate PK directories, deletes one, runscollect(dry_run=False), then asserts: the orphan's file is gone, its now-empty PK directory is pruned, the sibling PK directory is preserved, and the store root itself survives. Regressing thewhile ... rmdirloop atgc.py:293-302makestest_pruning_removes_empty_pk_directoryfail.tests/integration/test_object.py::TestStoragePathGeneration(2 tests) — coversbuild_object_pathwithschema_prefix=""(the documented pre-2.3.1 legacy layout — no test previously exercised it). Asserts the path is{schema}/{table}/pk=v/field_token.datwith no leading empty segment. Regressing theif prefix:guard atstorage.py:267-269(always-prepend) makes both tests fail.All 6 tests confirm existing behavior; no source-code changes needed. Each was verified in place: gate/remove the source condition → tests fail; restore → tests pass.
2. Env-isolation fixture for
tests/unit/test_settings.pyFour tests in
TestStoreEnv(test_ignore_config_file_skips_secrets,test_ignore_config_file_default_loads_both) andTestBackendConfiguration(test_backend_postgresql,test_backend_env_var) construct pydantic-settings objects that readDJ_HOST/DJ_USER/DJ_PASS/DJ_PORT/DJ_BACKENDfrom the process env. A developer with any of those vars exported (e.g. from a local.env) sees these tests fail even though their own contract is "ignore config file" or "auto-detect port" — because a leaked ambient value overrides the default under test.Reproduces cleanly:
DJ_HOST=localhost DJ_USER=root DJ_PASS=x pytest tests/unit/test_settings.py::TestStoreEnv::test_ignore_config_file_skips_secrets→ FAILenv -i pytest tests/unit/test_settings.py::TestStoreEnv::test_ignore_config_file_skips_secrets→ PASSCI runs in a clean env, so this isn't a CI regression — but it bites devs who source project
.envfiles.Adds a class-level
@pytest.fixture(autouse=True)to bothTestStoreEnvandTestBackendConfigurationthatmonkeypatch.delenv's the fiveDJ_*vars at the top of each test. Tests that explicitlymonkeypatch.setenv(...)still work — the fixture runs first, so their setenv wins. Also protects other tests in these classes from the same latent issue (there are more than just the 4 flagged).Verified locally:
pytest tests/unit/test_settings.py::TestStoreEnv tests/unit/test_settings.py::TestBackendConfiguration→ 19/19 pass with the fixture.Follow-up to the v2.3.1 audit (post-#1503, #1504, #1505, #1506, #1510, #1511). Docs-issue traceable: the 3 coverage additions close deferred audit findings #7, #8, #19; the env-isolation fix surfaced during a follow-up local test run.