Skip to content

fix: validation guards must survive python -O; add the missing test module - #12

Open
JohnRDOrazio wants to merge 2 commits into
mainfrom
fix/validation-guards-not-asserts
Open

fix: validation guards must survive python -O; add the missing test module#12
JohnRDOrazio wants to merge 2 commits into
mainfrom
fix/validation-guards-not-asserts

Conversation

@JohnRDOrazio

@JohnRDOrazio JohnRDOrazio commented Aug 8, 2026

Copy link
Copy Markdown
Member

Closes #11.

The change

All three guards were asserts. They move into a validate(entries, known_types) function raising ValueError, matching the shape coecdr already uses. main() catches it and exits through sys.exit, so a data problem reads as a diagnostic rather than a traceback:

generate_seed.py: unknown circumscription types: ['ctype:bogus']; malformed church_sui_iuris references: ['esi:']

Problems are now collected and reported together. Before, repairing seed data cost one run per error.

Verified, not asserted

Re-running the exact demonstration from #11 — two deliberately invalid values seeded, generator run three ways:

exit seed on disk
python3 -O 1 unchanged
python3 -OO 1 unchanged
python3 1 unchanged

Previously the first row was exit=0 with Wrote 2935 circumscriptions and a corrupted seed. A valid run still regenerates data/circumscriptions.json byte-identical.

The missing test module

This repository had no tests, which is precisely why the -O behaviour went unnoticed — crpdr ships test_generate_seed.py and coecdr ships test_generate_registry.py. Thirteen tests in scripts/test_generate_seed.py:

python3 -m unittest discover -s scripts -v     # 13 tests, OK
  • Validate — accepts the committed seed and null type; rejects duplicate ids, unknown ctype: values, and every malformed esi: form (esi:, latin, esi:Latin, esi:-latin, esi:latin-, ""); accepts well-formed references CESIDR mints but the seed does not yet use; and reports three simultaneous problems in one message.
  • SurvivesOptimizedMode — the regression guard proper. Runs validate in a subprocess under -O and -OO and requires it to still raise. Reintroducing assert here fails the suite.
  • Slugify — pins the strip rule, including the Italian arcidiocesi case fixed in fix: strip the Italian archdiocesan type word (58 slugs) #2, which had no test. Also pins that Archdiocese for the Military Services is not stripped, since only the leading styled form is generic.

Not included

The companion conversion in cesidr is tracked separately at CatholicOS/cesidr#1; its failure mode differs (silent data loss rather than bad references) and it needs its own fix.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes

    • Improved validation of generated entries, including duplicate identifiers, unknown types, and malformed references.
    • Added clear, aggregated error messages when validation fails.
    • Ensured validation remains reliable when Python optimization is enabled.
  • Tests

    • Added automated coverage for validation, reference formats, error handling, and slug generation.
  • Documentation

    • Updated repository documentation with validation details and test instructions.

Closes #11.

All three guards in generate_seed.py were `assert`s, which Python strips
under -O. Running the generator with that flag skipped validation entirely:
it exited 0, printed its usual success line, and wrote a seed carrying
unresolvable cross-references.

The three checks move into a `validate(entries, known_types)` function that
raises ValueError, following the shape coecdr already uses for its own
generator. main() catches it and exits via sys.exit with a one-line message,
so a data problem reads as a diagnostic rather than a traceback.

Every problem is now collected and reported together. Repairing seed data
took one run per error before; a run that finds a duplicate id, an unknown
ctype and a malformed esi: reference now reports all three at once.

Adds scripts/test_generate_seed.py, matching crpdr's and coecdr's test
modules — this repository had none, which is why the -O behaviour went
unnoticed. Thirteen tests: validate accepts the committed seed and null
types, rejects duplicate ids, unknown ctype: values and every malformed
esi: form including the bare prefix, and reports multiple problems together.

The SurvivesOptimizedMode class is the regression guard proper: it runs
validate in a subprocess under -O and -OO and requires it to still raise, so
reintroducing `assert` here fails the suite.

Also pins slugify, including the Italian arcidiocesi case fixed in #2, which
had no test.

Verified: exit 1 under -O, -OO and no flags with the seed left byte-identical
on disk; a valid run still regenerates data/circumscriptions.json unchanged.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@JohnRDOrazio, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 54 minutes

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 9c2f592f-f90d-4e8f-b3c5-6689271fb731

📥 Commits

Reviewing files that changed from the base of the PR and between 7558a4d and fb86dac.

📒 Files selected for processing (1)
  • scripts/test_generate_seed.py
📝 Walkthrough

Walkthrough

The seed generator now validates duplicate IDs, circumscription types, and church_sui_iuris references with collected errors before writing output. A unittest module covers validation, optimized execution, and slugification. The README documents the generator validation and test command.

Changes

Seed validation hardening

Layer / File(s) Summary
Centralized validation and failure handling
scripts/generate_seed.py, README.md
generate_seed.py adds reusable church-reference validation, collects duplicate-ID, unknown-type, and malformed-reference errors, and reports failures before writing. The README documents this behavior and the new test command.
Validation and slugification regression coverage
scripts/test_generate_seed.py
Tests cover valid and invalid seed entries, aggregated errors, normal and optimized execution, and Italian and English slugification cases.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant Generator
  participant Validator
  participant Output
  Generator->>Validator: validate(entries, known_types)
  Validator-->>Generator: return or collected ValueError
  Generator->>Output: write validated entries
Loading

Possibly related PRs

  • CatholicOS/cecdr#8: Adds related type-reference validation in generate_seed.py; this PR extends the validation approach with centralized error aggregation and tests.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies the validation fix and the addition of the missing test module.
Linked Issues check ✅ Passed The changes satisfy issue [#11] by replacing optimized-away assertions, aggregating validation errors, preventing invalid writes, and adding regression tests.
Out of Scope Changes check ✅ Passed The README update and slugification tests remain related to documenting and testing the updated generator module.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/validation-guards-not-asserts

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@JohnRDOrazio

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 8, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/test_generate_seed.py`:
- Around line 96-116: Extend the optimized-mode tests around run_validate and
main() to exercise invalid church_sui_iuris, duplicate-ID, and unknown-type
inputs. For each case, assert a non-zero exit under -O and -OO where applicable,
and verify data/circumscriptions.json remains unchanged or absent, covering
main()’s no-write behavior rather than only validate().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 001d1828-d909-46ff-b70f-9ad36db640cb

📥 Commits

Reviewing files that changed from the base of the PR and between 3ffa357 and 7558a4d.

📒 Files selected for processing (3)
  • README.md
  • scripts/generate_seed.py
  • scripts/test_generate_seed.py

Comment thread scripts/test_generate_seed.py
The suite drove validate() only, so it pinned detection but not the thing
that actually went wrong in #11: the generator writing a corrupted seed and
exiting 0. Mutating main() to detect the problem and write anyway left the
previous suite fully green.

Adds MainRefusesToWrite, which runs main() as a subprocess against a
synthetic source in a temporary repo and asserts the output file is never
created. Covers duplicate ids (two same-named sees with no province, which
the generator cannot qualify) and unknown ctype: values (injected through
MANUAL), each under no flags, -O and -OO, plus a sound-input case that must
still write, and a case proving an existing seed is left byte-intact when a
later run fails.

church_sui_iuris has no main()-level counterpart: the value is a literal in
main() that no source input or MANUAL override can reach, so it is
unreachable end-to-end by construction. Noted in the test file and left
covered at the validate() level.

Also extends the optimized-mode test from one guard to all three, so
reintroducing `assert` for any single check fails the suite rather than only
for the church_sui_iuris one.

17 tests. Verified by mutation: reverting the guards to asserts fails 23
subtests, and making main() write anyway fails 7 — the latter passing
cleanly before this commit.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@JohnRDOrazio

Copy link
Copy Markdown
Member Author

Verified. Both parts hold, and the second was a real hole — fixed in fb86dac.

Fixed — main()'s no-write behaviour was untested

This is the sharper point. The suite drove validate() only, so it pinned detection but not the thing that actually went wrong in #11: exiting 0 and writing a corrupted seed.

Demonstrated by mutation — patching main() to detect the problem and write anyway:

suite result
as committed in this PR OK ← the bug slips through
after this commit FAILED (7 failures)

I had verified the no-write behaviour by hand and then not committed it as a test, which is the same ad-hoc-and-discard habit issue #11 criticises. Now MainRefusesToWrite runs main() as a subprocess against a synthetic source in a temporary repo and asserts the output file is never created:

  • duplicate ids — two same-named sees with no province, which the generator cannot qualify
  • unknown ctype: — injected through MANUAL
  • each under no flags, -O and -OO
  • a sound-input case that must still write entry_count: 2
  • a case proving an existing seed is left byte-intact when a later run fails

Fixed — optimized-mode test covered one guard, not three

Also correct. test_guards_still_raise_under_dash_O exercised only the malformed esi: case, so reintroducing assert for just the duplicate-id or ctype: check would have passed. Now all three, each under -O and -OO.

Partially skipped — main()-level coverage for church_sui_iuris

Not reachable, and not for want of trying. The value is a literal in main(); no source input and no MANUAL override can influence it, so there is no end-to-end path to an invalid value without editing the module. Covering it at main() level would mean testing a mutation rather than the program. It stays covered at the validate() level, and the test file records why rather than leaving the asymmetry looking accidental.

This is the same reachability point from the earlier review round: the malformed-esi: guard is latent by design.

Validation

17 tests, green. Mutation-checked both ways: reverting the guards to assert fails 23 subtests; making main() write anyway fails 7.

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.

Validation guards are asserts: stripped under python -O, generator writes unvalidated output

1 participant