@@ -1023,3 +1023,99 @@ def raise_runtime_error():
10231023
10241024 assert captured_payload ["retrieval_mode" ] == "fts"
10251025 assert captured_payload ["text" ] == "test query"
1026+
1027+
1028+ # --- Tests for default entity_types (issue #31) --------------------------------
1029+
1030+
1031+ @pytest .mark .asyncio
1032+ async def test_search_notes_defaults_entity_types_to_entity (monkeypatch ):
1033+ """search_notes defaults entity_types to ['entity'] when not explicitly provided.
1034+
1035+ This prevents individual observations/relations from appearing as separate
1036+ search results, since the entity row already indexes full file content.
1037+ """
1038+ import importlib
1039+
1040+ search_mod = importlib .import_module ("basic_memory.mcp.tools.search" )
1041+ clients_mod = importlib .import_module ("basic_memory.mcp.clients" )
1042+
1043+ class StubProject :
1044+ name = "test-project"
1045+ external_id = "test-external-id"
1046+
1047+ @asynccontextmanager
1048+ async def fake_get_project_client (* args , ** kwargs ):
1049+ yield (object (), StubProject ())
1050+
1051+ async def fake_resolve_project_and_path (
1052+ client , identifier , project = None , context = None , headers = None
1053+ ):
1054+ return StubProject (), identifier , False
1055+
1056+ captured_payload : dict = {}
1057+
1058+ class MockSearchClient :
1059+ def __init__ (self , * args , ** kwargs ):
1060+ pass
1061+
1062+ async def search (self , payload , page , page_size ):
1063+ captured_payload .update (payload )
1064+ return SearchResponse (results = [], current_page = page , page_size = page_size )
1065+
1066+ monkeypatch .setattr (search_mod , "get_project_client" , fake_get_project_client )
1067+ monkeypatch .setattr (search_mod , "resolve_project_and_path" , fake_resolve_project_and_path )
1068+ monkeypatch .setattr (clients_mod , "SearchClient" , MockSearchClient )
1069+
1070+ await search_mod .search_notes (
1071+ project = "test-project" ,
1072+ query = "test" ,
1073+ )
1074+
1075+ # entity_types should default to ["entity"]
1076+ assert captured_payload ["entity_types" ] == ["entity" ]
1077+
1078+
1079+ @pytest .mark .asyncio
1080+ async def test_search_notes_explicit_entity_types_overrides_default (monkeypatch ):
1081+ """Explicit entity_types parameter overrides the default ['entity'] filter."""
1082+ import importlib
1083+
1084+ search_mod = importlib .import_module ("basic_memory.mcp.tools.search" )
1085+ clients_mod = importlib .import_module ("basic_memory.mcp.clients" )
1086+
1087+ class StubProject :
1088+ name = "test-project"
1089+ external_id = "test-external-id"
1090+
1091+ @asynccontextmanager
1092+ async def fake_get_project_client (* args , ** kwargs ):
1093+ yield (object (), StubProject ())
1094+
1095+ async def fake_resolve_project_and_path (
1096+ client , identifier , project = None , context = None , headers = None
1097+ ):
1098+ return StubProject (), identifier , False
1099+
1100+ captured_payload : dict = {}
1101+
1102+ class MockSearchClient :
1103+ def __init__ (self , * args , ** kwargs ):
1104+ pass
1105+
1106+ async def search (self , payload , page , page_size ):
1107+ captured_payload .update (payload )
1108+ return SearchResponse (results = [], current_page = page , page_size = page_size )
1109+
1110+ monkeypatch .setattr (search_mod , "get_project_client" , fake_get_project_client )
1111+ monkeypatch .setattr (search_mod , "resolve_project_and_path" , fake_resolve_project_and_path )
1112+ monkeypatch .setattr (clients_mod , "SearchClient" , MockSearchClient )
1113+
1114+ await search_mod .search_notes (
1115+ project = "test-project" ,
1116+ query = "test" ,
1117+ entity_types = ["observation" ],
1118+ )
1119+
1120+ # Explicit entity_types should be used, not the default
1121+ assert captured_payload ["entity_types" ] == ["observation" ]
0 commit comments