Skip to content

Add unit tests closing coverage gaps in pure-logic modules - #320

Merged
willronchetti merged 6 commits into
masterfrom
fm/snov-test-cov-h4
Jul 7, 2026
Merged

Add unit tests closing coverage gaps in pure-logic modules#320
willronchetti merged 6 commits into
masterfrom
fm/snov-test-cov-h4

Conversation

@willronchetti

@willronchetti willronchetti commented Jul 3, 2026

Copy link
Copy Markdown
Member

Summary

Adds 138 direct unit tests across 8 new files (initial audit), plus a follow-up
round of ~50 more tests across 4 new files + 2 extended files
closing the gaps the
initial audit had explicitly deferred. No production code was changed; the new tests
run without ElasticSearch or Postgres. Also folds in a known CI-flakiness fix
(flaky-rerun decorators in test_indexing.py) and repairs a version/changelog mismatch
introduced by the master merge.

What I audited (round 1)

I mapped the 30 existing test_*.py files against the 60 top-level snovault/*.py
modules, ran a targeted read of the pure-logic candidates, and checked each for
(a) testability without live services, (b) correctness-sensitivity, and (c) current
coverage. I confirmed gaps by grepping the whole test tree for each target function
and by exercising actual runtime behavior before pinning it in assertions.

Gaps found and closed (round 1 — 8 new files, 138 tests)

New test file Module under test Why it mattered
test_typedsheets.py typedsheets.py Zero coverage. Deterministic CSV/insert value casting used at load time. Pins the null/empty-string asymmetry, right-to-left cast ordering, array/object parsing, and row generators.
test_authorization.py authorization.py Security-critical groupfinder had only indirect coverage. Covers all synthetic remoteuser.* results, user + access-key lookup branches, _create_principals, and is_admin_request.
test_etag.py etag.py Caching-correctness decorators with no test. Covers the HTTP 304 short-circuit, ETag setting, private/no-cache headers, and per-principal ETag distinctness.
test_schema_formats.py schema_formats.py is_uuid / is_accession identity validators, untested; documents the known re.match laxness.
test_server_defaults_misc.py server_defaults_misc.py All 5 add_last_modified branches covered via mocked get_userid/get_now.
test_predicates.py predicates.py View predicates (route matching) untested; covers int→tuple normalization, text/phash, and __call__ matching.
test_typeinfo.py typeinfo.extract_schema_links Pure recursive schema-walk generator; empty/top-level/nested/array linkTo cases plus the KeyError edge.
test_util_helpers.py util.py (pure helpers) deduplicate_list, ensurelist, gunzip_content, simple_path_ids, recursively_process_field, merge_calculated_into_properties edge branches, and more.

Round 2 — follow-up gaps implemented (4 new files, 2 extended)

These are the items round 1 listed under "deliberately left as follow-up", now done:

Test file Module under test What it locks
test_local_roles.py (new) local_roles.py Security-sensitive principal expansion: local_principals (lineage accumulation, callable roles, string-role normalization, __ac_local_roles_block__ stopping ancestors but keeping the blocking node's own roles, identity-return when nothing matches), merged_local_principals (reverse role→principal mapping), and LocalRolesAuthorizationPolicy.permits / principals_allowed_by_permission end-to-end against a real ACL.
test_calculated_registry.py (new) calculated.py CalculatedProperty schema default guard (ValueError), schema copy + calculatedProperty: True marking, condition gating, attr dispatch to context methods, fn-arg resolution from the namespace; ItemNamespace per-fn memoization and string-name resolution; CalculatedProperties.props_for MRO ordering (subclass overrides base), instance-or-class input, and category isolation.
test_json_renderer.py (new) json_renderer.py UUID/set/frozenset/datetime adapters, dumps() working outside any request (the indexer calls it that way), JSONResult.serializerBinaryFromJSON UTF-8 chunk encoding.
test_manager_lru_cache.py (new) cache.py ManagerLRUCache degrading to a no-op with no threadlocal stack, capacity resolution from registry settings vs. default, and the afterCompletion transaction flush (stale-entry protection for retried transactions).
test_edw_hash.py (extended) edw_hash.py Previously hash-only golden values; adds verify() round-trip, bytes/str secret equivalence, and the exact "Password too long" boundary (454-char max secret, computed from the salt layout).

CI flakiness fix folded in (test-only, known issue)

  • Re-enabled @pytest.mark.flaky(max_runs=3, rerun_filter=delay_rerun) on
    test_aggregated_items (was commented out).
  • Added the same decorator to test_indexer_namespacing and
    test_indexer_queue_adds_telemetry_id, which use the same SQS/ES polling pattern as
    their already-protected neighbors but never had it.
  • Documented the convention in AGENTS.md.

Post-merge repair

The master merge (6b7ef67c9) resolved pyproject.toml to 11.31.7 but left this PR's
CHANGELOG entry headed 11.31.1, which failed the changelog checker in both the
Static Checks and UNIT CI jobs. Fixed the heading, then bumped to 11.31.8 for the new
work (checked open PRs for collisions: #321 is at 11.30.8, #308 at 11.24.0 — none).

Notes / observations (no action taken)

Testing

  • All new tests pass locally.
  • Full non-ES suite: pytest -m "not es and not indexing and not performance"
    790 passed, 28 skipped, with the single pre-existing environment-specific failure
    (test_postgresql_fixture.py::test_snovault_db_test_port), which fails identically on
    a clean baseline.
  • test_indexing.py (ES-marked, not runnable locally) collects cleanly with the new
    decorators; delay_rerun was already imported in that file.

🤖 Generated with Claude Code

willronchetti and others added 3 commits July 2, 2026 22:44
Adds 138 direct unit tests across 8 new files, targeting previously
untested (or only indirectly-tested) pure-logic, correctness-sensitive
modules. No production code changed; all tests run without ES/Postgres.

- test_typedsheets.py: value casting/parsing helpers (cast, parse_*,
  convert, cast_row_values, remove_nulls) incl. null/empty asymmetry.
- test_authorization.py: groupfinder synthetic + user/access-key lookup
  branches, _create_principals, is_admin_request (security-sensitive).
- test_etag.py: ETag/conditional-GET decorators incl. 304 short-circuit
  and per-principal ETag distinctness.
- test_schema_formats.py: is_uuid / is_accession regex validators.
- test_server_defaults_misc.py: all add_last_modified branches.
- test_predicates.py: SubpathSegments / AdditionalPermission predicates.
- test_typeinfo.py: extract_schema_links recursive generator.
- test_util_helpers.py: deduplicate_list, ensurelist, gunzip_content,
  convert_integer_to_comma_string, simple_path_ids,
  recursively_process_field, resolve_file_path, and
  merge_calculated_into_properties edge branches.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Patch bump for the test-only coverage additions in the previous commit.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Aligns this PR's version with the captain's merge-order across the open
snovault PRs (#317->11.30.4, #321->11.30.5, #318->11.30.6, #320->11.30.7,
#319->11.31.0).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
willronchetti added a commit that referenced this pull request Jul 6, 2026
PR #318 already merged at 11.30.5; PR #320 claims 11.30.7. Next free.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
willronchetti and others added 3 commits July 6, 2026 21:00
…nd flaky marks

- Fix the CHANGELOG entry heading left as 11.31.1 by the master merge while
  pyproject.toml said 11.31.7 -- this mismatch was failing both the Static
  Checks and UNIT CI jobs on the changelog checker.
- Add unit tests for the gaps PR #320's own audit listed as deliberate
  follow-ups: local_roles (principal expansion, role->principal reverse
  mapping, LocalRolesAuthorizationPolicy), the calculated-property registry
  (schema default guard, copy+mark, condition/attr dispatch, MRO-ordered
  props_for, category isolation, ItemNamespace memoization), json_renderer
  adapters (UUID/set/frozenset/datetime, request-less dumps, BinaryFromJSON,
  JSONResult.serializer), ManagerLRUCache (no-op outside a request, settings
  capacity, afterCompletion flush), and the untested EDWHash branches
  (verify round-trip, bytes/str equivalence, password-too-long boundary).
- Re-enable @pytest.mark.flaky(max_runs=3, rerun_filter=delay_rerun) on
  test_aggregated_items and add it to test_indexer_namespacing and
  test_indexer_queue_adds_telemetry_id, which use the same SQS/ES polling
  pattern as their already-protected neighbors (known CI flakiness fix).
- Bump version to 11.31.8 (checked open PRs: #321 is at 11.30.8, #308 at
  11.24.0 -- no collision) and add a changelog entry.
- Record the flaky-mark convention and the ItemNamespace.__getattr__
  request=None recursion sharp edge in AGENTS.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@willronchetti
willronchetti merged commit 3e81939 into master Jul 7, 2026
2 of 3 checks passed
willronchetti added a commit that referenced this pull request Jul 7, 2026
#321)

* Fix self-registration privilege-escalation in create_unauthorized_user

POST /create-unauthorized-user ran with request.remote_user = 'EMBED'
(restricted_fields write permission) while validating/creating the new
User, but took the caller-submitted body almost as-is - letting a
caller self-assign privileged fields (e.g. "groups": ["admin"]) on
their own new account.

Whitelist (not blocklist) the fields the endpoint accepts, since
snovault is consumed by apps with different User schemas and a
hardcoded blocklist of "dangerous" field names only protects against
names its author thought to enumerate. pending_lab is a documented
exception since it is only a self-declared request requiring later
admin promotion to lab, not an immediate grant of access.

Also fixes a TestRoot ACL drift (missing EMBED -> restricted_fields,
present in production SnovaultRoot) needed to exercise this endpoint
in tests.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

* Bump version to 11.30.8 to avoid collision with PR #318 (11.30.5)

PR #318 already merged at 11.30.5; PR #320 claims 11.30.7. Next free.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant