from datetime import datetime from typing import Optional import pytest from db_schema.postgres.connection import Connection from db_schema.schemas import slz from slz_apple_music_charts_scrapper.entities import Payload from slz_apple_music_charts_scrapper.services.content_status import ContentStatusService @pytest.mark.integration @pytest.mark.parametrize( 'market, content_status_value, content_name', [ ('gb', slz.ContentStatusEnum.ACTIVE.value, 'pl.606afcbb70264d2eb2b51d8dbcfa6a12_us.json'), ('gb', slz.ContentStatusEnum.ACTIVE.value, 'test_name'), ('us', slz.ContentStatusEnum.ACTIVE.value, 'pl.606afcbb70264d2eb2b51d8dbcfa6a12_us.json'), ('us', slz.ContentStatusEnum.MISSING.value, 'test_name'), ('us', slz.ContentStatusEnum.COMPLETE.value, 'test_name'), ('us', slz.ContentStatusEnum.CANCELLED.value, 'test_name'), ('us', slz.ContentStatusEnum.ON_HOLD.value, 'test_name'), ('us', slz.ContentStatusEnum.FAILED.value, 'test_name'), ('us', slz.ContentStatusEnum.COMPLETE_VOLATILE.value, 'test_name'), ] ) def test_save_or_update_content_status__create_success( db: Connection, unit_of_work: slz.UnitOfWork, # UoW instance must exist in repo. content_status_service: ContentStatusService, payload_test: Payload, content_status_value: str, market: str, content_name: str, ): # Initially content status does not exist. assert db.session.query(slz.ContentStatus).count() == 0 is_success = content_status_service.save_or_update_content_status( content_status=content_status_value, market=market, content_name=content_name, ) assert is_success is True assert db.session.query(slz.ContentStatus).count() == 1 created_content_status: slz.ContentStatus = db.session.query(slz.ContentStatus).one() assert created_content_status.content_status.value == content_status_value assert created_content_status.latest_job_id == payload_test.job_id assert created_content_status.content_name == content_name assert created_content_status.context == market @pytest.mark.integration def test_save_or_update_content_status__create_fail( db: Connection, content_status_service: ContentStatusService, payload_test: Payload, ): market: str = 'us' content_name: str = 'pl.606afcbb70264d2eb2b51d8dbcfa6a12_us.json' # Initially content status does not exist. assert db.session.query(slz.ContentStatus).count() == 0 is_success = content_status_service.save_or_update_content_status( content_status=slz.ContentStatusEnum.ACTIVE.value, market=market, content_name=content_name, ) assert is_success is False @pytest.mark.integration @pytest.mark.parametrize( 'market, content_name', [ # value "market" must match the "context" of content_status fixture. ('us', 'test'), ('us', 'pl.606afcbb70264d2eb2b51d8dbcfa6a12_us.json'), ] ) def test_save_or_update_content_status__update_success( db: Connection, content_status: slz.ContentStatus, content_status_service: ContentStatusService, payload_test: Payload, market: str, content_name: str, ): # Initially content status already exist. assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus)\ .filter(slz.ContentStatus.content_status_id == content_status.content_status_id)\ .one_or_none() is not None # During update this function will not modify content status value. active_status_value: str = slz.ContentStatusEnum.ACTIVE.value assert content_status.content_status.value == active_status_value # Save initial values. initial_job_id = content_status.latest_job_id initial_last_checked_at = content_status.last_checked_at is_success = content_status_service.save_or_update_content_status( content_status=active_status_value, market=market, content_name=content_name, ) assert is_success is True assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus) \ .filter(slz.ContentStatus.content_status_id == content_status.content_status_id) \ .one_or_none() is not None modified_content_status: slz.ContentStatus = db.session.query(slz.ContentStatus).one() assert modified_content_status.content_status_id == content_status.content_status_id # Status value hasn't changed assert modified_content_status.content_status.value == active_status_value # Latest job_id and last_checked_at timestamp are changed. assert modified_content_status.latest_job_id != initial_job_id assert modified_content_status.last_checked_at > initial_last_checked_at @pytest.mark.integration @pytest.mark.parametrize( 'content_status_of_status', [ slz.ContentStatusEnum.COMPLETE, slz.ContentStatusEnum.ON_HOLD, ], indirect=['content_status_of_status'], ) def test_save_or_update_content_status__update_fail( db: Connection, content_status_of_status: slz.ContentStatus, content_status_service: ContentStatusService, payload_test: Payload, ): market: str = 'us' content_name: str = 'pl.606afcbb70264d2eb2b51d8dbcfa6a12_us.json' # Initially content status already exist. assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus) \ .filter(slz.ContentStatus.content_status_id == content_status_of_status.content_status_id) \ .one_or_none() is not None # During update this function will not modify content status value. active_status_value: str = slz.ContentStatusEnum.ACTIVE.value is_success = content_status_service.save_or_update_content_status( content_status=active_status_value, market=market, content_name=content_name, ) assert is_success is False @pytest.mark.integration @pytest.mark.parametrize( 'market, content_name, size, expected_name, expected_size', [ ('us', 'cont_name', 123, 'cont_name', 123), ('us', 'cont_name', 0, 'cont_name', 0), ('us', '', 0, '', 0), ('us', None, None, '', 0), ] ) def test_save_complete_volatile_content_status__success( db: Connection, content_status: slz.ContentStatus, content_status_service: ContentStatusService, market: str, content_name: Optional[str], size: Optional[int], expected_name: str, expected_size: int, ): assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus) \ .filter(slz.ContentStatus.content_status_id == content_status.content_status_id) \ .one_or_none() is not None if size is None: assert content_status.content_size is not None else: assert size != content_status.content_size if content_name is None: assert content_status.content_name is not None else: assert content_name != content_status.content_name is_success = content_status_service.save_complete_volatile_content_status( market=market, content_name=content_name, size=size, ) assert is_success is True modified_content_status = db.session.query(slz.ContentStatus).one() assert modified_content_status.content_status == slz.ContentStatusEnum.COMPLETE_VOLATILE assert modified_content_status.content_status_id == content_status.content_status_id assert expected_name == modified_content_status.content_name assert expected_size == modified_content_status.content_size @pytest.mark.integration @pytest.mark.parametrize( 'market, content_name, size', [ ('us', 'cont_name', 9_223_372_036_854_775_808), # Max value allowed to store in the field. ('gb', 'cont_name', 0), # Wrong market. ] ) def test_save_complete_volatile_content_status__fail( db: Connection, content_status: slz.ContentStatus, content_status_service: ContentStatusService, market: str, content_name: str, size: int, ): assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus) \ .filter(slz.ContentStatus.content_status_id == content_status.content_status_id) \ .one_or_none() is not None assert content_status.content_size != size assert content_status.content_name != content_name is_success = content_status_service.save_complete_volatile_content_status( market=market, content_name=content_name, size=size, ) assert is_success is False @pytest.mark.integration def test_save_complete_volatile_content_status__fail_no_objects( db: Connection, content_status_service: ContentStatusService, ): assert db.session.query(slz.ContentStatus).count() == 0 assert db.session.query(slz.UnitOfWork).count() == 0 is_success = content_status_service.save_complete_volatile_content_status( market='us', content_name='content_name', size=123, ) assert is_success is False @pytest.mark.integration def test_save_missing_content_status( db: Connection, content_status: slz.ContentStatus, content_status_service: ContentStatusService, ): assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus) \ .filter(slz.ContentStatus.content_status_id == content_status.content_status_id) \ .one_or_none() is not None is_success = content_status_service.save_missing_content_status(market='us') assert is_success is True modified_content_status = db.session.query(slz.ContentStatus).one() assert modified_content_status.content_status_id == content_status.content_status_id assert modified_content_status.content_status == slz.ContentStatusEnum.MISSING @pytest.mark.integration def test_save_failed_content_status( db: Connection, payload_test: Payload, content_status: slz.ContentStatus, content_status_service: ContentStatusService, ): assert db.session.query(slz.ContentStatus).count() == 1 assert db.session.query(slz.ContentStatus) \ .filter(slz.ContentStatus.content_status_id == content_status.content_status_id) \ .one_or_none() is not None assert db.session.query(slz.ContentFailureLog).count() == 0 initial_fail_count: int = content_status.failure_count failure_description = 'test_failure_description' failure_code = 'test_failure_code' is_success = content_status_service.save_failed_content_status( failure_description=failure_description, failure_code=failure_code, market='us', ) assert is_success is True modified_content_status: slz.ContentStatus = db.session.query(slz.ContentStatus).one() assert modified_content_status.content_status_id == content_status.content_status_id assert modified_content_status.content_status == slz.ContentStatusEnum.FAILED assert modified_content_status.failure_count == initial_fail_count + 1 assert db.session.query(slz.ContentFailureLog).count() == 1 failure_log: slz.ContentFailureLog = db.session.query(slz.ContentFailureLog).one() assert failure_log.content_status_id == content_status.content_status_id assert failure_log.job_id == payload_test.job_id assert failure_log.failure_code == failure_code assert failure_log.failure_description == failure_description