test: use real statistic query repositories (#43219)

This commit is contained in:
Asuka Minato authored and GitHub committed 2026-10-02 13:53:39 +00:00
1 parent 1dbf027277
commit f3074ced5b
2 files changed
+78 -59

No files matched your search

@@ -1,6 +1,5 @@
from __future__ import annotations
from collections.abc import Callable
from datetime import UTC, datetime
from decimal import Decimal
from inspect import unwrap
@@ -10,22 +9,15 @@ from unittest.mock import MagicMock
import pytest
from flask import Flask
from pytest_mock import MockerFixture
from sqlalchemy.orm import Session, sessionmaker
from werkzeug.exceptions import BadRequest
from controllers.console.app import statistic as statistic_module
from machinery.context import RequestContext
from models.model import App
from services.app_statistic_query import (
AppStatisticQuery,
AverageResponseTimeStatisticRecord,
AverageSessionInteractionStatisticRecord,
DailyConversationStatisticRecord,
DailyMessageStatisticRecord,
DailyTerminalStatisticRecord,
DailyTokenCostStatisticRecord,
TokensPerSecondStatisticRecord,
UserSatisfactionRateStatisticRecord,
)
from repositories.app_statistic_query_repository import AppStatisticQueryRepository
from services.app_statistic_query import AppStatisticQuery
def _request_context() -> RequestContext:
@@ -43,7 +35,7 @@ def _app_model() -> App:
def _install_dependencies(
monkeypatch: pytest.MonkeyPatch,
statistics: MagicMock,
statistics: AppStatisticQuery,
*,
time_range: tuple[datetime | None, datetime | None] = (None, None),
) -> None:
@@ -80,66 +72,65 @@ def _invoke(
@pytest.mark.parametrize(
("resource_type", "query_call_getter", "record", "expected"),
("resource_type", "query_method", "row", "expected"),
[
pytest.param(
statistic_module.DailyMessageStatistic,
lambda query: query.get_daily_messages,
DailyMessageStatisticRecord(date="2024-01-01", message_count=3),
"get_daily_messages",
{"date": "2024-01-01", "message_count": 3},
{"date": "2024-01-01", "message_count": 3},
id="daily-messages",
),
pytest.param(
statistic_module.DailyConversationStatistic,
lambda query: query.get_daily_conversations,
DailyConversationStatisticRecord(date="2024-01-02", conversation_count=5),
"get_daily_conversations",
{"date": "2024-01-02", "conversation_count": 5},
{"date": "2024-01-02", "conversation_count": 5},
id="daily-conversations",
),
pytest.param(
statistic_module.DailyTerminalsStatistic,
lambda query: query.get_daily_terminals,
DailyTerminalStatisticRecord(date="2024-01-03", terminal_count=7),
"get_daily_terminals",
{"date": "2024-01-03", "terminal_count": 7},
{"date": "2024-01-03", "terminal_count": 7},
id="daily-terminals",
),
pytest.param(
statistic_module.DailyTokenCostStatistic,
lambda query: query.get_daily_token_costs,
DailyTokenCostStatisticRecord(
date="2024-01-04",
token_count=10,
total_price=Decimal("0.25"),
currency="USD",
),
"get_daily_token_costs",
{
"date": "2024-01-04",
"token_count": 10,
"total_price": Decimal("0.25"),
},
{"date": "2024-01-04", "token_count": 10, "total_price": "0.25", "currency": "USD"},
id="daily-token-costs",
),
pytest.param(
statistic_module.AverageSessionInteractionStatistic,
lambda query: query.get_average_session_interactions,
AverageSessionInteractionStatisticRecord(date="2024-01-05", interactions=2.5),
"get_average_session_interactions",
{"date": "2024-01-05", "interactions": Decimal("2.5")},
{"date": "2024-01-05", "interactions": 2.5},
id="average-session-interactions",
),
pytest.param(
statistic_module.UserSatisfactionRateStatistic,
lambda query: query.get_user_satisfaction_rates,
UserSatisfactionRateStatisticRecord(date="2024-01-06", rate=100.0),
"get_user_satisfaction_rates",
{"date": "2024-01-06", "feedback_count": 1, "message_count": 10},
{"date": "2024-01-06", "rate": 100.0},
id="user-satisfaction-rate",
),
pytest.param(
statistic_module.AverageResponseTimeStatistic,
lambda query: query.get_average_response_times,
AverageResponseTimeStatisticRecord(date="2024-01-07", latency=1234.0),
"get_average_response_times",
{"date": "2024-01-07", "latency": 1.234},
{"date": "2024-01-07", "latency": 1234.0},
id="average-response-time",
),
pytest.param(
statistic_module.TokensPerSecondStatistic,
lambda query: query.get_tokens_per_second,
TokensPerSecondStatisticRecord(date="2024-01-08", tps=15.5),
"get_tokens_per_second",
{"date": "2024-01-08", "tokens_per_second": 15.5},
{"date": "2024-01-08", "tps": 15.5},
id="tokens-per-second",
),
@@ -149,13 +140,15 @@ def test_statistic_endpoint_delegates_to_statistic_query(
app: Flask,
monkeypatch: pytest.MonkeyPatch,
resource_type: type,
query_call_getter: Callable[[MagicMock], MagicMock],
record: tuple,
query_method: str,
row: dict[str, object],
mocker: MockerFixture,
sqlite_session_factory: sessionmaker[Session],
expected: dict[str, Any],
) -> None:
statistics = MagicMock(spec=AppStatisticQuery)
query_call = query_call_getter(statistics)
query_call.return_value = [record]
statistics = AppStatisticQueryRepository(session_factory=sqlite_session_factory)
mocker.patch.object(statistics, "_execute", return_value=(row,))
query_call = mocker.spy(statistics, query_method)
_install_dependencies(monkeypatch, statistics)
assert _invoke(app, resource_type) == {"data": [expected]}
@@ -167,15 +160,21 @@ def test_statistic_endpoint_delegates_to_statistic_query(
)
def test_statistic_endpoint_passes_time_range(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
statistics = MagicMock(spec=AppStatisticQuery)
statistics.get_daily_messages.return_value = []
def test_statistic_endpoint_passes_time_range(
app: Flask,
monkeypatch: pytest.MonkeyPatch,
mocker: MockerFixture,
sqlite_session_factory: sessionmaker[Session],
) -> None:
statistics = AppStatisticQueryRepository(session_factory=sqlite_session_factory)
query_call = mocker.spy(statistics, "get_daily_messages")
mocker.patch.object(statistics, "_execute", return_value=())
start_date = datetime(2024, 1, 1, tzinfo=UTC)
end_date = datetime(2024, 1, 2, tzinfo=UTC)
_install_dependencies(monkeypatch, statistics, time_range=(start_date, end_date))
assert _invoke(app, statistic_module.DailyMessageStatistic, start="start", end="end") == {"data": []}
statistics.get_daily_messages.assert_called_once_with(
query_call.assert_called_once_with(
app_id="app-1",
start_date=start_date,
end_date=end_date,
@@ -183,8 +182,14 @@ def test_statistic_endpoint_passes_time_range(app: Flask, monkeypatch: pytest.Mo
)
def test_statistic_endpoint_rejects_invalid_time_range(app: Flask, monkeypatch: pytest.MonkeyPatch) -> None:
statistics = MagicMock(spec=AppStatisticQuery)
def test_statistic_endpoint_rejects_invalid_time_range(
app: Flask,
monkeypatch: pytest.MonkeyPatch,
mocker: MockerFixture,
sqlite_session_factory: sessionmaker[Session],
) -> None:
statistics = AppStatisticQueryRepository(session_factory=sqlite_session_factory)
query_call = mocker.spy(statistics, "get_daily_messages")
_install_dependencies(monkeypatch, statistics)
monkeypatch.setattr(
statistic_module,
@@ -195,4 +200,4 @@ def test_statistic_endpoint_rejects_invalid_time_range(app: Flask, monkeypatch:
with pytest.raises(BadRequest, match="Invalid time range"):
_invoke(app, statistic_module.DailyMessageStatistic)
statistics.get_daily_messages.assert_not_called()
query_call.assert_not_called()
@@ -1,9 +1,11 @@
from datetime import UTC, datetime
from unittest.mock import MagicMock
from pytest_mock import MockerFixture
from sqlalchemy.orm import Session, sessionmaker
from machinery.context import RequestContext
from models.enums import WorkflowRunTriggeredFrom
from repositories.api_workflow_run_repository import APIWorkflowRunRepository
from repositories.sqlalchemy_api_workflow_run_repository import DifyAPISQLAlchemyWorkflowRunRepository
from services.workflow_statistic_query_service import WorkflowStatisticQueryService
@@ -16,12 +18,24 @@ def _request_context() -> RequestContext:
)
def test_workflow_statistic_queries_delegate_to_workflow_run_repository() -> None:
workflow_runs = MagicMock(spec=APIWorkflowRunRepository)
workflow_runs.get_daily_runs_statistics.return_value = [{"date": "2024-01-01", "runs": 2}]
workflow_runs.get_daily_terminals_statistics.return_value = [{"date": "2024-01-01", "terminal_count": 3}]
workflow_runs.get_daily_token_cost_statistics.return_value = [{"date": "2024-01-01", "token_count": 4}]
workflow_runs.get_average_app_interaction_statistics.return_value = [{"date": "2024-01-01", "interactions": 2.5}]
def test_workflow_statistic_queries_delegate_to_workflow_run_repository(
sqlite_session_factory: sessionmaker[Session], mocker: MockerFixture
) -> None:
workflow_runs = DifyAPISQLAlchemyWorkflowRunRepository(session_maker=sqlite_session_factory)
get_daily_runs_statistics = mocker.patch.object(
workflow_runs, "get_daily_runs_statistics", return_value=[{"date": "2024-01-01", "runs": 2}]
)
get_daily_terminals_statistics = mocker.patch.object(
workflow_runs, "get_daily_terminals_statistics", return_value=[{"date": "2024-01-01", "terminal_count": 3}]
)
get_daily_token_cost_statistics = mocker.patch.object(
workflow_runs, "get_daily_token_cost_statistics", return_value=[{"date": "2024-01-01", "token_count": 4}]
)
get_average_app_interaction_statistics = mocker.patch.object(
workflow_runs,
"get_average_app_interaction_statistics",
return_value=[{"date": "2024-01-01", "interactions": 2.5}],
)
service = WorkflowStatisticQueryService(workflow_runs=workflow_runs)
context = _request_context()
start_date = datetime(2024, 1, 1, tzinfo=UTC)
@@ -64,7 +78,7 @@ def test_workflow_statistic_queries_delegate_to_workflow_run_repository() -> Non
"end_date": end_date,
"timezone": "Asia/Shanghai",
}
workflow_runs.get_daily_runs_statistics.assert_called_once_with(**expected_arguments)
workflow_runs.get_daily_terminals_statistics.assert_called_once_with(**expected_arguments)
workflow_runs.get_daily_token_cost_statistics.assert_called_once_with(**expected_arguments)
workflow_runs.get_average_app_interaction_statistics.assert_called_once_with(**expected_arguments)
get_daily_runs_statistics.assert_called_once_with(**expected_arguments)
get_daily_terminals_statistics.assert_called_once_with(**expected_arguments)
get_daily_token_cost_statistics.assert_called_once_with(**expected_arguments)
get_average_app_interaction_statistics.assert_called_once_with(**expected_arguments)