From c5d2fdcc3c6117ee442fbb8f54137af5db2b7987 Mon Sep 17 00:00:00 2001 From: Andrew Robbertz <24920994+Alrobbertz@users.noreply.github.com> Date: Fri, 12 Jun 2026 15:59:32 -0400 Subject: [PATCH 1/2] Refactor Artifacts --- .github/workflows/build_and_push.yml | 49 ------ .github/workflows/codestyle.yml | 38 +---- .github/workflows/testing.yml | 25 +-- README.md | 36 ----- README.rst | 101 ++++++++++++ lambda_function/src/config.yaml | 19 --- lambda_function/src/lambda.py | 29 ++-- .../process_artifacts/process_artifacts.py | 147 +++++++++++------- .../tests/test_process_artifacts.py | 25 ++- requirements.txt => requirements.dev.txt | 7 +- 10 files changed, 247 insertions(+), 229 deletions(-) delete mode 100644 .github/workflows/build_and_push.yml delete mode 100755 README.md create mode 100644 README.rst delete mode 100644 lambda_function/src/config.yaml rename requirements.txt => requirements.dev.txt (77%) diff --git a/.github/workflows/build_and_push.yml b/.github/workflows/build_and_push.yml deleted file mode 100644 index 2809046..0000000 --- a/.github/workflows/build_and_push.yml +++ /dev/null @@ -1,49 +0,0 @@ -name: Build and Push Docker Image to AWS ECR - -on: - push: - branches: [ main ] - tags: [ '*' ] - - -jobs: - build: - runs-on: self-hosted - - steps: - - name: Build and Push Docker Image to AWS Development ECR - if: github.ref == 'refs/heads/main' - run: | - # Clean up the workspace - rm -rf sdc_aws_artifacts_lambda - docker system prune -f - - # Login to AWS ECR - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 351967858401.dkr.ecr.us-east-1.amazonaws.com - - # Clone Repository - git clone https://github.com/HERMES-SOC/sdc_aws_artifacts_lambda.git && cd sdc_aws_artifacts_lambda/lambda_function - - # Build and push the Docker image - docker build -t dev-sdc_aws_artifacts_lambda --network=host . --no-cache - docker tag dev-sdc_aws_artifacts_lambda:latest 351967858401.dkr.ecr.us-east-1.amazonaws.com/dev-sdc_aws_artifacts_lambda:latest - docker push 351967858401.dkr.ecr.us-east-1.amazonaws.com/dev-sdc_aws_artifacts_lambda:latest - - - - name: Build and Push Docker Image to AWS Production ECR - if: startsWith(github.ref, 'refs/tags/') - run: | - # Clean up the workspace - rm -rf sdc_aws_artifacts_lambda - docker system prune -f - - # Login to AWS ECR - aws ecr get-login-password --region us-east-1 | docker login --username AWS --password-stdin 351967858401.dkr.ecr.us-east-1.amazonaws.com - - # Clone Repository - git clone https://github.com/HERMES-SOC/sdc_aws_artifacts_lambda.git && cd sdc_aws_artifacts_lambda/lambda_function - - # Build and push the Docker image - docker build -t dev-sdc_aws_artifacts_lambda --network=host . --no-cache - docker tag dev-sdc_aws_artifacts_lambda:latest 351967858401.dkr.ecr.us-east-1.amazonaws.com/sdc_aws_artifacts_lambda:latest - docker push 351967858401.dkr.ecr.us-east-1.amazonaws.com/sdc_aws_artifacts_lambda:latest \ No newline at end of file diff --git a/.github/workflows/codestyle.yml b/.github/workflows/codestyle.yml index ee00917..7b955e0 100755 --- a/.github/workflows/codestyle.yml +++ b/.github/workflows/codestyle.yml @@ -1,32 +1,10 @@ -# This workflow will install Python dependencies, run tests, run linting, and test building docs -name: Codestyle and Linting - -on: - pull_request: - branches: - - main - workflow_dispatch: # For on demand runs - schedule: - - cron: 0 0 * * * # Scheduled run every day at midnight +name: Codestyle and linting with Ruff +on: [ push, pull_request ] jobs: - build: - - runs-on: ${{ matrix.platform }} - strategy: - fail-fast: false - matrix: - platform: [ubuntu-latest] - python-version: [3.10] + ruff: + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - - name: Install dependencies - run: | - python -m pip install --upgrade pip - python -m pip install -r requirements.txt - - name: Lint Code with Black - run: | - black --check --diff lambda_function - - name: Lint Code with Flake - run: | - flake8 --count --max-line-length 100 lambda_function - + - uses: actions/checkout@v4 + - uses: astral-sh/ruff-action@v3 + - run: ruff check --fix + - run: ruff format \ No newline at end of file diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index c2a5963..8089a74 100755 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -2,32 +2,39 @@ name: Testing on: + push: + branches: + - main pull_request: branches: - main workflow_dispatch: # For on demand runs schedule: - cron: 0 0 * * * # Scheduled run every day at midnight + jobs: build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-latest steps: - - uses: actions/checkout@v3 - + - uses: actions/checkout@v4 # Set up Python 3.10 to match the version of Python used in AWS Lambda - - name: Set up Python 10 - uses: actions/setup-python@v3 + - name: Set up Python 3.10 + uses: actions/setup-python@v5 with: - python-version: 3.10.12 + python-version: '3.10' - name: Install dependencies run: | python -m pip install --upgrade pip - python -m pip install -r requirements.txt + python -m pip install -r requirements.dev.txt - name: Run tests run: | - pytest -s lambda_function/tests --cov=lambda_function/src --cov-report=html --log-cli-level=INFO + pytest --pyargs lambda_function/tests --cov=lambda_function/src --cov-report=html + + # Upload coverage reports to Codecov + - name: Upload coverage to Codecov + uses: codecov/codecov-action@v4 env: - SWXSOC_MISSION: hermes + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/README.md b/README.md deleted file mode 100755 index 05d14d3..0000000 --- a/README.md +++ /dev/null @@ -1,36 +0,0 @@ -# SWSOC File Artifacts Lambda Container - -| **CodeBuild Status** |![aws build status](https://codebuild.us-east-2.amazonaws.com/badges?uuid=eyJlbmNyeXB0ZWREYXRhIjoiNi9WaG5pa1V4MUpoVURjRXlWc0w5d1lKR293RWJPSGtudmUzNHljd2JWaHZaQ09TVE12UTVOMWdFdU9rMFA1QWs0eCtLTW9vblV1emNwQ01HN0hqMm9vPSIsIml2UGFyYW1ldGVyU3BlYyI6IjdUVHlYZUZsc0dCV2lnUDAiLCJtYXRlcmlhbFNldFNlcmlhbCI6MX0%3D&branch=main)| -|-|-| - -### **Base Image Used For Container:** https://github.com/HERMES-SOC/docker-lambda-base - -### **Description**: -This repository is to define the image to be used for the SWSOC file artifacts Lambda function container. This container will be built and and stored in an ECR Repo. -The container will contain the latest release code as the production environment and the latest code on master as the development. Files with the appropriate naming convention will be handled in production while files prefixed with `dev_` will be handled using the development environment. - -### **Testing Locally (Using own Test Data)**: -1. Build the lambda container image (from within the lambda_function folder) you'd like to test: - - `docker build -t artifacts_function:latest .` - -2. Run the lambda container image you've built (After using your mfa script), this will start the lambda runtime environment: - - `docker run -p 9000:8080 -v /home/dbarrous/dbarrous/sdc_aws_artifacts_lambda/lambda_function/tests/test_data:/test_data -e SDC_AWS_FILE_PATH=/test_data/hermes_EEA_l0_2023042-000000_v0.bin artifacts_function:latest` - -3. From a `separate` terminal, make a curl request to the running lambda function: - - `curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d @lambda_function/tests/test_data/test_eea_event.json` - -### **Testing Locally (Using own Instrument Package Test Data)**: -1. Build the lambda container image (from within the lambda_function folder) you'd like to test: - - `docker build -t artifacts_function:latest .` - -2. Run the lambda container image you've built (After using your mfa script), this will start the lambda runtime environment: - - `docker run -p 9000:8080 -v /home/dbarrous/dbarrous/sdc_aws_artifacts_lambda/lambda_function/tests/test_data:/test_data -e USE_INSTRUMENT_TEST_DATA=True artifacts_function:latest` - -3. From a `separate` terminal, make a curl request to the running lambda function: - - `curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" -d @lambda_function/tests/test_data/test_eea_event.json` diff --git a/README.rst b/README.rst new file mode 100644 index 0000000..9e95420 --- /dev/null +++ b/README.rst @@ -0,0 +1,101 @@ +======== +Overview +======== + +.. start-badges + +.. list-table:: + :stub-columns: 1 + + * - build status + - |testing| |codestyle| |coverage| + +.. |testing| image:: https://github.com/swxsoc/sdc_aws_artifacts_lambda/actions/workflows/testing.yml/badge.svg + :target: https://github.com/swxsoc/sdc_aws_artifacts_lambda/actions/workflows/testing.yml + :alt: testing status + +.. |codestyle| image:: https://github.com/swxsoc/sdc_aws_artifacts_lambda/actions/workflows/codestyle.yml/badge.svg + :target: https://github.com/swxsoc/sdc_aws_artifacts_lambda/actions/workflows/codestyle.yml + :alt: codestyle and linting + +.. |coverage| image:: https://codecov.io/gh/swxsoc/sdc_aws_artifacts_lambda/graph/badge.svg + :target: https://codecov.io/gh/swxsoc/sdc_aws_artifacts_lambda + :alt: code coverage + +.. end-badges + +This repository defines the image to be used for the SWSOC file artifacts Lambda function container. +This container will be built and and stored in an ECR Repo. +The container will contain the latest release code as the production environment and the latest code on master as the development. + +Running Unit Tests +------------------ + +.. code-block:: sh + + pytest --pyargs lambda_function/tests --cov=lambda_function/src --cov-report=html + +Testing Locally (Using own Test Data) +------------------------------------- + +The container image can be built and run locally. You can specify the base image at runtime. +At the time of writing, the base image defaults to +``padre-swsoc-docker-lambda-base:latest`` in AWS. + +.. code-block:: sh + + # Chose a Base Image for you desired mission + export BASE_IMAGE=public.ecr.aws/w5r9l1c8/padre-swsoc-docker-lambda-base:latest + export IMAGE_NAME=swxsoc_sdc_aws_artifacts_lambda + export VERSION=$(date -u +"%Y%m%d%H%M%S") + + # Build the image + docker build --no-cache --build-arg BASE_IMAGE=$BASE_IMAGE -t $IMAGE_NAME:latest lambda_function/. + + # Tag the image with a version + docker tag $IMAGE_NAME:latest $IMAGE_NAME:$VERSION + +Run the lambda container image you've built (After using your mfa script), this will start the lambda runtime environment: + +.. code-block:: sh + + docker run -p 9000:8080 \ + -v ~/lambda_function/tests/test_data:/test_data \ + -e SDC_AWS_FILE_PATH=/test_data/hermes_EEA_l0_2023042-000000_v0.bin \ + artifacts_function:latest + +From a **separate** terminal, make a curl request to the running lambda function: + +.. code-block:: sh + + curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" \ + -d @lambda_function/tests/test_data/test_eea_event.json + +Testing Locally (Using own Instrument Package Test Data) +-------------------------------------------------------- + +Run the lambda container image you've built (After using your mfa script), this will start the lambda runtime environment: + +.. code-block:: sh + + docker run -p 9000:8080 \ + -v ~/lambda_function/tests/test_data:/test_data \ + -e USE_INSTRUMENT_TEST_DATA=True \ + artifacts_function:latest + +From a **separate** terminal, make a curl request to the running lambda function: + +.. code-block:: sh + + curl -XPOST "http://localhost:9000/2015-03-31/functions/function/invocations" \ + -d @lambda_function/tests/test_data/test_eea_event.json + + +Acknowledgements +---------------- + +The package template used by this package is based on the one developed by the +`NASA Space Weather Science Operations Center (SWxSOC) `_ which is based on those provided by +`OpenAstronomy community `_ and the `SunPy Project `_. + +This project makes use of the `NASA Space Weather Science Operations Center (SWxSOC) `_. diff --git a/lambda_function/src/config.yaml b/lambda_function/src/config.yaml deleted file mode 100644 index 64fa0cb..0000000 --- a/lambda_function/src/config.yaml +++ /dev/null @@ -1,19 +0,0 @@ -# Mission name -MISSION_NAME: 'hermes' - -# Package name for mission -MISSION_PKG: 'hermes_core' - -# Incoming data bucket -INCOMING_BUCKET: 'swsoc-incoming' - -# Instrument names used in the mission. -# The names are used to dynamically import the instrument packages. -INSTR_NAMES: -- 'eea' -- 'nemisis' -- 'merit' -- 'spani' - -# Timestream region -TSD_REGION: 'us-east-1' diff --git a/lambda_function/src/lambda.py b/lambda_function/src/lambda.py index 4ea9cdd..2b89a43 100755 --- a/lambda_function/src/lambda.py +++ b/lambda_function/src/lambda.py @@ -1,23 +1,30 @@ """ This module contains the handler function and the main function -which contains the logicthat initializes the FileProcessor class -in it's correct environment. +which contains the logic that initializes the FileProcessor class +in its correct environment. """ +from typing import Any + from process_artifacts import process_artifacts -def handler(event, context) -> dict: +def handler(event: dict[str, Any], context: Any) -> dict[str, int | str]: """ - This is the lambda handler function that acts as a proxy - to the main function handle_event + Lambda handler that proxies to :func:`process_artifacts.handle_event`. + + Parameters + ---------- + event : dict[str, Any] + Event data passed from the Lambda trigger. + context : Any + AWS Lambda context object. - :param event: Event data passed from the lambda trigger - :type event: dict - :param context: Lambda context - :type context: dict - :return: Returns a 200 (Successful) / 500 (Error) HTTP response - :rtype: dict + Returns + ------- + dict[str, int | str] + HTTP-style response with a ``statusCode`` (200 on success, 500 on + error) and a serialized ``body`` string. """ return process_artifacts.handle_event(event, context) diff --git a/lambda_function/src/process_artifacts/process_artifacts.py b/lambda_function/src/process_artifacts/process_artifacts.py index ac4e61c..56f3ddb 100755 --- a/lambda_function/src/process_artifacts/process_artifacts.py +++ b/lambda_function/src/process_artifacts/process_artifacts.py @@ -4,46 +4,48 @@ the artifacts for science files based off which bucket the file is located in. """ -import os import json +import os +from typing import Any import botocore - -from sdc_aws_utils.logging import log, configure_logger -from sdc_aws_utils.config import ( - TSD_REGION, - parser as science_filename_parser, - get_instrument_bucket, -) from sdc_aws_utils.aws import ( create_timestream_client_session, - log_to_timestream, get_science_file, + log_to_timestream, parse_file_key, ) - +from sdc_aws_utils.config import TSD_REGION, get_instrument_bucket +from sdc_aws_utils.config import parser as science_filename_parser +from sdc_aws_utils.logging import configure_logger, log from sdc_aws_utils.slack import ( + SlackApiError, get_slack_client, send_pipeline_notification, - SlackApiError, ) - # Configure logger configure_logger() -def handle_event(event, context) -> dict: +def handle_event(event: dict[str, Any], context: Any) -> dict[str, int | str]: """ - Handles the event passed to the lambda function to initialize the ArtifactProcessor - - :param event: Event data passed from the lambda trigger - :type event: dict - :param context: Lambda context - :type context: dict - :return: Returns a 200 (Successful) / 500 (Error) HTTP response - :rtype: dict + Process a Lambda event and dispatch file artifact processing work. + + Parameters + ---------- + event : dict[str, Any] + Triggering AWS Lambda event. Supports S3 ``Records`` events and empty + events that trigger a full incoming-bucket scan and sorting of all files. + context : Any + AWS Lambda context object (accepted for compatibility). + + Returns + ------- + dict[str, int | str] + Response dictionary containing ``statusCode`` and serialized ``body``. """ + try: environment = os.getenv("LAMBDA_ENVIRONMENT", "DEVELOPMENT") @@ -73,21 +75,29 @@ def handle_event(event, context) -> dict: class ArtifactProcessor: """ - The ArtifactProcessor class will then determine which instrument - library to use to process the file. - - :param s3_bucket: The name of the S3 bucket the file is located in - :type s3_bucket: str - :param file_key: The name of the S3 object that is being processed - :type file_key: str - :param environment: The environment the ArtifactProcessor is running in - :type environment: str - :param dry_run: Whether or not the ArtifactProcessor is performing a dry run - :type dry_run: bool + Dispatch artifact generation for a science file to the appropriate + instrument library based on its source S3 bucket. + + Parameters + ---------- + s3_bucket : str + Name of the S3 bucket the file is located in. + file_key : str + Key (object name) of the S3 object being processed. + environment : str + Environment the ArtifactProcessor is running in + (e.g. ``"DEVELOPMENT"`` or ``"PRODUCTION"``). + dry_run : str, optional + Truthy value indicates a dry run in which side effects are skipped. + Defaults to ``None``. """ def __init__( - self, s3_bucket: str, file_key: str, environment: str, dry_run: str = None + self, + s3_bucket: str, + file_key: str, + environment: str, + dry_run: str | None = None, ) -> None: # Initialize Class Variables self.instrument_bucket_name = s3_bucket @@ -105,11 +115,14 @@ def __init__( def _process_artifacts(self) -> None: """ - This method serves as the main entry point for the ArtifactProcessor class. - It will then determine which instrument library to use to process the file. + Main entry point for the ArtifactProcessor. + + Parses the file key, downloads the science file, and generates + Slack and Timestream artifacts for it. - :return: None - :rtype: None + Returns + ------- + None """ log.debug( { @@ -131,7 +144,7 @@ def _process_artifacts(self) -> None: destination_bucket = get_instrument_bucket(this_instr, self.environment) # Download file from S3 or get local file path - file_path = get_science_file( + _ = get_science_file( self.instrument_bucket_name, self.file_key, parsed_file_key, @@ -153,14 +166,22 @@ def _process_artifacts(self) -> None: @staticmethod def _generate_slack_artifacts( - filename_path, - ): + filename_path: str, + ) -> None: """ - Generates and sends Slack notifications for the file processing pipeline. - Includes error handling for Slack API interactions. + Send Slack notifications for the file processing pipeline. + + Handles errors raised by the Slack API so that failures do not + interrupt processing of subsequent artifacts. - :param filename_path: The pathname of the new file. - :type filename_path: str + Parameters + ---------- + filename_path : str + Pathname of the new file to announce in Slack. + + Returns + ------- + None """ try: # Initialize the slack client @@ -200,21 +221,31 @@ def _generate_slack_artifacts( @staticmethod def _generate_timestream_artifacts( - file_key, new_file_key, destination_bucket, environment - ): + file_key: str, + new_file_key: str, + destination_bucket: str, + environment: str, + ) -> None: """ - Logs file processing events to Amazon Timestream. - Handles the initialization of the Timestream client - and logs the necessary information. - - :param file_key: The key of the original file. - :type file_key: str - :param new_file_key: The key of the processed file. - :type new_file_key: str - :param destination_bucket: The name of the S3 bucket where the processed file is stored. - :type destination_bucket: str - :param environment: The current running environment. - :type environment: str + Log file processing events to Amazon Timestream. + + Initializes the Timestream client and records a ``PUT`` action + for the processed file. + + Parameters + ---------- + file_key : str + Key of the original file. + new_file_key : str + Key of the processed file. + destination_bucket : str + Name of the S3 bucket where the processed file is stored. + environment : str + Current running environment. + + Returns + ------- + None """ try: # Initialize Timestream Client diff --git a/lambda_function/tests/test_process_artifacts.py b/lambda_function/tests/test_process_artifacts.py index 6146d73..7a177f1 100755 --- a/lambda_function/tests/test_process_artifacts.py +++ b/lambda_function/tests/test_process_artifacts.py @@ -1,15 +1,12 @@ import json import os from unittest.mock import patch -from moto import mock_s3, mock_secretsmanager -os.environ["SDC_AWS_CONFIG_FILE_PATH"] = "lambda_function/src/config.yaml" - -from src.process_artifacts.process_artifacts import ( # noqa: E402 - handle_event, # noqa: E402 - ArtifactProcessor, # noqa: E402 -) # noqa: E402 +from moto import mock_aws as moto_mock_aws +import pytest +from src.process_artifacts.process_artifacts import ArtifactProcessor # noqa: E402 +from src.process_artifacts.process_artifacts import handle_event # noqa: E402; noqa: E402 # Constants for testing TEST_S3_BUCKET = "hermes-eea" @@ -37,9 +34,13 @@ } +@pytest.fixture(scope="function") +def mock_aws(): + """Mock AWS services using moto.""" + with moto_mock_aws(): + yield + # Mock boto3 S3 and Secrets Manager services -@mock_s3 -@mock_secretsmanager def setup_mocks(): # Setup S3 import boto3 @@ -65,8 +66,7 @@ def setup_mocks(): # Tests for handle_event function -@mock_s3 -def test_handle_event_success(): +def test_handle_event_success(mock_aws): setup_mocks() response = handle_event(TEST_EVENT, None) assert response == {"statusCode": 200, "body": "Artifacts Processed Successfully"} @@ -74,8 +74,7 @@ def test_handle_event_success(): # Tests for ArtifactProcessor class @patch("src.process_artifacts.process_artifacts.ArtifactProcessor._process_artifacts") -@mock_s3 -def test_artifact_processor_initialization(mock_process_artifacts): +def test_artifact_processor_initialization(mock_process_artifacts, mock_aws): setup_mocks() processor = ArtifactProcessor(TEST_S3_BUCKET, TEST_FILE_KEY, TEST_ENVIRONMENT) assert processor is not None diff --git a/requirements.txt b/requirements.dev.txt similarity index 77% rename from requirements.txt rename to requirements.dev.txt index dea8fe2..3daee49 100755 --- a/requirements.txt +++ b/requirements.dev.txt @@ -1,9 +1,8 @@ sdc_aws_utils @ git+https://github.com/swxsoc/sdc_aws_utils.git hermes_core @ git+https://github.com/HERMES-SOC/hermes_core.git psycopg2-binary==2.9.7 -moto==4.2.14 -pytest==8.0.0 +pytest==9.0.3 pytest-astropy==0.11.0 pytest-cov==4.1.0 -flake8==7.0.0 -black==24.1.1 \ No newline at end of file +moto==5.0.15 +ruff \ No newline at end of file From 27fa90ffe8d631d5ee85b7d044b2053aa9cdf26c Mon Sep 17 00:00:00 2001 From: Andrew Robbertz <24920994+Alrobbertz@users.noreply.github.com> Date: Tue, 16 Jun 2026 08:37:11 -0400 Subject: [PATCH 2/2] Updae Unit Tests Dependencies, Add Conftest for Default Mission --- lambda_function/tests/conftest.py | 88 +++++++++++++++++++ .../tests/test_process_artifacts.py | 6 +- requirements.dev.txt | 1 - 3 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 lambda_function/tests/conftest.py diff --git a/lambda_function/tests/conftest.py b/lambda_function/tests/conftest.py new file mode 100644 index 0000000..4235b77 --- /dev/null +++ b/lambda_function/tests/conftest.py @@ -0,0 +1,88 @@ +""" +Shared pytest fixtures for all tests. + +These fixtures are automatically available to all test modules in the package. +""" + +import os + +import pytest + + +@pytest.fixture(autouse=True, scope="function") +def default_test_mission(monkeypatch): + """ + Automatically set HERMES as the default mission for all tests. + + This fixture runs automatically before each test function and ensures + that tests have a consistent mission configuration (HERMES) unless + explicitly overridden by the test itself or the use_mission fixture. + + The autouse=True makes this fixture apply to all tests without explicit declaration. + The monkeypatch ensures environment changes are cleaned up after each test. + + Note: This does NOT affect doctests. Doctests must explicitly set the + mission in their example code if they need a specific mission configuration. + """ + import swxsoc + from sdc_aws_utils.config import _reconfigure_globals + + # Only set if not already set (allows tests to override) + if "SWXSOC_MISSION" not in os.environ: + monkeypatch.setenv("SWXSOC_MISSION", "hermes") + swxsoc._reconfigure() + # Re-read module-level globals in config.py so they reflect the new mission + _reconfigure_globals() + + +@pytest.fixture(scope="function") +def use_mission(request, monkeypatch): + """ + Fixture to explicitly set a mission for a test function. + + This fixture allows tests to specify which mission configuration to use + via indirect parametrization. It overrides the default_test_mission fixture. + + Parameters + ---------- + request : pytest.Request + The pytest request object containing the parameter for the mission. + monkeypatch : pytest.MonkeyPatch + The pytest monkeypatch fixture for environment modification. + + Yields + ------ + str + The name of the mission that was configured for the test. + + Examples + -------- + Single mission test:: + + @pytest.mark.parametrize('use_mission', ['padre'], indirect=True) + def test_with_padre(use_mission): + # Test runs with PADRE mission config + assert swxsoc.config['mission']['mission_name'] == 'padre' + + Multiple missions:: + + @pytest.mark.parametrize('use_mission', ['hermes', 'padre', 'swxsoc'], indirect=True) + def test_all_missions(use_mission): + # Test runs three times, once for each mission + assert swxsoc.config['mission']['mission_name'] == use_mission + """ + import swxsoc + from sdc_aws_utils.config import _reconfigure_globals + + mission = request.param if hasattr(request, "param") else "hermes" + monkeypatch.setenv("SWXSOC_MISSION", mission) + swxsoc._reconfigure() + _reconfigure_globals() + yield mission + # Explicitly reconfigure back to default after test completes + # This is necessary because swxsoc.config is module-level state + # that persists across tests in the same process + # This ensures the config is reset even if monkeypatch cleanup hasn't run yet + monkeypatch.setenv("SWXSOC_MISSION", "hermes") + swxsoc._reconfigure() + _reconfigure_globals() diff --git a/lambda_function/tests/test_process_artifacts.py b/lambda_function/tests/test_process_artifacts.py index 7a177f1..f9e2b9c 100755 --- a/lambda_function/tests/test_process_artifacts.py +++ b/lambda_function/tests/test_process_artifacts.py @@ -1,12 +1,11 @@ import json -import os from unittest.mock import patch from moto import mock_aws as moto_mock_aws import pytest -from src.process_artifacts.process_artifacts import ArtifactProcessor # noqa: E402 -from src.process_artifacts.process_artifacts import handle_event # noqa: E402; noqa: E402 +from src.process_artifacts.process_artifacts import ArtifactProcessor +from src.process_artifacts.process_artifacts import handle_event # Constants for testing TEST_S3_BUCKET = "hermes-eea" @@ -40,6 +39,7 @@ def mock_aws(): with moto_mock_aws(): yield + # Mock boto3 S3 and Secrets Manager services def setup_mocks(): # Setup S3 diff --git a/requirements.dev.txt b/requirements.dev.txt index 3daee49..616e513 100755 --- a/requirements.dev.txt +++ b/requirements.dev.txt @@ -1,5 +1,4 @@ sdc_aws_utils @ git+https://github.com/swxsoc/sdc_aws_utils.git -hermes_core @ git+https://github.com/HERMES-SOC/hermes_core.git psycopg2-binary==2.9.7 pytest==9.0.3 pytest-astropy==0.11.0