"""Integration test configuration.""" import os from typing import Any import pytest from abacus_common_logic.connectors.database import db from abacus_contract.tests.integration.consts.headers import ADMIN_HEADERS from abacus_contract.tests.integration.utils import auth from abacus_contract.tests.integration.utils.ows_abacus_contract_api_client import ( AbacusContractAPIClient, ) from core.config import Config ROYALTIES_API_BASE_URL = os.environ.get( 'ROYALTIES_API_BASE_URL', 'http://localhost:6052' ) print(f'Using royalties URL {ROYALTIES_API_BASE_URL}') class TestConfig(Config): """Test configuration.""" MYSQL_DB_NAME = os.environ.get('MYSQL_DB_NAME', Config.MYSQL_DB_NAME) MYSQL_DB_USER = os.environ.get('MYSQL_DB_USER', 'royalties') MYSQL_DB_HOST = os.environ.get('MYSQL_DB_HOST', 'mysql-royalties-container') MYSQL_DB_PORT = os.environ.get('MYSQL_DB_PORT', '3306') MYSQL_DB_PASS = os.environ.get('MYSQL_DB_PASS', '1234') @pytest.fixture(scope='session', autouse=True) def test_app(): """Create a test application.""" from core.app_factory import create_app return create_app(TestConfig) @pytest.mark.fixture @pytest.fixture(autouse=True) def test_app_in_context(test_app): """Push the test app onto the context.""" with test_app.app_context(): yield test_app @pytest.fixture def basic_headers(): """Return basic headers.""" return {'Content-Type': 'application/json'} def ows_abacus_contract_api_client(headers: dict[str, str]) -> AbacusContractAPIClient: """Create ows-abacus-contract client.""" return AbacusContractAPIClient(ROYALTIES_API_BASE_URL, headers) @pytest.fixture(autouse=True) def clear_db(): """Clean tables between runs.""" tables = [ 'abacus_event', 'abacus_state', 'account', 'account_contract', 'account_payment_term', 'account_tax_info', 'accounting_period', 'accounting_period_report', 'accounting_run', 'contract', 'contract_flowthrough', 'contract_reserve', 'contract_term_condition', 'exchange_rate', 'legacy_contract', 'ledger_contract_advance_applied', 'contract_advance', 'contract_exclusion', 'contract_lifecycle', 'contract_lifecycle_schedule', 'contract_lifecycle_schedule_detail', 'contract_template', 'contract_term_schedule', 'contract_party', 'contract_term', 'contract_mechanical_deduction', 'contract_mechanical_deduction_history', 'exchange_rate', 'run_controller', 'run_controller_contract', 'reference_flowthrough_calculation', 'sales_file', 'schedule', 'statement_period', 'statement_period_payment_entity', 'statement_period_adjustment_file', 'worksheet_adjustment', 'signing_entity_sap_profit_center', 'reference_sap_profit_center', 'reference_payment_entity', ] with db.engine.connect() as conn: conn.exec_driver_sql('SET FOREIGN_KEY_CHECKS=0') for table in tables: conn.exec_driver_sql(f'TRUNCATE TABLE {table}') conn.exec_driver_sql('SET FOREIGN_KEY_CHECKS=1') @pytest.fixture def create_account_fixture(): """Create a DB fixture in account table.""" query = """INSERT INTO royalty_accounting.account ( account_id, account_name, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 'Test Account 1', 'Test', '2021-01-01 00:00:00', 'Test', '2021-01-01 00:00:00') """ db.engine.execute(query) @pytest.fixture def create_contract_fixture(): """Create a DB fixture in contract table. Seeds the (PE, PC, SE) parent chain so the NOT NULL FK contract.reference_signing_entity_id is satisfied. clear_db truncates the PE/PC tables between tests but leaves reference_signing_entity intact, so INSERT IGNORE on all three keeps the chain valid each run. """ db.engine.execute(""" INSERT IGNORE INTO reference_payment_entity ( reference_payment_entity_id, payment_entity_name, country_of_tax_reporting, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 'Test PE', 'GBR', 'Test', NOW(), 'Test', NOW()) """) db.engine.execute(""" INSERT IGNORE INTO reference_sap_profit_center ( reference_sap_profit_center_id, profit_center, company_code, business_group, display_name ) VALUES (1, 'TST0001', '4914', 'ORC', 'PC TST0001') """) db.engine.execute(""" INSERT IGNORE INTO reference_signing_entity ( reference_signing_entity_id, reference_payment_entity_id, reference_sap_profit_center_id, company_code, legal_name ) VALUES (2, 1, 1, '4914', 'Test Signing Entity') """) db.engine.execute("""INSERT INTO royalty_accounting.contract ( contract_id, contract_name, reference_signing_entity_id, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 'Test contract', 2, 'Test', NOW(), 'Test', NOW()) """) @pytest.fixture def create_account_contract_fixture(): """Create a DB fixture in account_contract table.""" query = """INSERT INTO royalty_accounting.account_contract ( account_contract_id, account_id, contract_id ) VALUES (1, 1, 1); """ db.engine.execute(query) @pytest.fixture def reference_payment_entity_fixtures(): """Create reference_payment_entity records for testing.""" reference_payment_entity_insert = """ INSERT INTO reference_payment_entity( reference_payment_entity_id, payment_entity_name, country_of_tax_reporting, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 'AWAL-UK', 'GBR', 'test_user', NOW(), 'test_user', NOW()), (2, 'AWAL-US', 'USA', 'test_user', NOW(), 'test_user', NOW()), (3, 'KNR-UK', 'NLD', 'test_user', NOW(), 'test_user', NOW()), (4, 'KNR-NL', 'GBR', 'test_user', NOW(), 'test_user', NOW()); """ db.engine.execute(reference_payment_entity_insert) @pytest.fixture def reference_sap_profit_center_fixtures(): """Create reference_sap_profit_center records for testing.""" reference_sap_profit_center_insert = """ INSERT INTO reference_sap_profit_center( reference_sap_profit_center_id, profit_center, company_code, business_group, display_name ) VALUES ( 1, 'UK4914', 4914, 'ORC', 'PC UK4914'), ( 2, 'UK4915', 4915, 'ORC', 'PC UK4915') """ db.engine.execute(reference_sap_profit_center_insert) @pytest.fixture def create_account_tax_info_fixture(): """Create a DB fixture in account_tax_info table.""" query = """INSERT INTO royalty_accounting.account_tax_info( account_tax_info_id, account_id, country_of_tax_residence, is_sba_signed, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 1, 'GBR', 1, 'VZ', NOW(), 'VZ', NOW()) """ db.engine.execute(query) @pytest.fixture def create_abacus_event_fixture(): """Create abacus_event fixture.""" query = """ INSERT INTO abacus_event ( `abacus_event_id`, `statement_period_id`, `event_date`, `event_name`, `target_type`, `target_id`, `created_by` ) VALUES ( 1, 300, '2022-09-13', 'confirm_advance_payment', 'contract', 1, 'Test' ) """ db.engine.execute(query) @pytest.fixture def create_ledger_contract_advance_applied_fixture(): """Create ledger_contract_advance_applied fixture.""" query = """ INSERT INTO ledger_contract_advance_applied ( abacus_event_id, account_id, contract_id, contract_advance_id, statement_period_id, advance_amount, advance_currency_code, advance_amount_payee_currency, advance_payee_currency_code, created_by, created_at, last_modified_by, last_modified ) VALUES ( 1, 1, 1, 1, 300, 50.00, 'GBP', 50.00, 'GBP', 'vz', '2022-09-26 03:41:03', 'vz', '2022-09-26 03:41:05' ) """ db.engine.execute(query) @pytest.fixture def create_paid_contract_advance_fixture(): """Create contract_advance with 'paid' status fixture.""" query = """ INSERT INTO contract_advance ( contract_id, advance_description, amount, currency_code, milestone, milestone_description, milestone_date, advance_status, note, created_by, created_at, last_modified_by, last_modified, deleted_at, deleted_by ) VALUES ( 1, 'test', 50.00, 'GBP', 'contract_execution', 'words', '2022-09-26', 'paid', 'text', 'vz', '2022-09-26 04:39:09', 'vz', '2022-09-26 04:39:10', null, null ) """ db.engine.execute(query) @pytest.fixture def create_schedule_fixtures(): """Create a DB fixture in schedules table.""" query = """ INSERT INTO royalty_accounting.schedule ( schedule_id, schedule_name, target_type, target_id, conditions, created_by, created_at, last_modified_by, last_modified ) VALUES ( 1, 'integr_autotest_1', 'contributor', '9d9c0699-30ba-4f7e-b444-4795ddbc7379', null, 'default_user_id', '2023-08-28 12:27:14', 'default_user_id', '2023-08-28 12:27:19' ), ( 2, 'integr_autotest_2', 'contributor', '1b8d2754-30ba-4f7e-b444-4795ddbc7654', null, 'default_user_id', '2023-08-28 12:27:14', 'default_user_id', '2023-08-28 12:27:19' ) """ db.engine.execute(query) @pytest.fixture def create_contract_lifecycle_schedule_detail_fixture(): """Create contract_lifecycle_schedule_detail fixture.""" query = """ INSERT INTO contract_lifecycle_schedule_detail ( contract_lifecycle_schedule_detail_id, period_interval, period_type ) VALUES ( 1, 1, 'year'), ( 2, 30, 'day'), ( 3, 6, 'month') """ db.engine.execute(query) @pytest.fixture def create_contract_lifecycle_fixture(): """Create contract_lifecycle fixture.""" query = """ INSERT INTO contract_lifecycle( contract_lifecycle_id, contract_id, contract_lifecycle_schedule_id, lifecycle_status, lifecycle_term_start, lifecycle_term_end, renewal_effective, termination_notice_deadline, termination_notice_received, termination_effective, collection_start, collection_end, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 1, 1, 'active', '2024-01-01', null, null, null, null, null, null, null, 'test', NOW(), 'test', NOW() ) """ db.engine.execute(query) @pytest.fixture def create_contract_lifecycle_schedule_fixture(): """Create contract_lifecycle_schedule fixture.""" query = """ INSERT INTO contract_lifecycle_schedule ( contract_lifecycle_schedule_id, contract_id, termination_notice_detail_id, renewal_offset_detail_id, collection_period_detail_id, renewal_type, schedule_end, created_by, created_at, last_modified_by, last_modified ) VALUES (1, 1, 2, null, null, 'continuously_active', null, 'test', NOW(), 'test', NOW() ) """ db.engine.execute(query) @pytest.fixture def create_reference_flowthrough_calculation(): """Create reference_flowthrough_calculation fixture.""" query = """ INSERT INTO reference_flowthrough_calculation ( reference_flowthrough_calculation_id, flowthrough_calculation_name, flowthrough_calculation, flowthrough_calculation_example, flowthrough_calculation_example_summary ) VALUES ( %s, %s, %s, %s, %s ) """ values = ( 1, 'integration_tests_flowthrough_calculation', '(Net Revenue - Expenses) * FT%', 'Example of Calculation: ($100,000 - $5,000) * 20% = $19,000', 'Net Revenue: 100,000, Expenses: 5,000, Flowthrough: 20%', ) db.engine.execute(query, values) def update_db_field_value(table, column, condition, new_value): """Update the value of the specified column in a table based on a condition.""" query = f'UPDATE {table} SET {column} = %s WHERE {condition}' db.engine.execute(query, (new_value,)) @pytest.fixture(scope='session') def bearer_token_read_only_user() -> str: """Fetch Auth0 secrets from AWS Secret Manager.""" login_secrets = auth.login_from_secrets_manager( password_secret_name=auth.READONLY_USER_PASSWORD_SECRET_NAME, auth_client_id_secret_name=auth.AUTH0_CLIENT_ID_SECRET_NAME, auth_client_secret_secret_name=auth.AUTH0_CLIENT_SECRET_SECRET_NAME, ) token = auth.generate_auth_token( auth.LoginInfo( auth_client_id=login_secrets.auth_client_id, auth_client_secret=login_secrets.auth_client_secret, password=login_secrets.password, username=auth.READONLY_USER, ) ) return token @pytest.fixture(scope='session') def admin_headers() -> dict[str, str]: """Return profile headers.""" return ADMIN_HEADERS @pytest.fixture(params=['read_only', 'admin'], scope='session') def auth_headers( request: Any, bearer_token_read_only_user: str, admin_headers: dict[str, str] ) -> dict[str, str]: """Return appropriate headers based on the parameter.""" if request.param == 'read_only': return { 'Authorization': f'Bearer {bearer_token_read_only_user}', 'Content-Type': 'application/json', 'Orchard-Identity-Id': 'ff3737b4-7734-4339-9798-5ddd1c9998b0', 'Orchard-Profile-Id': '1005', 'Orchard-Profile-Type': 'Account360Profile', 'Orchard-Requestor-Service': 'graphql-abacus', 'Orchard-Roles': 'account360', } else: return admin_headers @pytest.fixture() def unauthorized_headers(bearer_token_read_only_user_unauthorized) -> dict[str, str]: """Return headers for unauthorized user.""" return { 'Authorization': f'Bearer {bearer_token_read_only_user_unauthorized}', 'Orchard-Requestor-Service': 'graphql-abacus', } @pytest.fixture(scope='session') def bearer_token_read_only_user_unauthorized() -> str: """Fetch Auth0 secrets from AWS Secret Manager.""" login_secrets = auth.login_from_secrets_manager( password_secret_name=auth.READONLY_USER_PASSWORD_SECRET_NAME, auth_client_id_secret_name=auth.AUTH0_CLIENT_ID_SECRET_NAME, auth_client_secret_secret_name=auth.AUTH0_CLIENT_SECRET_SECRET_NAME, ) token = auth.generate_auth_token( auth.LoginInfo( auth_client_id=login_secrets.auth_client_id, auth_client_secret=login_secrets.auth_client_secret, password=login_secrets.password, username=auth.READONLY_USER_UNAUTHORIZED, ) ) return token