Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
213410e
Move Python tests to new directory
simonkurtz-MSFT May 23, 2025
96a408d
Add passing tests
simonkurtz-MSFT May 23, 2025
1e6da13
Add basic working tests
simonkurtz-MSFT May 23, 2025
4d57874
Move code coverage files
simonkurtz-MSFT May 23, 2025
eaf1a6f
Update Python Tests workflow
simonkurtz-MSFT May 23, 2025
204e092
Add test scripts, instructions, and update Readme
simonkurtz-MSFT May 23, 2025
4c24e90
Update test information
simonkurtz-MSFT May 23, 2025
5298dfe
Add Python tests badge
simonkurtz-MSFT May 23, 2025
26bc173
Enable workflow dispatch
simonkurtz-MSFT May 23, 2025
d142cb9
Fix module path
simonkurtz-MSFT May 23, 2025
d90587e
Separate test results by python version
simonkurtz-MSFT May 23, 2025
fb02f8f
Update tests/python/test_apimrequests.py
simonkurtz-MSFT May 23, 2025
3a6bfa2
JUnit test results
simonkurtz-MSFT May 23, 2025
5d89692
Publish Unit Test Results to the PR
simonkurtz-MSFT May 23, 2025
feb99d0
Fix permissions
simonkurtz-MSFT May 23, 2025
7b86a49
Customize test results title
simonkurtz-MSFT May 23, 2025
9208c9c
Fix extract_json docstring
simonkurtz-MSFT May 23, 2025
74ad544
Ensure code coverage and test comments still occur upon test failures
simonkurtz-MSFT May 23, 2025
84aacdf
Fix is_string_json
simonkurtz-MSFT May 23, 2025
0876c5d
Rename test run files
simonkurtz-MSFT May 23, 2025
d9da993
Add tests, fix failing code
simonkurtz-MSFT May 23, 2025
7b7d6d0
Clean up
simonkurtz-MSFT May 23, 2025
86141d2
Clean up
simonkurtz-MSFT May 23, 2025
f0d281e
Add infra deployment cleanup
simonkurtz-MSFT May 23, 2025
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
46 changes: 44 additions & 2 deletions .github/workflows/python-tests.yml
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
name: Python Tests

on:
workflow_dispatch:
push:
branches: [ main ]
pull_request:
branches: [ main ]

permissions:
contents: read
checks: write
pull-requests: write

jobs:
test:
Expand All @@ -17,14 +20,53 @@ jobs:
python-version: [ '3.12', '3.13' ]
steps:
- uses: actions/checkout@v4

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}

- name: Install dependencies
run: |
python -m pip install --upgrade pip
pip install -r requirements.txt
- name: Run tests

- name: Install pytest and pytest-cov
run: |
pip install pytest pytest-cov

# Run tests with continue-on-error so that coverage and PR comments are always published.
# The final step will explicitly fail the job if any test failed, ensuring PRs cannot be merged with failing tests.
- name: Run pytest with coverage and generate JUnit XML
run: |
PYTHONPATH=$(pwd) COVERAGE_FILE=tests/python/.coverage-${{ matrix.python-version }} pytest --cov=shared/python --cov-config=tests/python/.coveragerc --cov-report=html:tests/python/htmlcov-${{ matrix.python-version }} --junitxml=tests/python/junit-${{ matrix.python-version }}.xml tests/python/
continue-on-error: true

- name: Upload coverage HTML report
uses: actions/upload-artifact@v4
with:
name: coverage-html-${{ matrix.python-version }}
path: tests/python/htmlcov-${{ matrix.python-version }}/

- name: Upload JUnit test results
uses: actions/upload-artifact@v4
with:
name: junit-results-${{ matrix.python-version }}
path: tests/python/junit-${{ matrix.python-version }}.xml

- name: Publish Unit Test Results to PR
if: always()
uses: EnricoMi/publish-unit-test-result-action@v2
with:
files: tests/python/junit-${{ matrix.python-version }}.xml
comment_title: Python ${{ matrix.python-version }} Test Results

# Explicitly fail the job if any test failed (so PRs cannot be merged with failing tests).
# This runs after all reporting steps, meaning coverage and PR comments are always published.
- name: Fail if tests failed
if: always()
run: |
python -m unittest discover shared/python-tests
if grep -q 'failures="[1-9]' tests/python/junit-${{ matrix.python-version }}.xml; then
echo "::error ::Unit tests failed. See above for details."
exit 1
fi
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,8 @@ labs-in-progress/

# Exclude sensitive or generated files
.env


# Coverage data and reports
.coverage
tests/python/htmlcov/
50 changes: 47 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# Azure API Management Samples

[![Python Tests](https://github.com/Azure-Samples/Apim-Samples/actions/workflows/python-tests.yml/badge.svg?branch=main)](https://github.com/Azure-Samples/Apim-Samples/actions/workflows/python-tests.yml)

This repository provides a playground to safely experiment with and learn Azure API Management (APIM) policies in various architectures.

## Objectives
Expand Down Expand Up @@ -87,11 +89,53 @@ The repo uses the bicep linter and has rules defined in `bicepconfig.json`. See

We welcome contributions! Please consider forking the repo and creating issues and pull requests to share your samples. Please see [CONTRIBUTING.md](CONTRIBUTING.md) for details. Thank you!

### Testing

Python modules and files in the `shared` folder are supported with tests in the `python-tests` folder. They can be invoked manually via `pytest -v` but are also run upon push to GitHub origin. The `pytest.ini` file in the root sets up the test structure.
### Testing & Code Coverage

Python modules in `shared/python` are covered by comprehensive unit tests located in `tests/python`. All tests use [pytest](https://docs.pytest.org/) and leverage modern pytest features, including custom markers for unit and HTTP tests.

#### Running Tests Locally

- **PowerShell (Windows):**
- Run all tests with coverage: `./tests/python/run_tests.ps1`
- **Shell (Linux/macOS):**
- Run all tests with coverage: `./tests/python/run_tests.sh`

Both scripts:
- Run all tests in `tests/python` using pytest
- Generate a code coverage report (HTML output in `tests/python/htmlcov`)
- Store the raw coverage data in `tests/python/.coverage`

You can also run tests manually and see details in the console:
```sh
pytest -v --cov=shared/python --cov-report=html:tests/python/htmlcov --cov-report=term tests/python
```

#### Viewing Coverage Reports

After running tests, open `tests/python/htmlcov/index.html` in your browser to view detailed coverage information.

#### Pytest Markers

- `@pytest.mark.unit` — marks a unit test
- `@pytest.mark.http` — marks a test involving HTTP/mocking

Markers are registered in `pytest.ini` to avoid warnings.

#### Continuous Integration (CI)

On every push or pull request, GitHub Actions will:
- Install dependencies
- Run all Python tests in `tests/python` with coverage
- Store the `.coverage` file in `tests/python`
- Upload the HTML coverage report as a workflow artifact for download

#### Additional Notes

- The `.gitignore` is configured to exclude coverage output and artifacts.
- All test and coverage features work both locally and in CI.

Additional information can be found [here](# https://docs.pytest.org/en/8.2.x/).
For more details on pytest usage, see the [pytest documentation](https://docs.pytest.org/en/8.2.x/).

---

Expand Down
4 changes: 2 additions & 2 deletions infrastructure/afd-apim/clean-up.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"from apimtypes import INFRASTRUCTURE\n",
"\n",
"deployment = INFRASTRUCTURE.AFD_APIM_PE\n",
"index = 1\n",
"indexes = [1]\n",
"\n",
"utils.cleanup_resources(deployment, utils.get_infra_rg_name(deployment, index))"
"utils.cleanup_infra_deployments(deployment, indexes)"
]
}
],
Expand Down
4 changes: 2 additions & 2 deletions infrastructure/apim-aca/clean-up.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"from apimtypes import INFRASTRUCTURE\n",
"\n",
"deployment = INFRASTRUCTURE.APIM_ACA\n",
"index = 1\n",
"indexes = [1]\n",
"\n",
"utils.cleanup_resources(deployment, utils.get_infra_rg_name(deployment, index))"
"utils.cleanup_infra_deployments(deployment, indexes)"
]
}
],
Expand Down
6 changes: 3 additions & 3 deletions infrastructure/simple-apim/clean-up.ipynb
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@
"from apimtypes import INFRASTRUCTURE\n",
"\n",
"deployment = INFRASTRUCTURE.SIMPLE_APIM\n",
"index = 1\n",
"indexes = [1]\n",
"\n",
"utils.cleanup_resources(deployment, utils.get_infra_rg_name(deployment, index))"
"utils.cleanup_infra_deployments(deployment, indexes)"
]
}
],
Expand All @@ -41,7 +41,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.9"
"version": "3.12.10"
}
},
"nbformat": 4,
Expand Down
7 changes: 0 additions & 7 deletions pytest.ini

This file was deleted.

3 changes: 2 additions & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@

requests
setuptools
pytest
pytest
pytest-cov
1 change: 0 additions & 1 deletion shared/python-tests/__init__.py

This file was deleted.

32 changes: 0 additions & 32 deletions shared/python-tests/test_apimtypes.py

This file was deleted.

7 changes: 7 additions & 0 deletions shared/python/apimtypes.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,13 @@ class APIOperation:
# ------------------------------

def __init__(self, name: str, displayName: str, urlTemplate: str, method: HTTP_VERB, description: str, policyXml: str):
# Validate that method is a valid HTTP_VERB
if not isinstance(method, HTTP_VERB):
try:
method = HTTP_VERB(method)
except Exception:
raise ValueError(f"Invalid HTTP_VERB: {method}")

self.name = name
self.displayName = displayName
self.method = method
Expand Down
Loading