Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support configuration for multiple projects #309

Merged
merged 126 commits into from
Mar 5, 2024
Merged
Show file tree
Hide file tree
Changes from 104 commits
Commits
Show all changes
126 commits
Select commit Hold shift + click to select a range
0b5b694
Update config design diagram
milanmlft Feb 19, 2024
0b863de
Add example config yaml
milanmlft Feb 19, 2024
028be09
Add pydantic model for project configuration
milanmlft Feb 19, 2024
1cbedbc
Add tests for pydantic config model
milanmlft Feb 19, 2024
5a74734
Add config loading helper
milanmlft Feb 20, 2024
4fa71ef
fix: Remove `classmethod` decorators and tell ruff that pydantic's va…
milanmlft Feb 20, 2024
07188ff
Refactor config tests: split up invalid fields tests and create commo…
milanmlft Feb 20, 2024
1741888
Add some more asserts to config test and use `load_config()` helper
milanmlft Feb 20, 2024
96980e7
Add PyYAML as core dependency
milanmlft Feb 20, 2024
d656679
Add copyright headers
milanmlft Feb 20, 2024
2703bba
Move `load_config` to top of file
milanmlft Feb 20, 2024
158b663
feat: orthanc-anon project config (#312)
peshence Feb 22, 2024
582c995
Add project config template
milanmlft Feb 22, 2024
c24e86c
Add documentation for Parquet files and export process (#280)
milanmlft Feb 20, 2024
948414d
Correct instructions for editable installs (#314)
peshence Feb 22, 2024
f0fb25d
Add copyright header to config template
milanmlft Feb 22, 2024
40d7c6b
Add secret fetching from Azure keyvault
milanmlft Feb 22, 2024
60e95a9
Ignore local secrets
milanmlft Feb 22, 2024
e1aadb3
Add Azure dependenices
milanmlft Feb 22, 2024
1442354
Refactor: move FTPS setup to separet module
milanmlft Feb 22, 2024
6dba81e
Create AzureKeyVault class to handle project secrets fetching
milanmlft Feb 23, 2024
c68eaf9
Refactor `_connect_to_ftp` to take FTP settings as parameters
milanmlft Feb 23, 2024
5dad1be
Add abstract `Uploader` and `FTPSUploader` classes to define uploadin…
milanmlft Feb 23, 2024
58afdbb
Update upload tests to use `Uploader` interface
milanmlft Feb 23, 2024
b01fd16
Revert back to `pydantic.validator`
milanmlft Feb 23, 2024
1bc7871
Add `MockFTPSUploader` for tests
milanmlft Feb 23, 2024
f59a11b
Update tests with `MockFTPSUploader` class
milanmlft Feb 23, 2024
331d600
Partially added docs to describe project configuration
milanmlft Feb 23, 2024
697840f
Small code reordering
milanmlft Feb 26, 2024
6564127
Add `upload` method to `ParquetExports` class using the uploader stra…
milanmlft Feb 26, 2024
5a8a540
Update EHR API to use the new uploader strategy
milanmlft Feb 26, 2024
633ad54
Load project config only when necessary
milanmlft Feb 26, 2024
d1208db
Move `parquet_export` fixture out of conftest
milanmlft Feb 26, 2024
e8930d2
Update test instruction in EHR API
milanmlft Feb 26, 2024
5302276
Use uploader strategy in orthanc-anonn for DICOM uploads
milanmlft Feb 26, 2024
e155979
Update required environment variables
milanmlft Feb 26, 2024
0596205
Format docker-compose
milanmlft Feb 26, 2024
51ab9d9
Load `.secrets.env` in system tests
milanmlft Feb 26, 2024
da46f95
Add instructions for setting up project config
milanmlft Feb 26, 2024
325f344
Merge branch 'main' into milanmlft/232-multi-project-config
milanmlft Feb 26, 2024
2f20eb6
Update core docs
milanmlft Feb 26, 2024
06841ca
Set PROJECT_CONFIGS_DIR envvar for cli tests
milanmlft Feb 26, 2024
ea0aa8b
Use same config for `MockFTPSUploader` as for the `ftps_server` fixture
milanmlft Feb 26, 2024
df89d77
Fix core tests
milanmlft Feb 26, 2024
54d3cee
Format docker-compose
milanmlft Feb 26, 2024
15f6d5e
Merge branch 'main' into milanmlft/232-multi-project-config
milanmlft Feb 26, 2024
3244b57
Update cli docs
milanmlft Feb 26, 2024
52388cb
Fix: keyvault name envvar
milanmlft Feb 26, 2024
155d60a
Load local `.env` if it exists before setting `PROJECT_CONFIGS_DIR`
milanmlft Feb 26, 2024
ad758b6
Improve logging
milanmlft Feb 26, 2024
3a8e3c9
Merge branch 'main' into milanmlft/232-multi-project-config
milanmlft Feb 26, 2024
cb0c2f3
Move project configs in top-level `projects/` directory
milanmlft Feb 26, 2024
0de653a
Move exports dir in top-level projects
milanmlft Feb 26, 2024
1f58028
Allow setting an alias for Azure KV secret fetching
milanmlft Feb 26, 2024
93a13dc
Remove debugging message
milanmlft Feb 26, 2024
20617f7
Set Azure Keyvault credentials as secrets in CI
milanmlft Feb 26, 2024
1dc640c
Add Azure keyvault setup instructions
milanmlft Feb 26, 2024
ecaf450
Add sample `.secrets.env`
milanmlft Feb 27, 2024
72c21ea
Report which secret is missing
milanmlft Feb 27, 2024
7c973b0
Set default value for `PROJECT_CONFIGS_DIR` in sample `.env`
milanmlft Feb 27, 2024
e99d59c
Update destination options in README
milanmlft Feb 27, 2024
e1fd394
Update Azure KV setup instructions
milanmlft Feb 27, 2024
eee4caa
Raise wanring when destination not supported instead of error
milanmlft Feb 27, 2024
878e22d
Don't mention unsupported alternatives
milanmlft Feb 27, 2024
7815259
No need for line continuation
milanmlft Feb 27, 2024
3350bcc
Better function naming
milanmlft Feb 27, 2024
2865a91
Get FTP port from Azure Keyvault as well
milanmlft Feb 27, 2024
9e25175
Remove more references to `FTP_` environment variables
milanmlft Feb 27, 2024
60314f0
Fix formatting
milanmlft Feb 27, 2024
8edbe55
Forgot another renaming instance
milanmlft Feb 27, 2024
6a555e2
Remove more instances of `FTP_*` env variables
milanmlft Feb 27, 2024
56fad9d
No more FTP dummy service
milanmlft Feb 28, 2024
313cab9
Refer to pytest-pixl plugin in test docs
milanmlft Feb 28, 2024
5ccd23a
Get FTP port from Keyvault as well
milanmlft Feb 28, 2024
72553f4
Allow 'none' destination for uploading
milanmlft Feb 28, 2024
8fbb330
This string expansion isn't working
milanmlft Feb 28, 2024
795060d
Exports are now under `projects/exports`
milanmlft Feb 28, 2024
52a44ef
Docker mounts need absolute paths
milanmlft Feb 28, 2024
19537bf
Seems that we still need the `FTP_*` environment variables
milanmlft Feb 28, 2024
9410466
Update exports dir in gitignore
milanmlft Feb 28, 2024
48d1963
Mostly for debugging but probably a useful check to avoid surprises
milanmlft Feb 28, 2024
893ec56
Think I finally found the bug
milanmlft Feb 28, 2024
de3dbe0
Update docs
milanmlft Feb 28, 2024
26f77a3
Make sure exports dir exists in unit tests
milanmlft Feb 28, 2024
0e19bfe
Add check for public parquet exports
milanmlft Feb 28, 2024
fbd7b5b
Rename system test checks for exports
milanmlft Feb 28, 2024
b8b4f2a
refactor[config]: remove tag-operations from filenames
peshence Feb 29, 2024
bc13dab
Update README.md typo
peshence Feb 29, 2024
bcf0a5f
Update template_config.yaml
peshence Feb 29, 2024
dc84e0c
Update cli/README.md
peshence Feb 29, 2024
dcd5b8b
Switch back to `pydantic.field_validator`
milanmlft Feb 29, 2024
10f85db
Record pydantic version
milanmlft Feb 29, 2024
d0f2ac2
Revert "Format docker-compose"
milanmlft Feb 29, 2024
5779623
Cache secret fetching from AZ keyvault
milanmlft Feb 29, 2024
438144f
Set secret ENV variables for the whole system-test job
milanmlft Feb 29, 2024
83a1b5b
Fix Keyvault secret names in diagram
milanmlft Feb 29, 2024
ba7d180
Add more abstract methods into the base Uploader class
milanmlft Feb 29, 2024
f1e60ce
add slugify reference
peshence Feb 29, 2024
2a557b2
fix[dcmd]: type hints
peshence Feb 29, 2024
682a882
refactor[core]: define uploader subpackage
peshence Feb 29, 2024
c277423
fix[cli]: convert to_posix to str()
peshence Feb 29, 2024
e92819a
fix[imports]
peshence Feb 29, 2024
56c8394
fix docker compose
peshence Feb 29, 2024
c7a20a4
fix[core]: add back ftps uploader __init__
peshence Mar 1, 2024
b0725c0
Add static `create` method to `Uploader` base class to handle upload …
milanmlft Mar 1, 2024
b137f31
Make `uploader._base` non-private
milanmlft Mar 1, 2024
87e6064
Make `uploader.ftps` private
milanmlft Mar 1, 2024
c6f39e0
Update client code to use new `Uploader.create` method
milanmlft Mar 1, 2024
6ce589e
Move `FTPSUploader` to `core.uploader.base` and make private
milanmlft Mar 1, 2024
19ca997
Fix test
milanmlft Mar 1, 2024
6e62cb7
remove mention of non-existing options in enum and in diagram
peshence Mar 1, 2024
bc64636
clarify lastpass secrets note
peshence Mar 1, 2024
3fa5411
Limit scope of secret envvars
milanmlft Mar 1, 2024
cd0d134
Revert "Move `FTPSUploader` to `core.uploader.base` and make private"
milanmlft Mar 4, 2024
adee916
Use package-level `get_uploader` factory instead of static method
milanmlft Mar 4, 2024
991b17e
Update client code to use `get_uploader`
milanmlft Mar 4, 2024
f969d50
Merge branch 'main' into milanmlft/232-multi-project-config
milanmlft Mar 4, 2024
d908c56
Remove duplicate template
milanmlft Mar 4, 2024
2f97327
Merge branch 'main' into milanmlft/232-multi-project-config
milanmlft Mar 5, 2024
0baef01
Doc fix
milanmlft Mar 5, 2024
56044c8
Fix import
milanmlft Mar 5, 2024
c8a7ebb
Fix export dir for cli tests
milanmlft Mar 5, 2024
80e66fc
Fix: Delay checking for `export_dir` existence until it's needed
milanmlft Mar 5, 2024
18673ef
Switch back to editable installs
milanmlft Mar 5, 2024
c48e1be
fix orthanc raw?
peshence Mar 5, 2024
a85533e
Merge branch 'main' into milanmlft/232-multi-project-config
milanmlft Mar 5, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 2 additions & 8 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,6 @@ AZ_DICOM_ENDPOINT_CLIENT_SECRET=
AZ_DICOM_ENDPOINT_TENANT_ID=

# EHR extraction API
PIXL_EHR_API_AZ_SUBSCRIPTION_ID=
PIXL_EHR_API_AZ_CLIENT_ID=
PIXL_EHR_API_AZ_CLIENT_SECRET=
PIXL_EHR_API_AZ_TENANT_ID=
PIXL_EHR_API_AZ_STORAGE_ACCOUNT_NAME=
PIXL_EHR_API_AZ_STORAGE_CONTAINER_NAME=
PIXL_EHR_COGSTACK_REDACT_URL=
Expand All @@ -97,7 +93,5 @@ RABBITMQ_PASSWORD=
PIXL_DICOM_TRANSFER_TIMEOUT=240
PIXL_QUERY_TIMEOUT=10

# FTP server
FTP_HOST=
FTP_USER_NAME=
FTP_USER_PASSWORD=
# Project configs directory
PROJECT_CONFIGS_DIR=projects/configs
10 changes: 9 additions & 1 deletion .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ jobs:

- name: Install Python dependencies
run: |
pip install pixl_core/ cli/[test]
pip install -e pixl_core/ -e cli/[test]

- name: Run tests
working-directory: cli/tests
Expand Down Expand Up @@ -182,6 +182,11 @@ jobs:
if: ${{ ! github.event.pull_request.draft || contains(github.event.pull_request.title, '[force-system-test]') }}
runs-on: ubuntu-22.04
timeout-minutes: 30
env:
EXPORT_AZ_CLIENT_ID: ${{ secrets.EXPORT_AZ_CLIENT_ID }}
EXPORT_AZ_CLIENT_PASSWORD: ${{ secrets.EXPORT_AZ_CLIENT_PASSWORD }}
EXPORT_AZ_TENANT_ID: ${{ secrets.EXPORT_AZ_TENANT_ID }}
EXPORT_AZ_KEY_VAULT_NAME: ${{ secrets.EXPORT_AZ_KEY_VAULT_NAME }}
milanmlft marked this conversation as resolved.
Show resolved Hide resolved
steps:
- uses: actions/checkout@v3
- uses: docker/setup-buildx-action@v3
Expand Down Expand Up @@ -223,6 +228,9 @@ jobs:
pip install -e cli/[test]
pip install -e pytest-pixl/

- name: Create .secrets.env
run: touch .secrets.env

- name: Build test services
working-directory: test
run: |
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
.env
local.env
pixl_config.yml
.secrets.env
milanmlft marked this conversation as resolved.
Show resolved Hide resolved

# Byte-compiled / optimized / DLL files
__pycache__/
Expand Down Expand Up @@ -146,6 +147,6 @@ dmypy.json
.vscode

# project specific files
/exports/
/projects/exports/
**/test_producer.csv
/test/dummy-services/ftp-server/mounts/data/*
6 changes: 6 additions & 0 deletions .secrets.env.sample
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Azure key vault
EXPORT_AZ_CLIENT_ID=
EXPORT_AZ_CLIENT_PASSWORD=
EXPORT_AZ_TENANT_ID=
EXPORT_AZ_KEY_VAULT_NAME=

142 changes: 142 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,6 +188,148 @@ For the deidentification of the EHR extracts, we rely on an instance running the
[CogStack API](https://cogstack.org/) with a `/redact` endpoint. The URL of this instance should be
set in `.env` as `COGSTACK_REDACT_URL`.

### 3. Configure a new project

To configure a new project, follow these steps:

1. Create a new `git` branch from `main`

```sh
git checkout main
git pull
git switch -c <branch-name>
```

1. Copy the `template_config.yaml` file to a new file in the `projects/config` directory and fill
in the details.
1. The filename of the project config should be `<project-slug>`.yaml

>[!NOTE]
> The project slug should match the [slugify](https://github.com/un33k/python-slugify)-ed project name in the `extract_summary.json` log file!

2. [Open a PR in PIXL](https://github.com/UCLH-Foundry/PIXL/compare) to merge the new project config into `main`

#### The config YAML file

The configuration file defines:

- Project name: the `<project-slug>` name of the Project
- The DICOM dataset modalities to retain (e.g. `["DX", "CR"]` for X-Ray studies)
- The anonymisation operations to be applied to the DICOM tags, by providing a file path to one or multiple YAML files
- The endpoints used to upload the anonymised DICOM data and the public and radiology
[parquet files](./docs/file_types/parquet_files.md). We currently support the following endpoints:
- `"none"`: no upload
- `"ftps"`: a secure FTP server (for both _DICOM_ and _parquet_ files)
<!-- - `"azure"`: a secure Azure Dicom web service (for both _DICOM_ and _parquet_ files) -->
<!-- Requires the `AZURE_*` environment variables to be set in `.env` -->
<!-- - `"dicomweb"`: a DICOMweb server (for _DICOM_ files only) -->
<!-- Requires the `DICOMWEB_*` environment variables to be set in `.env` -->

#### Project secrets

Any credentials required for uploading the project's results should be stored in an **Azure Key Vault**
(set up instructions below).
PIXL will query this key vault for the required secrets at runtime. This requires the following
environment variables to be set in `.secrets.env` so that PIXL can connect to the key vault:

- `EXPORT_AZ_CLIENT_ID`: the service principal's client ID, mapped to `AZURE_CLIENT ID` in `docker-compose`
- `EXPORT_AZ_CLIENT_PASSWORD`: the password, mapped to `AZURE_CLIENT_SECRET` in `docker-compose`
- `EXPORT_AZ_TENANT_ID`: ID of the service principal's tenant. Also called its 'directory' ID. Mapped to `AZURE_TENANT_ID` in `docker-compose`
- `EXPORT_AZ_KEY_VAULT_NAME` the name of the key vault, used to connect to the correct key vault

Create the `.secrets.env` file in the _PIXL_ directory by copying the sample:

```bash
cp .secrets.env.sample .secrets.env
```

and fill in the missing values.

<details><summary>Azure Keyvault setup</summary>

## Azure Keyvault setup

_This is done for the \_UCLH_DIF_ `dev` tenancy, will need to be done once in the _UCLHAZ_ `prod`
tenancy when ready to deploy to production.\_

This Key Vault and secret must persist any infrastructure changes so should be separate from disposable
infrastructure services. ServicePrincipal is required to connect to the Key Vault.

The application uses the ServicePrincipal and password to authenticate with Azure via environment
variables. See [here](https://learn.microsoft.com/en-us/python/api/azure-identity/azure.identity.environmentcredential?view=azure-python)
for more info.

The Key Vault and ServicePrincipal have already been created for the `dev` environment and details
are stored in the `pixl-secrets` note in the shared PIXL folder on _LastPass_.

The process for doing so using the `az` CLI tool is described below.
This process must be repeated for `staging` & `prod` environments.

### Step 1

Create the Azure Key Vault in an appropriate resource group:

```bash
az keyvault create --resource-group <resource-group-name> --name <key-vault-name> --location "UKSouth"
```

### Step 2

Create Service Principal & grant access as per

```bash
az ad sp create-for-rbac -n pixl-secrets --skip-assignment
```

This will produce the following output

```json
{
"appId": "<generated-app-ID>",
"displayName": "<app-name>",
"name": "http://<app-name>",
"password": "<generated-password>",
"tenant": "<tenant-ID>"
}
```

### Step 3

Assign correct permissions to the newly created ServicePrincipal

```bash
az keyvault set-policy --name <key-vault-name> --spn <generated-app-ID> --secret-permissions backup delete get list set
```

### Step 4

Create a secret and store in the Key Vault

Use Python to create a secret:

```python
import secrets
secrets.token_urlsafe(32)
```

copy the secret and paste as <secret-value> below

```bash
az keyvault secret set --vault-name "<key-vault-name>" --name "<secret-name>" --value "<secret-value>"
```

### Step 5

Save credentials in `.secrets.env` and a LastPass `PIXL Keyvault <project-slug> secrets` note.

```
EXPORT_AZ_CLIENT_ID=<generated-app-ID>
EXPORT_AZ_CLIENT_PASSWORD=<generated-password>
EXPORT_AZ_TENANT_ID=<tenant-ID>
EXPORT_AZ_KEY_VAULT_NAME=<key-vault-name>
```
</details>
peshence marked this conversation as resolved.
Show resolved Hide resolved

## Run

### Start
Expand Down
5 changes: 3 additions & 2 deletions cli/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,9 +141,10 @@ pip install -e ../pixl_core/ -e .[test]

### Running tests

Tests can be run with `pytest` from the `tests` directory.
The CLI tests require a running instance of the `rabbitmq` service, for which we provide a
`docker-compose` [file](./tests/docker-compose.yml). The service is automatically started by the
`run_containers` _pytest_ fixture. So to run the tests, run

```bash
cd tests
pytest
```
9 changes: 2 additions & 7 deletions cli/pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,22 +1,17 @@
[project]
name = "pixl_cli"
version = "0.0.4"
authors = [
{ name="PIXL authors" },
]
authors = [{ name = "PIXL authors" }]
description = "PIXL command line interface"
readme = "README.md"
requires-python = "<3.12"
classifiers = [
"Programming Language :: Python :: 3"
]
classifiers = ["Programming Language :: Python :: 3"]
dependencies = [
"core",
"click==8.1.3",
"coloredlogs==15.0.1",
"pandas==1.5.1",
"pyarrow==14.0.1",
"PyYAML==6.0.1"
]

[project.optional-dependencies]
Expand Down
2 changes: 1 addition & 1 deletion cli/src/pixl_cli/_io.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@

# The export root dir from the point of view of the docker host (which is where the CLI runs)
# For the view from inside, see pixl_ehr/main.py: EHR_EXPORT_ROOT_DIR
HOST_EXPORT_ROOT_DIR = Path(__file__).parents[3] / "exports"
HOST_EXPORT_ROOT_DIR = Path(__file__).parents[3] / "projects" / "exports"


def messages_from_state_file(filepath: Path) -> list[Message]:
Expand Down
30 changes: 30 additions & 0 deletions cli/src/pixl_cli/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import requests
from core.patient_queue.producer import PixlProducer
from core.patient_queue.subscriber import PixlBlockingConsumer
from decouple import RepositoryEnv, UndefinedValueError, config

from pixl_cli._config import SERVICE_SETTINGS, api_config_for_queue
from pixl_cli._database import filter_exported_or_add_to_db
Expand All @@ -47,6 +48,35 @@ def cli(*, debug: bool) -> None:
set_log_level("WARNING" if not debug else "DEBUG")


@cli.command()
@click.option(
"--error",
is_flag=True,
show_default=True,
default=False,
help="Exit with error on missing env vars",
)
@click.option(
"--sample_env_file",
show_default=True,
default=None,
type=click.Path(exists=True),
help="Path to the sample env file",
)
def check_env(*, error: bool, sample_env_file: click.Path) -> None:
"""Check that all variables from .env.sample are set either in .env or in environ"""
if not sample_env_file:
sample_env_file = Path(__file__).parents[3] / ".env.sample"
sample_config = RepositoryEnv(sample_env_file)
for key in sample_config.data:
try:
config(key)
except UndefinedValueError: # noqa: PERF203
logger.warning("Environment variable %s is not set", key)
if error:
raise


@cli.command()
@click.option(
"--queues",
Expand Down
6 changes: 5 additions & 1 deletion cli/tests/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
from sqlalchemy import Engine, create_engine
from sqlalchemy.orm import Session, sessionmaker

os.environ["PROJECT_CONFIGS_DIR"] = str(pathlib.Path(__file__).parents[2] / "projects/configs")
peshence marked this conversation as resolved.
Show resolved Hide resolved

# Set the necessary environment variables
os.environ["PIXL_EHR_API_HOST"] = "localhost"
os.environ["PIXL_EHR_API_RATE"] = "1"
Expand All @@ -46,7 +48,9 @@
@pytest.fixture(autouse=True)
def export_dir(tmp_path_factory: pytest.TempPathFactory) -> pathlib.Path:
"""Tmp dir to for tests to extract to."""
return tmp_path_factory.mktemp("export_base") / "exports"
export_dir = tmp_path_factory.mktemp("export_base") / "exports"
export_dir.mkdir()
return export_dir


@pytest.fixture()
Expand Down
43 changes: 43 additions & 0 deletions cli/tests/test_check_env.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Copyright (c) University College London Hospitals NHS Foundation Trust
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Tests for check_env function of CLI."""
from pathlib import Path

from click.testing import CliRunner
from pixl_cli.main import check_env

SAMPLE_ENV_FILE = Path(__file__).parents[2] / ".env.sample"


def test_check_env():
"""
Test that the check_env command runs without error.
- check_env works
- current test env file matches the sample env file
"""
runner = CliRunner()
result = runner.invoke(check_env)
assert result.exit_code == 0


def test_check_env_fails(tmp_path):
"""
Test that check_env fails when the current test env file does not match the sample env file.
""" # noqa: D200 either this or it's 102 chars
tmp_sample_env_file = tmp_path / ".env.sample"
tmp_sample_env_file.write_text("NONEXISTENT_VARIABLE=")

runner = CliRunner()
result = runner.invoke(check_env, str(tmp_sample_env_file))
assert result.exit_code != 0
Loading
Loading