"""Regression tests for SFTP and S3 environment variable resolution. Bug 2 (introduced commit ffa216d, fixed INT-2649): The Jenkins parameter was renamed from TRANSFER_FILES to SFTP_TRANSFER_ENABLED but config.py and feed_file_exporter.py still read only TRANSFER_FILES. Result: selecting "Transfer Files = Yes" in Jenkins had no effect — SFTP transfer was silently disabled on every production run. Fix: config.SFTP_TRANSFER_ENABLED now reads SFTP_TRANSFER_ENABLED first (Jenkins) and falls back to TRANSFER_FILES (CLI / legacy) for backwards compat. Bug 3 (introduced commits 351445a / d2ea76c, fixed INT-2649): S3 config was originally split into separate S3_* and SAP_S3_* blocks, which has been corrected to a single S3_* namespace. This falsely implied two independent S3 subsystems. There is ONE S3 transfer capability; whether a given PROCESS_MODE may use it is determined by the whitelist guard in feed_file_exporter.py. S3 transfer via S3_TRANSFER_ENABLED is whitelist-gated: only SAP_SETTLEMENT is currently authorised. Any other mode that sets S3_TRANSFER_ENABLED will receive a logged warning and be skipped gracefully (no crash, no transfer). """ import sys def _reload_config(monkeypatch, **env_overrides): """Reload config module with a controlled environment. Removes the cached module so that os.environ changes take effect, applies overrides (None means delete the variable), and returns the fresh module. """ monkeypatch.delitem(sys.modules, 'config', raising=False) for key, value in env_overrides.items(): if value is None: monkeypatch.delenv(key, raising=False) else: monkeypatch.setenv(key, value) import config as cfg # noqa: PLC0415 — intentional deferred import return cfg # ── Bug 2: SFTP env var rename backwards-compat ─────────────────────────────── def test_sftp_enabled_via_new_jenkins_env_var(monkeypatch): """SFTP_TRANSFER_ENABLED=True (the Jenkins param) activates SFTP transfer.""" cfg = _reload_config( monkeypatch, SFTP_TRANSFER_ENABLED='True', TRANSFER_FILES=None, ) assert cfg.SFTP_TRANSFER_ENABLED == 1 def test_sftp_enabled_via_legacy_env_var(monkeypatch): """TRANSFER_FILES=True (CLI --enable-sftp path) still activates SFTP. Backwards-compatibility: the CLI sets TRANSFER_FILES; must not break. """ cfg = _reload_config( monkeypatch, TRANSFER_FILES='True', SFTP_TRANSFER_ENABLED=None, ) assert cfg.SFTP_TRANSFER_ENABLED == 1 def test_sftp_disabled_when_neither_env_var_set(monkeypatch): """SFTP is disabled (default False) when neither env var is present.""" cfg = _reload_config( monkeypatch, SFTP_TRANSFER_ENABLED=None, TRANSFER_FILES=None, ) assert cfg.SFTP_TRANSFER_ENABLED == 0 def test_sftp_disabled_explicitly_via_new_env_var(monkeypatch): """SFTP_TRANSFER_ENABLED=False explicitly disables SFTP.""" cfg = _reload_config( monkeypatch, SFTP_TRANSFER_ENABLED='False', TRANSFER_FILES=None, ) assert cfg.SFTP_TRANSFER_ENABLED == 0 def test_sftp_disabled_explicitly_via_legacy_env_var(monkeypatch): """TRANSFER_FILES=No explicitly disables SFTP (Jenkins Yes/No string).""" cfg = _reload_config( monkeypatch, TRANSFER_FILES='No', SFTP_TRANSFER_ENABLED=None, ) assert cfg.SFTP_TRANSFER_ENABLED == 0 # ── Bug 3: S3 config reads correct env vars ────────────────────────────────── # S3_TRANSFER_ENABLED is whitelist-gated to certain modes only. # These tests confirm the config-level read is correct; whitelist enforcement # at runtime is tested in test_s3_whitelist.py. def test_s3_transfer_enabled_via_env_var(monkeypatch): """S3_TRANSFER_ENABLED=True is read correctly by config.""" cfg = _reload_config(monkeypatch, S3_TRANSFER_ENABLED='True') assert cfg.S3_TRANSFER_ENABLED == 1 def test_s3_transfer_disabled_by_default(monkeypatch): """S3 transfer is disabled by default (no env var set).""" cfg = _reload_config(monkeypatch, S3_TRANSFER_ENABLED=None) assert cfg.S3_TRANSFER_ENABLED == 0 def test_s3_gzip_enabled_via_env_var(monkeypatch): """S3_GZIP_ENABLED=True enables gzip before S3 transfer.""" cfg = _reload_config(monkeypatch, S3_GZIP_ENABLED='True') assert cfg.S3_GZIP_ENABLED == 1 def test_s3_gzip_defaults_to_enabled(monkeypatch): """S3 gzip defaults to enabled.""" cfg = _reload_config(monkeypatch, S3_GZIP_ENABLED=None) assert cfg.S3_GZIP_ENABLED == 1 def test_s3_config_vars_read_from_env(monkeypatch): """S3_BUCKET, S3_FOLDER, S3_REGION are read from environment correctly.""" cfg = _reload_config( monkeypatch, S3_TARGET_ENV='prod', S3_ROLE_ARN='arn:aws:iam::123456789012:role/custom-role', S3_BUCKET='my-prod-bucket', S3_FOLDER='stars/monthly', S3_REGION='eu-west-1', ) assert cfg.S3_TARGET_ENV == 'prod' assert cfg.S3_ROLE_ARN == 'arn:aws:iam::123456789012:role/custom-role' assert cfg.S3_BUCKET == 'my-prod-bucket' assert cfg.S3_FOLDER == 'stars/monthly' assert cfg.S3_REGION == 'eu-west-1' def test_s3_retries_and_backoff_from_env(monkeypatch): """S3_RETRIES and S3_RETRY_BACKOFF_SEC are read from environment.""" cfg = _reload_config( monkeypatch, S3_RETRIES='5', S3_RETRY_BACKOFF_SEC='3.5', ) assert cfg.S3_RETRIES == 5 assert cfg.S3_RETRY_BACKOFF_SEC == 3.5 def test_s3_continue_on_error_from_env(monkeypatch): """S3_CONTINUE_ON_ERROR=True sets the config flag correctly.""" cfg = _reload_config(monkeypatch, S3_CONTINUE_ON_ERROR='True') assert cfg.S3_CONTINUE_ON_ERROR == 1 def test_s3_bucket_and_folder_defaults(monkeypatch): """S3 bucket and folder defaults match the production values.""" cfg = _reload_config( monkeypatch, S3_TARGET_ENV=None, S3_ROLE_ARN=None, S3_BUCKET=None, S3_FOLDER=None, ) assert cfg.S3_TARGET_ENV == 'qa' assert cfg.S3_ROLE_ARN == ( 'arn:aws:iam::989790945997:role/' 'qa-sme-feed-file-exporter-jenkins-role' ) assert cfg.S3_BUCKET == 'prod-abacus-sap' assert cfg.S3_FOLDER == 'bw_settlement_files' def test_s3_role_arn_defaults_from_prod_target_env(monkeypatch): """prod target env derives the prod Jenkins assume-role ARN.""" cfg = _reload_config( monkeypatch, S3_TARGET_ENV='prod', S3_ROLE_ARN=None, ) assert cfg.S3_TARGET_ENV == 'prod' assert cfg.S3_ROLE_ARN == ( 'arn:aws:iam::989790945997:role/' 'prod-sme-feed-file-exporter-jenkins-role' ) def test_s3_target_env_invalid_value_falls_back_to_qa(monkeypatch): """Unexpected target env values fall back to the safe QA role.""" cfg = _reload_config( monkeypatch, S3_TARGET_ENV='staging', S3_ROLE_ARN=None, ) assert cfg.S3_TARGET_ENV == 'qa' assert cfg.S3_ROLE_ARN == ( 'arn:aws:iam::989790945997:role/' 'qa-sme-feed-file-exporter-jenkins-role' ) def test_s3_bucket_and_folder_read_from_env(monkeypatch): """S3 target can be overridden via environment.""" cfg = _reload_config( monkeypatch, S3_BUCKET='qa-abacus-sap', S3_FOLDER='bw_settlement_files', ) assert cfg.S3_BUCKET == 'qa-abacus-sap' assert cfg.S3_FOLDER == 'bw_settlement_files' def test_s3_transfer_mode_defaults_to_individual_files(monkeypatch): """S3 uploads individual files by default.""" cfg = _reload_config(monkeypatch, S3_TRANSFER_MODE=None) assert cfg.S3_TRANSFER_MODE == 'Individual Files' def test_s3_allowed_modes_defaults_to_sap_settlement(monkeypatch): """S3_ALLOWED_MODES defaults to {SAP_SETTLEMENT} when env var absent.""" cfg = _reload_config(monkeypatch, S3_ALLOWED_MODES=None) assert cfg.S3_ALLOWED_MODES == frozenset({'SAP_SETTLEMENT'}) def test_s3_allowed_modes_parsed_from_env(monkeypatch): """S3_ALLOWED_MODES parses a comma-separated list into a frozenset.""" cfg = _reload_config(monkeypatch, S3_ALLOWED_MODES='SAP_SETTLEMENT,US,GB') assert cfg.S3_ALLOWED_MODES == frozenset({'SAP_SETTLEMENT', 'US', 'GB'}) def test_s3_allowed_modes_strips_whitespace(monkeypatch): """Surrounding whitespace in S3_ALLOWED_MODES entries is stripped.""" cfg = _reload_config(monkeypatch, S3_ALLOWED_MODES=' SAP_SETTLEMENT , US ') assert cfg.S3_ALLOWED_MODES == frozenset({'SAP_SETTLEMENT', 'US'})