Skip to content

[TEST] MC/DC Integration Test Suite (Redis Module) #27

Description

@VladRodionov

MC/DC Integration Test Suite (Redis Module)

Summary

Introduce and maintain a Python-based integration test suite for the MC/DC Redis module that runs against a real Redis server with the module loaded. The tests validate command behavior, compression logic, and the command filter layer end‑to‑end.

The suite is executed via:

make itest

This target will:

  • Build the MC/DC module (mcdc.so)
  • Create / reuse a Python virtualenv (.venv)
  • Install Python test dependencies from test/integration/requirements.txt
  • Start a temporary Redis instance with MC/DC loaded
  • Run pytest on test/integration/

Scope

1. Redis test harness

  • test/integration/conftest.py:
    • Spawns a temporary Redis instance on a random free port using redis-server.
    • Loads the MC/DC module using --loadmodule with a test config:
      • MCDC_MODULE_PATH points to the built mcdc.so (defaults to ./build/mcdc.so).
      • Uses cfg=./test/integration/mcdc.conf so tests have a stable MC/DC configuration.
    • Waits for Redis to become ready (via PING).
    • Provides a redis_server session fixture that owns process lifecycle.
    • Provides an r fixture that:
      • Creates a Redis client bound to the test server
      • Calls FLUSHALL before each test to isolate state.

2. String command tests (test_strings.py)

Covers both direct MC/DC commands and filter-based rewrites.

Direct MC/DC string commands

  • mcdc.set, mcdc.get round‑trip, for:

    • Small, non‑compressible values (e.g., "hello").
    • Large, highly compressible values (e.g., "X" * 512).
  • Uses:

    • STRLEN key → logical size.
    • mcdc.cstrlen key → compressed / stored size.
  • Assertions:

    • For small values (≤ 32 bytes): cstrlen >= strlen (no compression benefit expected).
    • For large compressible values: cstrlen < strlen.
  • Similar coverage for:

    • mcdc.setex
    • mcdc.setnx
    • mcdc.psetex
    • mcdc.getex

Filter rewrites for strings

Verifies that plain Redis commands are transparently rewritten by the command filter when the key is in an MC/DC namespace (e.g., mcdc: prefix):

  • SET key val → behaves like mcdc.set.
  • GET key → behaves like mcdc.get.
  • STRLEN key → routed to mcdc.strlen.

Tests check:

  • Value equality on readback.
  • Logical vs compressed size via STRLEN vs mcdc.cstrlen.

Async string commands

  • mcdc.msetasync
  • mcdc.mgetasync

Tests:

  • Use multiple keys and values (mix of small + highly compressible values).
  • Assert that:
    • mcdc.msetasync returns an OK‑style status (non‑error).
    • Subsequent mcdc.mgetasync returns a list with values equal to what was written.
    • Compression behavior still holds for large values (using mcdc.cstrlen).

3. Hash command tests (test_hashes.py)

Covers all hash commands implemented by MC/DC and their filter rewrites.

Direct MC/DC hash commands

  • mcdc.hset / mcdc.hget round‑trip for:
    • Small values.
    • Large, compressible values.
  • Compression verification:
    • Uses mcdc.hcstrlen key field to check stored size vs logical length.

Other covered commands:

  • mcdc.hmget / mcdc.hset with multiple fields
  • mcdc.hsetnx / mcdc.hgetdel
  • mcdc.hvals
  • mcdc.hgetall
  • mcdc.hstrlen
  • mcdc.hrandfield

Assertions include:

  • Field presence and value equality.
  • Correct length reporting via HSTRLEN / mcdc.hcstrlen.
  • For compressible values: compressed size < logical size.

Filter rewrites for hashes

Verifies that plain Redis hash commands are rewritten when the key is in an MC/DC namespace:

  • HSETmcdc.hset / mcdc.hsetasync (depending on config)
  • HMGETmcdc.hmget / mcdc.hmgetasync
  • HVALSmcdc.hvals
  • HGETALLmcdc.hgetall
  • HSTRLENmcdc.hstrlen
  • HGETmcdc.hget
  • HRANDFIELDmcdc.hrandfield

Tests ensure:

  • Hash semantics remain identical to plain Redis behavior.
  • Values are transparently compressed/decompressed.
  • The filter does not interfere with non‑MC/DC keys.

Async hash commands

  • mcdc.hsetasync
  • mcdc.hmgetasync

Tests:

  • Write multiple fields using mcdc.hsetasync.
  • Read them via mcdc.hmgetasync and plain HMGET.
  • Assert value equality and proper compression for large fields using mcdc.hcstrlen.

4. Redis version–specific behavior

Where needed (e.g., commands introduced in Redis 8+ like HSETEX), tests:

  • Detect Redis server version via INFO SERVER.
  • Skip tests if a feature is not available (e.g., running against Redis 7.2.5).

This keeps the suite compatible with current dev environments while still documenting behavior for newer versions.


Makefile Integration

The itest target:

  • Depends on the Redis module being built: $(TARGET).
  • Ensures a Python virtualenv exists (.venv).
  • Installs dependencies from test/integration/requirements.txt.
  • Runs pytest inside the venv, pointing MCDC_MODULE_PATH to the built module.

Example wiring:

PYTHON      ?= python3
VENV_DIR    ?= .venv
ITEST_DIR   ?= test/integration
REQ_FILE    ?= $(ITEST_DIR)/requirements.txt

PYTHON_BIN  := $(VENV_DIR)/bin/python
PIP_BIN     := $(VENV_DIR)/bin/pip
PYTEST_BIN  := $(VENV_DIR)/bin/pytest

.PHONY: venv
venv:
	@if [ ! -d "$(VENV_DIR)" ]; then \
	  echo "Creating Python virtualenv in $(VENV_DIR)..."; \
	  $(PYTHON) -m venv $(VENV_DIR); \
	fi; \
	echo "Upgrading pip..."; \
	"$(PIP_BIN)" install --upgrade pip >/dev/null; \
	if [ -f "$(REQ_FILE)" ]; then \
	  echo "Installing Python dependencies from $(REQ_FILE)..."; \
	  "$(PIP_BIN)" install -r "$(REQ_FILE)"; \
	else \
	  echo "WARNING: $(REQ_FILE) not found; skipping dependency install."; \
	fi

.PHONY: itest
itest: $(TARGET) venv
	@if [ ! -x "$(PYTEST_BIN)" ]; then \
	  echo "ERROR: pytest not installed in $(VENV_DIR)."; \
	  exit 1; \
	fi; \
	if [ ! -d "$(ITEST_DIR)" ]; then \
	  echo "ERROR: integration test directory '$(ITEST_DIR)' not found."; \
	  exit 1; \
	fi; \
	echo "Running integration tests using $(PYTEST_BIN) on $(ITEST_DIR)"; \
	MCDC_MODULE_PATH="$(TARGET)" "$(PYTEST_BIN)" -q "$(ITEST_DIR)"

Dependencies

test/integration/requirements.txt:

pytest>=8.0
pytest-timeout>=2.3
pytest-asyncio>=0.23
redis>=7.0

Acceptance Criteria

  • make itest builds the module, sets up the venv, starts Redis, and runs all tests.
  • All string and hash MC/DC commands have integration coverage (direct + filtered).
  • Both compressible and non‑compressible values are tested via mcdc.cstrlen / mcdc.hcstrlen.
  • Async commands (mcdc.mgetasync, mcdc.msetasync, mcdc.hmgetasync, mcdc.hsetasync) are fully validated (values and compression), not just “no error”.
  • Non‑MC/DC keys behave like vanilla Redis (no filter interference).
  • Tests pass on Redis 7.2.x with MC/DC loaded, with version‑specific tests skipped when features are unavailable.

Metadata

Metadata

Assignees

Labels

enhancementNew feature or request

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions