|
1 | 1 | """Tests for the VectorStore adapter functionality."""
|
2 | 2 |
|
| 3 | +import asyncio |
3 | 4 | from unittest.mock import AsyncMock, MagicMock, patch
|
4 | 5 |
|
5 | 6 | import pytest
|
6 | 7 |
|
| 8 | +from agent_memory_server.filters import Namespace |
7 | 9 | from agent_memory_server.models import MemoryRecord, MemoryTypeEnum
|
8 | 10 | from agent_memory_server.vectorstore_adapter import (
|
9 | 11 | LangChainVectorStoreAdapter,
|
@@ -544,3 +546,70 @@ async def asimilarity_search_with_relevance_scores(
|
544 | 546 | )
|
545 | 547 |
|
546 | 548 | assert len(unprocessed_results_after.memories) == 0
|
| 549 | + |
| 550 | + def test_redis_adapter_preserves_discrete_memory_extracted_flag(self): |
| 551 | + """Regression test: Ensure Redis adapter preserves discrete_memory_extracted='t' during search. |
| 552 | +
|
| 553 | + This test catches the bug where MCP-created memories with discrete_memory_extracted='t' |
| 554 | + were being returned as 'f' because the Redis vector store adapter wasn't populating |
| 555 | + the field during document-to-memory conversion. |
| 556 | + """ |
| 557 | + from datetime import UTC, datetime |
| 558 | + from unittest.mock import MagicMock |
| 559 | + |
| 560 | + # Create mock vectorstore and embeddings |
| 561 | + mock_vectorstore = MagicMock() |
| 562 | + mock_embeddings = MagicMock() |
| 563 | + |
| 564 | + # Create Redis adapter |
| 565 | + adapter = RedisVectorStoreAdapter(mock_vectorstore, mock_embeddings) |
| 566 | + |
| 567 | + # Mock document that simulates what Redis returns for an MCP-created memory |
| 568 | + mock_doc = MagicMock() |
| 569 | + mock_doc.page_content = "User likes green tea" |
| 570 | + mock_doc.metadata = { |
| 571 | + "id_": "memory_001", |
| 572 | + "session_id": None, |
| 573 | + "user_id": None, |
| 574 | + "namespace": "user_preferences", |
| 575 | + "created_at": datetime.now(UTC).timestamp(), |
| 576 | + "updated_at": datetime.now(UTC).timestamp(), |
| 577 | + "last_accessed": datetime.now(UTC).timestamp(), |
| 578 | + "topics": "preferences,beverages", |
| 579 | + "entities": "", |
| 580 | + "memory_hash": "abc123", |
| 581 | + "discrete_memory_extracted": "t", # This should be preserved! |
| 582 | + "memory_type": "semantic", |
| 583 | + "persisted_at": None, |
| 584 | + "extracted_from": "", |
| 585 | + "event_date": None, |
| 586 | + } |
| 587 | + |
| 588 | + # Mock the search to return our test document |
| 589 | + mock_vectorstore.asimilarity_search_with_relevance_scores = AsyncMock( |
| 590 | + return_value=[(mock_doc, 0.9)] |
| 591 | + ) |
| 592 | + |
| 593 | + # Perform search |
| 594 | + result = asyncio.run( |
| 595 | + adapter.search_memories( |
| 596 | + query="green tea", |
| 597 | + namespace=Namespace(field="namespace", eq="user_preferences"), |
| 598 | + limit=10, |
| 599 | + ) |
| 600 | + ) |
| 601 | + |
| 602 | + # Verify we got the memory back |
| 603 | + assert len(result.memories) == 1 |
| 604 | + memory = result.memories[0] |
| 605 | + |
| 606 | + # REGRESSION TEST: This should be 't', not 'f' |
| 607 | + assert memory.discrete_memory_extracted == "t", ( |
| 608 | + f"Regression: Expected discrete_memory_extracted='t', got '{memory.discrete_memory_extracted}'. " |
| 609 | + f"This indicates the Redis adapter is not preserving the flag during search." |
| 610 | + ) |
| 611 | + |
| 612 | + # Also verify other expected properties |
| 613 | + assert memory.memory_type.value == "semantic" |
| 614 | + assert memory.namespace == "user_preferences" |
| 615 | + assert memory.text == "User likes green tea" |
0 commit comments