chore: eliminate all deprecation warnings from tests and linter

Warnings fixed:
- datetime.utcnow() → datetime.now(UTC) across 48+ files (src/ + tests/)
- datetime.utcnow (callback ref) → lambda: datetime.now(UTC) in model fields (18 files)
- Pydantic class Config → model_config = ConfigDict(...) (16 files)
- Pydantic .dict() → .model_dump() (8 files)
- ConfigDict(allow_population_by_field_name=True) → validate_by_name=True
- SQLAlchemy declarative_base() import path updated
- FastAPI on_event → lifespan context manager (app.py)
- Import sorting (ruff I001) auto-fixed across all files
- Fixed broken re-export chains that ruff F401 cleanup broke:
  _validate_bcp47: service.py now imports from dictionary_validation directly
  job_to_response: _job_routes.py and test imports from service_utils directly
  fetch_datasource_metadata: restored re-export in service.py
- Added missing TranslateJobService import in _job_routes.py (was deleted by F401)
- Added ConfigDict(protected_namespaces=()) for DashboardDatasetItem schema field
- pytest.ini: replaced deprecated importmode with asyncio_mode

All 440 tests pass with zero deprecation warnings.
This commit is contained in:
2026-05-26 19:18:28 +03:00
parent eda346979b
commit 4205618ee6
182 changed files with 779 additions and 897 deletions

View File

@@ -10,20 +10,13 @@
# @TEST_EDGE: missing_tables -> 422 validation error
# @TEST_EDGE: end_time_before_start_time -> 400 validation error
# @TEST_EDGE: non_existent_event_end -> 404 not found
import json
import os
import uuid
from datetime import datetime, timezone, timedelta
from typing import Any
from unittest.mock import AsyncMock, MagicMock, patch, PropertyMock
from datetime import UTC, datetime, timedelta
import pytest
from fastapi import status
# Patch GitService at module level to prevent /app/storage/repositories error
# This patches the base module BEFORE any imports can reach it
import sys
from unittest.mock import MagicMock
from unittest.mock import AsyncMock, MagicMock
_mock_git_svc_cls = MagicMock()
_mock_git_svc_cls.return_value = MagicMock()
@@ -37,6 +30,7 @@ sys.modules['src.services.git_service'] = MagicMock(GitService=_mock_git_svc_cls
def client():
"""Create a TestClient with all dependencies mocked."""
from fastapi.testclient import TestClient
from src.app import app
return TestClient(app)
@@ -44,10 +38,10 @@ def client():
@pytest.fixture(autouse=True)
def auth_mock(client, mock_db):
"""Create a valid API key with maintenance permissions and mock JWT user."""
from src.core.auth.api_key import generate_api_key
from src.models.api_key import APIKey
from src.dependencies import get_current_user
from src.app import app
from src.core.auth.api_key import generate_api_key
from src.dependencies import get_current_user
from src.models.api_key import APIKey
# Create API key for mutation endpoints
raw, prefix, key_hash = generate_api_key()
@@ -93,11 +87,12 @@ def mock_db():
Creates all tables on the shared Base.metadata.
"""
from sqlalchemy import create_engine
from sqlalchemy.orm import sessionmaker, Session as SASession
from sqlalchemy.orm import sessionmaker
from sqlalchemy.pool import StaticPool
from src.models.mapping import Base
from src.dependencies import get_db
from src.models.maintenance import MaintenanceSettings, DashboardScope
from src.models.maintenance import DashboardScope, MaintenanceSettings
from src.models.mapping import Base
# StaticPool ensures all sessions share the same in-memory connection
engine = create_engine(
@@ -165,8 +160,8 @@ class TestStartEndpoint:
"/api/maintenance/start",
json={
"tables": ["raw.sales"],
"start_time": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
"end_time": (datetime.now(timezone.utc) + timedelta(hours=3)).isoformat(),
"start_time": (datetime.now(UTC) + timedelta(hours=1)).isoformat(),
"end_time": (datetime.now(UTC) + timedelta(hours=3)).isoformat(),
"message": "Test maintenance",
"environment_id": "test-env",
},
@@ -184,7 +179,7 @@ class TestStartEndpoint:
response = client.post(
"/api/maintenance/start",
json={
"start_time": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
"start_time": (datetime.now(UTC) + timedelta(hours=1)).isoformat(),
},
)
assert response.status_code == 422
@@ -197,8 +192,8 @@ class TestStartEndpoint:
"/api/maintenance/start",
json={
"tables": ["raw.sales"],
"start_time": (datetime.now(timezone.utc) + timedelta(hours=3)).isoformat(),
"end_time": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
"start_time": (datetime.now(UTC) + timedelta(hours=3)).isoformat(),
"end_time": (datetime.now(UTC) + timedelta(hours=1)).isoformat(),
"environment_id": "test-env",
},
)
@@ -213,7 +208,7 @@ class TestStartEndpoint:
"/api/maintenance/start",
json={
"tables": tables,
"start_time": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
"start_time": (datetime.now(UTC) + timedelta(hours=1)).isoformat(),
"environment_id": "test-env",
},
)
@@ -227,8 +222,8 @@ class TestStartEndpoint:
payload = {
"tables": ["raw.sales"],
"start_time": (datetime.now(timezone.utc) + timedelta(hours=1)).isoformat(),
"end_time": (datetime.now(timezone.utc) + timedelta(hours=3)).isoformat(),
"start_time": (datetime.now(UTC) + timedelta(hours=1)).isoformat(),
"end_time": (datetime.now(UTC) + timedelta(hours=3)).isoformat(),
"environment_id": "test-env",
}
@@ -266,7 +261,7 @@ class TestEndEndpoint:
event = MaintenanceEvent(
tables=["raw.sales"],
start_time=datetime.now(timezone.utc),
start_time=datetime.now(UTC),
status=MaintenanceEventStatus.ACTIVE,
environment_id="test-env",
)
@@ -315,7 +310,7 @@ class TestEventsEndpoint:
active = MaintenanceEvent(
tables=["raw.active"],
start_time=datetime.now(timezone.utc),
start_time=datetime.now(UTC),
status=MaintenanceEventStatus.ACTIVE,
environment_id="test-env",
)
@@ -323,7 +318,7 @@ class TestEventsEndpoint:
completed = MaintenanceEvent(
tables=["raw.done"],
start_time=datetime.now(timezone.utc),
start_time=datetime.now(UTC),
status=MaintenanceEventStatus.COMPLETED,
environment_id="test-env",
)
@@ -340,13 +335,14 @@ class TestEventsEndpoint:
# #region test_expired_event_auto_ends [C:2] [TYPE Function]
# @BRIEF Event past end_time with ACTIVE status → create_task called with auto-end params.
def test_expired_event_auto_ends(self, client, mock_db, mock_task_manager):
from src.models.maintenance import MaintenanceEvent, MaintenanceEventStatus
from datetime import timedelta
from src.models.maintenance import MaintenanceEvent, MaintenanceEventStatus
event = MaintenanceEvent(
tables=["raw.sales"],
start_time=datetime.now(timezone.utc) - timedelta(hours=2),
end_time=datetime.now(timezone.utc) - timedelta(hours=1),
start_time=datetime.now(UTC) - timedelta(hours=2),
end_time=datetime.now(UTC) - timedelta(hours=1),
message="Expired maintenance",
status=MaintenanceEventStatus.ACTIVE,
environment_id="test-env",
@@ -371,13 +367,14 @@ class TestEventsEndpoint:
# #region test_future_end_time_skipped [C:2] [TYPE Function]
# @BRIEF Event with future end_time → no task created, skipped by < now filter.
def test_future_end_time_skipped(self, client, mock_db, mock_task_manager):
from src.models.maintenance import MaintenanceEvent, MaintenanceEventStatus
from datetime import timedelta
from src.models.maintenance import MaintenanceEvent, MaintenanceEventStatus
event = MaintenanceEvent(
tables=["raw.sales"],
start_time=datetime.now(timezone.utc) + timedelta(hours=1),
end_time=datetime.now(timezone.utc) + timedelta(hours=3),
start_time=datetime.now(UTC) + timedelta(hours=1),
end_time=datetime.now(UTC) + timedelta(hours=3),
message="Future maintenance",
status=MaintenanceEventStatus.ACTIVE,
environment_id="test-env",
@@ -394,12 +391,13 @@ class TestEventsEndpoint:
# #region test_no_end_time_skipped [C:2] [TYPE Function]
# @BRIEF Event with end_time=None → no task created, skipped by isnot(None) filter.
def test_no_end_time_skipped(self, client, mock_db, mock_task_manager):
from src.models.maintenance import MaintenanceEvent, MaintenanceEventStatus
from datetime import timedelta
from src.models.maintenance import MaintenanceEvent, MaintenanceEventStatus
event = MaintenanceEvent(
tables=["raw.sales"],
start_time=datetime.now(timezone.utc) - timedelta(hours=2),
start_time=datetime.now(UTC) - timedelta(hours=2),
end_time=None,
message="No end time",
status=MaintenanceEventStatus.ACTIVE,