"""App level tests.""" from functools import partial from operator import attrgetter from typing import Container import pytest from src.app import process_states from src.models.process_summary import ProcessSummary from src.models.store import Store from src.models.store_state import StoreState from tests.unit import data def fail_send_notification(state: StoreState, stores_to_fail: Container[Store]): """Fail send notification in certain cases.""" if state.store in stores_to_fail: raise Exception('API Error') @pytest.mark.parametrize( 'latest_states, saved_states, notification_side_effect, expected_summary', [ # --- Happy Path: One state is updated, notification succeeds --- pytest.param( [data.STATE_SPOTIFY_NEW, data.STATE_APPLE_NEW], [data.STATE_SPOTIFY_OLD, data.STATE_APPLE_NEW], None, ProcessSummary(updated=[data.STATE_SPOTIFY_NEW], succeeded=[data.STATE_SPOTIFY_NEW], failed=[]), id='success_one_update', ), # --- Partial Failure: Two states are updated, one notification fails --- pytest.param( [data.STATE_SPOTIFY_NEW, data.STATE_APPLE_NEW], [data.STATE_SPOTIFY_OLD], partial(fail_send_notification, stores_to_fail=(Store.APPLE_MUSIC,)), ProcessSummary( updated=[data.STATE_APPLE_NEW, data.STATE_SPOTIFY_NEW], succeeded=[data.STATE_SPOTIFY_NEW], failed=[data.STATE_APPLE_NEW], ), id='partial_notification_failure', ), # --- Total Failure: One state is updated, all notifications fail --- pytest.param( [data.STATE_SPOTIFY_NEW, data.STATE_APPLE_NEW], [data.STATE_SPOTIFY_OLD, data.STATE_APPLE_NEW], Exception('All notifications fail'), ProcessSummary(updated=[data.STATE_SPOTIFY_NEW], succeeded=[], failed=[data.STATE_SPOTIFY_NEW]), id='total_notification_failure', ), # --- No Updates: Latest and saved states are identical --- pytest.param( [data.STATE_SPOTIFY_NEW], [data.STATE_SPOTIFY_NEW], None, ProcessSummary(updated=[], succeeded=[], failed=[]), id='no_updates_found', ), ], ) def test_process_states(mocker, latest_states, saved_states, notification_side_effect, expected_summary): """Tests the complete workflow of the process_states function.""" mocker.patch('src.app.get_latest_store_states', return_value=latest_states) mocker.patch('src.app.load_store_states', return_value=saved_states) mocker.patch('src.app.send_streams_updated_notification', side_effect=notification_side_effect) mock_save_states = mocker.patch('src.app.save_store_states') result_summary = process_states() result_summary.updated.sort(key=attrgetter('store')) result_summary.succeeded.sort(key=attrgetter('store')) result_summary.failed.sort(key=attrgetter('store')) assert result_summary == expected_summary if expected_summary.succeeded: mock_save_states.assert_called_once_with(expected_summary.succeeded) else: mock_save_states.assert_not_called()