"""Tests for the service layer (cache-aware fetching).""" from unittest.mock import patch import pytest from finn_eiendom.models import EiendomUnit, FinnAd, SimilarUnit from finn_eiendom.service import ( get_or_fetch_ad, get_or_fetch_eiendom_unit, get_or_fetch_similar_units, ) @pytest.mark.asyncio async def test_get_or_fetch_ad_uses_cache(): """Test that get_or_fetch_ad returns cached ad without fetching.""" mock_ad = FinnAd(finnkode="123", url="http://example.com") with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_finn_ad", return_value=mock_ad) as mock_get, patch("finn_eiendom.service.fetch_ad_details") as mock_fetch, ): result = await get_or_fetch_ad("123") assert result.finnkode == "123" mock_get.assert_called_once() mock_fetch.assert_not_called() @pytest.mark.asyncio async def test_get_or_fetch_ad_fetches_when_cache_miss(): """Test that get_or_fetch_ad fetches when cache is empty.""" mock_ad = FinnAd(finnkode="123", url="http://example.com") with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_finn_ad", return_value=None), patch("finn_eiendom.service.fetch_ad_details", return_value=mock_ad) as mock_fetch, patch("finn_eiendom.service.save_finn_ad", return_value=("hash123", True)) as mock_save, ): result = await get_or_fetch_ad("123") assert result.finnkode == "123" mock_fetch.assert_called_once_with("123") mock_save.assert_called_once() @pytest.mark.asyncio async def test_get_or_fetch_ad_force_refresh(): """Test that force_refresh=True bypasses cache.""" mock_ad = FinnAd(finnkode="123", url="http://example.com") with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_finn_ad", return_value=mock_ad) as mock_get, patch("finn_eiendom.service.fetch_ad_details", return_value=mock_ad) as mock_fetch, patch("finn_eiendom.service.save_finn_ad", return_value=("hash123", True)) as mock_save, ): result = await get_or_fetch_ad("123", force_refresh=True) assert result.finnkode == "123" mock_get.assert_not_called() mock_fetch.assert_called_once_with("123") mock_save.assert_called_once() @pytest.mark.asyncio async def test_get_or_fetch_eiendom_unit_uses_cache(): """Test that get_or_fetch_eiendom_unit returns cached unit without fetching.""" mock_unit = EiendomUnit(unit_code="test-code") with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_cached_eiendom_unit", return_value=mock_unit) as mock_get, patch("finn_eiendom.service.get_unit") as mock_fetch, ): result = await get_or_fetch_eiendom_unit("test-code") assert result.unit_code == "test-code" mock_get.assert_called_once() mock_fetch.assert_not_called() @pytest.mark.asyncio async def test_get_or_fetch_eiendom_unit_fetches_when_cache_miss(): """Test that get_or_fetch_eiendom_unit fetches when cache is empty.""" mock_unit = EiendomUnit(unit_code="test-code") with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_cached_eiendom_unit", return_value=None), patch("finn_eiendom.service.get_unit", return_value=mock_unit) as mock_fetch, patch( "finn_eiendom.service.save_eiendom_unit", return_value=("hash123", True) ) as mock_save, ): result = await get_or_fetch_eiendom_unit("test-code") assert result.unit_code == "test-code" mock_fetch.assert_called_once_with("test-code") mock_save.assert_called_once() @pytest.mark.asyncio async def test_get_or_fetch_similar_units_uses_cache(): """Test that get_or_fetch_similar_units returns cached units without fetching.""" mock_unit = EiendomUnit(unit_code="test-code") mock_similar = [SimilarUnit(unit_code="comp1"), SimilarUnit(unit_code="comp2")] with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_or_fetch_eiendom_unit", return_value=mock_unit), patch( "finn_eiendom.service.get_cached_similar_units", return_value=mock_similar ) as mock_get, patch("finn_eiendom.service.get_similar_units") as mock_fetch, ): result = await get_or_fetch_similar_units("test-code", "RECENTLY_SOLD") assert len(result) == 2 assert result[0].unit_code == "comp1" mock_get.assert_called_once() mock_fetch.assert_not_called() @pytest.mark.asyncio async def test_get_or_fetch_similar_units_fetches_when_cache_miss(): """Test that get_or_fetch_similar_units fetches when cache is empty.""" mock_unit = EiendomUnit(unit_code="test-code") mock_similar = [SimilarUnit(unit_code="comp1"), SimilarUnit(unit_code="comp2")] with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_or_fetch_eiendom_unit", return_value=mock_unit), patch("finn_eiendom.service.get_cached_similar_units", return_value=[]), patch("finn_eiendom.service.build_unit_vector", return_value="vector_data"), patch("finn_eiendom.service.get_similar_units", return_value=mock_similar) as mock_fetch, patch( "finn_eiendom.service.save_similar_units", return_value=("hash123", True) ) as mock_save, ): result = await get_or_fetch_similar_units("test-code", "RECENTLY_SOLD") assert len(result) == 2 assert result[0].unit_code == "comp1" mock_fetch.assert_called_once_with("vector_data", listing_status="RECENTLY_SOLD") mock_save.assert_called_once() @pytest.mark.asyncio async def test_get_or_fetch_similar_units_force_refresh(): """Test that force_refresh=True bypasses cache.""" mock_unit = EiendomUnit(unit_code="test-code") mock_similar = [SimilarUnit(unit_code="comp1"), SimilarUnit(unit_code="comp2")] with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_or_fetch_eiendom_unit", return_value=mock_unit), patch( "finn_eiendom.service.get_cached_similar_units", return_value=mock_similar ) as mock_get, patch("finn_eiendom.service.build_unit_vector", return_value="vector_data"), patch("finn_eiendom.service.get_similar_units", return_value=mock_similar) as mock_fetch, patch( "finn_eiendom.service.save_similar_units", return_value=("hash123", True) ) as mock_save, ): result = await get_or_fetch_similar_units("test-code", "RECENTLY_SOLD", force_refresh=True) assert len(result) == 2 mock_get.assert_not_called() mock_fetch.assert_called_once_with("vector_data", listing_status="RECENTLY_SOLD") mock_save.assert_called_once() @pytest.mark.asyncio async def test_get_or_fetch_similar_units_handles_missing_unit(): """Test that get_or_fetch_similar_units returns empty list when unit is missing.""" with ( patch("finn_eiendom.service.init_db"), patch("finn_eiendom.service.get_or_fetch_eiendom_unit", return_value=None), ): result = await get_or_fetch_similar_units("test-code", "RECENTLY_SOLD") assert result == []