Skip to content

Testing Helpers

chumicro_kvstore.testing provides FakeKVStore, an in-memory KVStore with explicit corruption and capacity hooks for your tests. It runs on the same in-memory backend the auto-selector picks on a host, so every assertion you write goes through the real KVStore code path rather than a mock of it. The module declares the __chumicro_test_support__ marker, and the deploy tool drops every module carrying it, so the fake never ships to a board.

Usage

from chumicro_kvstore.testing import FakeKVStore

def test_boot_counter_persists():
    store = FakeKVStore()
    store["boot_count"] = 1
    store.commit()

    # Re-construct against the same underlying payload to simulate reboot.
    fresh = FakeKVStore(initial_payload=store.raw_payload)
    assert fresh["boot_count"] == 1

Simulating a small NVM

CircuitPython NVM is the tightest backend: typically 256 bytes on SAMD21 boards, around 4 KB on RP2040, and 8 KB on SAMD51 and ESP32 boards, minus 10 bytes of CRC framing. Use capacity= to drive KVStoreFull deterministically without needing the real hardware:

from chumicro_kvstore import KVStoreFull
from chumicro_kvstore.testing import FakeKVStore

def test_dropping_keys_recovers_from_full():
    store = FakeKVStore(capacity=64)            # tight on purpose
    store["a"] = "x" * 32
    store["b"] = "y" * 32
    try:
        store.commit()
        raise AssertionError("expected KVStoreFull")
    except KVStoreFull:
        del store["b"]
        store.commit()                          # succeeds now

    assert store.bytes_used <= 64

set_capacity(new_capacity) adjusts the limit mid-test if you want to cross the threshold from below:

store = FakeKVStore(capacity=1024)
store["a"] = "x" * 200
store.commit()                                   # fits

store.set_capacity(64)                           # tighten
try:
    store["a"] = "x" * 200
    store.commit()
except KVStoreFull:
    pass                                         # expected

Simulating corruption

simulate_corrupt() marks the underlying backend corrupt; the next reload() (or KVStore re-construction) surfaces a corruption event. In-memory state stays intact until you explicitly reload, matching the real device behavior, where a backend-level fault doesn't poison state already in hand.

from chumicro_kvstore import KVStoreCorrupt
from chumicro_kvstore.testing import FakeKVStore

def test_reload_raises_on_corrupt():
    store = FakeKVStore()
    store["x"] = 1
    store.commit()
    store.simulate_corrupt()

    try:
        store.reload()
        raise AssertionError("expected KVStoreCorrupt")
    except KVStoreCorrupt:
        pass

The construction path treats corruption as recoverable: is_corrupt becomes True and the store resets to empty.

def test_construction_recovers_from_corrupt():
    payload = b"\xff" * 32                       # garbage
    store = FakeKVStore(initial_payload=payload)
    assert store.is_corrupt is True
    assert len(store) == 0                       # blank, but usable

Recording calls

Pass record_calls=True to capture every public-API call as a (method, args) tuple in store.calls:

def test_publisher_only_commits_once_per_minute():
    store = FakeKVStore(record_calls=True)
    publisher = TelemetryPublisher(store=store, ...)

    for _ in range(60):
        publisher.tick()

    commit_calls = [event for event in store.calls if event[0] == "commit"]
    assert len(commit_calls) == 1

Recorded methods: __setitem__, __delitem__, commit, commit_if_changed, reload.

Inspecting the raw payload

raw_payload returns the encoded msgpack bytes the backend currently holds. Useful for round-trip assertions or for seeding a second FakeKVStore from the first's persisted state:

seeded = FakeKVStore()
seeded["boot_count"] = 7
seeded.commit()

restarted = FakeKVStore(initial_payload=seeded.raw_payload)
assert restarted["boot_count"] == 7

Using these fakes in your own tests

Installing chumicro-kvstore puts chumicro_kvstore.testing on your path, so your own suite imports the fake and passes it wherever your code expects a KVStore:

from chumicro_kvstore.testing import FakeKVStore

Project convention: libraries that expose injectable services ship their own test fakes alongside the production code.

API Reference

chumicro_kvstore.testing

Test helpers for libraries that depend on chumicro-kvstore.

FakeKVStore gives downstream tests a real store to run against.

FakeKVStore

Bases: KVStore

In-memory KVStore with explicit corruption and capacity hooks.

Parameters:

Name Type Description Default
capacity int | None

Optional capacity override in bytes, to drive the KVStoreFull path.

None
initial_payload bytes | None

Optional pre-seeded msgpack payload.

None
record_calls bool

When True, each public-API call is appended to self.calls.

False

raw_payload property

Raw msgpack bytes currently held by the backend.

simulate_corrupt()

Mark the underlying memory backend corrupt.

reset_corrupt()

Clear the simulated-corrupt flag.

set_capacity(capacity)

Adjust the simulated capacity mid-test.