Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 86 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -173,16 +173,18 @@ cfg-if = "1"
valkey-module-macros-internals = { path = "valkeymodule-rs-macros-internals", version = "0.1.4"}
log = "0.4"
paste = "1.0.15"
mockall = { version = "0.14.0", optional = true }

[dev-dependencies]
anyhow = "1"
redis = "0.28"
lazy_static = "1"
valkey-module-macros = { path = "valkeymodule-rs-macros", version = "0.1.4" }
valkey-module = { path = "./", default-features = false, features = ["min-valkey-compatibility-version-8-0", "min-redis-compatibility-version-7-2"] }
valkey-module = { path = "./", default-features = false, features = ["min-valkey-compatibility-version-8-0", "min-redis-compatibility-version-7-2", "test-mocks", "enable-system-alloc"] }
cron = "0.15.0"
chrono = "0.4.41"
dashmap = "6.1.0"
mockall = { version = "0.14.0"}

[build-dependencies]
bindgen = "0.70"
Expand All @@ -199,3 +201,5 @@ min-redis-compatibility-version-6-0 = []
enable-system-alloc = []
# this is to indicate the Module wants to use RedisModule APIs for calls
use-redismodule-api = []
# exposes MockContext (generated via mockall) for downstream unit tests
test-mocks = ["dep:mockall"]
39 changes: 39 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,42 @@ default = []
```
cargo build --release --features use-redismodule-api
```

3. Mock contexts for unit tests

`Context`, `CommandFilterCtx`, and `InfoContext` are thin wrappers around raw pointers that Valkey hands to the module at runtime. To unit-test module logic without a live Valkey server, `valkey-module` exposes three trait abstractions — `ContextTrait`, `CommandFilterCtxTrait`, `InfoContextTrait` — each implemented for the concrete wrapper, plus `mockall`-generated mocks behind the `test-mocks` feature.

The traits are always available; only the `Mock*` types require the feature. Add `valkey-module` as a `dev-dependency` with `test-mocks` enabled:

```toml
[dev-dependencies]
valkey-module = { version = "...", features = ["test-mocks"] }
mockall = "0.14"
```

Write your command / filter / info handler against the trait (`&impl ContextTrait`) instead of the concrete `&Context`. Monomorphization still produces a `fn(&Context, ...)` for the `valkey_module!` macro to register.

```rust
use valkey_module::{ContextTrait, ValkeyResult, ValkeyString, ValkeyValue};

fn get_client_id(ctx: &impl ContextTrait, _args: Vec<ValkeyString>) -> ValkeyResult {
Ok((ctx.get_client_id() as i64).into())
}

#[cfg(test)]
mod tests {
use super::*;
use valkey_module::MockContext;

#[test]
fn returns_client_id_from_context() {
let mut ctx = MockContext::new();
ctx.expect_get_client_id().times(1).returning(|| 42);

let reply = get_client_id(&ctx, vec![ValkeyString::create_for_test("")]).unwrap();
assert_eq!(reply, ValkeyValue::Integer(42));
}
}
```

`MockCommandFilterCtx` and `MockInfoContext` follow the same pattern. See `examples/client.rs`, `examples/preload.rs`, `examples/server_events.rs`, and `examples/info_handler_struct.rs` for full working tests.
Loading