Skip to content

Added integration tests for AdapterCacheRedis#27467

Merged
allouis merged 1 commit into
mainfrom
add-redis-adapter-integration-tests
Apr 21, 2026
Merged

Added integration tests for AdapterCacheRedis#27467
allouis merged 1 commit into
mainfrom
add-redis-adapter-integration-tests

Conversation

@allouis
Copy link
Copy Markdown
Collaborator

@allouis allouis commented Apr 20, 2026

Why

The redis cache adapter only has unit tests today, and they stub the cache layer completely. They verify the implementation but not the behavioural contract against a real Redis. We have changes to the redis cache coming up and want the current behaviour locked in before we touch anything.

What

  • New ghost/core/test/integration/adapters/redis/adapter-cache-redis.test.js with 12 tests against a real Redis:
    • setget round-trip and overwrite
    • Invalidation contract: set → reset → get === null, and fetchData is re-called after a reset
    • keyPrefix isolation: reset() on prefix A does not affect prefix B
    • get(k, fetchData) cache-miss / cache-hit semantics
    • Behaviour with no keyPrefix configured
    • getTimeoutMilliseconds short-circuit (under and over the timeout)
  • config.testing.json / config.testing-mysql.json: added a default adapters.Redis block (127.0.0.1:6379) so the test reads the host via the standard config layer. Override via env (adapters__Redis__host, adapters__Redis__port).
  • .github/workflows/ci.yml: added redis:7.0 as a service to the Acceptance tests job, matching how mysql is already wired up.

Coverage

Coverage of core/server/adapters/lib/redis/AdapterCacheRedis.js from this suite alone:

Metric Coverage
Statements 77.7% (223/287)
Branches 74.35% (29/39)
Functions 71.42% (10/14)
Lines 77.7% (223/287)

What's not covered:

  • Redis Cluster code paths (see the next section)
  • Catch blocks for redis client errors
  • The background-refresh path (refreshAheadFactor); the existing unit tests already cover this, and timing-based integration tests are flaky
  • keys() is about to be replaced with a stub that throws (see Stubbed AdapterCacheRedis.keys() with an IncorrectUsageError #27465); the unit-level assertion for the new behaviour lives there

Cluster: out of scope

The adapter has a #getPrimaryRedisNode branch that runs when the underlying client is an ioredis Cluster, used to route SCAN to a single primary node. This suite doesn't cover it. Adding it would mean spinning up a multi-node cluster (something like grokzen/redis-cluster) as a separate CI service. The value isn't there right now, since the planned changes shrink this code path further. Worth revisiting if cluster correctness becomes a concern.

Notes for reviewers

  • Each test uses a random keyPrefix (via crypto.randomBytes) so tests don't collide. Connections are closed in afterEach; any data left behind ages out via the prefix.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 20, 2026

Important

Review skipped

Draft detected.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 49659407-b5a3-4034-8005-a5d2ec6da63e

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • ✅ Review completed - (🔄 Check again to review again)
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch add-redis-adapter-integration-tests

Comment @coderabbitai help to get the list of available commands and usage tips.

@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 20, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 73.04%. Comparing base (511a438) to head (0da19c3).
⚠️ Report is 15 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main   #27467      +/-   ##
==========================================
+ Coverage   72.85%   73.04%   +0.18%     
==========================================
  Files        1553     1553              
  Lines      125079   125079              
  Branches    15104    15140      +36     
==========================================
+ Hits        91129    91361     +232     
+ Misses      32996    32764     -232     
  Partials      954      954              
Flag Coverage Δ
admin-tests 49.73% <ø> (ø)
e2e-tests 73.04% <ø> (+0.18%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

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

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
ghost/core/test/integration/adapters/redis/adapter-cache-redis.test.js (1)

153-163: Consider using sinon.stub for timeout simulation.

The direct monkey-patch of cache.cache.get works but bypasses sinon's automatic cleanup. Since afterEach quits the client anyway, this is acceptable, but using sinon.stub(cache.cache, 'get') would be more idiomatic and self-documenting.

♻️ Optional refactor using sinon.stub
         it('returns null when the underlying get exceeds the timeout', async function () {
             const cache = createCache({getTimeoutMilliseconds: 1});
             await cache.set('slow', 'value');

             const original = cache.cache.get.bind(cache.cache);
-            cache.cache.get = k => new Promise((resolve) => {
-                setTimeout(() => original(k).then(resolve), 50);
-            });
+            sinon.stub(cache.cache, 'get').callsFake(k => new Promise((resolve) => {
+                setTimeout(() => original(k).then(resolve), 50);
+            }));

             assert.equal(await cache.get('slow'), null);
         });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@ghost/core/test/integration/adapters/redis/adapter-cache-redis.test.js`
around lines 153 - 163, Replace the direct monkey-patch of cache.cache.get with
a sinon stub: capture the original method into a variable (original =
cache.cache.get), then call sinon.stub(cache.cache, 'get').callsFake(k => new
Promise(resolve => setTimeout(() => original.call(cache.cache, k).then(resolve),
50))); remove the direct assignment (cache.cache.get = ...) so sinon handles
replacement and automatic cleanup; reference symbols: cache.cache.get, original,
and sinon.stub(...).callsFake.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@ghost/core/test/integration/adapters/redis/adapter-cache-redis.test.js`:
- Around line 153-163: Replace the direct monkey-patch of cache.cache.get with a
sinon stub: capture the original method into a variable (original =
cache.cache.get), then call sinon.stub(cache.cache, 'get').callsFake(k => new
Promise(resolve => setTimeout(() => original.call(cache.cache, k).then(resolve),
50))); remove the direct assignment (cache.cache.get = ...) so sinon handles
replacement and automatic cleanup; reference symbols: cache.cache.get, original,
and sinon.stub(...).callsFake.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 7a25e131-f6c5-4900-adb2-f11980483861

📥 Commits

Reviewing files that changed from the base of the PR and between f6edd55 and 270b0d1.

📒 Files selected for processing (4)
  • .github/workflows/ci.yml
  • ghost/core/core/shared/config/env/config.testing-mysql.json
  • ghost/core/core/shared/config/env/config.testing.json
  • ghost/core/test/integration/adapters/redis/adapter-cache-redis.test.js

ref no-issue

The redis cache adapter only has unit tests today, and they stub the
underlying cache layer completely - so they verify the implementation
but not the behavioural contract against a real Redis. We're planning
some changes to the redis cache and want full coverage of the current
behaviour locked in before we touch the implementation, so any
behavioural regression is caught immediately.

Wires redis as a CI service alongside mysql and adds a default
adapters.Redis block to the testing config so the suite reads the host
through the standard config layer.
@allouis allouis force-pushed the add-redis-adapter-integration-tests branch from 270b0d1 to 0da19c3 Compare April 20, 2026 13:52
@sonarqubecloud
Copy link
Copy Markdown

@allouis allouis marked this pull request as ready for review April 20, 2026 14:00
@allouis allouis merged commit 07062e0 into main Apr 21, 2026
46 checks passed
@allouis allouis deleted the add-redis-adapter-integration-tests branch April 21, 2026 06:58
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.

2 participants