Skip to content
Merged
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
187 changes: 187 additions & 0 deletions tests/unit/models/config/test_authentication_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,187 @@
"""Unit tests for AuthenticationConfiguration model."""

from pathlib import Path

import pytest

from pydantic import ValidationError

from models.config import (
AuthenticationConfiguration,
Configuration,
JwkConfiguration,
LlamaStackConfiguration,
ServiceConfiguration,
UserDataCollection,
)

from constants import (
AUTH_MOD_NOOP,
AUTH_MOD_K8S,
AUTH_MOD_JWK_TOKEN,
)


def test_authentication_configuration() -> None:
"""Test the AuthenticationConfiguration constructor."""

auth_config = AuthenticationConfiguration(
module=AUTH_MOD_NOOP,
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
)
assert auth_config is not None
assert auth_config.module == AUTH_MOD_NOOP
assert auth_config.skip_tls_verification is False
assert auth_config.k8s_ca_cert_path is None
assert auth_config.k8s_cluster_api is None

# try to retrieve JWK configuration
with pytest.raises(
ValueError,
match="JWK configuration is only available for JWK token authentication module",
):
_ = auth_config.jwk_configuration


def test_authentication_configuration_jwk_token() -> None:
"""Test the AuthenticationConfiguration with JWK token."""

auth_config = AuthenticationConfiguration(
module=AUTH_MOD_JWK_TOKEN,
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
jwk_config=JwkConfiguration(url="http://foo.bar.baz"),
)
assert auth_config is not None
assert auth_config.module == AUTH_MOD_JWK_TOKEN
assert auth_config.skip_tls_verification is False
assert auth_config.k8s_ca_cert_path is None
assert auth_config.k8s_cluster_api is None

# try to retrieve JWK configuration
assert auth_config.jwk_configuration is not None


def test_authentication_configuration_jwk_token_but_insufficient_config() -> None:
"""Test the AuthenticationConfiguration with JWK token."""

with pytest.raises(ValidationError, match="JwkConfiguration"):
AuthenticationConfiguration(
module=AUTH_MOD_JWK_TOKEN,
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
jwk_config=JwkConfiguration(),
)


def test_authentication_configuration_jwk_token_but_not_config() -> None:
"""Test the AuthenticationConfiguration with JWK token."""

with pytest.raises(
ValidationError,
match="Value error, JWK configuration must be specified when using JWK token",
):
AuthenticationConfiguration(
module=AUTH_MOD_JWK_TOKEN,
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
# no JwkConfiguration
)


def test_authentication_configuration_jwk_broken_config() -> None:
"""Test the AuthenticationConfiguration with JWK set, but not configured."""

auth_config = AuthenticationConfiguration(
module=AUTH_MOD_JWK_TOKEN,
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
jwk_config=JwkConfiguration(url="http://foo.bar.baz"),
)
assert auth_config is not None

# emulate broken config
auth_config.jwk_config = None
# try to retrieve JWK configuration

with pytest.raises(ValueError, match="JWK configuration should not be None"):
_ = auth_config.jwk_configuration


def test_authentication_configuration_supported() -> None:
"""Test the AuthenticationConfiguration constructor."""
auth_config = AuthenticationConfiguration(
module=AUTH_MOD_K8S,
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
)
assert auth_config is not None
assert auth_config.module == AUTH_MOD_K8S
assert auth_config.skip_tls_verification is False
assert auth_config.k8s_ca_cert_path is None
assert auth_config.k8s_cluster_api is None


def test_authentication_configuration_module_unsupported() -> None:
"""Test the AuthenticationConfiguration constructor with module as None."""
with pytest.raises(ValidationError, match="Unsupported authentication module"):
AuthenticationConfiguration(
module="non-existing-module",
skip_tls_verification=False,
k8s_ca_cert_path=None,
k8s_cluster_api=None,
)


def test_authentication_configuration_in_config() -> None:
"""Test the authentication configuration in main config."""
cfg = Configuration(
name="test_name",
service=ServiceConfiguration(),
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
),
user_data_collection=UserDataCollection(
feedback_enabled=False, feedback_storage=None
),
mcp_servers=[],
)
assert cfg.authentication is not None
assert cfg.authentication.module == AUTH_MOD_NOOP
assert cfg.authentication.skip_tls_verification is False
assert cfg.authentication.k8s_ca_cert_path is None
assert cfg.authentication.k8s_cluster_api is None

cfg2 = Configuration(
name="test_name",
service=ServiceConfiguration(),
llama_stack=LlamaStackConfiguration(
use_as_library_client=True,
library_client_config_path="tests/configuration/run.yaml",
),
user_data_collection=UserDataCollection(
feedback_enabled=False, feedback_storage=None
),
mcp_servers=[],
authentication=AuthenticationConfiguration(
module=AUTH_MOD_K8S,
skip_tls_verification=True,
k8s_ca_cert_path="tests/configuration/server.crt",
k8s_cluster_api=None,
),
)
assert cfg2.authentication is not None
assert cfg2.authentication.module == AUTH_MOD_K8S
assert cfg2.authentication.skip_tls_verification is True
assert cfg2.authentication.k8s_ca_cert_path == Path(
"tests/configuration/server.crt"
)
assert cfg2.authentication.k8s_cluster_api is None
78 changes: 78 additions & 0 deletions tests/unit/models/config/test_cors.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
"""Unit tests for CORSConfiguration model."""

import pytest

from models.config import CORSConfiguration

Comment on lines +3 to +6
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue

Expect ValidationError from Pydantic model validation (not ValueError)

The validator raises ValueError internally, but Pydantic surfaces a ValidationError. Import and assert against ValidationError.

 import pytest
+from pydantic import ValidationError

 from models.config import CORSConfiguration
@@
-    with pytest.raises(ValueError, match=expected):
+    with pytest.raises(ValidationError, match=expected):
         # allow_credentials can not be true when allow_origins contains '*'
         CORSConfiguration(
             allow_origins=["*"],
             allow_credentials=True,
             allow_methods=["foo_method", "bar_method", "baz_method"],
             allow_headers=["foo_header", "bar_header", "baz_header"],
         )

Also applies to: 63-78

🤖 Prompt for AI Agents
In tests/unit/models/config/test_cors.py around lines 3-6 (and also apply the
same change at lines 63-78), the test currently expects a ValueError but
Pydantic surfaces a pydantic.error_wrappers.ValidationError; update the tests to
import ValidationError from pydantic and change the pytest.raises contexts (or
assert statements) to expect ValidationError instead of ValueError so the test
asserts the correct exception type from model validation.


def test_cors_default_configuration() -> None:
"""Test the CORS configuration."""
cfg = CORSConfiguration()
assert cfg is not None
assert cfg.allow_origins == ["*"]
assert cfg.allow_credentials is False
assert cfg.allow_methods == ["*"]
assert cfg.allow_headers == ["*"]


def test_cors_custom_configuration_v1() -> None:
"""Test the CORS configuration."""
cfg = CORSConfiguration(
allow_origins=["foo_origin", "bar_origin", "baz_origin"],
allow_credentials=False,
allow_methods=["foo_method", "bar_method", "baz_method"],
allow_headers=["foo_header", "bar_header", "baz_header"],
)
assert cfg is not None
assert cfg.allow_origins == ["foo_origin", "bar_origin", "baz_origin"]
assert cfg.allow_credentials is False
assert cfg.allow_methods == ["foo_method", "bar_method", "baz_method"]
assert cfg.allow_headers == ["foo_header", "bar_header", "baz_header"]


def test_cors_custom_configuration_v2() -> None:
"""Test the CORS configuration."""
cfg = CORSConfiguration(
allow_origins=["foo_origin", "bar_origin", "baz_origin"],
allow_credentials=True,
allow_methods=["foo_method", "bar_method", "baz_method"],
allow_headers=["foo_header", "bar_header", "baz_header"],
)
assert cfg is not None
assert cfg.allow_origins == ["foo_origin", "bar_origin", "baz_origin"]
assert cfg.allow_credentials is True
assert cfg.allow_methods == ["foo_method", "bar_method", "baz_method"]
assert cfg.allow_headers == ["foo_header", "bar_header", "baz_header"]


def test_cors_custom_configuration_v3() -> None:
"""Test the CORS configuration."""
cfg = CORSConfiguration(
allow_origins=["*"],
allow_credentials=False,
allow_methods=["foo_method", "bar_method", "baz_method"],
allow_headers=["foo_header", "bar_header", "baz_header"],
)
assert cfg is not None
assert cfg.allow_origins == ["*"]
assert cfg.allow_credentials is False
assert cfg.allow_methods == ["foo_method", "bar_method", "baz_method"]
assert cfg.allow_headers == ["foo_header", "bar_header", "baz_header"]


def test_cors_improper_configuration() -> None:
"""Test the CORS configuration."""
expected = (
"Value error, Invalid CORS configuration: "
+ "allow_credentials can not be set to true when allow origins contains '\\*' wildcard."
+ "Use explicit origins or disable credential."
)

with pytest.raises(ValueError, match=expected):
# allow_credentials can not be true when allow_origins contains '*'
CORSConfiguration(
allow_origins=["*"],
allow_credentials=True,
allow_methods=["foo_method", "bar_method", "baz_method"],
allow_headers=["foo_header", "bar_header", "baz_header"],
)
73 changes: 73 additions & 0 deletions tests/unit/models/config/test_database_configuration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
"""Unit tests for DatabaseConfiguration model."""

from pathlib import Path

import pytest

from pydantic import ValidationError

from models.config import (
PostgreSQLDatabaseConfiguration,
SQLiteDatabaseConfiguration,
DatabaseConfiguration,
)


def test_database_configuration(subtests) -> None:
"""Test the database configuration handling."""
with subtests.test(msg="PostgreSQL"):
d1 = PostgreSQLDatabaseConfiguration(
db="db",
user="user",
password="password",
port=1234,
ca_cert_path=Path("tests/configuration/server.crt"),
)
d = DatabaseConfiguration(postgres=d1)
assert d is not None
assert d.sqlite is None
assert d.postgres is not None
assert d.db_type == "postgres"
assert d.config is d1

with subtests.test(msg="SQLite"):
d1 = SQLiteDatabaseConfiguration(
db_path="/tmp/foo/bar/baz",
)
d = DatabaseConfiguration(sqlite=d1)
assert d is not None
assert d.sqlite is not None
assert d.postgres is None
assert d.db_type == "sqlite"
assert d.config is d1


def test_no_databases_configuration() -> None:
"""Test if no databases configuration is checked."""
d = DatabaseConfiguration()
assert d is not None

# default should be SQLite when nothing is provided
assert d.db_type == "sqlite"

# simulate no DB configuration
d.sqlite = None
d.postgres = None

with pytest.raises(ValueError, match="No database configuration found"):
# access property to call its getter
_ = d.db_type

with pytest.raises(ValueError, match="No database configuration found"):
# access property to call its getter
_ = d.config


def test_two_databases_configuration() -> None:
"""Test if two databases configuration is checked."""
d1 = PostgreSQLDatabaseConfiguration(db="db", user="user", password="password")
d2 = SQLiteDatabaseConfiguration(db_path="foo_bar_baz")
with pytest.raises(
ValidationError, match="Only one database configuration can be provided"
):
DatabaseConfiguration(postgres=d1, sqlite=d2)
Loading