Skip to content

Commit 5e1f7c5

Browse files
committed
test(redis): add RecencyAggregationQuery and server_side_recency adapter tests; formatting fixes
1 parent eac6268 commit 5e1f7c5

File tree

1 file changed

+104
-0
lines changed

1 file changed

+104
-0
lines changed

tests/test_recency_aggregation.py

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
from unittest.mock import AsyncMock, MagicMock
2+
3+
import pytest
4+
5+
from agent_memory_server.utils.redis_query import RecencyAggregationQuery
6+
from agent_memory_server.vectorstore_adapter import RedisVectorStoreAdapter
7+
8+
9+
@pytest.mark.asyncio
10+
async def test_recency_aggregation_query_builds_and_paginates():
11+
# Build a VectorQuery without touching Redis (pure construction)
12+
from redisvl.query import VectorQuery
13+
14+
dummy_vec = [0.0, 0.0, 0.0]
15+
vq = VectorQuery(vector=dummy_vec, vector_field_name="vector", k=10)
16+
17+
# Build aggregation
18+
agg = (
19+
RecencyAggregationQuery.from_vector_query(vq)
20+
.load_default_fields()
21+
.apply_recency(
22+
now_ts=1_700_000_000,
23+
params={
24+
"w_sem": 0.7,
25+
"w_recency": 0.3,
26+
"wf": 0.5,
27+
"wa": 0.5,
28+
"half_life_last_access_days": 5.0,
29+
"half_life_created_days": 20.0,
30+
},
31+
)
32+
.sort_by_boosted_desc()
33+
.paginate(5, 7)
34+
)
35+
36+
# Implementation detail: AggregationQuery has a private builder we can sanity-check
37+
# We only assert key substrings to avoid coupling to exact formatting
38+
qs = agg._build_query_string() # type: ignore[attr-defined]
39+
assert "APPLY" in qs
40+
assert "boosted_score" in qs
41+
assert "SORTBY" in qs
42+
assert "LIMIT" in qs
43+
44+
45+
@pytest.mark.asyncio
46+
async def test_redis_adapter_uses_aggregation_when_server_side_recency():
47+
# Mock vectorstore and its underlying RedisVL index
48+
mock_index = MagicMock()
49+
50+
class Rows:
51+
def __init__(self, rows):
52+
self.rows = rows
53+
54+
# Simulate aaggregate returning rows from FT.AGGREGATE
55+
mock_index.aaggregate = AsyncMock(
56+
return_value=Rows(
57+
[
58+
{
59+
"id_": "m1",
60+
"namespace": "ns",
61+
"session_id": "s1",
62+
"user_id": "u1",
63+
"created_at": 1_700_000_000,
64+
"last_accessed": 1_700_000_000,
65+
"updated_at": 1_700_000_000,
66+
"pinned": 0,
67+
"access_count": 1,
68+
"topics": "",
69+
"entities": "",
70+
"memory_hash": "h",
71+
"discrete_memory_extracted": "t",
72+
"memory_type": "semantic",
73+
"persisted_at": None,
74+
"extracted_from": "",
75+
"event_date": None,
76+
"text": "hello",
77+
"__vector_score": 0.9,
78+
}
79+
]
80+
)
81+
)
82+
83+
mock_vectorstore = MagicMock()
84+
mock_vectorstore._index = mock_index
85+
86+
# Mock embeddings
87+
mock_embeddings = MagicMock()
88+
mock_embeddings.embed_query.return_value = [0.0, 0.0, 0.0]
89+
90+
adapter = RedisVectorStoreAdapter(mock_vectorstore, mock_embeddings)
91+
92+
results = await adapter.search_memories(
93+
query="hello",
94+
server_side_recency=True,
95+
namespace=None,
96+
limit=5,
97+
offset=0,
98+
)
99+
100+
# Ensure we went through aggregate path
101+
assert mock_index.aaggregate.await_count == 1
102+
assert len(results.memories) == 1
103+
assert results.memories[0].id == "m1"
104+
assert results.memories[0].text == "hello"

0 commit comments

Comments
 (0)