Skip to content

Commit

Permalink
Merge pull request #1190 from lsst-sqre/tickets/DM-48196
Browse files Browse the repository at this point in the history
DM-48196: Add tests for metrics events
  • Loading branch information
rra authored Dec 18, 2024
2 parents 5f12b52 + e03608b commit 5c5076a
Show file tree
Hide file tree
Showing 10 changed files with 113 additions and 41 deletions.
6 changes: 3 additions & 3 deletions requirements/main.txt
Original file line number Diff line number Diff line change
Expand Up @@ -1520,9 +1520,9 @@ safir==9.1.0 \
--hash=sha256:38718de0d2e5f9623eddc511700a15fb56fee88da7bba0e1210dc831276aa9aa \
--hash=sha256:630b2e59ae87edab25fb2c3d5ccd6ca156c0d525eb543624532bbf4fd3c85536
# via gafaelfawr (pyproject.toml)
safir-logging==9.0.1 \
--hash=sha256:775ab8b2c1a62fe5779f8d4504797df7affb5dbebc70fcad00307cda419d637e \
--hash=sha256:e5dfdfbdafb0a60dd2b8cdd63f8171cb703830cd5d433d60c27028cc9a4044f1
safir-logging==9.1.0 \
--hash=sha256:614cf63ec2f54dec640df8da9ece69d8e5fb43b19f98a943430ceefac7b2f267 \
--hash=sha256:c486c4406ed9ffc41e3ff977ac8938cb89cd57ba7f20436a03f9b336bf0008e0
# via safir
sentry-sdk==2.19.2 \
--hash=sha256:467df6e126ba242d39952375dd816fbee0f217d119bf454a8ce74cf1e7909e8d \
Expand Down
1 change: 1 addition & 0 deletions src/gafaelfawr/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,7 @@ async def maintenance(*, config_path: Path | None) -> None:
events = StateEvents()
await events.initialize(event_manager)
await token_service.gather_state_metrics(events)
await event_manager.aclose()
await engine.dispose()
logger.debug("Finished background maintenance")

Expand Down
1 change: 1 addition & 0 deletions tests/data/config/github.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -46,3 +46,4 @@ quota: {}
metrics:
enabled: false
application: "gafaelfawr"
mock: true
1 change: 1 addition & 0 deletions tests/data/config/oidc-enrollment.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,4 @@ oidc:
metrics:
enabled: false
application: "gafaelfawr"
mock: true
50 changes: 46 additions & 4 deletions tests/handlers/ingress_logging_test.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
"""Tests for logging in the ``/ingress/auth`` route."""
"""Tests for logging and metrics in the ``/ingress/auth`` route."""

from __future__ import annotations

import base64
from typing import Any
from unittest.mock import ANY

import pytest
from httpx import AsyncClient
from safir.datetime import format_datetime_for_logging
from safir.metrics import MockEventPublisher

from gafaelfawr.dependencies.context import context_dependency
from gafaelfawr.factory import Factory
from gafaelfawr.models.token import Token

Expand All @@ -35,7 +38,7 @@ async def test_success(
},
)
assert r.status_code == 200
expected_log = {
expected_log: dict[str, Any] = {
"auth_uri": "/foo",
"event": "Token authorized",
"httpRequest": {
Expand All @@ -61,14 +64,16 @@ async def test_success(
caplog.clear()
r = await client.get(
"/ingress/auth",
params={"scope": "exec:admin"},
params={"scope": "exec:admin", "service": "service-one"},
headers={
"Authorization": f"Basic {basic_b64}",
"X-Original-Uri": "/foo",
"X-Forwarded-For": "192.0.2.1",
},
)
assert r.status_code == 200
url = expected_log["httpRequest"]["requestUrl"]
expected_log["httpRequest"]["requestUrl"] += "&service=service-one"
expected_log["token_source"] = "basic-username"
assert parse_log(caplog) == [expected_log]

Expand All @@ -78,17 +83,32 @@ async def test_success(
caplog.clear()
r = await client.get(
"/ingress/auth",
params={"scope": "exec:admin"},
params={"scope": "exec:admin", "service": "service-two"},
headers={
"Authorization": f"Basic {basic_b64}",
"X-Original-Uri": "/foo",
"X-Forwarded-For": "192.0.2.1",
},
)
assert r.status_code == 200
expected_log["httpRequest"]["requestUrl"] = url + "&service=service-two"
expected_log["token_source"] = "basic-password"
assert parse_log(caplog) == [expected_log]

# Check the logged metrics events.
events = context_dependency._events
assert events
assert isinstance(events.auth_user, MockEventPublisher)
events.auth_user.published.assert_published_all(
[
{"username": token_data.username, "service": None},
{"username": token_data.username, "service": "service-one"},
{"username": token_data.username, "service": "service-two"},
]
)
assert isinstance(events.auth_bot, MockEventPublisher)
events.auth_bot.published.assert_published_all([])


@pytest.mark.asyncio
async def test_authz_failed(
Expand Down Expand Up @@ -395,3 +415,25 @@ async def test_internal(
"user": token_data.username,
},
]


@pytest.mark.asyncio
async def test_bot_metrics(client: AsyncClient, factory: Factory) -> None:
token_data = await create_session_token(
factory, username="bot-something", scopes={"read:all"}
)
r = await client.get(
"/ingress/auth",
params={"scope": "read:all", "service": "service"},
headers={"Authorization": f"Bearer {token_data.token}"},
)
assert r.status_code == 200

events = context_dependency._events
assert events
assert isinstance(events.auth_bot, MockEventPublisher)
events.auth_bot.published.assert_published_all(
[{"username": "bot-something", "service": "service"}]
)
assert isinstance(events.auth_user, MockEventPublisher)
events.auth_user.published.assert_published_all([])
28 changes: 28 additions & 0 deletions tests/handlers/login_github_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,13 @@
import pytest
import respx
from httpx import AsyncClient, Response
from safir.metrics import NOT_NONE, MockEventPublisher
from safir.testing.slack import MockSlackWebhook

from gafaelfawr.config import Config
from gafaelfawr.constants import COOKIE_NAME
from gafaelfawr.dependencies.config import config_dependency
from gafaelfawr.dependencies.context import context_dependency
from gafaelfawr.factory import Factory
from gafaelfawr.models.github import GitHubTeam, GitHubUserInfo
from gafaelfawr.models.state import State
Expand Down Expand Up @@ -200,6 +202,20 @@ async def test_login(
],
}

# Check that the correct metrics events were logged.
events = context_dependency._events
assert events
assert isinstance(events.login_attempt, MockEventPublisher)
events.login_attempt.published.assert_published_all([{}])
assert isinstance(events.login_success, MockEventPublisher)
events.login_success.published.assert_published_all(
[{"username": "githubuser", "elapsed": NOT_NONE}]
)
assert isinstance(events.login_enrollment, MockEventPublisher)
events.login_enrollment.published.assert_published_all([])
assert isinstance(events.login_failure, MockEventPublisher)
events.login_failure.published.assert_published_all([])


@pytest.mark.asyncio
async def test_redirect_header(
Expand Down Expand Up @@ -520,6 +536,18 @@ async def test_no_valid_groups(
# None of these errors should have resulted in Slack alerts.
assert mock_slack.messages == []

# Check that the correct metrics events were logged.
events = context_dependency._events
assert events
assert isinstance(events.login_attempt, MockEventPublisher)
events.login_attempt.published.assert_published_all([{}])
assert isinstance(events.login_success, MockEventPublisher)
events.login_success.published.assert_published_all([])
assert isinstance(events.login_enrollment, MockEventPublisher)
events.login_enrollment.published.assert_published_all([])
assert isinstance(events.login_failure, MockEventPublisher)
events.login_failure.published.assert_published_all([{}])


@pytest.mark.asyncio
async def test_unicode_name(
Expand Down
14 changes: 14 additions & 0 deletions tests/handlers/login_oidc_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,11 @@
import pytest
import respx
from httpx import AsyncClient, ConnectError
from safir.metrics import MockEventPublisher
from safir.testing.slack import MockSlackWebhook

from gafaelfawr.constants import GID_MIN, UID_USER_MIN
from gafaelfawr.dependencies.context import context_dependency
from gafaelfawr.factory import Factory
from gafaelfawr.models.userinfo import Group, UserInfo

Expand Down Expand Up @@ -1009,6 +1011,18 @@ async def test_enrollment_url(
assert r.status_code == 307
assert r.headers["Cache-Control"] == "no-cache, no-store"

# Check that the correct metrics events were logged.
events = context_dependency._events
assert events
assert isinstance(events.login_attempt, MockEventPublisher)
events.login_attempt.published.assert_published_all([{}])
assert isinstance(events.login_success, MockEventPublisher)
events.login_success.published.assert_published_all([])
assert isinstance(events.login_enrollment, MockEventPublisher)
events.login_enrollment.published.assert_published_all([{}])
assert isinstance(events.login_failure, MockEventPublisher)
events.login_failure.published.assert_published_all([])


@pytest.mark.asyncio
async def test_missing_username(
Expand Down
19 changes: 19 additions & 0 deletions tests/services/token_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from cryptography.fernet import Fernet
from pydantic import ValidationError
from safir.datetime import current_datetime
from safir.metrics import MockEventPublisher
from safir.testing.slack import MockSlackWebhook

from gafaelfawr.config import Config
Expand Down Expand Up @@ -1754,6 +1755,17 @@ async def test_audit(factory: Factory) -> None:
@pytest.mark.asyncio
async def test_state_metrics(config: Config, factory: Factory) -> None:
token_service = factory.create_token_service()
event_manager = config.metrics.make_manager()
await event_manager.initialize()
events = StateEvents()
await events.initialize(event_manager)
await token_service.gather_state_metrics(events)

assert isinstance(events.active_user_sessions, MockEventPublisher)
events.active_user_sessions.published.assert_published_all([{"count": 0}])
assert isinstance(events.active_user_tokens, MockEventPublisher)
events.active_user_tokens.published.assert_published_all([{"count": 0}])

await create_session_token(factory, username="someone")
await create_session_token(factory, username="someone")
token_data = await create_session_token(factory, username="other")
Expand All @@ -1765,9 +1777,16 @@ async def test_state_metrics(config: Config, factory: Factory) -> None:
ip_address="127.0.0.1",
)

await event_manager.aclose()
event_manager = config.metrics.make_manager()
await event_manager.initialize()
events = StateEvents()
await events.initialize(event_manager)
await token_service.gather_state_metrics(events)

assert isinstance(events.active_user_sessions, MockEventPublisher)
events.active_user_sessions.published.assert_published_all([{"count": 2}])
assert isinstance(events.active_user_tokens, MockEventPublisher)
events.active_user_tokens.published.assert_published_all([{"count": 1}])

await event_manager.aclose()
Empty file removed tests/storage/__init__.py
Empty file.
34 changes: 0 additions & 34 deletions tests/storage/token_test.py

This file was deleted.

0 comments on commit 5c5076a

Please sign in to comment.