# lambda-product-transfer
Lambda functions used in product-transfer SFN. application_family: content-management

## Integration Tests

Integration tests run against the deployed QA environment after each deploy to `master`.

### Prerequisites

- Docker
- AWS credentials with access to the QA account (`437795906767`)

### Running all tests

```bash
cd integration_tests
docker compose up --build --exit-code-from integration-tests
```

### Running tests for a specific lambda

Use the `run_tests.sh` script, passing the lambda name and optionally the environment (`qa` is the default):

```bash
cd integration_tests

# Against the deployed QA lambda
./scripts/run_tests.sh finalize-job

# Against a locally running lambda container
./scripts/run_tests.sh finalize-job local
```

When running locally, the lambda container is started via the AWS Lambda Runtime Interface Emulator (RIE) using your exported AWS credentials to call downstream services (e.g. `ows-project-manager`) in QA.

## Adding integration tests for a new lambda

### 1. `integration_tests/config.py`

Add a function name constant. The helper resolves to `"function"` when running locally against
the RIE, and the real QA function name otherwise:

```python
HANDLE_ERROR_FUNCTION_NAME = _lambda_function_name("handle-error")
```

Where `_lambda_function_name` is already defined in `config.py`.

### 2. `integration_tests/docker-compose.yml`

Add a service for the lambda using its function directory name as the profile:

```yaml
handle-error:
  profiles: [handle-error]
  platform: linux/amd64
  build:
    context: ../lambda/handle-error
    dockerfile: Dockerfile
    target: deploy
  environment:
    ENVIRONMENT: qa
    AWS_ACCESS_KEY_ID: ${AWS_ACCESS_KEY_ID:-}
    AWS_SECRET_ACCESS_KEY: ${AWS_SECRET_ACCESS_KEY:-}
    AWS_SESSION_TOKEN: ${AWS_SESSION_TOKEN:-}
    AWS_DEFAULT_REGION: us-east-1
    DD_TRACE_ENABLED: "false"
  tmpfs:
    - /opt/extensions
  healthcheck:
    test: ["CMD-SHELL", "bash -c '</dev/tcp/localhost/8080'"]
    interval: 60s
    timeout: 3s
    retries: 5
    start_period: 24s
    start_interval: 15s
```

A few things to note:

- **`platform: linux/amd64`** — required on Apple Silicon. Without it the Lambda RIE runs under QEMU and crashes with Go runtime errors.
- **`DD_TRACE_ENABLED: "false"` + `tmpfs: [/opt/extensions]`** — the Datadog Lambda extension registers as an external extension and holds the RIE in a reserved state during its post-invocation flush. Without a valid DD API key locally it times out, causing subsequent invocations to crash (`AlreadyReserved`). Disabling tracing and shadowing the extensions directory prevents the extension from registering.
- **`start_interval: 15s`** — the Lambda TCP port opens before the Python runtime finishes initialising (~10s). A 15s start interval ensures the healthcheck doesn't fire until after `INIT RTDONE`, so `depends_on: service_healthy` genuinely means the RIE is ready to handle invocations.

If the lambda requires additional environment (e.g. Snowflake credentials, SSH keys), add those under `environment` and `volumes` as needed — see the `update-dim-tables` service for an example.

Also add the service to the `depends_on` block of the `integration-tests` service:

```yaml
depends_on:
  finalize-job:
    condition: service_healthy
    required: false
  handle-error:
    condition: service_healthy
    required: false
```

### 3. Write the test file

Create `integration_tests/tests/test_<lambda_name>.py`. Mark the class with the lambda's
directory name so it is skipped when that lambda wasn't deployed:

```python
@pytest.mark.lambda_name("handle-error")
class TestHandleError:
    def test_marks_job_failed(self, art_relations_db, lambda_handler):
        ...
```

### 4. Run locally

```bash
cd integration_tests
./scripts/run_tests.sh handle-error local
```
