Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ services:
build:
dockerfile: src/brp_personen_mock/Dockerfile
args:
- JSON_URL=https://raw.githubusercontent.com/BRP-API/personen-informatie-service/refs/heads/main/src/config/BrpService/test-data.json
- JSON_URL=https://raw.githubusercontent.com/Amsterdam/brp-amsterdam-api/refs/heads/main/src/brp_personen_mock/test-data.json
environment:
- ASPNETCORE_ENVIRONMENT=Release
- ASPNETCORE_URLS=http://+:5010
Expand All @@ -31,7 +31,7 @@ services:
#DATABASE_URL: "${DATABASE_URL:-postgresql://postgres:insecure@database/dataservices}"
CORS_ALLOW_ALL_ORIGINS: "${CORS_ALLOW_ALL_ORIGINS:-true}"
DJANGO_LOG_LEVEL: "${DJANGO_LOG_LEVEL:-INFO}"
HAAL_CENTRAAL_BRP_URL: "http://localhost:5010/haalcentraal/api/brp/personen"
BRP_URL: "http://personen-mock:5010/haalcentraal/api/brp"
LOG_LEVEL: "${LOG_LEVEL:-INFO}"
AUDIT_LOG_LEVEL: "${AUDIT_LOG_LEVEL:-INFO}"
AZURE_APPI_AUDIT_CONNECTION_STRING: "${AZURE_APPI_AUDIT_CONNECTION_STRING:-}"
Expand Down
1 change: 1 addition & 0 deletions integration_tests/.dockerignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
.venv
33 changes: 33 additions & 0 deletions integration_tests/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
FROM ghcr.io/astral-sh/uv:0.10-python3.14-trixie-slim AS builder

ENV UV_COMPILE_BYTECODE=1
ENV UV_LINK_MODE=copy

RUN apt update && apt install --no-install-recommends -y \
build-essential \
tree

WORKDIR /app
COPY ./pyproject.toml ./uv.lock ./README.md ./

RUN uv sync --frozen --no-install-project --all-groups
COPY /src /app/src

RUN uv sync --frozen --all-groups

# Start runtime image,
FROM ghcr.io/astral-sh/uv:0.10-python3.14-trixie-slim

# Create user integration_tests with the same UID as github actions runner.
RUN groupadd --system --gid 999 integration_tests && useradd --system --gid 999 \
--uid 1001 --create-home integration_tests
RUN apt update && apt install --no-install-recommends -y \
curl

WORKDIR /app
# Copy python build artifacts from builder image
RUN chown integration_tests:integration_tests /app
COPY --from=builder --chown=integration_tests:integration_tests /app /app
USER integration_tests

ENTRYPOINT ["uv", "run", "brp-test"]
31 changes: 31 additions & 0 deletions integration_tests/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
.PHONY: install
install: ## Perform all install steps.
if ! command -v uv >/dev/null 2>&1; \
then echo "uv not found, installing..." \
&& curl -LsSf https://astral.sh/uv/install.sh | sh; \
fi
uv sync
uv run pre-commit install

##
## Package management:
##

.PHONY: upgrade
upgrade: ## Upgrade the dependencies.
uv sync --upgrade

##
## Development tools:
##

format: ## Fix code-formatting of all files.
uv run ruff check --fix-only .
# pre-commit run -a

lint: ## Report linting errors for all files
uv run ruff check .

type: ## Show typing errors for all files
uv run ty check
##
49 changes: 49 additions & 0 deletions integration_tests/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# BRP Amsterdam API - Integration Tests

These integration tests have been set up to make sure all connections to the external API's (RvIG) still
work when deploying changes to the application or underlying infrastucture.

# Installation

Requirements:

* Python >= 3.14
* Recommended: Docker/Docker Compose (or uv for local installs)

## BRP Amsterdam API

The easiest way to run these integration tests is by using Docker Compose. This will also start the BRP Amsterdam API
and the BRP Personen Mock.

To be able to run the tests you will need a valid token to be able to make the requests. The easiest way is to set up
direnv with an `.envrc` file or export a token from the command line:

```shell
export TOKEN="$(docker compose run web python get-token.py benk-brp-personen-api benk-brp-gegevensset-1 benk-brp-zoekvraag-bsn benk-brp-zoekvraag-geslachtsnaam-geboortedatum benk-brp-zoekvraag-naam-gemeente benk-brp-zoekvraag-adresseerbaar-object benk-brp-zoekvraag-nummeraanduiding benk-brp-zoekvraag-postcode-huisnummer benk-brp-zoekvraag-straatnaam-huisnummer benk-brp-bewoning-api benk-brp-verblijfplaatshistorie-api)"
```

After having a valid token in your environment you'll be able to start the tests using:

```shell
docker compose run tests
```

The BRP Personen Mock only has the `personen` endpoint available, so running the tests against the `bewoningen` and
`verblijfplaatshistorie` will result in a failed test. To see the test run succesfully you can run the tests for the
`personen` endpoint only:

```shell
docker compose run tests -e personen
```

## Using Local Python

Make sure you have the BRP Amsterdam API and BRP Personen Mock running

```shell
docker compose up -d -f ../docker-compose.yml
```

```shell
uv sync
```
Empty file added integration_tests/__init__.py
Empty file.
14 changes: 14 additions & 0 deletions integration_tests/docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
include:
- ../docker-compose.yml
services:
tests:
build:
context: .
links:
- web
depends_on:
- web
- personen-mock
environment:
TOKEN: "${TOKEN}"
BRP_URL: "${BRP_URL:-http://web:8000}"
23 changes: 23 additions & 0 deletions integration_tests/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
[project]
name = "integration-tests"
version = "0.1.0"
description = "Integration tests to validate deployment and integration with the RvIG Backend of the BRP Amsterdam API"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"click>=8.3.1",
"locust>=2.43.1",
]

[project.scripts]
brp-test = "integration_tests.cli:cli"

[build-system]
requires = ["uv_build>=0.9.25,<0.10.0"]
build-backend = "uv_build"

[tool.uv.build-backend]
module-root = "src"

[tool.ruff.lint.isort]
known-first-party = ["integration_tests"]
Empty file.
Empty file.
28 changes: 28 additions & 0 deletions integration_tests/src/integration_tests/cli.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import logging

import click

from integration_tests.testrunner import run_tests

logging.basicConfig(format="%(levelname)s:%(message)s", level=logging.INFO)


ENDPOINTS = [
"personen",
"bewoningen",
"verblijfsplaatshistorie",
]


@click.command()
@click.option(
"-e",
"--endpoints",
type=click.Choice(ENDPOINTS),
multiple=True,
default=ENDPOINTS,
help="Endpoints to test",
)
@click.option("-l", "--load-test", type=bool, default=False, help="Perform a load test")
def cli(endpoints: list, load_test: bool):
run_tests(endpoints)
2 changes: 2 additions & 0 deletions integration_tests/src/integration_tests/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
class ImproperlyConfigured(Exception):
pass
4 changes: 4 additions & 0 deletions integration_tests/src/integration_tests/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
from .cli import cli

if __name__ == "__main__":
cli()
38 changes: 38 additions & 0 deletions integration_tests/src/integration_tests/settings.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import os
from pathlib import Path

from integration_tests.exceptions import ImproperlyConfigured

CLOUD_ENV = os.environ.get("CLOUD_ENV", "default").lower()
BRP_URL = os.environ.get("BRP_URL", "http://localhost:8095")
DEBUG = os.environ.get("DEBUG", False)
LOG_LEVEL = os.environ.get("LOG_LEVEL", "DEBUG" if DEBUG else "INFO").upper()
ALLOWED_FAILURES = int(os.environ.get("ALLOWED_FAILURES", 0))

if CLOUD_ENV.startswith("azure"):
TOKEN = None

# On Azure we'll get a token from the app registration
TENANT_ID = os.environ.get("TENANT_ID")
AUDIENCE = os.environ.get("AUDIENCE")
CLIENT_ID = os.environ.get("CLIENT_ID")
SCOPE = os.environ.get("SCOPE", f"{AUDIENCE}/.default")

_USE_SECRET_STORE = Path("/mnt/secrets-store").exists()

if _USE_SECRET_STORE:
CLIENT_SECRET = Path("/mnt/secrets-store/brp-integration-tests-client-secret").read_text()
else:
CLIENT_SECRET = os.environ.get("CLIENT_SECRET")

if not TENANT_ID:
raise ImproperlyConfigured("Missing TENTANT_ID environment variable")
if not AUDIENCE:
raise ImproperlyConfigured("Missing AUDIENCE environment variable")
if not CLIENT_ID:
raise ImproperlyConfigured("Missing CLIENT_ID environment variable")
if not CLIENT_SECRET:
raise ImproperlyConfigured("Missing CLIENT_SECRET environment variable")
else:
# For local development we use a token from the environment
TOKEN = os.environ.get("TOKEN")
5 changes: 5 additions & 0 deletions integration_tests/src/integration_tests/tasks/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from .bewoningen import Bewoningen
from .personen import Personen
from .verblijfsplaatshistorie import Verblijfsplaatshistorie

__all__ = ["Personen", "Bewoningen", "Verblijfsplaatshistorie"]
13 changes: 13 additions & 0 deletions integration_tests/src/integration_tests/tasks/base.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
from locust import TaskSet, task


class BaseTaskSet(TaskSet):
path: str = ""

@property
def url(self):
return self.user.base_url + self.path

@task
def stop(self):
self.interrupt()
38 changes: 38 additions & 0 deletions integration_tests/src/integration_tests/tasks/bewoningen.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
from locust import tag, task
from requests import JSONDecodeError

from integration_tests.tasks.base import BaseTaskSet


@tag("bewoningen")
class Bewoningen(BaseTaskSet):
path = "/bewoningen"

@task
def bewoning_met_peildatum(self):
data = {
"type": "BewoningMetPeildatum",
"adresseerbaarObjectIdentificatie": "0363010012064483",
"peildatum": "2025-01-01",
}
with self.client.post(self.url, json=data, catch_response=True) as response:
try:
if len(response.json()["personen"]) == 2:
response.success()
except (KeyError, JSONDecodeError):
response.failure("Expected output not in response")

@task
def bewoning_met_periode(self):
data = {
"type": "BewoningMetPeriode",
"adresseerbaarObjectIdentificatie": "0363010012064483",
"datumVan": "2019-01-01",
"datumTot": "2020-01-01",
}
with self.client.post(self.url, json=data, catch_response=True) as response:
try:
if len(response.json()["personen"]) == 0:
response.success()
except (KeyError, JSONDecodeError):
response.failure("Expected output not in response")
Loading