Skip to content

test: add mock unit tests for LibOpSLFRCurrentExchangeRate run() and integrity() - #163

Open
thedavidmeister wants to merge 10 commits into
mainfrom
test/issue-59-sflr-opcode-mock-tests
Open

thedavidmeister wants to merge 10 commits into
mainfrom
test/issue-59-sflr-opcode-mock-tests

Conversation

@thedavidmeister

@thedavidmeister thedavidmeister commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Closes #59

The only existing test for the sFLR exchange-rate opcode was a single fork snapshot at a fixed block. That test pins one magic value but cannot catch regressions in the coefficient-to-Float mapping for arbitrary rates.

This PR adds a lib-level test file test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol with:

  • testIntegrity: fuzz-verifies that integrity() always returns (0, 1) regardless of operand/input/output arguments.
  • testRunMappingMocked: fuzz-verifies that run() maps an arbitrary mocked sFLR rate through LibDecimalFloat.fromFixedDecimalLosslessPacked(rate18, 18) correctly, using vm.mockCall on SFLR_CONTRACT. Constrained to rate18 < 2^223 (the int224 coefficient limit of the Float format).

Co-Authored-By: Claude noreply@anthropic.com

Summary by CodeRabbit

  • Tests
    • Added comprehensive coverage for staked FLR exchange-rate operations, including integrity validation and mocked execution.
    • Added validation across typical, zero, parity, maximum, and boundary-value exchange rates.
    • Confirmed expected results and appropriate handling of coefficient-overflow scenarios, improving confidence in exchange-rate calculations and edge-case behavior.

…integrity()

Closes #59

Co-Authored-By: Claude <noreply@anthropic.com>
@thedavidmeister thedavidmeister self-assigned this Jun 16, 2026
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

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

Next review available in: 48 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: bd85f44b-672d-4776-87bf-00b87bef5562

📥 Commits

Reviewing files that changed from the base of the PR and between b1b3303 and 77204eb.

📒 Files selected for processing (1)
  • test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 8ac72587-ae42-420e-abef-35bcd068d7e5

📥 Commits

Reviewing files that changed from the base of the PR and between 5b61eec and b1b3303.

📒 Files selected for processing (1)
  • test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol

Walkthrough

Adds Forge tests for LibOpSFLRCurrentExchangeRate. The tests cover operation integrity, mocked fuzzed rate conversion, zero and parity values, and coefficient overflow at boundary inputs.

Changes

SFLR Exchange Rate Unit Tests

Layer / File(s) Summary
Operation wrapper, integrity, and rate mapping
test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol
Adds the test contract, an external run wrapper, an integrity assertion for zero inputs and one output, and fuzzed mocked-rate checks against LibDecimalFloat.fromFixedDecimalLosslessPacked.
Canonical values and overflow boundaries
test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol
Verifies zero and parity float encodings. Confirms that 2**223 and uint256.max revert with CoefficientOverflow.

Estimated code review effort: 2 (Simple) | ~10 minutes

🚥 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 added mock unit tests for run() and integrity().
Linked Issues check ✅ Passed The PR fulfills issue #59 with mock mapping tests, integrity coverage, and all requested boundary cases.
Out of Scope Changes check ✅ Passed The PR changes only the targeted test file, and all additions support issue #59.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch test/issue-59-sflr-opcode-mock-tests

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.

@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 `@test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol`:
- Around line 23-25: The boundary coverage for the testRunMappingMocked function
is incomplete because the current assumption excludes the zero case and lacks
explicit tests for edge conditions. Remove the vm.assume constraint that
excludes zero from testRunMappingMocked, then add separate dedicated test
functions to explicitly cover the boundary cases: a test for rate18 equal to 0,
a test for rate18 equal to 1e18 (standard decimal), and a test for
type(uint256).max that verifies the overflow/revert behavior. This ensures the
test suite comprehensively covers all required boundary conditions as stated in
issue `#59`.
🪄 Autofix (Beta)

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: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0b8b35b1-d359-4424-b6c6-b71719f9b9b5

📥 Commits

Reviewing files that changed from the base of the PR and between cb20d03 and 5b61eec.

📒 Files selected for processing (1)
  • test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol

Comment thread test/src/lib/op/LibOpSFlrCurrentExchangeRate.t.sol
@thedavidmeister

Copy link
Copy Markdown
Collaborator Author

Rework note (human reject, 2026-07-04): (a) mapping fuzz and (c) integrity fuzz are good and stay as-is. Missing is #59's leg (b): EXPLICIT boundary pins — add named tests for rate18 = 0 and rate18 = 1e18 (parity), both ruling-neutral (exactly representable, lossy/lossless agree). Do NOT add the type(uint256).max boundary test yet: that input's semantics (revert vs floor) are gated on the org revert-vs-floor design ruling at rainlanguage/rain.erc4626.words#70 — pinning it now would enshrine one side. Relink Closes #59 → Refs #59 (the max leg stays open on the issue until the ruling), and note the deferral on #59.

@thedavidmeister

Copy link
Copy Markdown
Collaborator Author

Update to the rework note: the org REVERT ruling landed (rain.erc4626.words#70, closed premise-corrected) — the deferred type(uint256).max boundary leg is now UNBLOCKED and should be included: assert the exact CoefficientOverflow-class revert for a mocked rate exceeding the int224 coefficient bound (~1.35e67), alongside the 0 and 1e18 explicit pins. Then Closes #59 in full.

@thedavidmeister

Copy link
Copy Markdown
Collaborator Author

🤖 ai:producer
Producer note: executed the 2026-07-04 rework note + its update — added explicit boundary pins rate18=0, rate18=1e18, and the int224-coefficient overflow revert pin (testRunRateZero, testRunRateParityOne, testRunRateCoefficientOverflow; overflow asserts the exact CoefficientOverflow(2^223, -18) revert). Body already reads Closes #59 — no relink needed.

@thedavidmeister thedavidmeister added the human:needs-work Human reviewer: needs rework label Jul 6, 2026
@thedavidmeister thedavidmeister added ai:needs-work Needs rework — the producer's inbox (vetter verdict or human ruling) and removed human:needs-work Human reviewer: needs rework labels Jul 30, 2026
thedavidmeister and others added 5 commits August 12, 2026 16:31
The rework note's overflow leg is named for type(uint256).max; what landed
pinned 2**223, the smallest overflowing rate. Those are not the same test:
type(uint256).max is the only input that reaches the int224 coefficient bound
through a uint256 -> int256 conversion that wraps to -1, so an implementation
that checks the bound after converting would accept it and emit a negative
rate. Pins the CoefficientOverflow class for that input alongside the existing
boundary pin.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Merging main in changed the premise under this test. getSFLRPerFLR18 now
rejects a zero exchange rate with ZeroSFLRRate, so the op no longer encodes
zero as FLOAT_ZERO the way the rework note assumed when it asked for an
explicit rate18 = 0 pin. The pin stays and its expectation moves to the
current behaviour: the op propagates the guard rather than swallowing it,
which the lib-level pin on main cannot observe from its side of the call.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
CI answered what the boundary pin could not: type(uint256).max does not take
the CoefficientOverflow path 2**223 takes. It is far enough above the
coefficient bound that the packing first normalises one decimal place down,
and that shift drops a non-zero digit, so a lossless conversion refuses it as
LossyConversionToFloat(type(uint256).max / 10, -17).

Two different errors from the two ends of the overflow region is exactly why
the rework note asked for this input by name and why the boundary test cannot
stand in for it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The legal job failed installing nix, not on anything in this branch: the
nix-quick-install tarball came down corrupt (zstd: unsupported format) while
static and test installed the same version fine on the same run.

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

Labels

ai:needs-work Needs rework — the producer's inbox (vetter verdict or human ruling)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[F12] [MEDIUM] Only a single happy-path fork test; no unit/mock test, no rate-mapping or float-conversion assertions

1 participant