From ad223460f2663a92751e13a9745c25a0ca131bc0 Mon Sep 17 00:00:00 2001 From: Som Samantray Date: Sun, 16 Aug 2026 09:41:22 +0530 Subject: [PATCH 01/11] fix(rueidiscompatmock): make Cache() commands mockable through the expectation queue (#972) --- rueidiscompatmock/adapter.go | 6 ++ rueidiscompatmock/adapter_test.go | 132 ++++++++++++++++++++++++++++++ 2 files changed, 138 insertions(+) diff --git a/rueidiscompatmock/adapter.go b/rueidiscompatmock/adapter.go index c31c04bc..e1300c81 100644 --- a/rueidiscompatmock/adapter.go +++ b/rueidiscompatmock/adapter.go @@ -562,6 +562,12 @@ func (m *clientMock) wire() { return m.consume(len(cmds), cmds) }). AnyTimes() + m.raw.EXPECT(). + DoCache(gomock.Any(), gomock.Any(), gomock.Any()). + DoAndReturn(func(_ context.Context, cmd rueidis.Cacheable, _ time.Duration) rueidis.RedisResult { + return m.consume(1, []rueidis.Completed{rueidis.Completed(cmd)})[0] + }). + AnyTimes() } func (m *clientMock) consume(n int, cmds []rueidis.Completed) []rueidis.RedisResult { diff --git a/rueidiscompatmock/adapter_test.go b/rueidiscompatmock/adapter_test.go index dbcf0a85..2c57f5c0 100644 --- a/rueidiscompatmock/adapter_test.go +++ b/rueidiscompatmock/adapter_test.go @@ -3066,6 +3066,138 @@ func wantAnyErr(err error) error { return nil } +func TestCacheCompatGet(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("key").SetVal("v") + cmd := rdb.Cache(100 * time.Millisecond).Get(ctx, "key") + if got := cmd.Val(); got != "v" { + t.Fatalf("expected %q, got %q", "v", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatViaNewAdapter(t *testing.T) { + ctrl := gomock.NewController(t) + defer ctrl.Finish() + ctx := context.Background() + raw := mock.NewClient(ctrl) + cm := NewAdapter(raw) + rdb := rueidiscompat.NewAdapter(raw) + + cm.ExpectGet("key").SetVal("v") + cmd := rdb.Cache(time.Second).Get(ctx, "key") + if got := cmd.Val(); got != "v" { + t.Fatalf("expected %q, got %q", "v", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatMultiCommand(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectMGet("k1", "k2").SetVal([]any{"v1", "v2"}) + cm.ExpectHGetAll("h").SetVal(map[string]string{"f": "v"}) + cm.ExpectGetRange("s", 0, 1).SetVal("ab") + + cache := rdb.Cache(time.Minute) + if got := cache.MGet(ctx, "k1", "k2").Val(); !reflect.DeepEqual(got, []any{"v1", "v2"}) { + t.Fatalf("unexpected MGet result %v", got) + } + if got := cache.HGetAll(ctx, "h").Val(); !reflect.DeepEqual(got, map[string]string{"f": "v"}) { + t.Fatalf("unexpected HGetAll result %v", got) + } + if got := cache.GetRange(ctx, "s", 0, 1).Val(); got != "ab" { + t.Fatalf("unexpected GetRange result %q", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatCacheableWrapped(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectEvalRO("return 1", []string{}).SetValInt(1) + cmd := rdb.Cache(time.Second).EvalRO(ctx, "return 1", []string{}) + if got := cmd.Val(); got != int64(1) { + t.Fatalf("unexpected EvalRO result %v", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatRedisNil(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("key").RedisNil() + if err := rdb.Cache(time.Second).Get(ctx, "key").Err(); !errors.Is(err, rueidiscompat.Nil) { + t.Fatalf("expected Nil, got %v", err) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatSetErr(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("key").SetErr(errors.New("any")) + if err := rdb.Cache(time.Second).Get(ctx, "key").Err(); err == nil || err.Error() != "any" { + t.Fatalf("expected err \"any\", got %v", err) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatTTLIrrelevant(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("k").SetVal("a") + cm.ExpectGet("k").SetVal("b") + if got := rdb.Cache(time.Millisecond).Get(ctx, "k").Val(); got != "a" { + t.Fatalf("expected %q, got %q", "a", got) + } + if got := rdb.Cache(time.Hour).Get(ctx, "k").Val(); got != "b" { + t.Fatalf("expected %q, got %q", "b", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatUnmatched(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + err := rdb.Cache(time.Second).Get(ctx, "key").Err() + if err == nil || !strings.Contains(err.Error(), "no expectation for command") { + t.Fatalf("expected no-expectation error, got %v", err) + } + if err := cm.ExpectationsWereMet(); err == nil { + t.Fatal("expected unmatched expectation error") + } +} + func runCases(t *testing.T, cm ClientMock, cases []func() error) { t.Helper() for i, c := range cases { From 0045d5fd1196d50fec08444d96b352a2fdc71442 Mon Sep 17 00:00:00 2001 From: Som Samantray Date: Sun, 16 Aug 2026 09:41:22 +0530 Subject: [PATCH 02/11] docs(rueidiscompatmock): document client-side caching mocking --- rueidiscompatmock/README.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/rueidiscompatmock/README.md b/rueidiscompatmock/README.md index 3d9722ea..3aaf5f72 100644 --- a/rueidiscompatmock/README.md +++ b/rueidiscompatmock/README.md @@ -46,4 +46,16 @@ p := rdb.Pipeline() p.Get(ctx, "k1") p.Set(ctx, "k2", "v2", 0) p.Exec(ctx) +``` + +### Client-side caching example + +Cached commands (`Cache(ttl)`) use the same `Expect*` calls as non-cached commands. +The TTL argument is not matched against expectations. + +```golang +compatmock.ExpectGet("key").SetVal("val") + +cached := rdb.Cache(100 * time.Millisecond) +cached.Get(context.Background(), "key") ``` \ No newline at end of file From 0e6a3b7ec5755cc2be188f925c6f13ace7cada41 Mon Sep 17 00:00:00 2001 From: Som Samantray Date: Sun, 16 Aug 2026 09:52:36 +0530 Subject: [PATCH 03/11] docs(review): record residual review findings --- docs/residual-review-findings/0045d5f.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 docs/residual-review-findings/0045d5f.md diff --git a/docs/residual-review-findings/0045d5f.md b/docs/residual-review-findings/0045d5f.md new file mode 100644 index 00000000..a40f8c4d --- /dev/null +++ b/docs/residual-review-findings/0045d5f.md @@ -0,0 +1,12 @@ +## Residual Review Findings + +- **P2** — `rueidiscompatmock/adapter.go:567` — Repeated in-TTL cached reads diverge from real client-side caching: the DoCache stub is always-miss — filed: https://github.com/redis/rueidis/issues/1022 + +--- + +Source run context: +- Review run: ce-code-review run-id `20260816-094523-053cdd06` (artifact dir `/tmp/compound-engineering-501/ce-code-review/20260816-094523-053cdd06`) +- Branch: `fix/rueidiscompatmock-cache-mock` @ `0045d5f` +- Plan: `docs/plans/2026-08-16-002-rueidiscompatmock-cache-mock-plan.md` +- Finding type: `settled_conflict` — stamped against KTD2 (Ignore the TTL argument in matching; session-settled). Report-only: the alternative arm (in-memory TTL cache in the stub) is the rejected alternative and is not applied; the compatible arm (document the always-miss divergence) is the ticket's suggested fix. +- Owner: human (advisory-grade; not in the downstream-resolver actionable queue) From fbf92e2ecf89d8f05ecddf6510d8e79930d6298f Mon Sep 17 00:00:00 2001 From: Som Samantray Date: Sun, 16 Aug 2026 09:53:54 +0530 Subject: [PATCH 04/11] refactor(rueidiscompatmock): reuse wantAnyErr helper in cache tests --- rueidiscompatmock/adapter_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rueidiscompatmock/adapter_test.go b/rueidiscompatmock/adapter_test.go index 2c57f5c0..89142509 100644 --- a/rueidiscompatmock/adapter_test.go +++ b/rueidiscompatmock/adapter_test.go @@ -3158,8 +3158,8 @@ func TestCacheCompatSetErr(t *testing.T) { defer rdb.Close() cm.ExpectGet("key").SetErr(errors.New("any")) - if err := rdb.Cache(time.Second).Get(ctx, "key").Err(); err == nil || err.Error() != "any" { - t.Fatalf("expected err \"any\", got %v", err) + if err := wantAnyErr(rdb.Cache(time.Second).Get(ctx, "key").Err()); err != nil { + t.Fatal(err) } if err := cm.ExpectationsWereMet(); err != nil { t.Fatalf("unexpected err %v", err) From f26ebd0e7b6d99f95021859dc526fc2eee657b65 Mon Sep 17 00:00:00 2001 From: Som Samantray Date: Sun, 16 Aug 2026 09:58:39 +0530 Subject: [PATCH 05/11] chore: remove review-tooling artifact from PR (#1023) --- docs/residual-review-findings/0045d5f.md | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 docs/residual-review-findings/0045d5f.md diff --git a/docs/residual-review-findings/0045d5f.md b/docs/residual-review-findings/0045d5f.md deleted file mode 100644 index a40f8c4d..00000000 --- a/docs/residual-review-findings/0045d5f.md +++ /dev/null @@ -1,12 +0,0 @@ -## Residual Review Findings - -- **P2** — `rueidiscompatmock/adapter.go:567` — Repeated in-TTL cached reads diverge from real client-side caching: the DoCache stub is always-miss — filed: https://github.com/redis/rueidis/issues/1022 - ---- - -Source run context: -- Review run: ce-code-review run-id `20260816-094523-053cdd06` (artifact dir `/tmp/compound-engineering-501/ce-code-review/20260816-094523-053cdd06`) -- Branch: `fix/rueidiscompatmock-cache-mock` @ `0045d5f` -- Plan: `docs/plans/2026-08-16-002-rueidiscompatmock-cache-mock-plan.md` -- Finding type: `settled_conflict` — stamped against KTD2 (Ignore the TTL argument in matching; session-settled). Report-only: the alternative arm (in-memory TTL cache in the stub) is the rejected alternative and is not applied; the compatible arm (document the always-miss divergence) is the ticket's suggested fix. -- Owner: human (advisory-grade; not in the downstream-resolver actionable queue) From 59eaff37eba5270244c67fd01ae2c3cdd7a571f9 Mon Sep 17 00:00:00 2001 From: SomSamantray <> Date: Wed, 19 Aug 2026 10:02:12 +0530 Subject: [PATCH 06/11] feat(rueidiscompatmock): add ViaCache() to differentiate cached expectations Addresses review feedback on #1023: sharing ExpectGet between a plain Get and a Cache(ttl).Get() call meant a test couldn't require that a specific call went through the cache path. ViaCache() is an opt-in chainable modifier on every Expected* builder; a marked expectation only matches a call routed through DoCache, while unmarked expectations keep matching either origin exactly as before. --- rueidiscompatmock/README.md | 16 +++ rueidiscompatmock/adapter.go | 44 +++--- rueidiscompatmock/adapter_test.go | 73 ++++++++++ rueidiscompatmock/viacache.go | 226 ++++++++++++++++++++++++++++++ 4 files changed, 339 insertions(+), 20 deletions(-) create mode 100644 rueidiscompatmock/viacache.go diff --git a/rueidiscompatmock/README.md b/rueidiscompatmock/README.md index 3aaf5f72..640b07e1 100644 --- a/rueidiscompatmock/README.md +++ b/rueidiscompatmock/README.md @@ -58,4 +58,20 @@ compatmock.ExpectGet("key").SetVal("val") cached := rdb.Cache(100 * time.Millisecond) cached.Get(context.Background(), "key") +``` + +If a test needs to assert that a call went through the cache path specifically +(as opposed to a plain, non-cached call), chain `ViaCache()` onto the +expectation. A `ViaCache()` expectation is only satisfied by a call made +through `Cache(ttl)`; a plain call for the same command will not match it. +Expectations that don't call `ViaCache()` are unaffected and continue to match +calls from either origin. + +```golang +compatmock.ExpectGet("key").ViaCache().SetVal("val") + +cached := rdb.Cache(100 * time.Millisecond) +cached.Get(context.Background(), "key") // matches + +rdb.Get(context.Background(), "key") // would NOT match the ViaCache() expectation ``` \ No newline at end of file diff --git a/rueidiscompatmock/adapter.go b/rueidiscompatmock/adapter.go index e1300c81..a182adf0 100644 --- a/rueidiscompatmock/adapter.go +++ b/rueidiscompatmock/adapter.go @@ -369,16 +369,17 @@ type clientMock struct { } type expectation struct { - matcher gomock.Matcher - expected []string - result rueidis.RedisResult - rawResult any - err error - resultSet bool - rawSet bool - redisNil bool - regexpMatch bool - customMatch CustomMatch + matcher gomock.Matcher + expected []string + result rueidis.RedisResult + rawResult any + err error + resultSet bool + rawSet bool + redisNil bool + regexpMatch bool + customMatch CustomMatch + requireCache bool } type resolvedExpectation struct { @@ -553,29 +554,29 @@ func (m *clientMock) wire() { m.raw.EXPECT(). Do(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, cmd rueidis.Completed) rueidis.RedisResult { - return m.consume(1, []rueidis.Completed{cmd})[0] + return m.consume(1, []rueidis.Completed{cmd}, false)[0] }). AnyTimes() m.raw.EXPECT(). DoMulti(gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, cmds ...rueidis.Completed) []rueidis.RedisResult { - return m.consume(len(cmds), cmds) + return m.consume(len(cmds), cmds, false) }). AnyTimes() m.raw.EXPECT(). DoCache(gomock.Any(), gomock.Any(), gomock.Any()). DoAndReturn(func(_ context.Context, cmd rueidis.Cacheable, _ time.Duration) rueidis.RedisResult { - return m.consume(1, []rueidis.Completed{rueidis.Completed(cmd)})[0] + return m.consume(1, []rueidis.Completed{rueidis.Completed(cmd)}, true)[0] }). AnyTimes() } -func (m *clientMock) consume(n int, cmds []rueidis.Completed) []rueidis.RedisResult { +func (m *clientMock) consume(n int, cmds []rueidis.Completed, viaCache bool) []rueidis.RedisResult { m.mu.Lock() defer m.mu.Unlock() out := make([]rueidis.RedisResult, n) for i, cmd := range cmds { - idx, ok := m.matchLocked(cmd) + idx, ok := m.matchLocked(cmd, viaCache) if !ok { err := fmt.Errorf("rueidiscompatmock: no expectation for command %v", cmd.Commands()) m.unmatched = append(m.unmatched, err) @@ -594,7 +595,7 @@ func (m *clientMock) consume(n int, cmds []rueidis.Completed) []rueidis.RedisRes func (m *clientMock) consumeWithRaw(cmd rueidis.Completed) resolvedExpectation { m.mu.Lock() defer m.mu.Unlock() - idx, ok := m.matchLocked(cmd) + idx, ok := m.matchLocked(cmd, false) if !ok { err := fmt.Errorf("rueidiscompatmock: no expectation for command %v", cmd.Commands()) m.unmatched = append(m.unmatched, err) @@ -610,25 +611,28 @@ func (m *clientMock) consumeWithRaw(cmd rueidis.Completed) resolvedExpectation { return resolved } -func (m *clientMock) matchLocked(cmd rueidis.Completed) (int, bool) { +func (m *clientMock) matchLocked(cmd rueidis.Completed, viaCache bool) (int, bool) { if m.ordered { if len(m.queue) == 0 { return 0, false } - if !m.queue[0].matches(cmd) { + if !m.queue[0].matches(cmd, viaCache) { return 0, false } return 0, true } for i, e := range m.queue { - if e.matches(cmd) { + if e.matches(cmd, viaCache) { return i, true } } return 0, false } -func (e *expectation) matches(cmd rueidis.Completed) bool { +func (e *expectation) matches(cmd rueidis.Completed, viaCache bool) bool { + if e.requireCache && !viaCache { + return false + } if e.customMatch == nil && !e.regexpMatch { return e.matcher.Matches(cmd) } diff --git a/rueidiscompatmock/adapter_test.go b/rueidiscompatmock/adapter_test.go index 89142509..96f529eb 100644 --- a/rueidiscompatmock/adapter_test.go +++ b/rueidiscompatmock/adapter_test.go @@ -3198,6 +3198,79 @@ func TestCacheCompatUnmatched(t *testing.T) { } } +func TestCacheCompatViaCacheRequired(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("key").ViaCache().SetVal("v") + if got := rdb.Cache(time.Second).Get(ctx, "key").Val(); got != "v" { + t.Fatalf("expected %q, got %q", "v", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatViaCacheRejectsPlainCall(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("key").ViaCache().SetVal("v") + err := rdb.Get(ctx, "key").Err() + if err == nil || !strings.Contains(err.Error(), "no expectation for command") { + t.Fatalf("expected no-expectation error for plain call against a ViaCache expectation, got %v", err) + } + if err := cm.ExpectationsWereMet(); err == nil { + t.Fatal("expected the ViaCache expectation to remain unmatched") + } +} + +func TestCacheCompatViaCacheUnordered(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + raw := mock.NewClient(ctrl) + cm := NewAdapter(raw) + rdb := rueidiscompat.NewAdapter(raw) + cm.MatchExpectationsInOrder(false) + + // Two expectations for the same key: one plain, one cache-required. In + // unordered mode the scan must pick the one matching the call's origin, + // not just the first one for the command/key. + cm.ExpectGet("key").ViaCache().SetVal("cached") + cm.ExpectGet("key").SetVal("plain") + + if got := rdb.Get(ctx, "key").Val(); got != "plain" { + t.Fatalf("expected plain call to match the non-cache expectation, got %q", got) + } + if got := rdb.Cache(time.Second).Get(ctx, "key").Val(); got != "cached" { + t.Fatalf("expected cached call to match the ViaCache expectation, got %q", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatUnmarkedMatchesBothOrigins(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + cm.ExpectGet("k1").SetVal("plain") + if got := rdb.Get(ctx, "k1").Val(); got != "plain" { + t.Fatalf("expected %q, got %q", "plain", got) + } + + cm.ExpectGet("k2").SetVal("cached") + if got := rdb.Cache(time.Second).Get(ctx, "k2").Val(); got != "cached" { + t.Fatalf("expected %q, got %q", "cached", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + func runCases(t *testing.T, cm ClientMock, cases []func() error) { t.Helper() for i, c := range cases { diff --git a/rueidiscompatmock/viacache.go b/rueidiscompatmock/viacache.go new file mode 100644 index 00000000..cc90f2e8 --- /dev/null +++ b/rueidiscompatmock/viacache.go @@ -0,0 +1,226 @@ +package rueidiscompatmock + +// ViaCache marks this expectation so it is only satisfied by a call routed +// through DoCache (i.e. rueidiscompat.Cache(ttl).()). A plain, non-cached +// call for the same command will not match it. Expectations that do not call +// ViaCache are unaffected and continue to match calls from either origin. + +func (e *ExpectedString) ViaCache() *ExpectedString { + e.exp.requireCache = true + return e +} + +func (e *ExpectedBool) ViaCache() *ExpectedBool { + e.exp.requireCache = true + return e +} + +func (e *ExpectedInt) ViaCache() *ExpectedInt { + e.exp.requireCache = true + return e +} + +func (e *ExpectedDuration) ViaCache() *ExpectedDuration { + e.exp.requireCache = true + return e +} + +func (e *ExpectedFloat) ViaCache() *ExpectedFloat { + e.exp.requireCache = true + return e +} + +func (e *ExpectedStringSlice) ViaCache() *ExpectedStringSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedIntSlice) ViaCache() *ExpectedIntSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedBoolSlice) ViaCache() *ExpectedBoolSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedFloatSlice) ViaCache() *ExpectedFloatSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedSlice) ViaCache() *ExpectedSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedStringStringMap) ViaCache() *ExpectedStringStringMap { + e.exp.requireCache = true + return e +} + +func (e *ExpectedStringIntMap) ViaCache() *ExpectedStringIntMap { + e.exp.requireCache = true + return e +} + +func (e *ExpectedStringStructMap) ViaCache() *ExpectedStringStructMap { + e.exp.requireCache = true + return e +} + +func (e *ExpectedKeyValueSlice) ViaCache() *ExpectedKeyValueSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedKeyValues) ViaCache() *ExpectedKeyValues { + e.exp.requireCache = true + return e +} + +func (e *ExpectedZSlice) ViaCache() *ExpectedZSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedZWithKey) ViaCache() *ExpectedZWithKey { + e.exp.requireCache = true + return e +} + +func (e *ExpectedRankWithScore) ViaCache() *ExpectedRankWithScore { + e.exp.requireCache = true + return e +} + +func (e *ExpectedTime) ViaCache() *ExpectedTime { + e.exp.requireCache = true + return e +} + +func (e *ExpectedScan) ViaCache() *ExpectedScan { + e.exp.requireCache = true + return e +} + +func (e *ExpectedGeoPos) ViaCache() *ExpectedGeoPos { + e.exp.requireCache = true + return e +} + +func (e *ExpectedGeoLocation) ViaCache() *ExpectedGeoLocation { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXMessageSlice) ViaCache() *ExpectedXMessageSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXStreamSlice) ViaCache() *ExpectedXStreamSlice { + e.exp.requireCache = true + return e +} + +func (e *ExpectedCmd) ViaCache() *ExpectedCmd { + e.exp.requireCache = true + return e +} + +func (e *ExpectedCommandsInfo) ViaCache() *ExpectedCommandsInfo { + e.exp.requireCache = true + return e +} + +func (e *ExpectedKeyFlags) ViaCache() *ExpectedKeyFlags { + e.exp.requireCache = true + return e +} + +func (e *ExpectedSlowLog) ViaCache() *ExpectedSlowLog { + e.exp.requireCache = true + return e +} + +func (e *ExpectedOKBool) ViaCache() *ExpectedOKBool { + e.exp.requireCache = true + return e +} + +func (e *ExpectedClusterSlots) ViaCache() *ExpectedClusterSlots { + e.exp.requireCache = true + return e +} + +func (e *ExpectedClusterShards) ViaCache() *ExpectedClusterShards { + e.exp.requireCache = true + return e +} + +func (e *ExpectedClusterLinks) ViaCache() *ExpectedClusterLinks { + e.exp.requireCache = true + return e +} + +func (e *ExpectedLCS) ViaCache() *ExpectedLCS { + e.exp.requireCache = true + return e +} + +func (e *ExpectedZSliceWithKey) ViaCache() *ExpectedZSliceWithKey { + e.exp.requireCache = true + return e +} + +func (e *ExpectedScriptExists) ViaCache() *ExpectedScriptExists { + e.exp.requireCache = true + return e +} + +func (e *ExpectedFunctionList) ViaCache() *ExpectedFunctionList { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXAutoClaim) ViaCache() *ExpectedXAutoClaim { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXAutoClaimJustID) ViaCache() *ExpectedXAutoClaimJustID { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXInfoConsumers) ViaCache() *ExpectedXInfoConsumers { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXInfoGroups) ViaCache() *ExpectedXInfoGroups { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXInfoStream) ViaCache() *ExpectedXInfoStream { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXInfoStreamFull) ViaCache() *ExpectedXInfoStreamFull { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXPending) ViaCache() *ExpectedXPending { + e.exp.requireCache = true + return e +} + +func (e *ExpectedXPendingExt) ViaCache() *ExpectedXPendingExt { + e.exp.requireCache = true + return e +} From 814612fdce4b8bc87a1123c81a5da98a20b2724e Mon Sep 17 00:00:00 2001 From: SomSamantray <> Date: Wed, 19 Aug 2026 10:14:27 +0530 Subject: [PATCH 07/11] fix(rueidiscompatmock): unordered mode must prefer cache-specific expectations Cursor Bugbot found that in MatchExpectationsInOrder(false) mode, an unmarked expectation queued ahead of a ViaCache()-marked expectation for the same command could "steal" a cached call, since the unordered scan picked the first matching entry regardless of specificity. matchLocked now scans for a requireCache=true candidate first when the call arrived via DoCache, falling back to the original scan otherwise. Plain calls are unaffected. Verified the new regression test actually fails without the fix before restoring it. --- rueidiscompatmock/adapter.go | 10 +++++++ rueidiscompatmock/adapter_test.go | 47 +++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+) diff --git a/rueidiscompatmock/adapter.go b/rueidiscompatmock/adapter.go index a182adf0..9d9b814f 100644 --- a/rueidiscompatmock/adapter.go +++ b/rueidiscompatmock/adapter.go @@ -621,6 +621,16 @@ func (m *clientMock) matchLocked(cmd rueidis.Completed, viaCache bool) (int, boo } return 0, true } + if viaCache { + // Prefer a cache-specific expectation over a generic (unmarked) one, + // regardless of queue order, so a ViaCache() expectation isn't stolen + // by an unmarked expectation queued ahead of it for the same command. + for i, e := range m.queue { + if e.requireCache && e.matches(cmd, viaCache) { + return i, true + } + } + } for i, e := range m.queue { if e.matches(cmd, viaCache) { return i, true diff --git a/rueidiscompatmock/adapter_test.go b/rueidiscompatmock/adapter_test.go index 96f529eb..840bc7f4 100644 --- a/rueidiscompatmock/adapter_test.go +++ b/rueidiscompatmock/adapter_test.go @@ -3252,6 +3252,53 @@ func TestCacheCompatViaCacheUnordered(t *testing.T) { } } +func TestCacheCompatViaCacheUnorderedGenericQueuedFirst(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + raw := mock.NewClient(ctrl) + cm := NewAdapter(raw) + rdb := rueidiscompat.NewAdapter(raw) + cm.MatchExpectationsInOrder(false) + + // Same as TestCacheCompatViaCacheUnordered but with queue order reversed: + // the unmarked (generic) expectation is queued BEFORE the ViaCache() + // expectation. A cached call must still prefer the cache-specific entry + // rather than "stealing" the generic one queued ahead of it. + cm.ExpectGet("key").SetVal("plain") + cm.ExpectGet("key").ViaCache().SetVal("cached") + + if got := rdb.Cache(time.Second).Get(ctx, "key").Val(); got != "cached" { + t.Fatalf("expected cached call to match the ViaCache expectation, got %q", got) + } + if got := rdb.Get(ctx, "key").Val(); got != "plain" { + t.Fatalf("expected the remaining plain call to match the unmarked expectation, got %q", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + +func TestCacheCompatViaCacheUnorderedFallsBackToGeneric(t *testing.T) { + ctx := context.Background() + ctrl := gomock.NewController(t) + raw := mock.NewClient(ctrl) + cm := NewAdapter(raw) + rdb := rueidiscompat.NewAdapter(raw) + cm.MatchExpectationsInOrder(false) + + // No cache-specific expectation is queued at all; a cached call must + // still fall back to matching the first generic expectation, exactly as + // before this fix. + cm.ExpectGet("key").SetVal("plain") + + if got := rdb.Cache(time.Second).Get(ctx, "key").Val(); got != "plain" { + t.Fatalf("expected cached call to fall back to the unmarked expectation, got %q", got) + } + if err := cm.ExpectationsWereMet(); err != nil { + t.Fatalf("unexpected err %v", err) + } +} + func TestCacheCompatUnmarkedMatchesBothOrigins(t *testing.T) { ctx := context.Background() rdb, cm := NewClientMock() From 3852a76a251d97ee77f9cee52ed40b619e771807 Mon Sep 17 00:00:00 2001 From: Som Date: Sun, 23 Aug 2026 02:35:41 +0000 Subject: [PATCH 08/11] fix(rueidiscompatmock): require ViaCache() symmetrically for cache-routed calls Per @rueian's review on #1023: a call routed through Cache(ttl) could previously be satisfied by an unmarked expectation as a compatibility fallback, so ExpectationsWereMet() passed even when a test never asserted the call went through the cache path. Matching is now symmetric: expectation.matches rejects the call whenever e.requireCache != viaCache, so a plain ExpectGet only matches a plain call and a ViaCache() expectation only matches a cache-routed call. Updates the TestCacheCompat* suite for the new contract and replaces the two tests that codified the old fallback with tests proving it no longer applies. Documents the symmetric behavior in rueidiscompatmock/README.md. --- rueidiscompatmock/README.md | 31 ++++++++++--------- rueidiscompatmock/adapter.go | 12 +------- rueidiscompatmock/adapter_test.go | 49 ++++++++++++++++--------------- 3 files changed, 43 insertions(+), 49 deletions(-) diff --git a/rueidiscompatmock/README.md b/rueidiscompatmock/README.md index 640b07e1..228b51c9 100644 --- a/rueidiscompatmock/README.md +++ b/rueidiscompatmock/README.md @@ -50,28 +50,31 @@ p.Exec(ctx) ### Client-side caching example -Cached commands (`Cache(ttl)`) use the same `Expect*` calls as non-cached commands. -The TTL argument is not matched against expectations. +Cached commands (`Cache(ttl)`) use the same `Expect*` calls as non-cached +commands, but expectation matching is origin-aware: a call made through +`Cache(ttl)` only matches an expectation marked `ViaCache()`, and a plain call +only matches an expectation without it. The TTL argument itself is not +matched against expectations. ```golang -compatmock.ExpectGet("key").SetVal("val") +compatmock.ExpectGet("key").ViaCache().SetVal("val") cached := rdb.Cache(100 * time.Millisecond) -cached.Get(context.Background(), "key") +cached.Get(context.Background(), "key") // matches + +rdb.Get(context.Background(), "key") // would NOT match the ViaCache() expectation ``` -If a test needs to assert that a call went through the cache path specifically -(as opposed to a plain, non-cached call), chain `ViaCache()` onto the -expectation. A `ViaCache()` expectation is only satisfied by a call made -through `Cache(ttl)`; a plain call for the same command will not match it. -Expectations that don't call `ViaCache()` are unaffected and continue to match -calls from either origin. +The reverse also holds: an expectation without `ViaCache()` only matches a +plain call and will not be satisfied by a call routed through `Cache(ttl)`. +`ExpectationsWereMet()` reports an unmatched expectation when the two origins +are mixed. ```golang -compatmock.ExpectGet("key").ViaCache().SetVal("val") +compatmock.ExpectGet("key").SetVal("val") -cached := rdb.Cache(100 * time.Millisecond) -cached.Get(context.Background(), "key") // matches +rdb.Get(context.Background(), "key") // matches -rdb.Get(context.Background(), "key") // would NOT match the ViaCache() expectation +cached := rdb.Cache(100 * time.Millisecond) +cached.Get(context.Background(), "key") // would NOT match the plain expectation ``` \ No newline at end of file diff --git a/rueidiscompatmock/adapter.go b/rueidiscompatmock/adapter.go index 9d9b814f..fa8fd856 100644 --- a/rueidiscompatmock/adapter.go +++ b/rueidiscompatmock/adapter.go @@ -621,16 +621,6 @@ func (m *clientMock) matchLocked(cmd rueidis.Completed, viaCache bool) (int, boo } return 0, true } - if viaCache { - // Prefer a cache-specific expectation over a generic (unmarked) one, - // regardless of queue order, so a ViaCache() expectation isn't stolen - // by an unmarked expectation queued ahead of it for the same command. - for i, e := range m.queue { - if e.requireCache && e.matches(cmd, viaCache) { - return i, true - } - } - } for i, e := range m.queue { if e.matches(cmd, viaCache) { return i, true @@ -640,7 +630,7 @@ func (m *clientMock) matchLocked(cmd rueidis.Completed, viaCache bool) (int, boo } func (e *expectation) matches(cmd rueidis.Completed, viaCache bool) bool { - if e.requireCache && !viaCache { + if e.requireCache != viaCache { return false } if e.customMatch == nil && !e.regexpMatch { diff --git a/rueidiscompatmock/adapter_test.go b/rueidiscompatmock/adapter_test.go index 840bc7f4..c9b33dba 100644 --- a/rueidiscompatmock/adapter_test.go +++ b/rueidiscompatmock/adapter_test.go @@ -3071,7 +3071,7 @@ func TestCacheCompatGet(t *testing.T) { rdb, cm := NewClientMock() defer rdb.Close() - cm.ExpectGet("key").SetVal("v") + cm.ExpectGet("key").ViaCache().SetVal("v") cmd := rdb.Cache(100 * time.Millisecond).Get(ctx, "key") if got := cmd.Val(); got != "v" { t.Fatalf("expected %q, got %q", "v", got) @@ -3089,7 +3089,7 @@ func TestCacheCompatViaNewAdapter(t *testing.T) { cm := NewAdapter(raw) rdb := rueidiscompat.NewAdapter(raw) - cm.ExpectGet("key").SetVal("v") + cm.ExpectGet("key").ViaCache().SetVal("v") cmd := rdb.Cache(time.Second).Get(ctx, "key") if got := cmd.Val(); got != "v" { t.Fatalf("expected %q, got %q", "v", got) @@ -3104,9 +3104,9 @@ func TestCacheCompatMultiCommand(t *testing.T) { rdb, cm := NewClientMock() defer rdb.Close() - cm.ExpectMGet("k1", "k2").SetVal([]any{"v1", "v2"}) - cm.ExpectHGetAll("h").SetVal(map[string]string{"f": "v"}) - cm.ExpectGetRange("s", 0, 1).SetVal("ab") + cm.ExpectMGet("k1", "k2").ViaCache().SetVal([]any{"v1", "v2"}) + cm.ExpectHGetAll("h").ViaCache().SetVal(map[string]string{"f": "v"}) + cm.ExpectGetRange("s", 0, 1).ViaCache().SetVal("ab") cache := rdb.Cache(time.Minute) if got := cache.MGet(ctx, "k1", "k2").Val(); !reflect.DeepEqual(got, []any{"v1", "v2"}) { @@ -3128,7 +3128,7 @@ func TestCacheCompatCacheableWrapped(t *testing.T) { rdb, cm := NewClientMock() defer rdb.Close() - cm.ExpectEvalRO("return 1", []string{}).SetValInt(1) + cm.ExpectEvalRO("return 1", []string{}).ViaCache().SetValInt(1) cmd := rdb.Cache(time.Second).EvalRO(ctx, "return 1", []string{}) if got := cmd.Val(); got != int64(1) { t.Fatalf("unexpected EvalRO result %v", got) @@ -3143,7 +3143,7 @@ func TestCacheCompatRedisNil(t *testing.T) { rdb, cm := NewClientMock() defer rdb.Close() - cm.ExpectGet("key").RedisNil() + cm.ExpectGet("key").ViaCache().RedisNil() if err := rdb.Cache(time.Second).Get(ctx, "key").Err(); !errors.Is(err, rueidiscompat.Nil) { t.Fatalf("expected Nil, got %v", err) } @@ -3157,7 +3157,7 @@ func TestCacheCompatSetErr(t *testing.T) { rdb, cm := NewClientMock() defer rdb.Close() - cm.ExpectGet("key").SetErr(errors.New("any")) + cm.ExpectGet("key").ViaCache().SetErr(errors.New("any")) if err := wantAnyErr(rdb.Cache(time.Second).Get(ctx, "key").Err()); err != nil { t.Fatal(err) } @@ -3171,8 +3171,8 @@ func TestCacheCompatTTLIrrelevant(t *testing.T) { rdb, cm := NewClientMock() defer rdb.Close() - cm.ExpectGet("k").SetVal("a") - cm.ExpectGet("k").SetVal("b") + cm.ExpectGet("k").ViaCache().SetVal("a") + cm.ExpectGet("k").ViaCache().SetVal("b") if got := rdb.Cache(time.Millisecond).Get(ctx, "k").Val(); got != "a" { t.Fatalf("expected %q, got %q", "a", got) } @@ -3278,7 +3278,7 @@ func TestCacheCompatViaCacheUnorderedGenericQueuedFirst(t *testing.T) { } } -func TestCacheCompatViaCacheUnorderedFallsBackToGeneric(t *testing.T) { +func TestCacheCompatViaCacheUnorderedRequiresViaCache(t *testing.T) { ctx := context.Background() ctrl := gomock.NewController(t) raw := mock.NewClient(ctrl) @@ -3286,20 +3286,20 @@ func TestCacheCompatViaCacheUnorderedFallsBackToGeneric(t *testing.T) { rdb := rueidiscompat.NewAdapter(raw) cm.MatchExpectationsInOrder(false) - // No cache-specific expectation is queued at all; a cached call must - // still fall back to matching the first generic expectation, exactly as - // before this fix. + // No cache-specific expectation is queued at all; a cached call must not + // fall back to the unmarked expectation for the same command. cm.ExpectGet("key").SetVal("plain") - if got := rdb.Cache(time.Second).Get(ctx, "key").Val(); got != "plain" { - t.Fatalf("expected cached call to fall back to the unmarked expectation, got %q", got) + err := rdb.Cache(time.Second).Get(ctx, "key").Err() + if err == nil || !strings.Contains(err.Error(), "no expectation for command") { + t.Fatalf("expected no-expectation error for cached call against only an unmarked expectation, got %v", err) } - if err := cm.ExpectationsWereMet(); err != nil { - t.Fatalf("unexpected err %v", err) + if err := cm.ExpectationsWereMet(); err == nil { + t.Fatal("expected the unmarked expectation to remain unmatched") } } -func TestCacheCompatUnmarkedMatchesBothOrigins(t *testing.T) { +func TestCacheCompatUnmarkedMatchesPlainOnly(t *testing.T) { ctx := context.Background() rdb, cm := NewClientMock() defer rdb.Close() @@ -3309,12 +3309,13 @@ func TestCacheCompatUnmarkedMatchesBothOrigins(t *testing.T) { t.Fatalf("expected %q, got %q", "plain", got) } - cm.ExpectGet("k2").SetVal("cached") - if got := rdb.Cache(time.Second).Get(ctx, "k2").Val(); got != "cached" { - t.Fatalf("expected %q, got %q", "cached", got) + cm.ExpectGet("k2").SetVal("plain") + err := rdb.Cache(time.Second).Get(ctx, "k2").Err() + if err == nil || !strings.Contains(err.Error(), "no expectation for command") { + t.Fatalf("expected no-expectation error for cached call against an unmarked expectation, got %v", err) } - if err := cm.ExpectationsWereMet(); err != nil { - t.Fatalf("unexpected err %v", err) + if err := cm.ExpectationsWereMet(); err == nil { + t.Fatal("expected the unmarked expectation for k2 to remain unmatched") } } From 5efc25d53bb433526dc96e5aeee51cfc554a45e7 Mon Sep 17 00:00:00 2001 From: Som Date: Sun, 23 Aug 2026 04:01:21 +0000 Subject: [PATCH 09/11] docs(rueidiscompatmock): fix stale ViaCache() doc comment The comment on ViaCache() still described the old "unmarked expectations match either origin" fallback that was removed in 3852a76. Flagged as stale by @rueian on #1023. --- rueidiscompatmock/viacache.go | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/rueidiscompatmock/viacache.go b/rueidiscompatmock/viacache.go index cc90f2e8..ee6b4016 100644 --- a/rueidiscompatmock/viacache.go +++ b/rueidiscompatmock/viacache.go @@ -3,7 +3,8 @@ package rueidiscompatmock // ViaCache marks this expectation so it is only satisfied by a call routed // through DoCache (i.e. rueidiscompat.Cache(ttl).()). A plain, non-cached // call for the same command will not match it. Expectations that do not call -// ViaCache are unaffected and continue to match calls from either origin. +// ViaCache match only plain calls and will not be satisfied by a call routed +// through DoCache. func (e *ExpectedString) ViaCache() *ExpectedString { e.exp.requireCache = true From a6410a993384f761c67da4db0d464dbd71087759 Mon Sep 17 00:00:00 2001 From: Som Date: Sun, 23 Aug 2026 04:49:55 +0000 Subject: [PATCH 10/11] fix(rueidiscompatmock): distinguish cache-origin in unmatched-expectation error, remove duplicate test, gofmt Addresses three review comments from @rueian on #1023: - adapter.go: the "no expectation for command" error now appends "via cache" when the unmatched call was routed through DoCache, so a forgotten .ViaCache() is distinguishable from an ordinary mismatch at a glance. Factored into a shared unmatchedErr helper used by both consume and consumeWithRaw. Added TestCacheCompatUnmatchedPlainCallOmitsViaCache and tightened TestCacheCompatUnmatched to assert the suffix. - adapter_test.go: removed TestCacheCompatGet, which became an exact duplicate of TestCacheCompatViaCacheRequired once ViaCache() was added to it in 3852a76. - adapter_test.go: applied gofmt (100*time.Millisecond, no spaces around *) per review suggestion. --- rueidiscompatmock/adapter.go | 11 +++++++++-- rueidiscompatmock/adapter_test.go | 33 +++++++++++++++---------------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/rueidiscompatmock/adapter.go b/rueidiscompatmock/adapter.go index fa8fd856..b9f0caeb 100644 --- a/rueidiscompatmock/adapter.go +++ b/rueidiscompatmock/adapter.go @@ -571,6 +571,13 @@ func (m *clientMock) wire() { AnyTimes() } +func unmatchedErr(cmd rueidis.Completed, viaCache bool) error { + if viaCache { + return fmt.Errorf("rueidiscompatmock: no expectation for command %v via cache", cmd.Commands()) + } + return fmt.Errorf("rueidiscompatmock: no expectation for command %v", cmd.Commands()) +} + func (m *clientMock) consume(n int, cmds []rueidis.Completed, viaCache bool) []rueidis.RedisResult { m.mu.Lock() defer m.mu.Unlock() @@ -578,7 +585,7 @@ func (m *clientMock) consume(n int, cmds []rueidis.Completed, viaCache bool) []r for i, cmd := range cmds { idx, ok := m.matchLocked(cmd, viaCache) if !ok { - err := fmt.Errorf("rueidiscompatmock: no expectation for command %v", cmd.Commands()) + err := unmatchedErr(cmd, viaCache) m.unmatched = append(m.unmatched, err) out[i] = mock.ErrorResult(err) for j := i + 1; j < n; j++ { @@ -597,7 +604,7 @@ func (m *clientMock) consumeWithRaw(cmd rueidis.Completed) resolvedExpectation { defer m.mu.Unlock() idx, ok := m.matchLocked(cmd, false) if !ok { - err := fmt.Errorf("rueidiscompatmock: no expectation for command %v", cmd.Commands()) + err := unmatchedErr(cmd, false) m.unmatched = append(m.unmatched, err) return resolvedExpectation{result: mock.ErrorResult(err)} } diff --git a/rueidiscompatmock/adapter_test.go b/rueidiscompatmock/adapter_test.go index c9b33dba..58199e8c 100644 --- a/rueidiscompatmock/adapter_test.go +++ b/rueidiscompatmock/adapter_test.go @@ -3066,21 +3066,6 @@ func wantAnyErr(err error) error { return nil } -func TestCacheCompatGet(t *testing.T) { - ctx := context.Background() - rdb, cm := NewClientMock() - defer rdb.Close() - - cm.ExpectGet("key").ViaCache().SetVal("v") - cmd := rdb.Cache(100 * time.Millisecond).Get(ctx, "key") - if got := cmd.Val(); got != "v" { - t.Fatalf("expected %q, got %q", "v", got) - } - if err := cm.ExpectationsWereMet(); err != nil { - t.Fatalf("unexpected err %v", err) - } -} - func TestCacheCompatViaNewAdapter(t *testing.T) { ctrl := gomock.NewController(t) defer ctrl.Finish() @@ -3190,8 +3175,22 @@ func TestCacheCompatUnmatched(t *testing.T) { defer rdb.Close() err := rdb.Cache(time.Second).Get(ctx, "key").Err() - if err == nil || !strings.Contains(err.Error(), "no expectation for command") { - t.Fatalf("expected no-expectation error, got %v", err) + if err == nil || !strings.Contains(err.Error(), "no expectation for command") || !strings.Contains(err.Error(), "via cache") { + t.Fatalf(`expected no-expectation error mentioning "via cache", got %v`, err) + } + if err := cm.ExpectationsWereMet(); err == nil { + t.Fatal("expected unmatched expectation error") + } +} + +func TestCacheCompatUnmatchedPlainCallOmitsViaCache(t *testing.T) { + ctx := context.Background() + rdb, cm := NewClientMock() + defer rdb.Close() + + err := rdb.Get(ctx, "key").Err() + if err == nil || !strings.Contains(err.Error(), "no expectation for command") || strings.Contains(err.Error(), "via cache") { + t.Fatalf(`expected no-expectation error without "via cache", got %v`, err) } if err := cm.ExpectationsWereMet(); err == nil { t.Fatal("expected unmatched expectation error") From def365b1c7b630456aa2f89d32ce7547ee85e325 Mon Sep 17 00:00:00 2001 From: Rueian Date: Sun, 23 Aug 2026 20:43:25 -0700 Subject: [PATCH 11/11] Update rueidiscompatmock/viacache.go --- rueidiscompatmock/viacache.go | 1 - 1 file changed, 1 deletion(-) diff --git a/rueidiscompatmock/viacache.go b/rueidiscompatmock/viacache.go index ee6b4016..530fb678 100644 --- a/rueidiscompatmock/viacache.go +++ b/rueidiscompatmock/viacache.go @@ -5,7 +5,6 @@ package rueidiscompatmock // call for the same command will not match it. Expectations that do not call // ViaCache match only plain calls and will not be satisfied by a call routed // through DoCache. - func (e *ExpectedString) ViaCache() *ExpectedString { e.exp.requireCache = true return e