Ë
    NËêju  ã                   óº   — d Z ddlZddlmc mZ ddlmZm	Z	m
Z
 ddlZddlmZ ddlmZ d„ Zd„ Zd„ Zd	„ Zd
„ Zd„ Zd„ Zd„ Zedk(  r ej2                  edg«       yy)zæTests for hybrid search functionality.

Hybrid search combines keyword search over document metadata with semantic
(vector) search via Ollama embeddings, then fuses results using Reciprocal
Rank Fusion (RRF) per RAGraph research.
é    N)Ú	MagicMockÚMockÚpatch)ÚSQLiteDatabase)ÚMemoryServicec                  óè  — dddœdddœdddœg} dddœd	d
dœg}t        j                  | |d¬«      }t        |«      }d}||k(  }|söt        j                  d|fd||f«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}x}}|D �ch c]  }|d   ’Œ	 }	}h d£}|	|k(  }
|
s™t        j                  d|
fd|	|f«      dt        j                  «       v st        j                  |	«      rt        j                  |	«      ndt        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}
}t        d„ |D «       «      }t        d„ |D «       «      }||kD  }
|
sÆt        j                  d|
fd ||f«      d!t        j                  «       v st        j                  |«      rt        j                  |«      nd!d"t        j                  «       v st        j                  |«      rt        j                  |«      nd"d#œz  }d$d%|iz  }t        t        j                  |«      «      ‚d}
yc c}w )&z$Test basic RRF fusion functionality.Údoc1é
   ©ÚidÚscoreÚdoc2é   Údoc3é   é   Údoc4é	   é<   ©Úké   ©ú==©z0%(py3)s
{%(py3)s = %(py0)s(%(py1)s)
} == %(py6)sÚlenÚfused©Úpy0Úpy1Úpy3Úpy6úassert %(py8)sÚpy8Nr   >   r	   r   r   r   ©z%(py0)s == %(py3)sÚ	fused_ids©r   r!   úassert %(py5)sÚpy5c              3   ó8   K  — | ]  }|d    dk(  sŒ|d   –— Œ y­w©r   r   Ú	rrf_scoreN© ©Ú.0Úrs     úD/home/oem/CyberBrain26-unified/tests/test_knowledge_hybrid_search.pyú	<genexpr>z&test_rrf_fuse_basic.<locals>.<genexpr>$   ó   è ø€ ÒI¨°q¸±wÀ&Ó7H�a˜•nÑIùó   ‚�
c              3   ó8   K  — | ]  }|d    dk(  sŒ|d   –— Œ y­w©r   r	   r,   Nr-   r.   s     r1   r2   z&test_rrf_fuse_basic.<locals>.<genexpr>%   r3   r4   ©ú>©z%(py0)s > %(py2)sÚ
doc2_scoreÚ
doc1_score©r   Úpy2úassert %(py4)sÚpy4)r   Ú	_rrf_fuser   Ú
@pytest_arÚ_call_reprcompareÚ@py_builtinsÚlocalsÚ_should_repr_global_nameÚ	_safereprÚAssertionErrorÚ_format_explanationÚnext)Ú	results_aÚ	results_br   Ú@py_assert2Ú@py_assert5Ú@py_assert4Ú@py_format7Ú@py_format9r0   r&   Ú@py_assert1Ú@py_format4Ú@py_format6r:   r;   Ú@py_format3Ú@py_format5s                    r1   Útest_rrf_fuse_basicrV      sá  € ð  Ñ#Ø Ñ"Ø Ñ"ð€Ið  Ñ#Ø Ñ"ð€Iô
 ×#Ñ# I¨y¸BÔ?€Eô ˆu‹:Ð˜Ðˆ:˜‰?××Õˆ:˜×××××ÑŒ3××ÓŒ3×××××Òˆu××Óˆu××Ôˆ:××Ò˜××××××ÓØ"'Ö(˜Q��4“Ð(€IÐ(Ú8Ð8ˆ9Ð8Ñ8×8×8Õ8ˆ9Ð8×8×8×8×8×8Ñ8ˆ9×8×8Ó8ˆ9×8×8Ô8Ð8×8×8×8×8×8×8Ñ8ô ÑI¨eÔIÓI€JÜÑI¨eÔIÓI€Jà˜
Ñ"×"×"Õ"ˆ:˜
×"×"×"×"×"Ñ"ˆ:×"×"Ó"ˆ:×"×"×"×"×"Ò"˜
×"×"Ó"˜
×"×"×"×"×"×"Ò"ùò )s   ÅK/c                  óú  — dddœdddœg} dddœdd	dœg}t        j                  | |d
¬«      }t        |«      }d}||k(  }|söt        j                  d|fd||f«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}x}}|D �ch c]  }|d   ’Œ	 }	}h d£}|	|k(  }
|
s™t        j                  d|
fd|	|f«      dt        j                  «       v st        j                  |	«      rt        j                  |	«      ndt        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}
}yc c}w )z9Test RRF fusion when there is no overlap between results.r	   r
   r   r   r   r   r   r   é   r   r   r   r   r   r   r   r   r#   r$   Nr   >   r	   r   r   r   r%   r&   r'   r(   r)   )r   r@   r   rA   rB   rC   rD   rE   rF   rG   rH   )rJ   rK   r   rL   rM   rN   rO   rP   r0   r&   rQ   rR   rS   s                r1   Útest_rrf_fuse_no_overlaprY   *   sM  € ð  Ñ#Ø Ñ"ð€Ið
  Ñ"Ø Ñ"ð€Iô
 ×#Ñ# I¨y¸BÔ?€Eäˆu‹:Ð˜Ðˆ:˜‰?××Õˆ:˜×××××ÑŒ3××ÓŒ3×××××Òˆu××Óˆu××Ôˆ:××Ò˜××××××ÓØ"'Ö(˜Q��4“Ð(€IÐ(Ú8Ð8ˆ9Ð8Ñ8×8×8Õ8ˆ9Ð8×8×8×8×8×8Ñ8ˆ9×8×8Ó8ˆ9×8×8Ô8Ð8×8×8×8×8×8×8Ò8ùò )s   ÅG8c                 óð   ‡— ddl m} t        t        ¬«      }t        «       Št        «       }| |j                  _        |‰j                  _        |ˆfd„«       }||_        t        |t        d¬«      ¬«      }|S )zDBuild a MemoryService whose database.connection() returns mock rows.r   )Úcontextmanager©Úspecc               3   ó   •K  — ‰ –— y ­w)Nr-   )Úmock_connections   €r1   Ú_mock_connectionz7_make_service_with_connection.<locals>._mock_connectionF   s   øè ø€ àÓùs   ƒ	ÚAuditLog©ÚdatabaseÚ	audit_log)
Ú
contextlibr[   r   r   ÚfetchallÚreturn_valueÚexecuteÚ
connectionr   r   )Úrowsr[   Úmock_dbÚmock_resultr`   Úservicer_   s         @r1   Ú_make_service_with_connectionrn   <   sr   ø€ å)äœ^Ô,€GÜ“k€OÜ“+€KØ(,€K×ÑÔ%Ø+6€O×ÑÔ(àóó ðð *€GÔÜØÜ˜JÔ'ô€Gð €Nó    c                  ó‚  — dddddœdddddœg} t        | «      }|j                  d	d
¬«      }t        |«      }d}||k(  }|söt        j                  d|fd||f«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}x}}|d   d   }d}	||	k(  }|slt        j                  d|fd||	f«      t        j                  |«      t        j                  |	«      dœz  }
dd|
iz  }t        t        j                  |«      «      ‚dx}x}}	y)z(Test basic keyword search functionality.r	   úfile:///test1.txtú
text/plainzv{"topic": "AI", "tags": ["machine learning", "neural networks"], "summary": "AI research paper about neural networks"}©Údocument_idÚ
source_uriÚ
media_typeÚmetadata_jsonr   úfile:///test2.txtzp{"topic": "Security", "tags": ["cryptography", "encryption"], "summary": "Security paper on encryption methods"}Úneuralr   ©Úlimité   r   r   r   Úresultsr   r#   r$   Nr   r   ©z%(py1)s == %(py4)s©r    r?   úassert %(py6)sr"   ©rn   Úkeyword_searchr   rA   rB   rC   rD   rE   rF   rG   rH   ©rj   rm   r}   rL   rM   rN   rO   rP   Ú@py_assert0Ú@py_assert3rU   s              r1   Útest_keyword_search_basicr†   R   s:  € ð "Ø-Ø&ð Vñ		
ð "Ø-Ø&ð Pñ		
ð€Dô ,¨DÓ1€GØ×$Ñ$ X°QÐ$Ó7€Gô ˆw‹<Ð˜1Ðˆ<˜1Ñ××Õˆ<˜1×××××ÑŒ3××ÓŒ3×××××Òˆw××Óˆw××Ôˆ<××Ò˜1××××××ÓØ�1‰:�dÑÐ%˜vÐ%Ð˜vÑ%×%×%Õ%Ð˜v×%×%Ó%Ð×%×%Ò%˜v×%×%×%×%×%×%Ô%ro   c                  ó”  — dddddœdddddœd	d
dddœg} t        | «      }|j                  dd¬«      }t        |«      }d}||k(  }|söt        j                  d|fd||f«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}x}}|d   d   }d}	||	k(  }|slt        j                  d|fd||	f«      t        j                  |«      t        j                  |	«      dœz  }
dd|
iz  }t        t        j                  |«      «      ‚dx}x}}	|d   d   }d}	||	k(  }|slt        j                  d|fd||	f«      t        j                  |«      t        j                  |	«      dœz  }
dd|
iz  }t        t        j                  |«      «      ‚dx}x}}	y)z*Test keyword search with multiple matches.r	   rq   rr   zv{"topic": "AI research on neural networks", "tags": ["deep learning"], "summary": "Neural networks for deep learning"}rs   r   rx   zj{"topic": "Machine Learning", "tags": ["neural networks", "AI"], "summary": "Machine learning techniques"}r   zfile:///test3.txtzT{"topic": "Data Science", "tags": ["statistics"], "summary": "Statistical analysis"}ry   r
   rz   é   r   r   r   r}   r   r#   r$   Nr   r   r~   r   r€   r"   r|   r�   rƒ   s              r1   Ú$test_keyword_search_multiple_matchesr‰   k   s¥  € ð "Ø-Ø&ð Vñ		
ð "Ø-Ø&ð Jñ		
ð "Ø-Ø&Øsñ		
ð€Dô* ,¨DÓ1€GØ×$Ñ$ X°RÐ$Ó8€Gô ˆw‹<Ð˜1Ðˆ<˜1Ñ××Õˆ<˜1×××××ÑŒ3××ÓŒ3×××××Òˆw××Óˆw××Ôˆ<××Ò˜1××××××Óð �1‰:�dÑÐ%˜vÐ%Ð˜vÑ%×%×%Õ%Ð˜v×%×%Ó%Ð×%×%Ò%˜v×%×%×%×%×%×%Ó%à�1‰:�dÑÐ%˜vÐ%Ð˜vÑ%×%×%Õ%Ð˜v×%×%Ó%Ð×%×%Ò%˜v×%×%×%×%×%×%Ô%ro   c                  óp  — dddddœg} t        | «      }|j                  dd¬«      }t        |«      }d	}||k(  }|söt        j                  d
|fd||f«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}x}}y)z/Test keyword search with no matching documents.r	   rq   rr   zL{"topic": "Security", "tags": ["cryptography"], "summary": "Security paper"}rs   ry   r
   rz   r   r   r   r   r}   r   r#   r$   Nr�   )rj   rm   r}   rL   rM   rN   rO   rP   s           r1   Útest_keyword_search_no_matchesr‹   Ž   sÉ   € ð "Ø-Ø&Økñ		
ð€Dô ,¨DÓ1€GØ×$Ñ$ X°RÐ$Ó8€Gäˆw‹<Ð˜1Ðˆ<˜1Ñ××Õˆ<˜1×××××ÑŒ3××ÓŒ3×××××Òˆw××Óˆw××Ôˆ<××Ò˜1××××××Ôro   c                  óž  — t        t        t        ¬«      t        d¬«      ¬«      } t        ddddœdd	d
dœg¬«      | _        t        ddd
dœddddœg¬«      | _        | j                  dd¬«      }t        |«      }d}||k(  }|söt        j                  d|fd||f«      dt        j                  «       v st        j                  t        «      rt        j                  t        «      nddt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      t        j                  |«      dœz  }dd|iz  }t        t        j                  |«      «      ‚dx}x}}|D �ch c]  }|d   ’Œ	 }}h d£}||k(  }	|	s™t        j                  d|	fd||f«      dt        j                  «       v st        j                  |«      rt        j                  |«      ndt        j                  |«      dœz  }
d d!|
iz  }t        t        j                  |«      «      ‚dx}	}t!        d"„ |D «       «      }t!        d#„ |D «       «      }t!        d$„ |D «       «      }||kD  }	|	sÆt        j                  d%|	fd&||f«      d't        j                  «       v st        j                  |«      rt        j                  |«      nd'd(t        j                  «       v st        j                  |«      rt        j                  |«      nd(d)œz  }d*d+|iz  }t        t        j                  |«      «      ‚d}	||kD  }	|	sÆt        j                  d%|	fd&||f«      d't        j                  «       v st        j                  |«      rt        j                  |«      nd'd,t        j                  «       v st        j                  |«      rt        j                  |«      nd,d)œz  }d*d+|iz  }t        t        j                  |«      «      ‚d}	| j                  j#                  dd-¬«       | j
                  j#                  dd-dd¬.«       yc c}w )/z@Test integration of keyword and semantic search with RRF fusion.r\   ra   rb   r	   r   Útext1©r   r   Útextr   r   Útext2©rg   r
   r   r   Útext3Útestrz   é   r   r   r   r}   r   r#   r$   Nr   >   r	   r   r   r%   Ú
result_idsr'   r(   r)   c              3   ó8   K  — | ]  }|d    dk(  sŒ|d   –— Œ y­wr+   r-   r.   s     r1   r2   z1test_hybrid_search_integration.<locals>.<genexpr>¸   ó   è ø€ ÒK¨¸¸4¹ÀFÓ9J�a˜•nÑKùr4   c              3   ó8   K  — | ]  }|d    dk(  sŒ|d   –— Œ y­wr6   r-   r.   s     r1   r2   z1test_hybrid_search_integration.<locals>.<genexpr>¹   r—   r4   c              3   ó8   K  — | ]  }|d    dk(  sŒ|d   –— Œ y­w)r   r   r,   Nr-   r.   s     r1   r2   z1test_hybrid_search_integration.<locals>.<genexpr>º   r—   r4   r7   r9   r:   r;   r<   r>   r?   Ú
doc3_scoreé   ©r{   ÚtopicÚtag)r   r   r   r   r‚   Úsemantic_searchÚhybrid_searchr   rA   rB   rC   rD   rE   rF   rG   rH   rI   Úassert_called_once_with)rm   r}   rL   rM   rN   rO   rP   r0   r•   rQ   rR   rS   r:   r;   rš   rT   rU   s                    r1   Útest_hybrid_search_integrationr¢   Ÿ   sÄ  € äÜ¤Ô/Ü˜JÔ'ô€Gô "Ø ¨GÑ4Ø ¨7Ñ3ð0ô €GÔô #Ø ¨GÑ4Ø ¨7Ñ3ð1ô €GÔð
 ×#Ñ# F°"Ð#Ó5€Gô ˆw‹<Ð˜1Ðˆ<˜1Ñ××Õˆ<˜1×××××ÑŒ3××ÓŒ3×××××Òˆw××Óˆw××Ôˆ<××Ò˜1××××××ÓØ#*Ö+˜a�!�D“'Ð+€JÐ+Ú1Ð1ˆ:Ð1Ñ1×1×1Õ1ˆ:Ð1×1×1×1×1×1Ñ1ˆ:×1×1Ó1ˆ:×1×1Ô1Ð1×1×1×1×1×1×1Ñ1ô ÑK¨gÔKÓK€JÜÑK¨gÔKÓK€JÜÑK¨gÔKÓK€Jà˜
Ñ"×"×"Õ"ˆ:˜
×"×"×"×"×"Ñ"ˆ:×"×"Ó"ˆ:×"×"×"×"×"Ò"˜
×"×"Ó"˜
×"×"×"×"×"×"Ñ"Ø˜
Ñ"×"×"Õ"ˆ:˜
×"×"×"×"×"Ñ"ˆ:×"×"Ó"ˆ:×"×"×"×"×"Ò"˜
×"×"Ó"˜
×"×"×"×"×"×"Ñ"ð ×Ñ×2Ñ2°6ÀÐ2ÔDØ×Ñ×3Ñ3°FÀ"ÈDÐVZÐ3Õ[ùò ,s   ÆQ
c                  óZ  — t        t        t        ¬«      t        d¬«      ¬«      } t        ddddœdd	d
dœg¬«      | _        t        ddd
dœddddœg¬«      | _        | j                  ddd¬«      }| j                  j                  dd¬«       | j
                  j                  dddd¬«       y)z%Test hybrid search with topic filter.r\   ra   rb   r	   r   r�   rŽ   r   r   r�   r‘   r
   r   r   r’   r“   Úai)r�   r{   r›   rz   Nrœ   )r   r   r   r   r‚   rŸ   r    r¡   )rm   r}   s     r1   Ú$test_hybrid_search_with_topic_filterr¥   Ä   s¾   € äÜ¤Ô/Ü˜JÔ'ô€Gô
 "Ø ¨GÑ4Ø ¨7Ñ3ð0ô €GÔô #Ø ¨GÑ4Ø ¨7Ñ3ð1ô €GÔð ×#Ñ# F°$¸bÐ#ÓA€Gð ×Ñ×2Ñ2°6ÀÐ2ÔDØ×Ñ×3Ñ3°FÀ"ÈDÐVZÐ3Õ[ro   Ú__main__z-v)Ú__doc__ÚbuiltinsrC   Ú_pytest.assertion.rewriteÚ	assertionÚrewriterA   Úunittest.mockr   r   r   ÚpytestÚ$cyberbrain26.data_plane.sqlite_storer   Ú+cyberbrain26.knowledge_plane.memory_servicer   rV   rY   rn   r†   r‰   r‹   r¢   r¥   Ú__name__ÚmainÚ__file__r-   ro   r1   ú<module>r³      sn   ðñ÷ „ × 0Ñ 0ã å ?Ý Eò#ò49ò$ò,&ò2 &òFò""\òJ\ð0 ˆzÒØ€F‡K�K�˜4Ð Õ!ð ro   