Skip to content

Declutter jwt_spec.rb - #760

Merged
anakinj merged 4 commits into
jwt:mainfrom
anakinj:refactor/jwt-spec-split
Sep 5, 2026
Merged

Declutter jwt_spec.rb#760
anakinj merged 4 commits into
jwt:mainfrom
anakinj:refactor/jwt-spec-split

Conversation

@anakinj

@anakinj anakinj commented Sep 5, 2026

Copy link
Copy Markdown
Member

Description

First pass at making spec/jwt/jwt_spec.rb about one thing: the JWT.encode / JWT.decode public surface. 951 lines and 130 examples down to 785 and 118, with no coverage lost.

Move the custom algorithm contract out. ~125 lines describing what a class must implement to be usable as an algorithm — that is the JWT::JWA::SigningAlgorithm contract, not the encode/decode surface. Now spec/jwt/jwa/signing_algorithm_spec.rb, mirroring the lib layout. The block depended only on the shared payload let, so this is a pure move: the suite reports the same 1246 example descriptions before and after, the only diff being the group header.

Trim the duplicated iat semantics. The verify_iat block re-tested a float iat, an integer iat, one second of clock drift, and a leeway covering that drift — all four already covered against the claim itself in claims/issued_at_spec.rb. The only thing this file can say that the claim spec cannot is that the decode options reach the verifier, so that is what it now asserts, plus the default of leaving iat unverified, which nothing covered before.

Drop an unused let in the issuer claim context.

Dismantle the data fixture. A 29 entry hash mixing symbol and string keys, holding an HMAC secret, hardcoded tokens and key fixtures all together, plus six '' placeholders that existed only so before(:each) could overwrite them by mutating a memoized let. Each value now lives where it is used: the HMAC and RSA loops iterate a map of algorithm to expected token instead of indexing the fixture by the loop variable, the ECDSA and PSS loops build their token in a let, and the four malformed token strings are inline at their single use each.

Two values were not doing what they appeared to. A keyfinder example resolved its key by using the decoded payload as an index into the shared hash, and the multi-key context passed the hardcoded HS256 token string as an HMAC secret. Both now say what they mean.

What did not move, and why

I had planned to pull the x5c block into x5c_key_finder_spec.rb, but it doubles out JWT::X5cKeyFinder entirely — it tests that JWT.decode routes the x5c option to the finder, which is squarely the decode surface. Moving it into a spec whose subject it mocks would have made things worse.

The same reasoning applies more broadly than I first expected: nearly everything left goes through JWT.encode / JWT.decode and legitimately belongs here. The remaining clutter is internal — chiefly the 29-entry data fixture hash with mixed symbol and string keys, six '' placeholders that exist only to be overwritten by before(:each), and the mutation of a memoized let. That is worth a follow-up, but it is a different change from this one.

Verification

  • 644 examples, 0 failures. RuboCop clean.
  • The data removal verified the same way: identical example descriptions across the whole suite before and after, so nothing was added, removed or renamed.
  • The move slice verified by diffing the full --dry-run --format doc output: identical example descriptions, 1246 before and after.

Checklist

Before the PR can be merged be sure the following are checked:

  • There are tests for the fix or feature added/changed
  • A description of the changes and a reference to the PR has been added to CHANGELOG.md — skipped, spec-only with no effect on the published gem

These examples describe what a class must implement to be usable as an
algorithm, which is the JWT::JWA::SigningAlgorithm contract, not the
JWT.encode/JWT.decode surface that jwt_spec is supposed to cover.

Pure move: the block depended only on the shared payload let, and the
full suite still reports the same 1246 example descriptions.
The verify_iat block re-tested what claims/issued_at_spec already covers
at the unit level: a float iat, an integer iat, one second of clock
drift, and a leeway covering that drift. The only thing jwt_spec can say
that the claim spec cannot is that the decode options reach the verifier,
so it now asserts that and nothing else, including the default of leaving
iat unverified, which nothing covered before.
The fixture was a 29 entry hash mixing symbol and string keys, holding
everything from an HMAC secret to hardcoded tokens to key fixtures, with
six '' placeholders that existed only so before(:each) could overwrite
them by mutating a memoized let.

Each value now lives where it is used. The HMAC and RSA loops iterate a
map of algorithm to expected token rather than indexing the fixture by
the loop variable; the ECDSA and PSS loops build their token in a let;
the four malformed token strings are inline at their single use each.

One value was doing something other than it looked: a keyfinder example
resolved its key by using the decoded payload as an index into the shared
hash, and the multi key context passed the hardcoded HS256 token string
as an HMAC secret. Both now say what they mean.

Same 118 examples, and the whole suite reports identical example
descriptions before and after.
@anakinj anakinj changed the title Give jwt_spec a subject Declutter jwt_spec.rb Sep 5, 2026
@anakinj
anakinj merged commit 4a576f8 into jwt:main Sep 5, 2026
15 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