Files
cleveragents-core/features/acms_hot_storage_tier.feature
HAL9000 7866e2c1f5
CI / push-validation (pull_request) Successful in 27s
CI / lint (pull_request) Successful in 42s
CI / quality (pull_request) Successful in 53s
CI / build (pull_request) Successful in 1m5s
CI / helm (pull_request) Successful in 1m17s
CI / typecheck (pull_request) Successful in 1m24s
CI / security (pull_request) Successful in 1m25s
CI / unit_tests (pull_request) Successful in 5m49s
CI / docker (pull_request) Successful in 1m36s
CI / integration_tests (pull_request) Successful in 10m8s
CI / coverage (pull_request) Successful in 12m59s
CI / status-check (pull_request) Successful in 4s
fix(acms): disambiguate hot tier size_bytes step from TierDistribution
The new step `the hot tier size_bytes should be {n:d}` in
features/steps/acms_hot_storage_tier_steps.py shared the matched
pattern of the existing
`the hot tier size_bytes should be {expected:d}` step in
features/steps/acms_context_analysis_engine_steps.py — Behave's
step registry strips parameter names when computing the pattern,
so both compile to the same regex. Every scenario hitting the
step raised AmbiguousStep at run-time, which Behave reports as
"errored" (not "failed"); that produced the 6 errored scenarios
on `features/acms_hot_storage_tier.feature` (lines 9, 96, 101,
149, 202, 210) seen in CI unit_tests.

Rename the new step and its `at most` companion to
`the hot storage tier size_bytes should be ...` (mirroring the
HotStorageTier class name) so the patterns no longer collide
with the analysis-engine TierDistribution step. Update the 8
feature-file references in `acms_hot_storage_tier.feature` to
match. The other-metric steps (entry_count, hit_count, miss_count,
max_entries, max_bytes) keep their `the hot tier` prefix because
they have no analogous collision — the analysis-engine file uses
`count` (not `entry_count`), so they are already unambiguous.

ISSUES CLOSED: #9972
2026-06-13 04:59:44 -04:00

243 lines
10 KiB
Gherkin

Feature: ACMS Hot Storage Tier (in-memory LRU cache)
As an ACMS developer
I want a hot storage tier backed by an in-memory LRU cache
So that recently and frequently accessed context entries are served
with minimal latency while capacity limits are enforced automatically
# ---- Construction ----
Scenario: Create HotStorageTier with no limits
Given a HotStorageTier with no capacity limits
Then the hot tier entry_count should be 0
And the hot storage tier size_bytes should be 0
And the hot tier hit_count should be 0
And the hot tier miss_count should be 0
Scenario: Create HotStorageTier with max_entries limit
Given a HotStorageTier with max_entries 10
Then the hot tier max_entries should be 10
And the hot tier max_bytes should be None
Scenario: Create HotStorageTier with max_bytes limit
Given a HotStorageTier with max_bytes 1024
Then the hot tier max_bytes should be 1024
And the hot tier max_entries should be None
Scenario: Create HotStorageTier with both limits
Given a HotStorageTier with max_entries 5 and max_bytes 512
Then the hot tier max_entries should be 5
And the hot tier max_bytes should be 512
Scenario: HotStorageTier rejects max_entries of zero
Then creating a HotStorageTier with max_entries 0 should raise ValueError
Scenario: HotStorageTier rejects negative max_entries
Then creating a HotStorageTier with max_entries -1 should raise ValueError
Scenario: HotStorageTier rejects max_bytes of zero
Then creating a HotStorageTier with max_bytes 0 should raise ValueError
Scenario: HotStorageTier rejects negative max_bytes
Then creating a HotStorageTier with max_bytes -1 should raise ValueError
# ---- Basic put/get ----
Scenario: Put and get a single entry
Given a HotStorageTier with no capacity limits
When I put entry "key1" with content "hello world"
Then getting "key1" from the hot tier should return "hello world"
Scenario: Get returns None for missing entry
Given a HotStorageTier with no capacity limits
Then getting "missing" from the hot tier should return None
Scenario: Put updates existing entry
Given a HotStorageTier with no capacity limits
When I put entry "key1" with content "original"
And I put entry "key1" with content "updated"
Then getting "key1" from the hot tier should return "updated"
And the hot tier entry_count should be 1
Scenario: Put rejects empty entry_id
Given a HotStorageTier with no capacity limits
Then putting an entry with empty id should raise ValueError
# ---- Metrics ----
Scenario: Hit count increments on successful get
Given a HotStorageTier with no capacity limits
When I put entry "k1" with content "data"
And I get "k1" from the hot tier
Then the hot tier hit_count should be 1
And the hot tier miss_count should be 0
Scenario: Miss count increments on failed get
Given a HotStorageTier with no capacity limits
When I get "nonexistent" from the hot tier
Then the hot tier hit_count should be 0
And the hot tier miss_count should be 1
Scenario: Hit and miss counts accumulate independently
Given a HotStorageTier with no capacity limits
When I put entry "k1" with content "data"
And I get "k1" from the hot tier
And I get "k1" from the hot tier
And I get "missing" from the hot tier
Then the hot tier hit_count should be 2
And the hot tier miss_count should be 1
Scenario: entry_count reflects current cache size
Given a HotStorageTier with no capacity limits
When I put entry "a" with content "aaa"
And I put entry "b" with content "bbb"
And I put entry "c" with content "ccc"
Then the hot tier entry_count should be 3
Scenario: size_bytes reflects UTF-8 encoded content size
Given a HotStorageTier with no capacity limits
When I put entry "k1" with content "hello"
Then the hot storage tier size_bytes should be 5
Scenario: size_bytes updates when entry is replaced
Given a HotStorageTier with no capacity limits
When I put entry "k1" with content "hi"
And I put entry "k1" with content "hello world"
Then the hot storage tier size_bytes should be 11
# ---- LRU eviction: max_entries ----
Scenario: LRU eviction triggers when max_entries is exceeded
Given a HotStorageTier with max_entries 3
When I put entry "e1" with content "first"
And I put entry "e2" with content "second"
And I put entry "e3" with content "third"
And I put entry "e4" with content "fourth"
Then the hot tier entry_count should be 3
And getting "e1" from the hot tier should return None
And getting "e4" from the hot tier should return "fourth"
Scenario: LRU order is updated on get
Given a HotStorageTier with max_entries 2
When I put entry "a" with content "aaa"
And I put entry "b" with content "bbb"
And I get "a" from the hot tier
And I put entry "c" with content "ccc"
Then getting "a" from the hot tier should return "aaa"
And getting "b" from the hot tier should return None
And getting "c" from the hot tier should return "ccc"
Scenario: LRU order is updated on put (update existing)
Given a HotStorageTier with max_entries 2
When I put entry "a" with content "aaa"
And I put entry "b" with content "bbb"
And I put entry "a" with content "aaa-updated"
And I put entry "c" with content "ccc"
Then getting "a" from the hot tier should return "aaa-updated"
And getting "b" from the hot tier should return None
And getting "c" from the hot tier should return "ccc"
# ---- LRU eviction: max_bytes ----
Scenario: LRU eviction triggers when max_bytes is exceeded
Given a HotStorageTier with max_bytes 10
When I put entry "k1" with content "12345"
And I put entry "k2" with content "67890"
And I put entry "k3" with content "abcde"
Then the hot storage tier size_bytes should be at most 10
And getting "k1" from the hot tier should return None
Scenario: Single entry larger than max_bytes is evicted immediately
Given a HotStorageTier with max_bytes 3
When I put entry "big" with content "hello"
Then the hot tier entry_count should be 0
And the hot storage tier size_bytes should be 0
# ---- Eviction callback (warm-tier demotion) ----
Scenario: on_evict callback is called with evicted entry id
Given a HotStorageTier with max_entries 2 and an eviction recorder
When I put entry "x1" with content "content1"
And I put entry "x2" with content "content2"
And I put entry "x3" with content "content3"
Then the eviction recorder should have recorded entry id "x1"
Scenario: on_evict callback receives correct content for evicted entry
Given a HotStorageTier with max_entries 1 and an eviction recorder
When I put entry "first" with content "first-content"
And I put entry "second" with content "second-content"
Then the eviction recorder should have recorded entry "first" with content "first-content"
Scenario: on_evict callback error does not interrupt cache operation
Given a HotStorageTier with max_entries 1 and a failing eviction callback
When I put entry "a" with content "aaa"
And I put entry "b" with content "bbb"
Then the hot tier entry_count should be 1
And getting "b" from the hot tier should return "bbb"
Scenario: on_evict is not called when no eviction occurs
Given a HotStorageTier with max_entries 5 and an eviction recorder
When I put entry "only" with content "data"
Then the eviction recorder should have recorded 0 evictions
# ---- Remove ----
Scenario: Remove an existing entry
Given a HotStorageTier with no capacity limits
When I put entry "r1" with content "remove-me"
And I remove "r1" from the hot tier
Then getting "r1" from the hot tier should return None
And the hot tier entry_count should be 0
Scenario: Remove returns the content of the removed entry
Given a HotStorageTier with no capacity limits
When I put entry "r2" with content "my-content"
And I remove "r2" from the hot tier
Then removing "r2" from the hot tier should return "my-content"
Scenario: Remove returns None for missing entry
Given a HotStorageTier with no capacity limits
When I remove "nonexistent" from the hot tier
Then removing "nonexistent" from the hot tier should return None
Scenario: Remove updates size_bytes correctly
Given a HotStorageTier with no capacity limits
When I put entry "s1" with content "hello"
And I remove "s1" from the hot tier
Then the hot storage tier size_bytes should be 0
# ---- Clear ----
Scenario: Clear removes all entries
Given a HotStorageTier with no capacity limits
When I put entry "c1" with content "aaa"
And I put entry "c2" with content "bbb"
And I clear the hot tier
Then the hot tier entry_count should be 0
And the hot storage tier size_bytes should be 0
Scenario: Clear does not reset hit/miss counters
Given a HotStorageTier with no capacity limits
When I put entry "c1" with content "aaa"
And I get "c1" from the hot tier
And I clear the hot tier
Then the hot tier hit_count should be 1
# ---- Thread safety ----
Scenario: Concurrent puts from multiple threads do not corrupt the cache
Given a HotStorageTier with max_entries 50
When 10 threads concurrently put 5 entries each into the hot tier
Then no exception should have been raised during concurrent puts
And the hot tier entry_count should be at most 50
Scenario: Concurrent gets and puts do not raise RuntimeError
Given a HotStorageTier with no capacity limits
When 5 threads concurrently put entries and 5 threads concurrently get entries
Then no exception should have been raised during concurrent gets and puts
Scenario: Concurrent evictions do not corrupt size_bytes
Given a HotStorageTier with max_bytes 100
When 8 threads concurrently put large entries into the hot tier
Then no exception should have been raised during concurrent evictions
And the hot storage tier size_bytes should be at most 100