""" Regression test for UnboundLocalError in recall when the reranker raises. Before the fix, `scored_results` and `pre_filtered_count` were only assigned inside the `try` block, but referenced in the `finally` block. If `reranker_instance.rerank()` (or `ensure_initialized()`) raised, the `finally` block crashed with `UnboundLocalError` instead of propagating the original exception. Fix: initialise both variables to safe defaults before the try/finally block. """ from datetime import datetime, timezone from unittest.mock import AsyncMock, patch import pytest @pytest.mark.asyncio async def test_recall_reranker_error_does_not_raise_unbound_local(memory, request_context): """Recall must propagate the reranker's exception, not an UnboundLocalError.""" bank_id = f"test_reranker_err_{datetime.now(timezone.utc).timestamp()}" try: await memory.retain_async( bank_id=bank_id, content="Paris is the capital of France", request_context=request_context, ) # Simulate a reranker failure (e.g. Cohere API error on empty/small candidate set) rerank_mock = AsyncMock(side_effect=RuntimeError("reranker API error")) memory._cross_encoder_reranker._initialized = True # skip ensure_initialized with patch.object(memory._cross_encoder_reranker, "rerank", rerank_mock): with pytest.raises(Exception, match="reranker API error"): await memory.recall_async( bank_id=bank_id, query="capital of France", request_context=request_context, ) finally: await memory.delete_bank(bank_id, request_context=request_context) @pytest.mark.asyncio async def test_recall_reranker_init_error_does_not_raise_unbound_local(memory, request_context): """Same regression when ensure_initialized() raises (before pre_filtered_count is set).""" bank_id = f"test_reranker_init_err_{datetime.now(timezone.utc).timestamp()}" try: await memory.retain_async( bank_id=bank_id, content="Paris is the capital of France", request_context=request_context, ) init_mock = AsyncMock(side_effect=RuntimeError("reranker init failed")) memory._cross_encoder_reranker._initialized = False with patch.object(memory._cross_encoder_reranker, "ensure_initialized", init_mock): with pytest.raises(Exception, match="reranker init failed"): await memory.recall_async( bank_id=bank_id, query="capital of France", request_context=request_context, ) finally: await memory.delete_bank(bank_id, request_context=request_context)