Skip to content

chore(credo): adopt mika_credo_rules and fix every finding - #55

Open
MikaAK wants to merge 6 commits into
mainfrom
chore/mika-credo-rules
Open

MikaAK wants to merge 6 commits into
mainfrom
chore/mika-credo-rules

Conversation

@MikaAK

@MikaAK MikaAK commented Sep 3, 2026

Copy link
Copy Markdown
Owner

Summary

  • Adds mika_credo_rules and enables every check that applies to a plain Elixir library. The dep points at GitHub because hex 0.1.0 only ships 14 of the 99 checks; switch it back to hex once a new version is published.
  • Checks that target consumers of this library rather than the library itself are listed as disabled with a comment: NoRawEts, CacheRequiresSandboxOption, CacheOptsNoHardcodedUri, NoDirectErlangRpc, NoReimplementedHelper. ErrorMessageRequired excludes lib/cache/ets.ex because opts_definition/1 is a NimbleOptions custom validator whose contract is {:error, String.t()}. SingleModulePerFile excludes the CI-only fixture file lib/cache_test_modules.ex.
  • BlitzCredoChecks.StrictComparison, TagTODO and TagFIXME are superseded by the equivalent mika checks and turned off to avoid double reports.

Code changes

  • Cache.RefreshAhead and Cache.HashRing run their background tasks under a Task.Supervisor in the strategy's supervision tree instead of Task.start/1.
  • Cache.HashRing.RingMonitor seeds its ETS table and calls :net_kernel.monitor_nodes/2 from handle_continue/2.
  • Adapter rescue clauses narrow to ArgumentError, the only exception :ets, :counters, :persistent_term and ConCache raise. Cache.HashRing.rpc_get/6 logs a raised exception before returning :unavailable.
  • Table-owner processes use spawn_link/1 instead of {:ok, pid} = Task.start_link/1; Cache.DETS exits with the :dets.open_file/2 reason instead of a MatchError.
  • function_exported?/3 calls are guarded by Code.ensure_loaded?/1; app env reads go through Cache.Config.fetch_env!/2; Cache.ETS Logger messages carry the module prefix and inspect/1 their values.

Tests

  • Process.sleep is gone: Cache.Wait.until/2 (new test/support/wait.ex) polls on observable state, :sys.get_state/1 syncs GenServer mailboxes, and the refresh-ahead dedup test gates its refresh callback so the concurrent reads are provably in flight.
  • CacheTest waits for the adapter to answer a read before testing, which is what the removed 100ms sleep was covering for Cache.Redis's async connect.
  • The app-env fixtures move to test_helper.exs.
  • The implicitly sync test modules now declare async: true.

Verification

  • mix credo --strict: no issues (81 checks).
  • mix dialyzer: 0 errors.
  • mix test: every non-Redis test green across 11 sequential local runs. Local Redis tests flake on main too (Redix.ConnectionError: closed in 3 of 5 baseline runs), so CI is the arbiter there.

https://claude.ai/code/session_014FgvF2AKpSdWdLEEQQjhjZ

Add mika_credo_rules (GitHub source, hex 0.1.0 only ships 14 of the 99
checks) and enable every check that applies to a plain Elixir library.
Checks that target consumers of this library rather than the library
itself (NoRawEts, CacheRequiresSandboxOption, CacheOptsNoHardcodedUri,
NoDirectErlangRpc, NoReimplementedHelper) are listed as disabled.

Code changes driven by the new checks:

- RefreshAhead and HashRing spawn their background tasks from a
  Task.Supervisor in the strategy's supervision tree.
- RingMonitor defers ETS seeding and node monitoring to handle_continue/2.
- Adapter rescues narrow to ArgumentError, the only exception ETS,
  counters, persistent_term and ConCache raise; HashRing logs an RPC
  exception before falling back.
- Table-owner processes use spawn_link instead of a bare {:ok, pid} match
  on Task.start_link/1; DETS exits with the open_file reason.
- function_exported?/3 is guarded by Code.ensure_loaded?/1.
- Application env reads go through Cache.Config; Logger messages carry
  the module prefix and inspect their values.

Tests replace Process.sleep with Cache.Wait.until polling, :sys.get_state
syncs and a gated refresh callback for the dedup test; the app-env
fixtures move to test_helper.exs.

Claude-Session: https://claude.ai/code/session_014FgvF2AKpSdWdLEEQQjhjZ
@codecov

codecov Bot commented Sep 3, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 83.72093% with 7 lines in your changes missing coverage. Please review.
✅ Project coverage is 87.09%. Comparing base (68c62a9) to head (5246c93).
⚠️ Report is 3 commits behind head on main.

Files with missing lines Patch % Lines
lib/cache/persistent_term.ex 0.00% 3 Missing ⚠️
lib/cache/case_template.ex 0.00% 2 Missing ⚠️
lib/cache/ets.ex 88.88% 1 Missing ⚠️
lib/cache/refresh_ahead.ex 80.00% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main      #55      +/-   ##
==========================================
+ Coverage   83.87%   87.09%   +3.22%     
==========================================
  Files          24       24              
  Lines         707      713       +6     
==========================================
+ Hits          593      621      +28     
+ Misses        114       92      -22     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

Codecov's patch check counted the retyped rescue clauses as new lines and
reported 51% patch coverage. Exercise the reachable ones: ETS and Counter
calls against a table that does not exist, ETS rehydration from a file
with the wrong table name or garbage contents, DETS open_file failure,
and RefreshAhead get/delete without a tracker table.

Cache.PersistentTerm drops its rescue clauses instead: :persistent_term
get/2, put/2 and erase/1 never raise, so they were dead code.

Claude-Session: https://claude.ai/code/session_014FgvF2AKpSdWdLEEQQjhjZ
Dropping the PersistentTerm rescue clauses narrowed its return type, and
Elixir 1.20 then flagged the generated {:error, _} clause of
handle_adapter_result/3 as unreachable for that adapter, which fails the
CI compile under --warnings-as-errors. The clauses stay so every adapter
honours the same {:ok, _} | {:error, %ErrorMessage{}} contract.

The ConCache rescue clauses are exercised by deleting the cache's public
ETS table out from under it, which is the only way ConCache raises.
IsolationTest and ExtraCachesTest both sandbox TestCache, and
Cache.SandboxRegistry.start/1 adopts an already-running sandbox Agent
into the calling test's supervisor, so with async: true one test's
teardown killed the other's cache mid-run. Declare them async: false and
retire BlitzCredoChecks.NoAsyncFalse, which MikaCredoRules.AsyncTrueRequired
supersedes: the concurrency choice must be explicit, and a deliberate
async: false is allowed.
Task.start_link/1 keeps the owner's $callers and $ancestors metadata and is
the documented way to start a linked process here. The bare {:ok, pid} match
is fine: Task.start_link/1 cannot return an error tuple. The
NoBarePatternMatchOnFallible finding on these three sites will be handled in
the credo rule itself.
@MikaAK MikaAK closed this Sep 8, 2026
@MikaAK MikaAK reopened this Sep 8, 2026
NoBarePatternMatchOnFallible now allows crash-preferable start functions,
so the {:ok, pid} = Task.start_link(...) matches in the ETS, DETS and
Counter adapters are no longer flagged.
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