Skip to content
Draft
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
14 changes: 8 additions & 6 deletions .github/workflows/_system_test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,20 +25,22 @@ jobs:
repository: epics-containers/example-services
path: example-services

- name: Run docker compose
- name: Compose devices
uses: hoverkraft-tech/compose-action@40041ff1b97dbf152cd2361138c2b03fa29139df # v2.3.0
with:
compose-file: "./example-services/compose.yaml"
compose-file: "example-services/compose.yaml"
services: |
bl01t-di-cam-01
bl01t-mo-sim-01
ca-gateway

- name: Start RabbitMQ
uses: namoshek/rabbitmq-github-action@d1d4455f4a8f72db66111c24cb0dc5654047a975 # v1
- name: Compose services
uses: hoverkraft-tech/compose-action@40041ff1b97dbf152cd2361138c2b03fa29139df # v2.3.0
with:
ports: "61613:61613"
plugins: rabbitmq_stomp
compose-file: "tests/system_tests/compose.yaml"
services: |
rabbitmq
numtracker

- name: Start Blueapi Server
env:
Expand Down
2 changes: 1 addition & 1 deletion dev-requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ deepdiff==8.5.0
deepmerge==2.0
Deprecated==1.2.18
distlib==0.3.9
dls-dodal==1.51.0
dls-dodal==1.53.0
dnspython==2.7.0
docopt==0.6.2
docutils==0.21.2
Expand Down
6 changes: 0 additions & 6 deletions docs/tutorials/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,6 @@ The worker can also be started using a custom config file:
blueapi --config path/to/file serve
```

An example of a config file that starts STOMP with default values can be found in:

```
src/script/stomp_config.yml
```

## Test that the Worker is Running

Blueapi comes with a CLI so that you can query and control the worker from the terminal.
Expand Down
6 changes: 3 additions & 3 deletions docs/tutorials/run-bus.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ Blueapi can publish updates to a message bus asynchronously, the CLI can then vi
## Start RabbitMQ

The worker requires a running instance of RabbitMQ. The easiest way to start it is
to execute the provided script:
to `compose` the services in `tests/system_tests/compose.yaml`

```
src/script/start_rabbitmq.sh
```sh
docker compose run --detach -f tests/system_tests/compose.yaml rabbitmq
```

## Config File
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ dependencies = [
"fastapi>=0.112.0",
"uvicorn",
"requests",
"dls-dodal>=1.51.0",
"dls-dodal>=1.53.0",
"super-state-machine", # https://github.com/DiamondLightSource/blueapi/issues/553
"GitPython",
"event-model==1.23", # https://github.com/DiamondLightSource/blueapi/issues/684
Expand Down
4 changes: 2 additions & 2 deletions src/blueapi/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,15 +157,15 @@ class ScratchConfig(BlueapiBaseModel):


class OIDCConfig(BlueapiBaseModel):
well_known_url: str = Field(
well_known_url: HttpUrl = Field(
description="URL to fetch OIDC config from the provider"
)
client_id: str = Field(description="Client ID")
client_audience: str = Field(description="Client Audience(s)", default="blueapi")

@cached_property
def _config_from_oidc_url(self) -> dict[str, Any]:
response: requests.Response = requests.get(self.well_known_url)
response: requests.Response = requests.get(str(self.well_known_url))
response.raise_for_status()
return response.json()

Expand Down
16 changes: 0 additions & 16 deletions src/script/start_rabbitmq.sh

This file was deleted.

47 changes: 47 additions & 0 deletions tests/system_tests/compose.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
services:
numtracker:
image: ghcr.io/diamondlightsource/numtracker:1.0.1
ports:
- "8001:8000"
post_start:
- command: /app/numtracker client configure adsim --directory '/tmp/' --scan '{instrument}-{scan_number}' --detector '{instrument}-{scan_number}-{detector}'

rabbitmq:
image: docker.io/rabbitmq:4.0-management
ports:
- "5672:5672"
- "15672:15672"
- "61613:61613"
volumes:
- type: bind
source: ./services/rabbitmq_plugins
target: /etc/rabbitmq/enabled_plugins
- type: bind
source: ./services/rabbitmq.conf
target: /etc/rabbitmq/rabbitmq.conf

oidc-server-mock:
image: ghcr.io/soluto/oidc-server-mock:latest
ports:
- '4011:8080'
environment:
ASPNETCORE_ENVIRONMENT: Development
SERVER_OPTIONS_PATH: /tmp/config/server-config.json
USERS_CONFIGURATION_PATH: /tmp/config/users-config.json
CLIENTS_CONFIGURATION_PATH: /tmp/config/clients-config.json
ASPNET_SERVICES_OPTIONS_INLINE: |
{
"ForwardedHeadersOptions": {
"ForwardedHeaders" : "All"
}
}
volumes:
- type: bind
source: ./services/clients-config.json
target: /tmp/config/clients-config.json
- type: bind
source: ./services/users-config.json
target: /tmp/config/users-config.json
- type: bind
source: ./services/server-config.json
target: /tmp/config/server-config.json
7 changes: 7 additions & 0 deletions tests/system_tests/config.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
api:
url: http://0.0.0.0:8000
env:
metadata:
instrument: adsim
sources:
- kind: dodal
module: dodal.beamlines.adsim
Expand All @@ -11,3 +13,8 @@ env:
stomp:
enabled: true
url: tcp://localhost:61613/
numtracker:
url: http://localhost:8001/graphql
oidc:
well_known_url: http://localhost:4011/.well-known/openid-configuration
client_id: blueapi-cli
13 changes: 13 additions & 0 deletions tests/system_tests/services/clients-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[
{
"ClientId": "blueapi-cli",
"ClientSecrets": [
"blueapi-cli-secret"
],
"Description": "Client for command-line access",
"AllowedScopes": [
"fedid",
"email"
]
}
]
2 changes: 2 additions & 0 deletions tests/system_tests/services/rabbitmq.conf
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
stomp.default_user = guest
stomp.default_pass = guest
10 changes: 10 additions & 0 deletions tests/system_tests/services/server-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"AccessTokenJwtType": "JWT",
"Discovery": {
"ShowKeySet": true
},
"Authentication": {
"CookieSameSiteMode": "Lax",
"CheckSessionCookieSameSiteMode": "Lax"
}
}
19 changes: 19 additions & 0 deletions tests/system_tests/services/users-config.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[
{
"SubjectId": "1",
"Username": "aa1",
"Password": "alice",
"Claims": [
{
"Type": "email",
"Value": "[email protected]",
"ValueType": "string"
},
{
"Type": "fedid",
"Value": "aa1",
"ValueType": "string"
}
]
}
]
58 changes: 28 additions & 30 deletions tests/system_tests/test_blueapi_system.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
from pathlib import Path

import pytest
from bluesky_stomp.models import BasicAuthentication
from pydantic import TypeAdapter
from requests.exceptions import ConnectionError
from scanspec.specs import Line
Expand Down Expand Up @@ -44,36 +43,37 @@

_DATA_PATH = Path(__file__).parent

_REQUIRES_AUTH_MESSAGE = """
Authentication credentials are required to run this test.
The test has been skipped because authentication is currently disabled.
For more details, see: https://github.com/DiamondLightSource/blueapi/issues/676.
To enable and execute these tests, set `REQUIRES_AUTH=1` and provide valid credentials.
"""

# Step 1: Ensure a message bus that supports stomp is running and available:
# src/script/start_rabbitmq.sh
# Start devices
# 1. $ git clone https://github.com/epics-containers/example-services
# 2. $ docker compose -f example-services/compose.yaml up \
# bl01t-di-cam-01 bl01t-mo-sim-01 ca-gateway --detach
#
# Start services
# in this directory (i.e. blueapi/tests/system_tests)
# $ docker compose up --detach
#
# Step 2: Start the BlueAPI server with valid configuration:
# blueapi -c tests/system_tests/config.yaml serve
# Start blueapi server configured to talk via the ca-gateway
# $ EPICS_CA_NAME_SERVERS=127.0.0.1:5094 EPICS_PVA_NAME_SERVERS=127.0.0.1:5095 \
# EPICS_CA_ADDR_LIST=127.0.0.1:5094 blueapi -c config.yaml serve
#
# Step 3: Run the system tests using tox:
# tox -e system-test
# Run the system tests using tox:
# $ tox -e system-test
#
# Tear down
# Tear down blueapi by passing SIGINT in the console where it was started (ctrl+c)
# Remove the containers and networking configured by the compose files:
# $ docker compose -f example-services/compose.yaml down
# $ docker compose down


@pytest.fixture
def client_without_auth(tmp_path: Path) -> BlueapiClient:
return BlueapiClient.from_config(config=ApplicationConfig(auth_token_path=tmp_path))


@pytest.fixture
def client_with_stomp() -> BlueapiClient:
return BlueapiClient.from_config(
config=ApplicationConfig(
auth_token_path=tmp_path,
stomp=StompConfig(
enabled=True,
auth=BasicAuthentication(username="guest", password="guest"), # type: ignore
)
),
)
)

Expand All @@ -92,10 +92,10 @@ def wait_for_server():
raise TimeoutError("No connection to the blueapi server")


# This client will have auth enabled if it finds cached valid token
@pytest.fixture
def client() -> BlueapiClient:
return BlueapiClient.from_config(config=ApplicationConfig())
def client(client_without_auth: BlueapiClient) -> BlueapiClient:
# TODO: Authenticate!
return client_without_auth


@pytest.fixture
Expand Down Expand Up @@ -135,7 +135,6 @@ def clean_existing_tasks(client: BlueapiClient):
yield


@pytest.mark.xfail(reason=_REQUIRES_AUTH_MESSAGE)
def test_cannot_access_endpoints(
client_without_auth: BlueapiClient, blueapi_client_get_methods: list[str]
):
Expand All @@ -147,7 +146,6 @@ def test_cannot_access_endpoints(
getattr(client_without_auth, get_method)()


@pytest.mark.xfail(reason=_REQUIRES_AUTH_MESSAGE)
def test_can_get_oidc_config_without_auth(client_without_auth: BlueapiClient):
assert client_without_auth.get_oidc_config() == OIDCConfig(
well_known_url="https://example.com/realms/master/.well-known/openid-configuration",
Expand Down Expand Up @@ -323,13 +321,13 @@ def test_get_task_by_status(client: BlueapiClient):
client.clear_task(task_id=task_2.task_id)


def test_progress_with_stomp(client_with_stomp: BlueapiClient):
def test_progress_with_stomp(client: BlueapiClient):
all_events: list[AnyEvent] = []

def on_event(event: AnyEvent):
all_events.append(event)

client_with_stomp.run_task(_SIMPLE_TASK, on_event=on_event)
client.run_task(_SIMPLE_TASK, on_event=on_event, timeout=10)
assert isinstance(all_events[0], WorkerEvent) and all_events[0].task_status
task_id = all_events[0].task_status.task_id
assert all_events == [
Expand Down Expand Up @@ -407,7 +405,7 @@ def test_delete_current_environment(client: BlueapiClient):
),
],
)
def test_plan_runs(client_with_stomp: BlueapiClient, task: TaskRequest):
final_event = client_with_stomp.run_task(task)
def test_plan_runs(client: BlueapiClient, task: TaskRequest):
final_event = client.run_task(task)
assert final_event.is_complete() and not final_event.is_error()
assert final_event.state is WorkerState.IDLE
Loading