tests: added login tests

This commit is contained in:
2024-11-21 10:16:21 +01:00
parent e7c8c39726
commit 87666afcf1
8 changed files with 202 additions and 22 deletions

View File

@ -6,18 +6,19 @@ stages:
variables:
SCYLLA_HOST: scylla
SCYLLA_PORT: 9042
SCYLLA_KEYSPACE: test_bec_atlas
SCYLLA_KEYSPACE: bec_atlas
services:
- name: $CI_DEPENDENCY_PROXY_GROUP_IMAGE_PREFIX/scylladb/scylla:latest
alias: scylla
before_script:
- pip install ./backend
- python -u ./backend/ci/setup_database.py
# before_script:
# - pip install ./backend
# - python -u ./backend/bec_atlas/utils/setup_database.py
test:
stage: test
script:
- python -c "from cassandra.cluster import Cluster; cluster = Cluster(['scylla']); session = cluster.connect(); session.set_keyspace('test_bec_atlas');"
- pip install ./backend[dev]
- pytest --skip-docker --random-order ./backend/tests

View File

@ -93,7 +93,8 @@ class ScylladbDatasource:
for account in functional_accounts:
# check if the account already exists in the database
password_hash = get_password_hash(account.pop("password"))
password = account.pop("password")
password_hash = get_password_hash(password)
result = schema.User.objects.filter(email=account["email"])
if result.count() > 0:
continue

View File

@ -6,7 +6,7 @@ from cassandra.cqlengine.models import Model
class User(Model):
email = columns.Text(primary_key=True)
user_id = columns.UUID(default=uuid.uuid4())
user_id = columns.UUID(default=uuid.uuid4)
first_name = columns.Text()
last_name = columns.Text()
groups = columns.Set(columns.Text)

View File

@ -12,10 +12,11 @@ CONFIG = {"redis": {"host": "localhost", "port": 6380}, "scylla": {"hosts": ["lo
class AtlasApp:
API_VERSION = "v1"
def __init__(self):
def __init__(self, config=None):
self.config = config or CONFIG
self.app = FastAPI()
self.prefix = f"/api/{self.API_VERSION}"
self.datasources = DatasourceManager(config=CONFIG)
self.datasources = DatasourceManager(config=self.config)
self.register_event_handler()
self.add_routers()

View File

@ -1,13 +1,14 @@
import sys
import time
from cassandra.cluster import Cluster
from cassandra.cluster import Cluster, Session
SCYLLA_HOST = "scylla"
SCYLLA_KEYSPACE = "test_bec_atlas"
SCYLLA_PORT = 9042
SCYLLA_KEYSPACE = "bec_atlas"
def wait_for_scylladb(scylla_host: str = SCYLLA_HOST):
def wait_for_scylladb(scylla_host: str = SCYLLA_HOST, scylla_port: int = SCYLLA_PORT):
"""
Wait for ScyllaDB to be ready by trying to connect to it.
@ -15,18 +16,21 @@ def wait_for_scylladb(scylla_host: str = SCYLLA_HOST):
scylla_host(str): The ScyllaDB host.
"""
print("Waiting for ScyllaDB to be ready...")
print(f"ScyllaDB host: {scylla_host}")
print(f"ScyllaDB port: {scylla_port}")
while True:
try:
cluster = Cluster([scylla_host])
cluster = Cluster([(scylla_host, scylla_port)])
# cluster = Cluster([scylla_host])
session = cluster.connect()
print("Connected to ScyllaDB")
break
return session
except Exception as e:
print(f"ScyllaDB not ready yet: {e}")
time.sleep(5)
def create_keyspace(scylla_host: str = SCYLLA_HOST, keyspace: str = SCYLLA_KEYSPACE):
def create_keyspace(session: Session, keyspace: str = SCYLLA_KEYSPACE):
"""
Create a new keyspace in ScyllaDB if it does not exist.
@ -36,8 +40,8 @@ def create_keyspace(scylla_host: str = SCYLLA_HOST, keyspace: str = SCYLLA_KEYSP
"""
print(f"Creating keyspace '{keyspace}' if not exists...")
try:
cluster = Cluster([scylla_host])
session = cluster.connect()
# drop the keyspace if it exists
session.execute(f"DROP KEYSPACE IF EXISTS {keyspace}")
session.execute(
f"""
CREATE KEYSPACE IF NOT EXISTS {keyspace}
@ -50,11 +54,10 @@ def create_keyspace(scylla_host: str = SCYLLA_HOST, keyspace: str = SCYLLA_KEYSP
sys.exit(1)
def setup_database():
wait_for_scylladb()
create_keyspace()
def setup_database(host: str = SCYLLA_HOST, port: int = SCYLLA_PORT):
session = wait_for_scylladb(scylla_host=host, scylla_port=port)
create_keyspace(session)
if __name__ == "__main__":
wait_for_scylladb()
create_keyspace()
setup_database()

98
backend/tests/conftest.py Normal file
View File

@ -0,0 +1,98 @@
import contextlib
import os
from typing import Iterator
import pytest
from pytest_docker.plugin import DockerComposeExecutor, Services
def pytest_addoption(parser):
parser.addoption(
"--skip-docker",
action="store_true",
default=False,
help="Skip spinning up docker containers",
)
@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")
@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."""
return "pytest_9070_atlas"
@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

View File

@ -0,0 +1,6 @@
version: '2'
services:
scylla:
image: scylladb/scylla:latest
ports:
- "9070:9042"

View File

@ -0,0 +1,70 @@
import os
import socket
import pytest
from bec_atlas.main import AtlasApp
from bec_atlas.utils.setup_database import setup_database
from fastapi.testclient import TestClient
@pytest.fixture(scope="session")
def scylla_container(docker_ip, docker_services):
host = docker_ip
if os.path.exists("/.dockerenv"):
# if we are running in the CI, scylla was started as 'scylla' service
host = "scylla"
if docker_services is None:
port = 9042
else:
port = docker_services.port_for("scylla", 9042)
setup_database(host=host, port=port)
return host, port
@pytest.fixture(scope="session")
def client(scylla_container):
host, port = scylla_container
config = {"scylla": {"hosts": [(host, port)]}}
with TestClient(AtlasApp(config).app) as _client:
yield _client
@pytest.mark.timeout(60)
def test_login(client):
"""
Test that the login endpoint returns a token.
"""
response = client.post(
"/api/v1/user/login", json={"username": "admin@bec_atlas.ch", "password": "admin"}
)
assert response.status_code == 200
token = response.json()
assert isinstance(token, str)
assert len(token) > 20
@pytest.mark.timeout(60)
def test_login_wrong_password(client):
"""
Test that the login returns a 401 when the password is wrong.
"""
response = client.post(
"/api/v1/user/login", json={"username": "admin@bec_atlas.ch", "password": "wrong_password"}
)
assert response.status_code == 401
assert response.json() == {"detail": "Invalid password"}
@pytest.mark.timeout(60)
def test_login_unknown_user(client):
"""
Test that the login returns a 404 when the user is unknown.
"""
response = client.post(
"/api/v1/user/login",
json={"username": "no_user@bec_atlas.ch", "password": "wrong_password"},
)
assert response.status_code == 404
assert response.json() == {"detail": "User not found"}