Skip to content

Commit cacfac4

Browse files
committed
docs(service-cluster-redis): record the measured ioredis 5-vs-6 gap in the contract test header, and drop a promised live-Redis path that never existed
The contract test runs `ioredis-mock@8.13.1` (peer `ioredis: ^5`) against a package depending on `ioredis@^6`, with the untyped import suppressed by `@ts-expect-error`. This commit changes no code and no dependency: it writes down the measurement that was missing, so the next reader starts where this one finished. Measured against ioredis 5.11.1 (newest release satisfying the mock's `^5` peer) and 6.0.0 (the version resolved in this workspace): - all 14 Redis commands this package issues carry every v5 overload verbatim into v6's RedisCommander.d.ts; `set` is a strict superset there (v6 adds IFEQ/IFNE/IFDEQ/IFDNE), and nothing used here was removed or re-shaped - v6's RESP3 reply mapping is opt-in: the class defaults its ReplyMapping parameter to "legacy", ChainableCommander defaults to "resp2", and duplicate() with no override inherits the caller's mapping. This package never passes replyMapping, so every reply shape it sees is the v5 one - the three RedisOptions keys client.ts sets (lazyConnect, maxRetriesPerRequest, enableAutoPipelining) are declared identically The peer mismatch is therefore real but inert on the surface these suites drive. The header now says so, names the version pair the claim rests on, and states that the claim expires if either range in package.json moves. Also recorded: these suites reach the mock only through the injected client and the pub/sub duplicate, never through createRedisClient(), so `new Redis(url, options)` — this package's only contact with ioredis's constructor and connection surface, and the area v6 changed most — is exercised by nothing in this file. That is why the gap is inert here, and it is not a reason to trust the double. The header additionally promised `RUN_REAL_REDIS=1` + `REDIS_URL` and "conditional describe.skipIf blocks at the bottom". No such blocks exist, and both names occurred nowhere in the package outside that one sentence. The false promise is removed rather than left standing as an escape hatch nobody can take. The `@ts-expect-error` stays. Removing it fails typecheck with TS7016 — ioredis-mock ships no `types` field and no .d.ts of its own — so the suppression has not rotted. Its comment now also records what it costs: with the module untyped the client is `any` and satisfies ioredis's Redis type without being checked against it, which is the second reason a v5-vs-v6 divergence could not surface here. `@types/ioredis-mock` would type the seam but only asserts `new(): ioredis.Redis` rather than describing the mock, so adopting it would trade an honest `any` for an unearned certainty; that trade is left to a maintainer. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XpTx2tbq3pZRYAdoGt6E6Y
1 parent ceb4877 commit cacfac4

1 file changed

Lines changed: 58 additions & 3 deletions

File tree

packages/services/service-cluster-redis/src/redis.contract.test.ts

Lines changed: 58 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,12 +3,67 @@
33
/**
44
* Driver contract tests for the Redis cluster driver, run against
55
* `ioredis-mock` so they execute without a real Redis instance in CI.
6+
* There is no live-Redis path in this file: every suite below runs on the
7+
* mock. (An earlier version of this header promised `RUN_REAL_REDIS=1` +
8+
* `REDIS_URL` and "conditional `describe.skipIf` blocks at the bottom" —
9+
* no such blocks were ever here, and the two names appeared nowhere in
10+
* this package outside that sentence.)
611
*
7-
* The same suites can be invoked against a live Redis by setting
8-
* `RUN_REAL_REDIS=1` and providing `REDIS_URL` — see the conditional
9-
* `describe.skipIf` blocks at the bottom.
12+
* ## The double is one major version behind the client it doubles
13+
*
14+
* This package depends on `ioredis@^6`, while `ioredis-mock@8.13.1`
15+
* declares `peerDependencies: { ioredis: "^5" }` — so `pnpm install`
16+
* prints an unmet-peer warning for it. That gap is real, and it is
17+
* declared here rather than closed, because it was measured to be inert
18+
* on the surface these suites actually drive. Measured against ioredis
19+
* 5.11.1 (newest release satisfying the mock's `^5` peer) and 6.0.0 (the
20+
* version resolved in this workspace):
21+
*
22+
* - Every Redis command issued by this package's `src/*.ts` — get, set,
23+
* del, incr, incrby, pttl, watch, unwatch, multi, exec, publish,
24+
* subscribe, unsubscribe, quit, eval — carries all of its v5
25+
* overloads verbatim into v6's `RedisCommander.d.ts`. `set` is a
26+
* strict superset there (v6 adds the IFEQ/IFNE/IFDEQ/IFDNE tokens);
27+
* nothing used here was removed or re-shaped.
28+
* - v6's one substantive change reachable from this package is RESP3
29+
* reply mapping, and it is opt-in: the class is declared with a
30+
* `ReplyMapping` parameter defaulting to "legacy", `ChainableCommander`
31+
* defaults to "resp2", and `duplicate()` with no override inherits the
32+
* caller's mapping. This package never passes `replyMapping`, so every
33+
* reply shape it sees is still the v5 one.
34+
* - The three `RedisOptions` keys client.ts sets — lazyConnect,
35+
* maxRetriesPerRequest, enableAutoPipelining — are declared
36+
* identically in both versions.
37+
*
38+
* That named set is the whole basis for the claim; it is not a statement
39+
* about ioredis 5 vs 6 in general. If the `ioredis` or `ioredis-mock`
40+
* range in package.json moves, this paragraph expires and the diff has to
41+
* be re-taken.
42+
*
43+
* ## What these suites therefore do NOT certify
44+
*
45+
* They reach the mock only through the injected `client` and the
46+
* `client.duplicate()` the pub/sub adapter makes. They never call
47+
* `createRedisClient()`, so `new Redis(url, options)` — this package's
48+
* only contact with ioredis's constructor and connection surface, and the
49+
* area v6 changed most — is exercised by nothing in this file. That is
50+
* why the version gap is inert here, and it is not a reason to trust the
51+
* double: the mock is simply never asked to stand in for the surface on
52+
* which the two majors differ.
1053
*/
1154

55+
// `ioredis-mock` publishes no type declarations of its own — no `types`
56+
// field in its manifest and no `.d.ts` in the tarball — so this import
57+
// raises TS7016 ("could not find a declaration file ... implicitly has an
58+
// 'any' type") and the directive below is what silences it. Note the
59+
// second cost, beyond the missing types: with the module untyped
60+
// `RedisMock` is `any`, so every `client:` argument constructed from it
61+
// satisfies ioredis's `Redis` type without ever being checked against it.
62+
// That is the other reason a v5-vs-v6 divergence could not surface here.
63+
// `@types/ioredis-mock` exists and would type this seam, but it only
64+
// *asserts* `new(): ioredis.Redis` rather than describing the mock, so
65+
// adopting it would trade an honest `any` for an unearned certainty —
66+
// that trade has not been made, deliberately.
1267
// @ts-expect-error — ioredis-mock has no published types
1368
import RedisMock from 'ioredis-mock';
1469
import { describe, expect, it, vi } from 'vitest';

0 commit comments

Comments
 (0)