From c6ac4e0d347179212e3797cd9ed69f2f47261b28 Mon Sep 17 00:00:00 2001 From: Min RK Date: Thu, 1 Dec 2022 08:57:43 +0100 Subject: [PATCH] test active users metrics --- jupyterhub/metrics.py | 5 ++- jupyterhub/tests/test_metrics.py | 66 +++++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 3 deletions(-) diff --git a/jupyterhub/metrics.py b/jupyterhub/metrics.py index 156796f1..eb3cff03 100644 --- a/jupyterhub/metrics.py +++ b/jupyterhub/metrics.py @@ -19,7 +19,7 @@ them manually here. added ``jupyterhub_`` prefix to metric names. """ -from datetime import datetime, timedelta +from datetime import timedelta from enum import Enum from prometheus_client import Gauge, Histogram @@ -28,6 +28,7 @@ from traitlets import Any, Bool, Integer from traitlets.config import LoggingConfigurable from . import orm +from .utils import utcnow REQUEST_DURATION_SECONDS = Histogram( 'jupyterhub_request_duration_seconds', @@ -265,7 +266,7 @@ class PeriodicMetricsCollector(LoggingConfigurable): # All the metrics should be based off a cutoff from a *fixed* point, so we calculate # the fixed point here - and then calculate the individual cutoffs in relation to this # fixed point. - now = datetime.utcnow() + now = utcnow() cutoffs = { ActiveUserPeriods.twenty_four_hours: now - timedelta(hours=24), ActiveUserPeriods.seven_days: now - timedelta(days=7), diff --git a/jupyterhub/tests/test_metrics.py b/jupyterhub/tests/test_metrics.py index 072dba7b..cf6586eb 100644 --- a/jupyterhub/tests/test_metrics.py +++ b/jupyterhub/tests/test_metrics.py @@ -1,11 +1,13 @@ import json +from datetime import timedelta from unittest import mock import pytest from jupyterhub import metrics, orm, roles -from .utils import api_request, get_page +from ..utils import utcnow +from .utils import add_user, api_request, get_page async def test_total_users(app): @@ -73,3 +75,65 @@ async def test_metrics_auth( else: assert r.status_code == 403 assert 'read:metrics' in r.text + + +async def test_active_users(app): + db = app.db + collector = metrics.PeriodicMetricsCollector(db=db) + collector.update() + now = utcnow() + + def collect(): + samples = metrics.ACTIVE_USERS.collect()[0].samples + by_period = { + metrics.ActiveUserPeriods(sample.labels["period"]): sample.value + for sample in samples + } + print(by_period) + return by_period + + baseline = collect() + + for i, offset in enumerate( + [ + None, + # in 24h + timedelta(hours=23, minutes=30), + # in 7d + timedelta(hours=24, minutes=1), + timedelta(days=6, hours=23, minutes=30), + # in 30d + timedelta(days=7, minutes=1), + timedelta(days=29, hours=23, minutes=30), + # not in any + timedelta(days=30, minutes=1), + ] + ): + user = add_user(db, name=f"active-{i}") + if offset: + user.last_activity = now - offset + else: + user.last_activity = None + db.commit() + + # collect before update is called, don't include new users + counts = collect() + for period in metrics.ActiveUserPeriods: + assert period in counts + assert counts[period] == baseline[period] + + # collect after updates, check updated counts + collector.update() + counts = collect() + assert ( + counts[metrics.ActiveUserPeriods.twenty_four_hours] + == baseline[metrics.ActiveUserPeriods.twenty_four_hours] + 1 + ) + assert ( + counts[metrics.ActiveUserPeriods.seven_days] + == baseline[metrics.ActiveUserPeriods.seven_days] + 3 + ) + assert ( + counts[metrics.ActiveUserPeriods.thirty_days] + == baseline[metrics.ActiveUserPeriods.thirty_days] + 5 + )