"""Integration test configuration.""" import os from typing import Any from abacus_common_logic.connectors.database import db from py.xml import html import pytest from pytest_html.extras import url from abacus_contract.api import create_app from abacus_contract.config import Config from tests.integration.consts import api from tests.integration.consts.headers import ADMIN_HEADERS from tests.integration.utils import auth from tests.integration.utils.ows_abacus_contract_api_client import \ AbacusContractAPIClient JIRA_PREFIX_URL = 'https://theorchard.atlassian.net/browse/' PYTEST_REPORT_PREFIX = 'ows-abacus-contract microservice integration tests results' PYTEST_REPORT_SUMMARY = 'integration tests of ows-abacus-contract microservice' 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.""" 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(api.QA_BASE_URL, headers) @pytest.fixture(autouse=True) def clear_db(): """Clean tables between runs.""" tables = [ 'abacus_event', 'account', 'account_contract', 'account_payment_term', 'account_tax_info', 'accounting_period', 'accounting_period_report', 'accounting_run', 'contract', 'contract_flowthrough', 'contract_reserve', 'contract_term_condition', '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' ] con = db.engine.connect() con.execute('SET FOREIGN_KEY_CHECKS = 0;') trans = con.begin() for table in tables: con.execute(f'TRUNCATE TABLE `{table}`') trans.commit() con.execute('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.""" query = """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()) """ db.engine.execute(query) @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 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) @pytest.mark.optionalhook def pytest_html_results_summary(prefix, summary, postfix): """Populate report with info and summary.""" prefix.extend([html.p(PYTEST_REPORT_PREFIX)]) summary.extend([html.p(PYTEST_REPORT_SUMMARY)]) def pytest_html_results_table_header(cells): """Create results table header.""" cells.insert(1, html.th('Jira ID')) cells.insert(2, html.th('Description')) cells.pop() def pytest_html_results_table_row(report, cells): """Populate results table row.""" jira_ids = getattr(report, 'jira_ids', []) jira_links = [] for index, item in enumerate(jira_ids): link = url('{}{}'.format(JIRA_PREFIX_URL, item), item) if index == 0: jira_links.append(html.a(link['name'], href=link['content'])) else: jira_links.append(', ') jira_links.append(html.a(link['name'], href=link['content'])) jira_links_html = html.td(*jira_links) cells.insert(1, jira_links_html) cells.insert(2, html.td(report.description)) cells.pop() @pytest.hookimpl(hookwrapper=True) def pytest_runtest_makereport(item, call): """Populate results table.""" outcome = yield report = outcome.get_result() report.description = str(item.function.__doc__) if item.get_closest_marker('jira') is not None: to_populate = [] for item in item.get_closest_marker('jira').args: to_populate.append(item) report.jira_ids = to_populate else: report.jira_id = [] 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