tests: removed docker-compose tests and moved instead to fakeredis and mongomock

This commit is contained in:
2025-02-10 17:54:27 +01:00
parent cce7712bb0
commit ca3f5f91b5
16 changed files with 1302 additions and 248 deletions

View File

@ -39,6 +39,18 @@ stages:
include:
- template: Security/Secret-Detection.gitlab-ci.yml
before_script:
- if [[ "$CI_PROJECT_PATH" != "bec/bec_atlas" ]]; then
echo -e "\033[35;1m Using branch $CHILD_PIPELINE_BRANCH of BEC Atlas \033[0;m";
test -d bec_atlas || git clone --branch $CHILD_PIPELINE_BRANCH https://gitlab.psi.ch/bec/bec_atlas.git; cd bec_atlas;
fi
- pip install ./backend[dev]
- git clone --branch $BEC_CORE_BRANCH https://gitlab.psi.ch/bec/bec.git
- pip install ./bec/bec_lib[dev]
# Remove the following line once there is a new release of fakeredis-py
- pip install git+https://github.com/cunla/fakeredis-py.git
formatter:
stage: Formatter
needs: []
@ -82,28 +94,13 @@ pylint:
backend_pytest:
stage: test
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/docker:23-dind
services:
- name: docker:dind
entrypoint: ["dockerd-entrypoint.sh", "--tls=false"]
image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/python:3.11
needs: []
script:
- if [[ "$CI_PROJECT_PATH" != "bec/bec_atlas" ]]; then
apk update; apk add git; echo -e "\033[35;1m Using branch $CHILD_PIPELINE_BRANCH of BEC Atlas \033[0;m";
test -d bec_atlas || git clone --branch $CHILD_PIPELINE_BRANCH https://gitlab.psi.ch/bec/bec_atlas.git; cd bec_atlas;
TARGET_BRANCH=$CHILD_PIPELINE_BRANCH;
else
TARGET_BRANCH=$CI_COMMIT_REF_NAME;
fi
# start services
- docker-compose -f ./backend/tests/docker-compose.yml up -d
# build test environment
- echo "$CI_DEPENDENCY_PROXY_PASSWORD" | docker login $CI_DEPENDENCY_PROXY_SERVER --username $CI_DEPENDENCY_PROXY_USER --password-stdin
- docker build -t bec_atlas_backend:test -f ./backend/tests/Dockerfile.run_pytest --build-arg PY_VERSION=3.10 --build-arg BEC_ATLAS_BRANCH=$TARGET_BRANCH --build-arg BEC_CORE_BRANCH=$BEC_CORE_BRANCH --build-arg CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX .
- docker run --network=host --name bec_atlas_backend bec_atlas_backend:test
after_script:
- docker cp bec_atlas_backend:/code/bec_atlas/test_files/. $CI_PROJECT_DIR
- pip install coverage
- coverage run --source=./backend --omit=*/backend/tests/* -m pytest -v --junitxml=report.xml --random-order --full-trace ./backend/tests
- coverage report
- coverage xml
coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
artifacts:
reports:
@ -113,6 +110,39 @@ backend_pytest:
path: coverage.xml
interruptible: true
# backend_pytest:
# stage: test
# image: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/docker:23-dind
# services:
# - name: docker:dind
# entrypoint: ["dockerd-entrypoint.sh", "--tls=false"]
# needs: []
# script:
# - if [[ "$CI_PROJECT_PATH" != "bec/bec_atlas" ]]; then
# apk update; apk add git; echo -e "\033[35;1m Using branch $CHILD_PIPELINE_BRANCH of BEC Atlas \033[0;m";
# test -d bec_atlas || git clone --branch $CHILD_PIPELINE_BRANCH https://gitlab.psi.ch/bec/bec_atlas.git; cd bec_atlas;
# TARGET_BRANCH=$CHILD_PIPELINE_BRANCH;
# else
# TARGET_BRANCH=$CI_COMMIT_REF_NAME;
# fi
# # start services
# - docker-compose -f ./backend/tests/docker-compose.yml up -d
# # build test environment
# - echo "$CI_DEPENDENCY_PROXY_PASSWORD" | docker login $CI_DEPENDENCY_PROXY_SERVER --username $CI_DEPENDENCY_PROXY_USER --password-stdin
# - docker build -t bec_atlas_backend:test -f ./backend/tests/Dockerfile.run_pytest --build-arg PY_VERSION=3.10 --build-arg BEC_ATLAS_BRANCH=$TARGET_BRANCH --build-arg BEC_CORE_BRANCH=$BEC_CORE_BRANCH --build-arg CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX .
# - docker run --network=host --name bec_atlas_backend bec_atlas_backend:test
# after_script:
# - docker cp bec_atlas_backend:/code/bec_atlas/test_files/. $CI_PROJECT_DIR
# coverage: '/(?i)total.*? (100(?:\.0+)?\%|[1-9]?\d(?:\.\d+)?\%)$/'
# artifacts:
# reports:
# junit: report.xml
# coverage_report:
# coverage_format: cobertura
# path: coverage.xml
# interruptible: true
# semver:
# stage: Deploy
# needs: ["backend_pytest"]

View File

@ -33,15 +33,19 @@ class MongoDBDatasource:
port = self.config.get("port")
username = self.config.get("username")
password = self.config.get("password")
if username and password:
self.client = pymongo.MongoClient(
f"mongodb://{username}:{password}@{host}:{port}/?authSource=bec_atlas"
)
if self.config.get("mongodb_client"):
self.client = self.config.get("mongodb_client")
else:
self.client = pymongo.MongoClient(f"mongodb://{host}:{port}/")
if username and password:
self.client = pymongo.MongoClient(
f"mongodb://{username}:{password}@{host}:{port}/?authSource=bec_atlas"
)
else:
self.client = pymongo.MongoClient(f"mongodb://{host}:{port}/")
# Check if the connection is successful
self.client.list_databases()
self.client.list_database_names()
logger.info(f"Connecting to MongoDB at {host}:{port}")
self.db = self.client["bec_atlas"]

View File

@ -4,7 +4,7 @@ from typing import TYPE_CHECKING
from bec_lib.redis_connector import RedisConnector
from redis.asyncio import Redis as AsyncRedis
from redis.exceptions import AuthenticationError
from redis.exceptions import AuthenticationError, ResponseError
if TYPE_CHECKING:
from bec_atlas.model.model import DeploymentCredential
@ -13,14 +13,18 @@ if TYPE_CHECKING:
class RedisDatasource:
def __init__(self, config: dict):
self.config = config
self.connector = RedisConnector(f"{config.get('host')}:{config.get('port')}")
if config.get("sync_instance"):
self.connector = config.get("sync_instance")
else:
self.connector = RedisConnector(f"{config.get('host')}:{config.get('port')}")
username = config.get("username")
password = config.get("password")
try:
self.connector._redis_conn.auth(password, username=username)
self.reconfigured_acls = False
except AuthenticationError:
except (AuthenticationError, ResponseError):
self.setup_acls()
self.connector._redis_conn.auth(password, username=username)
self.reconfigured_acls = True
@ -28,12 +32,15 @@ class RedisDatasource:
self.connector._redis_conn.connection_pool.connection_kwargs["username"] = username
self.connector._redis_conn.connection_pool.connection_kwargs["password"] = password
self.async_connector = AsyncRedis(
host=config.get("host"),
port=config.get("port"),
username="ingestor",
password=config.get("password"),
)
if config.get("async_instance"):
self.async_connector = config.get("async_instance")
else:
self.async_connector = AsyncRedis(
host=config.get("host"),
port=config.get("port"),
username="ingestor",
password=config.get("password"),
)
print("Connected to Redis")
def setup_acls(self):

View File

@ -29,9 +29,13 @@ class DataIngestor:
redis_host = config.get("redis", {}).get("host", "localhost")
redis_port = config.get("redis", {}).get("port", 6380)
self.redis = RedisConnector(
f"{redis_host}:{redis_port}" # username="ingestor", password="ingestor"
)
if config.get("redis", {}).get("sync_instance"):
self.redis = config.get("redis", {}).get("sync_instance")
else:
self.redis = RedisConnector(
f"{redis_host}:{redis_port}" # username="ingestor", password="ingestor"
)
# self.redis.authenticate(
# config.get("redis", {}).get("password", "ingestor"), username="ingestor"
# )

View File

@ -170,3 +170,4 @@ class ScanRouter(BaseRouter):
out = self.db.aggregate("scans", pipeline=pipeline, dtype=None, user=current_user)
if out:
return out[0]
return {"count": 0}

View File

@ -33,7 +33,8 @@ dev = [
"pytest-random-order~=1.1",
"pytest-timeout~=2.2",
"pytest~=8.0",
"pytest-docker",
"fakeredis",
"mongomock~=4.3",
"isort~=5.13, >=5.13.2",
"pytest-asyncio",
]

View File

@ -1,147 +1,121 @@
import contextlib
import json
import os
from typing import Iterator
from unittest import mock
import fakeredis
import mongomock
import pymongo
import pytest
from bec_lib.redis_connector import RedisConnector
from bson import ObjectId
from fastapi.testclient import TestClient
from pytest_docker.plugin import DockerComposeExecutor, Services
from bec_atlas.main import AtlasApp
from bec_atlas.router.redis_router import BECAsyncRedisManager
def pytest_addoption(parser):
parser.addoption(
"--skip-docker",
action="store_true",
default=False,
help="Skip spinning up docker containers",
)
def import_mongodb_data(mongo_client: pymongo.MongoClient):
"""
Import the test data into the mongodb container. The data is stored in the
tests/test_data directory as json files per collection.
Args:
mongo_client (pymongo.MongoClient): The mongo client
"""
client = mongo_client
db = client["bec_atlas"]
collections = [
# "bec_access_profiles",
"deployment_access",
"deployment_credentials",
"deployments",
# "fs.chunks",
# "fs.files",
"scans",
"sessions",
"user_credentials",
"users",
]
for collection in collections:
db.drop_collection(collection)
current_dir = os.path.dirname(os.path.abspath(__file__))
for collection in collections:
with open(
f"{current_dir}/test_data/bec_atlas.{collection}.json", "r", encoding="utf-8"
) as f:
data = f.read()
data = json.loads(data)
data = [convert_to_object_id(d) for d in data]
db[collection].insert_many(data)
client.close()
def convert_to_object_id(data):
"""
Convert the _id field in the data to an ObjectId.
Args:
data (dict): The data
Returns:
dict: The data with the _id field converted to an ObjectId
"""
if isinstance(data, dict) and "$oid" in data:
return ObjectId(data["$oid"])
if isinstance(data, dict):
for key, value in data.items():
data[key] = convert_to_object_id(value)
return data
@pytest.fixture(scope="session")
def docker_compose_file(pytestconfig):
test_directory = os.path.dirname(os.path.abspath(__file__))
return os.path.join(test_directory, "docker-compose.yml")
def redis_server():
redis_server = fakeredis.FakeServer()
yield redis_server
@pytest.fixture(scope="session")
def docker_compose_project_name() -> str:
"""Generate a project name using the current process PID. Override this
fixture in your tests if you need a particular project name."""
def backend(redis_server):
return "pytest_9070_atlas"
def _fake_redis(host, port):
return fakeredis.FakeStrictRedis(server=redis_server)
mongo_client = mongomock.MongoClient("localhost", 27027)
@contextlib.contextmanager
def get_docker_services(
docker_compose_command: str,
docker_compose_file: list[str] | str,
docker_compose_project_name: str,
docker_setup: list[str] | str,
docker_cleanup: list[str] | str,
) -> Iterator[Services]:
docker_compose = DockerComposeExecutor(
docker_compose_command, docker_compose_file, docker_compose_project_name
)
try:
if docker_cleanup:
# Maintain backwards compatibility with the string format.
if isinstance(docker_cleanup, str):
docker_cleanup = [docker_cleanup]
for command in docker_cleanup:
docker_compose.execute(command)
except Exception:
pass
# setup containers.
if docker_setup:
# Maintain backwards compatibility with the string format.
if isinstance(docker_setup, str):
docker_setup = [docker_setup]
for command in docker_setup:
docker_compose.execute(command)
try:
# Let test(s) run.
yield Services(docker_compose)
finally:
# Clean up.
if docker_cleanup:
# Maintain backwards compatibility with the string format.
if isinstance(docker_cleanup, str):
docker_cleanup = [docker_cleanup]
for command in docker_cleanup:
docker_compose.execute(command)
@pytest.fixture(scope="session")
def docker_services(
docker_compose_command: str,
docker_compose_file: list[str] | str,
docker_compose_project_name: str,
docker_setup: str,
docker_cleanup: str,
request,
) -> Iterator[Services]:
"""Start all services from a docker compose file (`docker-compose up`).
After test are finished, shutdown all services (`docker-compose down`)."""
if request.config.getoption("--skip-docker"):
yield
return
with get_docker_services(
docker_compose_command,
docker_compose_file,
docker_compose_project_name,
docker_setup,
docker_cleanup,
) as docker_service:
yield docker_service
@pytest.fixture(scope="session")
def redis_container(docker_ip, docker_services):
host = docker_ip
if os.path.exists("/.dockerenv"):
host = "redis"
if docker_services is None:
port = 6380
else:
port = docker_services.port_for("redis", 6379)
return "localhost", port
@pytest.fixture(scope="session")
def mongo_container(docker_ip, docker_services):
host = docker_ip
if os.path.exists("/.dockerenv"):
host = "mongo"
if docker_services is None:
port = 27017
else:
port = docker_services.port_for("mongodb", 27017)
return "localhost", port
@pytest.fixture(scope="session")
def backend(redis_container, mongo_container):
redis_host, redis_port = redis_container
mongo_host, mongo_port = mongo_container
config = {
"redis": {
"host": redis_host,
"port": redis_port,
"host": "localhost",
"port": 6480,
"username": "ingestor",
"password": "ingestor",
"sync_instance": RedisConnector("localhost:1", redis_cls=_fake_redis),
"async_instance": fakeredis.FakeAsyncRedis(server=redis_server),
},
"mongodb": {"host": mongo_host, "port": mongo_port},
"mongodb": {"host": "localhost", "port": 27027, "mongodb_client": mongo_client},
}
import_mongodb_data(mongo_client)
app = AtlasApp(config)
with TestClient(app.app) as _client:
yield _client, app
class PatchedBECAsyncRedisManager(BECAsyncRedisManager):
def _redis_connect(self):
self.redis = fakeredis.FakeAsyncRedis(
server=redis_server,
username=config["redis"]["username"],
password=config["redis"]["password"],
)
self.redis.connection_pool.connection_kwargs["username"] = config["redis"]["username"]
self.redis.connection_pool.connection_kwargs["password"] = config["redis"]["password"]
self.pubsub = self.redis.pubsub(ignore_subscribe_messages=True)
with mock.patch(
"bec_atlas.router.redis_router.BECAsyncRedisManager", PatchedBECAsyncRedisManager
):
with TestClient(app.app) as _client:
yield _client, app

View File

@ -0,0 +1,29 @@
import os
def export_mongodb_data(host: str, port: int):
"""Export data from MongoDB to a JSON file."""
collections = [
"bec_access_profiles",
"deployment_access",
"deployment_credentials",
"deployments",
"fs.chunks",
"fs.files",
"scans",
"sessions",
"user_credentials",
"users",
]
current_dir = os.path.dirname(os.path.abspath(__file__))
for collection in collections:
os.system(
f"mongoexport --host {host} --port {port} --db bec_atlas --collection {collection} --jsonArray --out {current_dir}/test_data/bec_atlas.{collection}.json"
)
if __name__ == "__main__":
export_mongodb_data("localhost", 27017)

View File

@ -0,0 +1,13 @@
[
{
"_id": { "$oid": "678aa8d4875568640bd92176" },
"owner_groups": ["admin", "demo"],
"access_groups": [],
"user_read_access": [],
"user_write_access": [],
"su_read_access": [],
"su_write_access": [],
"remote_read_access": [],
"remote_write_access": []
}
]

View File

@ -0,0 +1 @@
[{ "_id": { "$oid": "678aa8d4875568640bd92176" }, "credential": "Ae4833LFqPQHwDKH_iV1HJ--wcdGLWOjjRCrCxxYGKs" }]

View File

@ -0,0 +1,12 @@
[
{
"_id": { "$oid": "678aa8d4875568640bd92176" },
"owner_groups": ["admin", "demo"],
"access_groups": ["demo"],
"id": null,
"realm_id": "demo_beamline_1",
"name": "Demo Deployment 1",
"active_session_id": null,
"config_templates": []
}
]

View File

@ -0,0 +1,914 @@
[
{
"_id": "2f372a7b-e562-4df5-9ad6-bbf5ef5692a4",
"metadata": {},
"scan_id": "2f372a7b-e562-4df5-9ad6-bbf5ef5692a4",
"status": "closed",
"scan_number": 2254,
"num_points": 400,
"scan_name": "line_scan",
"scan_type": "step",
"dataset_number": 2253,
"scan_report_devices": ["samx"],
"user_metadata": {},
"readout_priority": {
"monitored": [
"bpm4xm",
"bpm5a",
"bpm5x",
"ring_current_sim",
"bpm4b",
"bpm3b",
"bpm6d",
"bpm6y",
"ebpmuy",
"bpm4d",
"bpm3i",
"bpm6z",
"bpm4z",
"ebpmdy",
"bpm6i",
"bpm4s",
"bpm3d",
"bpm3z",
"bpm4ym",
"ebpmux",
"bpm4y",
"ftp",
"bpm6x",
"bpm4xf",
"bpm5i",
"diode",
"temp",
"bpm5b",
"bpm6c",
"ebpmdx",
"bpm3a",
"bpm3c",
"bpm3x",
"bpm6a",
"bpm5d",
"bpm4i",
"bpm5z",
"bpm4x",
"bpm3y",
"bpm4a",
"bpm5y",
"transd",
"samx",
"bpm6b",
"curr",
"bpm4c",
"bpm5c",
"bpm4yf"
],
"baseline": [
"bm5trx",
"sl3wv",
"dttrx",
"dttrz",
"bm4try",
"fsh1x",
"sl2tryt",
"mobdbo",
"sl4tryt",
"stroy",
"sl0wh",
"eyefoc",
"hroy",
"pinz",
"ebfi2",
"idgap",
"moth1",
"sl5tryb",
"moth2e",
"moth1e",
"bs2x",
"sl2tryb",
"bm1trx",
"rt_controller",
"dtth",
"sl4trxi",
"sl3tryb",
"bm1try",
"hroz",
"bpm5r",
"ebtrx",
"mobdco",
"bpm4r",
"eyex",
"hy",
"sl0ch",
"motrz1",
"sl2wv",
"samz",
"moroll2",
"bm6trx",
"bim2x",
"burstn",
"dtpush",
"mitry3",
"bm5try",
"hrox",
"sttrx",
"sl5trxo",
"sl4wh",
"eyey",
"fttrz",
"dyn_signals",
"sl0trxo",
"sl1trxo",
"ebtrz",
"mibd2",
"sl5tryt",
"sl3ch",
"bm3trx",
"bim2y",
"mith",
"ddg1c",
"sl1trxi",
"aptrx",
"hx",
"bm2trx",
"ddg1e",
"mitry1",
"sl5ch",
"sl5trxi",
"sl1cv",
"ebtry",
"sttry",
"motrx2",
"motry",
"sl1tryb",
"mibd1",
"ebfi1",
"mitry2",
"ddg1a",
"samy",
"bm4trx",
"ddg1b",
"bm2try",
"ddg1f",
"ebcsy",
"sl3trxi",
"sl4cv",
"ebfi3",
"strox",
"sl1ch",
"mokev",
"ebfzpx",
"fsh2x",
"mobdai",
"sl3wh",
"stroz",
"bm3try",
"moyaw2",
"piny",
"sl5wh",
"sl3trxo",
"mitry",
"dettrx",
"fi1try",
"motry2",
"pseudo_signal1",
"sl5wv",
"hz",
"ebfzpy",
"motrz1e",
"ddg1h",
"moth2",
"sl3tryt",
"sl4trxo",
"bm6try",
"sl4ch",
"di2try",
"sl2wh",
"sl2ch",
"sl0trxi",
"sl4tryb",
"hexapod",
"mobd",
"bs1y",
"bs2y",
"sl2trxo",
"sl1wh",
"sl1wv",
"sl4wv",
"di2trx",
"mbsx",
"ebcsx",
"mopush1",
"bs1x",
"fttrx1",
"mopush2",
"dttry",
"fttrx2",
"miroll",
"burstr",
"mibd",
"sl1tryt",
"fi3try",
"mobddi",
"fttry1",
"sl2trxi",
"aptry",
"pinx",
"ftrans",
"ebfi4",
"sl3cv",
"sl2cv",
"ddg1d",
"sl5cv",
"moroll1",
"fttry2",
"mitrx",
"mbsy",
"ddg1g",
"fi2try"
],
"async": ["monitor_async"],
"continuous": [],
"on_request": ["flyer_sim"]
},
"scan_parameters": {
"exp_time": 0,
"frames_per_trigger": 1,
"settling_time": 0,
"readout_time": 0,
"optim_trajectory": null,
"return_to_start": false,
"relative": false,
"system_config": { "file_suffix": null, "file_directory": null }
},
"request_inputs": {
"arg_bundle": ["samx", -20, 20],
"inputs": {},
"kwargs": { "steps": 400, "relative": false, "system_config": { "file_suffix": null, "file_directory": null } }
},
"info": {
"readout_priority": {
"monitored": [
"bpm4xm",
"bpm5a",
"bpm5x",
"ring_current_sim",
"bpm4b",
"bpm3b",
"bpm6d",
"bpm6y",
"ebpmuy",
"bpm4d",
"bpm3i",
"bpm6z",
"bpm4z",
"ebpmdy",
"bpm6i",
"bpm4s",
"bpm3d",
"bpm3z",
"bpm4ym",
"ebpmux",
"bpm4y",
"ftp",
"bpm6x",
"bpm4xf",
"bpm5i",
"diode",
"temp",
"bpm5b",
"bpm6c",
"ebpmdx",
"bpm3a",
"bpm3c",
"bpm3x",
"bpm6a",
"bpm5d",
"bpm4i",
"bpm5z",
"bpm4x",
"bpm3y",
"bpm4a",
"bpm5y",
"transd",
"samx",
"bpm6b",
"curr",
"bpm4c",
"bpm5c",
"bpm4yf"
],
"baseline": [
"bm5trx",
"sl3wv",
"dttrx",
"dttrz",
"bm4try",
"fsh1x",
"sl2tryt",
"mobdbo",
"sl4tryt",
"stroy",
"sl0wh",
"eyefoc",
"hroy",
"pinz",
"ebfi2",
"idgap",
"moth1",
"sl5tryb",
"moth2e",
"moth1e",
"bs2x",
"sl2tryb",
"bm1trx",
"rt_controller",
"dtth",
"sl4trxi",
"sl3tryb",
"bm1try",
"hroz",
"bpm5r",
"ebtrx",
"mobdco",
"bpm4r",
"eyex",
"hy",
"sl0ch",
"motrz1",
"sl2wv",
"samz",
"moroll2",
"bm6trx",
"bim2x",
"burstn",
"dtpush",
"mitry3",
"bm5try",
"hrox",
"sttrx",
"sl5trxo",
"sl4wh",
"eyey",
"fttrz",
"dyn_signals",
"sl0trxo",
"sl1trxo",
"ebtrz",
"mibd2",
"sl5tryt",
"sl3ch",
"bm3trx",
"bim2y",
"mith",
"ddg1c",
"sl1trxi",
"aptrx",
"hx",
"bm2trx",
"ddg1e",
"mitry1",
"sl5ch",
"sl5trxi",
"sl1cv",
"ebtry",
"sttry",
"motrx2",
"motry",
"sl1tryb",
"mibd1",
"ebfi1",
"mitry2",
"ddg1a",
"samy",
"bm4trx",
"ddg1b",
"bm2try",
"ddg1f",
"ebcsy",
"sl3trxi",
"sl4cv",
"ebfi3",
"strox",
"sl1ch",
"mokev",
"ebfzpx",
"fsh2x",
"mobdai",
"sl3wh",
"stroz",
"bm3try",
"moyaw2",
"piny",
"sl5wh",
"sl3trxo",
"mitry",
"dettrx",
"fi1try",
"motry2",
"pseudo_signal1",
"sl5wv",
"hz",
"ebfzpy",
"motrz1e",
"ddg1h",
"moth2",
"sl3tryt",
"sl4trxo",
"bm6try",
"sl4ch",
"di2try",
"sl2wh",
"sl2ch",
"sl0trxi",
"sl4tryb",
"hexapod",
"mobd",
"bs1y",
"bs2y",
"sl2trxo",
"sl1wh",
"sl1wv",
"sl4wv",
"di2trx",
"mbsx",
"ebcsx",
"mopush1",
"bs1x",
"fttrx1",
"mopush2",
"dttry",
"fttrx2",
"miroll",
"burstr",
"mibd",
"sl1tryt",
"fi3try",
"mobddi",
"fttry1",
"sl2trxi",
"aptry",
"pinx",
"ftrans",
"ebfi4",
"sl3cv",
"sl2cv",
"ddg1d",
"sl5cv",
"moroll1",
"fttry2",
"mitrx",
"mbsy",
"ddg1g",
"fi2try"
],
"async": ["monitor_async"],
"continuous": [],
"on_request": ["flyer_sim"]
},
"file_suffix": null,
"file_directory": null,
"user_metadata": {},
"RID": "22a3a0e0-64f8-426d-b58e-1de75ef58c6f",
"scan_id": "2f372a7b-e562-4df5-9ad6-bbf5ef5692a4",
"queue_id": "a8984b11-214e-41ac-9a03-2690600b3f3c",
"scan_motors": ["samx"],
"num_points": 400,
"positions": [
[-20.0],
[-19.899749373433583],
[-19.79949874686717],
[-19.69924812030075],
[-19.598997493734338],
[-19.49874686716792],
[-19.398496240601503],
[-19.29824561403509],
[-19.19799498746867],
[-19.097744360902254],
[-18.99749373433584],
[-18.897243107769423],
[-18.796992481203006],
[-18.696741854636592],
[-18.596491228070175],
[-18.49624060150376],
[-18.395989974937343],
[-18.295739348370926],
[-18.195488721804512],
[-18.095238095238095],
[-17.99498746867168],
[-17.894736842105264],
[-17.794486215538846],
[-17.694235588972433],
[-17.593984962406015],
[-17.493734335839598],
[-17.393483709273184],
[-17.293233082706767],
[-17.19298245614035],
[-17.092731829573935],
[-16.992481203007518],
[-16.892230576441104],
[-16.791979949874687],
[-16.69172932330827],
[-16.591478696741856],
[-16.49122807017544],
[-16.390977443609025],
[-16.290726817042607],
[-16.19047619047619],
[-16.090225563909776],
[-15.989974937343359],
[-15.889724310776943],
[-15.789473684210527],
[-15.68922305764411],
[-15.588972431077694],
[-15.488721804511279],
[-15.388471177944862],
[-15.288220551378446],
[-15.18796992481203],
[-15.087719298245615],
[-14.9874686716792],
[-14.887218045112782],
[-14.786967418546366],
[-14.68671679197995],
[-14.586466165413533],
[-14.486215538847118],
[-14.385964912280702],
[-14.285714285714286],
[-14.18546365914787],
[-14.085213032581454],
[-13.984962406015038],
[-13.884711779448622],
[-13.784461152882205],
[-13.68421052631579],
[-13.583959899749374],
[-13.483709273182958],
[-13.383458646616543],
[-13.283208020050125],
[-13.18295739348371],
[-13.082706766917294],
[-12.982456140350877],
[-12.882205513784461],
[-12.781954887218046],
[-12.68170426065163],
[-12.581453634085214],
[-12.481203007518797],
[-12.380952380952381],
[-12.280701754385966],
[-12.180451127819548],
[-12.080200501253133],
[-11.979949874686717],
[-11.879699248120302],
[-11.779448621553886],
[-11.679197994987469],
[-11.578947368421053],
[-11.478696741854638],
[-11.37844611528822],
[-11.278195488721805],
[-11.177944862155389],
[-11.077694235588973],
[-10.977443609022558],
[-10.87719298245614],
[-10.776942355889725],
[-10.67669172932331],
[-10.576441102756892],
[-10.476190476190476],
[-10.37593984962406],
[-10.275689223057645],
[-10.17543859649123],
[-10.075187969924812],
[-9.974937343358397],
[-9.874686716791981],
[-9.774436090225564],
[-9.674185463659148],
[-9.573934837092732],
[-9.473684210526317],
[-9.373433583959901],
[-9.273182957393484],
[-9.172932330827068],
[-9.072681704260653],
[-8.972431077694235],
[-8.87218045112782],
[-8.771929824561404],
[-8.671679197994989],
[-8.571428571428573],
[-8.471177944862156],
[-8.37092731829574],
[-8.270676691729324],
[-8.170426065162907],
[-8.070175438596491],
[-7.969924812030076],
[-7.86967418546366],
[-7.769423558897245],
[-7.669172932330827],
[-7.568922305764412],
[-7.468671679197996],
[-7.368421052631579],
[-7.268170426065163],
[-7.167919799498748],
[-7.067669172932332],
[-6.967418546365916],
[-6.867167919799499],
[-6.766917293233083],
[-6.666666666666668],
[-6.5664160401002505],
[-6.466165413533835],
[-6.365914786967419],
[-6.265664160401004],
[-6.165413533834588],
[-6.065162907268171],
[-5.964912280701755],
[-5.86466165413534],
[-5.764411027568922],
[-5.664160401002507],
[-5.563909774436091],
[-5.463659147869675],
[-5.36340852130326],
[-5.2631578947368425],
[-5.162907268170427],
[-5.062656641604011],
[-4.962406015037594],
[-4.862155388471178],
[-4.761904761904763],
[-4.661654135338347],
[-4.561403508771932],
[-4.461152882205514],
[-4.360902255639099],
[-4.260651629072683],
[-4.160401002506266],
[-4.06015037593985],
[-3.9598997493734345],
[-3.859649122807017],
[-3.7593984962406033],
[-3.659147869674186],
[-3.558897243107772],
[-3.4586466165413547],
[-3.3583959899749374],
[-3.2581453634085236],
[-3.157894736842106],
[-3.057644110275689],
[-2.957393483709275],
[-2.8571428571428577],
[-2.7568922305764403],
[-2.6566416040100265],
[-2.556390977443609],
[-2.4561403508771953],
[-2.355889724310778],
[-2.2556390977443606],
[-2.1553884711779467],
[-2.0551378446115294],
[-1.9548872180451156],
[-1.8546365914786982],
[-1.7543859649122808],
[-1.654135338345867],
[-1.5538847117794496],
[-1.4536340852130323],
[-1.3533834586466185],
[-1.253132832080201],
[-1.1528822055137837],
[-1.05263157894737],
[-0.9523809523809526],
[-0.8521303258145387],
[-0.7518796992481214],
[-0.651629072681704],
[-0.5513784461152902],
[-0.4511278195488728],
[-0.350877192982459],
[-0.25062656641604164],
[-0.15037593984962427],
[-0.05012531328321046],
[0.05012531328320691],
[0.15037593984962427],
[0.2506265664160381],
[0.35087719298245545],
[0.4511278195488728],
[0.5513784461152866],
[0.651629072681704],
[0.7518796992481178],
[0.8521303258145352],
[0.9523809523809526],
[1.0526315789473664],
[1.1528822055137837],
[1.2531328320801975],
[1.353383458646615],
[1.4536340852130323],
[1.553884711779446],
[1.6541353383458635],
[1.7543859649122808],
[1.8546365914786946],
[1.954887218045112],
[2.0551378446115294],
[2.155388471177943],
[2.2556390977443606],
[2.3558897243107744],
[2.4561403508771917],
[2.556390977443609],
[2.656641604010023],
[2.7568922305764403],
[2.857142857142854],
[2.9573934837092715],
[3.057644110275689],
[3.1578947368421026],
[3.25814536340852],
[3.3583959899749374],
[3.458646616541351],
[3.5588972431077686],
[3.659147869674186],
[3.7593984962405997],
[3.859649122807017],
[3.959899749373431],
[4.060150375939848],
[4.160401002506266],
[4.2606516290726795],
[4.360902255639097],
[4.461152882205511],
[4.561403508771928],
[4.661654135338345],
[4.761904761904759],
[4.862155388471177],
[4.962406015037594],
[5.062656641604008],
[5.162907268170425],
[5.2631578947368425],
[5.363408521303256],
[5.463659147869674],
[5.5639097744360875],
[5.664160401002505],
[5.764411027568922],
[5.864661654135336],
[5.964912280701753],
[6.065162907268167],
[6.165413533834585],
[6.265664160401002],
[6.365914786967416],
[6.466165413533833],
[6.5664160401002505],
[6.666666666666664],
[6.766917293233082],
[6.867167919799499],
[6.967418546365913],
[7.06766917293233],
[7.167919799498744],
[7.268170426065161],
[7.368421052631579],
[7.468671679197993],
[7.56892230576441],
[7.669172932330824],
[7.769423558897241],
[7.8696741854636585],
[7.969924812030072],
[8.07017543859649],
[8.170426065162907],
[8.27067669172932],
[8.370927318295738],
[8.471177944862156],
[8.57142857142857],
[8.671679197994987],
[8.7719298245614],
[8.872180451127818],
[8.972431077694235],
[9.07268170426065],
[9.172932330827066],
[9.27318295739348],
[9.373433583959898],
[9.473684210526315],
[9.573934837092729],
[9.674185463659146],
[9.774436090225564],
[9.874686716791977],
[9.974937343358395],
[10.075187969924812],
[10.175438596491226],
[10.275689223057643],
[10.375939849624057],
[10.476190476190474],
[10.576441102756892],
[10.676691729323306],
[10.776942355889723],
[10.877192982456137],
[10.977443609022554],
[11.077694235588972],
[11.177944862155385],
[11.278195488721803],
[11.37844611528822],
[11.478696741854634],
[11.578947368421051],
[11.679197994987469],
[11.779448621553883],
[11.8796992481203],
[11.979949874686714],
[12.080200501253131],
[12.180451127819545],
[12.280701754385966],
[12.38095238095238],
[12.481203007518793],
[12.581453634085214],
[12.681704260651628],
[12.781954887218042],
[12.882205513784456],
[12.982456140350877],
[13.08270676691729],
[13.182957393483704],
[13.283208020050125],
[13.383458646616539],
[13.483709273182953],
[13.583959899749374],
[13.684210526315788],
[13.784461152882201],
[13.884711779448622],
[13.984962406015036],
[14.08521303258145],
[14.18546365914787],
[14.285714285714285],
[14.385964912280699],
[14.48621553884712],
[14.586466165413533],
[14.686716791979947],
[14.78696741854636],
[14.887218045112782],
[14.987468671679196],
[15.08771929824561],
[15.18796992481203],
[15.288220551378444],
[15.388471177944858],
[15.488721804511279],
[15.588972431077693],
[15.689223057644107],
[15.789473684210527],
[15.889724310776941],
[15.989974937343355],
[16.09022556390977],
[16.19047619047619],
[16.290726817042604],
[16.390977443609017],
[16.49122807017544],
[16.591478696741852],
[16.691729323308266],
[16.791979949874687],
[16.8922305764411],
[16.992481203007515],
[17.092731829573935],
[17.19298245614035],
[17.293233082706763],
[17.393483709273184],
[17.493734335839598],
[17.59398496240601],
[17.694235588972433],
[17.794486215538846],
[17.89473684210526],
[17.994987468671674],
[18.095238095238095],
[18.19548872180451],
[18.295739348370923],
[18.395989974937343],
[18.496240601503757],
[18.59649122807017],
[18.696741854636592],
[18.796992481203006],
[18.89724310776942],
[18.99749373433584],
[19.097744360902254],
[19.197994987468668],
[19.298245614035082],
[19.398496240601503],
[19.498746867167917],
[19.59899749373433],
[19.69924812030075],
[19.799498746867165],
[19.89974937343358],
[20.0]
],
"scan_name": "line_scan",
"scan_type": "step",
"scan_number": 2254,
"dataset_number": 2253,
"exp_time": 0,
"frames_per_trigger": 1,
"settling_time": 0,
"readout_time": 0,
"scan_report_devices": ["samx"],
"monitor_sync": "bec",
"scan_parameters": {
"exp_time": 0,
"frames_per_trigger": 1,
"settling_time": 0,
"readout_time": 0,
"optim_trajectory": null,
"return_to_start": false,
"relative": false,
"system_config": { "file_suffix": null, "file_directory": null }
},
"request_inputs": {
"arg_bundle": ["samx", -20, 20],
"inputs": {},
"kwargs": { "steps": 400, "relative": false, "system_config": { "file_suffix": null, "file_directory": null } }
},
"scan_msgs": [
"metadata={'file_suffix': None, 'file_directory': None, 'user_metadata': {}, 'RID': '22a3a0e0-64f8-426d-b58e-1de75ef58c6f'} scan_type='line_scan' parameter={'args': {'samx': [-20, 20]}, 'kwargs': {'steps': 400, 'relative': False, 'system_config': {'file_suffix': None, 'file_directory': None}}} queue='primary'"
],
"args": { "samx": [-20, 20] },
"kwargs": { "steps": 400, "relative": false, "system_config": { "file_suffix": null, "file_directory": null } }
},
"timestamp": 1.7377484829922938e9,
"owner_groups": ["admin"],
"access_groups": ["admin"],
"session_id": "6792392e3e28ff5364050c85"
}
]

View File

@ -0,0 +1,9 @@
[
{
"_id": { "$oid": "6792392e3e28ff5364050c85" },
"owner_groups": ["admin", "demo"],
"access_groups": ["demo"],
"deployment_id": "678aa8d4875568640bd92176",
"name": "_default_"
}
]

View File

@ -0,0 +1,18 @@
[
{
"_id": { "$oid": "67a5d652d22ba335392b915a" },
"owner_groups": ["admin"],
"access_groups": [],
"id": null,
"user_id": { "$oid": "67a5d652d22ba335392b9159" },
"password": "$argon2id$v=19$m=65536,t=3,p=4$SjLcoiN+JHSLWDnSv6ysTw$nrqkM6a/KkJ56pTRPWQRSGP7ht9JYnhBUcB1vBOhkag"
},
{
"_id": { "$oid": "67a5d694c13d2198c91cc47d" },
"owner_groups": ["admin"],
"access_groups": [],
"id": null,
"user_id": { "$oid": "67a5d694c13d2198c91cc47c" },
"password": "$argon2id$v=19$m=65536,t=3,p=4$hS1xfyCzbpUfHeNiZ54lyQ$DTZWjwHy95VWO5Btl/qcIcPfZseL+g/iljuRdMfWXok"
}
]

View File

@ -0,0 +1,22 @@
[
{
"_id": { "$oid": "67a5d652d22ba335392b9159" },
"owner_groups": ["admin"],
"access_groups": [],
"id": null,
"email": "admin@bec_atlas.ch",
"groups": ["demo", "admin", "atlas_func_account"],
"first_name": "Admin",
"last_name": "Admin"
},
{
"_id": { "$oid": "67a5d694c13d2198c91cc47c" },
"owner_groups": ["admin"],
"access_groups": [],
"id": null,
"email": "jane.doe@bec_atlas.ch",
"groups": ["demo", "atlas_func_account"],
"first_name": "Jane",
"last_name": "Doe"
}
]

View File

@ -1,7 +1,13 @@
from typing import TYPE_CHECKING
import pytest
from bec_lib import messages
from bec_atlas.ingestor.data_ingestor import DataIngestor
from bec_atlas.model.model import Deployments, Session
if TYPE_CHECKING:
from bec_atlas.datasources.mongodb.mongodb import MongoDBDatasource
@pytest.fixture
@ -13,95 +19,104 @@ def scan_ingestor(backend):
ingestor.shutdown()
# @pytest.mark.timeout(60)
# def test_scan_ingestor_create_scan(scan_ingestor, backend):
# """
# Test that the login endpoint returns a token.
# """
# client, app = backend
# msg = messages.ScanStatusMessage(
# metadata={},
# scan_id="92429a81-4bd4-41c2-82df-eccfaddf3d96",
# status="open",
# # session_id="5cc67967-744d-4115-a46b-13246580cb3f",
# info={
# "readout_priority": {
# "monitored": ["bpm3i", "diode", "ftp", "bpm5c", "bpm3x", "bpm3z", "bpm4x"],
# "baseline": ["ddg1a", "bs1y", "mobdco"],
# "async": ["eiger", "monitor_async", "waveform"],
# "continuous": [],
# "on_request": ["flyer_sim"],
# },
# "file_suffix": None,
# "file_directory": None,
# "user_metadata": {"sample_name": "testA"},
# "RID": "5cc67967-744d-4115-a46b-13246580cb3f",
# "scan_id": "92429a81-4bd4-41c2-82df-eccfaddf3d96",
# "queue_id": "7d77d976-bee0-4bb8-aabb-2b862b4506ec",
# "session_id": "5cc67967-744d-4115-a46b-13246580cb3f",
# "scan_motors": ["samx"],
# "num_points": 10,
# "positions": [
# [-5.0024118137239455],
# [-3.8913007026128343],
# [-2.780189591501723],
# [-1.6690784803906122],
# [-0.557967369279501],
# [0.5531437418316097],
# [1.6642548529427212],
# [2.775365964053833],
# [3.886477075164944],
# [4.9975881862760545],
# ],
# "scan_name": "line_scan",
# "scan_type": "step",
# "scan_number": 2,
# "dataset_number": 2,
# "exp_time": 0,
# "frames_per_trigger": 1,
# "settling_time": 0,
# "readout_time": 0,
# "acquisition_config": {"default": {"exp_time": 0, "readout_time": 0}},
# "scan_report_devices": ["samx"],
# "monitor_sync": "bec",
# "scan_msgs": [
# "metadata={'file_suffix': None, 'file_directory': None, 'user_metadata': {'sample_name': 'testA'}, 'RID': '5cc67967-744d-4115-a46b-13246580cb3f'} scan_type='line_scan' parameter={'args': {'samx': [-5, 5]}, 'kwargs': {'steps': 10, 'exp_time': 0, 'relative': True, 'system_config': {'file_suffix': None, 'file_directory': None}}} queue='primary'"
# ],
# "args": {"samx": [-5, 5]},
# "kwargs": {
# "steps": 10,
# "exp_time": 0,
# "relative": True,
# "system_config": {"file_suffix": None, "file_directory": None},
# },
# },
# timestamp=1732610545.15924,
# )
# scan_ingestor.update_scan_status(msg, deployment_id="678aa8d4875568640bd92174")
@pytest.mark.timeout(60)
def test_scan_ingestor_create_scan(scan_ingestor, backend):
"""
Test that the login endpoint returns a token.
"""
client, app = backend
mongo: MongoDBDatasource = app.datasources.datasources["mongodb"]
deployment_id = str(mongo.find_one("deployments", {}, dtype=Deployments).id)
session_id = str(mongo.find_one("sessions", {"deployment_id": deployment_id}, dtype=Session).id)
msg = messages.ScanStatusMessage(
metadata={},
scan_id="92429a81-4bd4-41c2-82df-eccfaddf3d96",
status="open",
# session_id="5cc67967-744d-4115-a46b-13246580cb3f",
info={
"readout_priority": {
"monitored": ["bpm3i", "diode", "ftp", "bpm5c", "bpm3x", "bpm3z", "bpm4x"],
"baseline": ["ddg1a", "bs1y", "mobdco"],
"async": ["eiger", "monitor_async", "waveform"],
"continuous": [],
"on_request": ["flyer_sim"],
},
"file_suffix": None,
"file_directory": None,
"user_metadata": {"sample_name": "testA"},
"RID": "5cc67967-744d-4115-a46b-13246580cb3f",
"scan_id": "92429a81-4bd4-41c2-82df-eccfaddf3d96",
"queue_id": "7d77d976-bee0-4bb8-aabb-2b862b4506ec",
"session_id": session_id,
"scan_motors": ["samx"],
"num_points": 10,
"positions": [
[-5.0024118137239455],
[-3.8913007026128343],
[-2.780189591501723],
[-1.6690784803906122],
[-0.557967369279501],
[0.5531437418316097],
[1.6642548529427212],
[2.775365964053833],
[3.886477075164944],
[4.9975881862760545],
],
"scan_name": "line_scan",
"scan_type": "step",
"scan_number": 2,
"dataset_number": 2,
"exp_time": 0,
"frames_per_trigger": 1,
"settling_time": 0,
"readout_time": 0,
"acquisition_config": {"default": {"exp_time": 0, "readout_time": 0}},
"scan_report_devices": ["samx"],
"monitor_sync": "bec",
"scan_msgs": [
"metadata={'file_suffix': None, 'file_directory': None, 'user_metadata': {'sample_name': 'testA'}, 'RID': '5cc67967-744d-4115-a46b-13246580cb3f'} scan_type='line_scan' parameter={'args': {'samx': [-5, 5]}, 'kwargs': {'steps': 10, 'exp_time': 0, 'relative': True, 'system_config': {'file_suffix': None, 'file_directory': None}}} queue='primary'"
],
"args": {"samx": [-5, 5]},
"kwargs": {
"steps": 10,
"exp_time": 0,
"relative": True,
"system_config": {"file_suffix": None, "file_directory": None},
},
},
timestamp=1732610545.15924,
)
scan_ingestor.update_scan_status(msg, deployment_id=deployment_id)
# response = client.post(
# "/api/v1/user/login", json={"username": "admin@bec_atlas.ch", "password": "admin"}
# )
# client.headers.update({"Authorization": f"Bearer {response.json()}"})
response = client.post(
"/api/v1/user/login", json={"username": "admin@bec_atlas.ch", "password": "admin"}
)
client.headers.update({"Authorization": f"Bearer {response.json()}"})
# session_id = msg.info.get("session_id")
# scan_id = msg.scan_id
# response = client.get("/api/v1/scans/session", params={"session_id": session_id})
# assert response.status_code == 200
# out = response.json()[0]
# # assert out["session_id"] == session_id
# assert out["scan_id"] == scan_id
# assert out["status"] == "open"
session_id = msg.info.get("session_id")
scan_id = msg.scan_id
response = client.get("/api/v1/scans/session", params={"session_id": session_id})
assert response.status_code == 200
out = response.json()
num_scans = len(out)
inserted_scan = [scan for scan in out if scan["scan_id"] == scan_id]
# msg.status = "closed"
# scan_ingestor.update_scan_status(msg, deployment_id="678aa8d4875568640bd92174")
# response = client.get("/api/v1/scans/id", params={"scan_id": scan_id})
# assert response.status_code == 200
# out = response.json()
# assert out["status"] == "closed"
# assert out["scan_id"] == scan_id
assert len(inserted_scan) == 1
out = inserted_scan[0]
# assert out["session_id"] == session_id
assert out["scan_id"] == scan_id
assert out["status"] == "open"
# response = client.get("/api/v1/scans/session", params={"session_id": session_id})
# assert response.status_code == 200
# out = response.json()
# assert len(out) == 1
msg.status = "closed"
scan_ingestor.update_scan_status(msg, deployment_id=deployment_id)
response = client.get("/api/v1/scans/id", params={"scan_id": scan_id})
assert response.status_code == 200
out = response.json()
assert out["status"] == "closed"
assert out["scan_id"] == scan_id
# Test that the number of scans did not change
response = client.get("/api/v1/scans/session", params={"session_id": session_id})
assert response.status_code == 200
out = response.json()
assert len(out) == num_scans