Skip to content

♻️ Refactor / Rewrite stale_time/1 in idiomatic Elixir (#14)#28

Merged
docJerem merged 2 commits into
mainfrom
refactor/stale-time-cleanup
May 19, 2026
Merged

♻️ Refactor / Rewrite stale_time/1 in idiomatic Elixir (#14)#28
docJerem merged 2 commits into
mainfrom
refactor/stale-time-cleanup

Conversation

@docJerem
Copy link
Copy Markdown
Owner

Summary

Closes #14 (scoped to stale_time/1; broader esaml-port audit deferred — see below).

Rewrites ExSaml.Core.Saml.stale_time/1 to drop the verbatim-from-arekinath/esaml structure that relied on variable shadowing inside nested case blocks. That shadowing made an inner branch tautological for Dialyzer and forced two pattern_match suppressions in .dialyzer_ignore.exs.

The new form:

  • Collects candidate expiries (subject.notonorafter, conditions[:not_on_or_after]) into a list, rejects nil, takes Enum.min/1.
  • Falls back to issue_instant + 5 * 60 when neither restriction is present.
  • Same contract — @spec stale_time(Assertion.t()) :: integer() — same behavior, no shadowing.
  • Removes the # credo:disable-for-next-line Credo.Check.Refactor.CyclomaticComplexity directive (no longer needed).
  • Removes the two lib/ex_saml/core/saml.ex:803 entries from .dialyzer_ignore.exs. The sp_handler.ex:95 defensive entry stays (out of scope, deliberate catch-all).

Tests

Adds a new describe "stale_time/1" block in test/ex_saml/core/saml_test.exs with five cases:

  • Fallback to issue_instant + 5 min when neither subject nor conditions carry NotOnOrAfter.
  • Subject-only restriction.
  • Conditions-only restriction.
  • Both present, conditions earlier — returns conditions seconds.
  • Both present, subject earlier — returns subject seconds.

These tests were written before the refactor and validated against the old implementation first, confirming behavioral parity.

Scope note

Issue #14 also asks to audit other esaml-ported functions for similar patterns. The audit found that decode_response/1, decode_assertion/1, decode_logout_request/1, to_xml/1 etc. already use idiomatic Elixir (with, Enum.reduce, functional accumulators). stale_time/1 was the only function forcing Dialyzer suppressions due to esaml-style code. A follow-up issue can track any additional cosmetic cleanup if desired.

Test plan

  • mix test — 204 tests, 0 failures
  • mix credo --strict lib/ex_saml/core/saml.ex — no issues
  • mix dialyzer — passed, 0 unnecessary skips (confirms the sp_handler.ex:95 entry is still needed and the removed saml.ex:803 entries were the only ones tied to this refactor)

docJerem added 2 commits May 19, 2026 14:14
Drop the verbatim-from-esaml structure that relied on variable shadowing
inside nested `case` blocks and triggered two Dialyzer pattern_match
suppressions. The new form collects candidate expiries from the Subject
and Conditions, takes `Enum.min/1`, and falls back to `issue_instant + 5
minutes` when neither restriction is present — same contract, no shadowing,
no `# credo:disable` directive needed.

Also adds dedicated unit tests for `stale_time/1` covering all three
branches and the min selection in both directions, and removes the two
`saml.ex:803` entries from `.dialyzer_ignore.exs` (the `sp_handler.ex:95`
defensive entry stays — out of scope for this refactor).
Tighten `conditions_expiry/1` from a two-arm `case` into the equivalent
`if stamp = Keyword.get(...), do: to_secs(stamp)` — same nil-or-value
behavior, one line.
@docJerem docJerem merged commit 1f2c161 into main May 19, 2026
3 checks passed
@docJerem docJerem mentioned this pull request May 19, 2026
5 tasks
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.

♻️ Refactor / Clean up upstream esaml code style

2 participants