"""Functional tests for terms and conditions endpoints.""" from unittest.mock import patch from collaborator.connectors import mysql from collaborator.constants import error from collaborator.constants.statement_period import StatementPeriodStatus from collaborator.models.rds.statement_period import StatementPeriod from collaborator.utils.error import OwsError from tests.testutils import db, mock_auth @db.test_schema_default_seed def test_agree_terms_and_conditions(auth_client, mocker, mock_account): """Test agree terms and conditions for vendor.""" mock_auth(mocker, mock_account.id) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value={"statement_period_id": 1}, ) # Patch at the import location in the terms_and_conditions logic module mocker.patch( "collaborator.logic.terms_and_conditions.is_account_payment_eligible", return_value=True, ) payload = {"vendor_id": mock_account.id, "terms_and_conditions_id": 2} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 200 assert result.json["terms_and_conditions_id"] == 2 assert result.json["vendor_id"] == mock_account.id @db.test_schema_default_seed def test_agree_terms_and_conditions_missing_vendor_id( auth_client, mocker, mock_account ): """Test agree terms and conditions with missing vendor_id.""" mock_auth(mocker, mock_account.id) payload = {"terms_and_conditions_id": 2} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 400 @db.test_schema_default_seed def test_agree_terms_and_conditions_missing_terms_conditions_id( auth_client, mocker, mock_account ): """Test agree terms and conditions with missing terms_and_conditions_id.""" mock_auth(mocker, mock_account.id) # Mock external dependencies - even with missing params, the handler reaches the logic layer mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value={"statement_period_id": 1}, ) mocker.patch( "collaborator.logic.terms_and_conditions.is_account_payment_eligible", return_value=True, ) # Mock that there's no open statement period (to avoid database calls with None params) with patch( "collaborator.models.rds.statement_period_persister.StatementPeriodPersister.get_open_statement_period", return_value=None, ): payload = {"vendor_id": mock_account.id} result = auth_client.post("/terms-and-conditions/agreement", json=payload) # Since handler doesn't validate terms_and_conditions_id, it will reach the persister # The persister will handle the None gracefully or fail with a database error # For now, we expect it to proceed and fail at the database level assert result.status_code != 200 @db.test_schema_default_seed def test_agree_terms_and_conditions_no_direct_payments( auth_client, mocker, mock_account ): """Test agree terms and conditions fails for vendor without direct payments.""" mock_auth(mocker, mock_account.id) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) payload = {"vendor_id": mock_account.id, "terms_and_conditions_id": 2} with patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=False ): result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 403 assert result.json["code"] == error.ERROR_CODE_AUTHORIZATION assert result.json["message"] == error.ERROR_MESSAGE_FORBIDDEN_USER @db.test_schema_default_seed def test_agree_terms_and_conditions_outdated_version(auth_client, mocker, mock_account): """Test agree terms and conditions with outdated version.""" mock_auth(mocker, mock_account.id) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value={"statement_period_id": 1}, ) payload = {"vendor_id": mock_account.id, "terms_and_conditions_id": 1} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 404 assert result.json["code"] == "not_found" assert "outdated" in result.json["message"] @db.test_schema_default_seed def test_agree_terms_and_conditions_not_payment_eligible( auth_client, mocker, mock_account ): """Test agree terms and conditions fails when vendor is not payment eligible.""" mock_auth(mocker, mock_account.id) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value={"statement_period_id": 1}, ) # Patch at the import location in the terms_and_conditions logic module mocker.patch( "collaborator.logic.terms_and_conditions.is_account_payment_eligible", return_value=False, ) payload = {"vendor_id": mock_account.id, "terms_and_conditions_id": 2} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 404 assert result.json["code"] == "not_found" assert "not payment eligible" in result.json["message"] @db.test_schema_default_seed def test_agree_terms_and_conditions_with_open_statement_period( auth_client, mocker, mock_account ): """Test agree ts&cs when open statement period exists and updates abacus ID.""" mock_auth(mocker, mock_account.id) # Mock only external dependencies (OWS services) mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) mocker.patch( "collaborator.logic.terms_and_conditions.is_account_payment_eligible", return_value=True, ) abacus_period_id = 42 mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value={"statement_period_id": abacus_period_id}, ) # The seeded data already has an open statement period (id=2) # for vendor_id=24601 (mock_account.id). We'll verify it gets updated with # the abacus_statement_period_id after the POST request payload = {"vendor_id": mock_account.id, "terms_and_conditions_id": 2} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 200 # Verify that the open statement period's abacus_statement_period_id was set correctly # by querying the database directly with mysql.create_session() as session: open_periods = ( session.query(StatementPeriod) .filter( StatementPeriod.vendor_id == mock_account.id, StatementPeriod.status == StatementPeriodStatus.OPEN, ) .all() ) assert len(open_periods) > 0, ( f"No open statement periods found for vendor {mock_account.id}" ) # Find the period and verify it has the correct abacus_statement_period_id has_abacus_id = any( p.abacus_statement_period_id == abacus_period_id for p in open_periods ) assert has_abacus_id, ( f"No period found with abacus_statement_period_id={abacus_period_id}" ) @db.test_schema_default_seed def test_agree_terms_and_conditions_no_open_statement_period( auth_client, mocker, mock_account ): """Test agree terms and conditions when no open statement period exists.""" # Use a vendor_id that has no statement periods in the seed data vendor_id_no_period = 99999 mock_auth(mocker, vendor_id_no_period) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value={"statement_period_id": 1}, ) # Patch at the import location in the terms_and_conditions logic module mocker.patch( "collaborator.logic.terms_and_conditions.is_account_payment_eligible", return_value=True, ) payload = {"vendor_id": vendor_id_no_period, "terms_and_conditions_id": 2} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 200 # No assertion needed for statement period since none exists @db.test_schema_default_seed def test_agree_terms_and_conditions_vendor_authorization_failure( auth_client, mocker, mock_account ): """Test agree terms and conditions when vendor authorization fails.""" mock_auth(mocker, mock_account.id) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) payload = {"vendor_id": mock_account.id, "terms_and_conditions_id": 2} with patch( "collaborator.handlers.terms_and_conditions.check_vendors_authorization" ) as mock_check: mock_check.side_effect = OwsError.forbidden( code=error.ERROR_CODE_AUTHORIZATION, message=error.ERROR_MESSAGE_FORBIDDEN_USER, ) result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 403 assert result.json["code"] == error.ERROR_CODE_AUTHORIZATION assert result.json["message"] == error.ERROR_MESSAGE_FORBIDDEN_USER @db.test_schema_default_seed def test_agree_terms_and_conditions_open_statement_no_abacus_period( auth_client, mocker, mock_account ): """Test agree terms and conditions fails when open statement exists but no abacus period.""" vendor_id_with_open_period = 77777 mock_auth(mocker, vendor_id_with_open_period) # Mock external dependencies mocker.patch( "collaborator.models.ows.ows_account.has_direct_payments", return_value=True ) mocker.patch( "collaborator.models.ows.ows_users.get_identity_metadata", return_value={"id": "user-1", "default_brand": "orchard"}, ) # Patch at the import location in the terms_and_conditions logic module mocker.patch( "collaborator.logic.terms_and_conditions.is_account_payment_eligible", return_value=True, ) # Mock that there's no current abacus period from external service mocker.patch( "collaborator.models.ows.ows_royalties.get_current_abacus_statement_period", return_value=None, ) # Create an open statement period for a test vendor with mysql.create_session() as session: period = StatementPeriod( vendor_id=vendor_id_with_open_period, name="Test Open Period", status=StatementPeriodStatus.OPEN, abacus_statement_period_id=None, # No abacus period ) session.add(period) session.commit() payload = {"vendor_id": vendor_id_with_open_period, "terms_and_conditions_id": 2} result = auth_client.post("/terms-and-conditions/agreement", json=payload) assert result.status_code == 404 assert result.json["code"] == "not_found" assert "No Abacus statement period found" in result.json["message"]