Skip to content

refactor(testing): replace the pre-state auto-inject with field defaults#852

Merged
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/remove-pre-auto-inject
Jun 5, 2026
Merged

refactor(testing): replace the pre-state auto-inject with field defaults#852
tcoratger merged 1 commit into
leanEthereum:mainfrom
tcoratger:refactor/remove-pre-auto-inject

Conversation

@tcoratger
Copy link
Copy Markdown
Collaborator

Motivation

Item 6 of the packages/testing/ audit (follow-up to #848#851). The pre-state default was a hybrid of two competing mechanisms:

  • FixtureWrapper.__init__ sniffed each fixture class's __annotations__ for a pre/anchor_state field and silently injected a pytest-fixture-provided genesis state when the test omitted one
  • yet 47/53 state_transition_test calls passed pre=generate_pre_state() explicitly anyway, while 47/84 fork_choice_test calls relied on the magic

Worst of both worlds: the default was invisible (the model declared pre: State required and a hidden wrapper papered over it), and the test suite had no consistent style.

Changes

The magic dies; the default moves onto the model field — visible, standard Pydantic:

pre: State = Field(default_factory=generate_pre_state)            # StateTransitionTest
anchor_state: State = Field(default_factory=generate_pre_state)   # ForkChoiceTest, VerifySignaturesTest
  • The pre pytest fixture, the annotation sniffing, and the wrapper kwargs injection are deleted (the request.param indirect branch was verified unused)
  • The now-always-set fields lose their | None types, None-branches, and assert ... is not None guards
  • Lstar.spec_class() lost its only caller (the deleted pre fixture) and is removed
  • One test convention (~34 lines stripped): the standard genesis pre-state is omitted; only non-default pre-states are spelled out (generate_pre_state(num_validators=8), advanced states, build_anchor(...)) — deviations from the baseline are now visually loud

Latent import-cycle fix in the spec

Removing the accidental LstarSpec import from the consensus forks module exposed a real bug: import lean_spec.spec.crypto.xmss crashes on current main with a circular-import error (xmss/containers.pyforks.lstar.slotforks/__init__aggregation.py → back into the partially-initialized xmss containers). It only ever worked because some other module happened to load the forks package first. Fix at the root: xmss/__init__.py now loads the forks package before its own containers, making the cycle resolve from any entry point (verified: xmss-first, forks-first, and consensus_testing imports all succeed).

Vector impact: none

The default factory produces exactly the state the deleted pytest fixture produced (same 4-validator genesis). The pre-state is always serialized into the emitted JSON, so clients see no difference.

Verification

  • just check passes (ruff, format, ty, codespell, mdformat)
  • Import smoke from all three entry orders passes
  • Smoke fill across all three affected fixture types (state_transition, verify_signatures, fork_choice — default and explicit paths): 14/14 pass

🤖 Generated with Claude Code

The filler wrapper sniffed each fixture class's annotations for a
pre or anchor state field and silently injected a default genesis
state into the constructor when the test omitted one. Meanwhile most
state-transition tests passed the default explicitly anyway, leaving
two competing mechanisms and a default invisible from the model.

Changes:

- the pre pytest fixture, the annotation sniffing, and the wrapper
  kwargs injection are deleted; the indirect-parametrization branch
  was already unused
- the three fixtures carrying a pre-state field now declare the
  default where it belongs, as a field default factory producing the
  standard genesis state; the always-set fields lose their None
  branches and guards
- the fork's spec accessor lost its only caller and is removed
- tests adopt one convention: the standard genesis pre-state is
  omitted, only non-default pre-states are spelled out; the redundant
  no-argument calls are stripped

Removing the accidental fork-package import in the consensus forks
module exposed a latent import cycle in the spec itself: the xmss
containers need the fork slot type, while the fork aggregation
containers import the xmss containers back. Importing the crypto
package first crashed; it only ever worked because some other module
loaded the forks package earlier. The xmss package init now loads the
forks package first, making the cycle resolve from any entry point.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@tcoratger tcoratger merged commit 4e89ecc into leanEthereum:main Jun 5, 2026
12 of 13 checks passed
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