merge: fix datetime.utcnow() deprecation warnings across all files
Replaces 94 occurrences of deprecated datetime.utcnow() and datetime.utcfromtimestamp() across 22 files with utcnow()/utcnow_iso() helpers. Zero DeprecationWarnings remain. All 1201 tests pass. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,7 +8,7 @@ sitemap integration, admin CRUD routes, and path collision prevention.
|
||||
import importlib
|
||||
import json
|
||||
import sqlite3
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
@@ -19,7 +19,7 @@ from padelnomics.content.routes import (
|
||||
bake_scenario_cards,
|
||||
is_reserved_path,
|
||||
)
|
||||
from padelnomics.core import execute, fetch_all, fetch_one, slugify
|
||||
from padelnomics.core import execute, fetch_all, fetch_one, slugify, utcnow_iso
|
||||
from padelnomics.planner.calculator import calc, validate_state
|
||||
|
||||
SCHEMA_PATH = Path(__file__).parent.parent / "src" / "padelnomics" / "migrations" / "schema.sql"
|
||||
@@ -70,7 +70,7 @@ async def _create_published_scenario(slug="test-scenario", city="TestCity", coun
|
||||
async def _create_article(slug="test-article", url_path="/test-article",
|
||||
status="published", published_at=None):
|
||||
"""Insert an article row, return its id."""
|
||||
pub = published_at or datetime.utcnow().strftime("%Y-%m-%d %H:%M:%S")
|
||||
pub = published_at or utcnow_iso()
|
||||
return await execute(
|
||||
"""INSERT INTO articles
|
||||
(url_path, slug, title, meta_description, country, region,
|
||||
@@ -936,8 +936,7 @@ class TestRouteRegistration:
|
||||
@pytest.fixture
|
||||
async def admin_client(app, db):
|
||||
"""Test client with admin user (has admin role)."""
|
||||
from datetime import datetime
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"INSERT INTO users (email, name, created_at) VALUES (?, ?, ?)",
|
||||
("admin@test.com", "Admin", now),
|
||||
|
||||
@@ -3,9 +3,8 @@ Tests for the credit system (credits.py).
|
||||
|
||||
Pure SQL operations against real in-memory SQLite — no mocking needed.
|
||||
"""
|
||||
from datetime import datetime
|
||||
|
||||
import pytest
|
||||
from padelnomics.core import utcnow_iso
|
||||
from padelnomics.credits import (
|
||||
InsufficientCredits,
|
||||
add_credits,
|
||||
@@ -24,7 +23,7 @@ from padelnomics.credits import (
|
||||
@pytest.fixture
|
||||
async def supplier(db):
|
||||
"""Supplier with credit_balance=100, monthly_credits=30, tier=growth."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"""INSERT INTO suppliers
|
||||
(name, slug, country_code, region, category, tier,
|
||||
@@ -41,7 +40,7 @@ async def supplier(db):
|
||||
@pytest.fixture
|
||||
async def lead(db):
|
||||
"""Lead request with heat_score=warm, credit_cost=20."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"""INSERT INTO lead_requests
|
||||
(lead_type, heat_score, credit_cost, status, created_at)
|
||||
@@ -154,7 +153,7 @@ class TestAlreadyUnlocked:
|
||||
assert await already_unlocked(supplier["id"], lead["id"]) is False
|
||||
|
||||
async def test_returns_true_after_unlock(self, db, supplier, lead):
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
await db.execute(
|
||||
"""INSERT INTO lead_forwards (lead_id, supplier_id, credit_cost, created_at)
|
||||
VALUES (?, ?, 20, ?)""",
|
||||
@@ -210,7 +209,7 @@ class TestUnlockLead:
|
||||
|
||||
async def test_raises_insufficient_credits(self, db, lead):
|
||||
"""Supplier with only 5 credits tries to unlock a 20-credit lead."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"""INSERT INTO suppliers
|
||||
(name, slug, country_code, region, category, tier,
|
||||
@@ -247,7 +246,7 @@ class TestMonthlyRefill:
|
||||
|
||||
async def test_noop_when_no_monthly_credits(self, db):
|
||||
"""Supplier with monthly_credits=0 gets no refill."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"""INSERT INTO suppliers
|
||||
(name, slug, country_code, region, category, tier,
|
||||
|
||||
@@ -7,15 +7,13 @@ Integration tests exercise full request/response flows via Quart test client.
|
||||
"""
|
||||
|
||||
import sqlite3
|
||||
from datetime import datetime
|
||||
from pathlib import Path
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from padelnomics import core
|
||||
from padelnomics.core import utcnow_iso
|
||||
from padelnomics.migrations.migrate import migrate
|
||||
|
||||
from padelnomics import core
|
||||
|
||||
# ── Fixtures & helpers ────────────────────────────────────────────
|
||||
|
||||
@@ -30,7 +28,7 @@ def mock_csrf_validation():
|
||||
@pytest.fixture
|
||||
async def admin_client(app, db):
|
||||
"""Test client with an admin-role user session (module-level, follows test_content.py)."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"INSERT INTO users (email, name, created_at) VALUES (?, ?, ?)",
|
||||
("flags_admin@test.com", "Flags Admin", now),
|
||||
@@ -293,8 +291,9 @@ class TestLeadUnlockGate:
|
||||
@pytest.mark.asyncio
|
||||
async def test_route_imports_is_flag_enabled(self):
|
||||
"""suppliers/routes.py imports is_flag_enabled (gate is wired up)."""
|
||||
from padelnomics.suppliers.routes import unlock_lead
|
||||
import inspect
|
||||
|
||||
from padelnomics.suppliers.routes import unlock_lead
|
||||
src = inspect.getsource(unlock_lead)
|
||||
assert "is_flag_enabled" in src
|
||||
assert "lead_unlock" in src
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
"""Tests for the SEO metrics module: queries, sync functions, admin routes."""
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from unittest.mock import AsyncMock, MagicMock, patch
|
||||
|
||||
import pytest
|
||||
from padelnomics.core import utcnow_iso
|
||||
from padelnomics.seo._queries import (
|
||||
cleanup_old_metrics,
|
||||
get_article_scorecard,
|
||||
@@ -21,11 +22,11 @@ from padelnomics import core
|
||||
# ── Fixtures ──────────────────────────────────────────────────
|
||||
|
||||
def _today():
|
||||
return datetime.utcnow().strftime("%Y-%m-%d")
|
||||
return datetime.now(UTC).strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
def _days_ago(n: int) -> str:
|
||||
return (datetime.utcnow() - timedelta(days=n)).strftime("%Y-%m-%d")
|
||||
return (datetime.now(UTC) - timedelta(days=n)).strftime("%Y-%m-%d")
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
@@ -72,7 +73,7 @@ async def seo_data(db):
|
||||
@pytest.fixture
|
||||
async def articles_data(db, seo_data):
|
||||
"""Create articles that match the SEO data URLs."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
pub = _days_ago(10)
|
||||
|
||||
for title, url, tpl, lang in [
|
||||
@@ -91,7 +92,7 @@ async def articles_data(db, seo_data):
|
||||
@pytest.fixture
|
||||
async def admin_client(app, db):
|
||||
"""Authenticated admin client."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"INSERT INTO users (email, name, created_at) VALUES (?, ?, ?)",
|
||||
("admin@test.com", "Admin", now),
|
||||
@@ -258,7 +259,7 @@ class TestSyncStatus:
|
||||
"""Tests for get_sync_status()."""
|
||||
|
||||
async def test_returns_last_sync_per_source(self, db):
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
await db.execute(
|
||||
"""INSERT INTO seo_sync_log (source, status, rows_synced, started_at, completed_at, duration_ms)
|
||||
VALUES ('gsc', 'success', 100, ?, ?, 500)""",
|
||||
@@ -286,7 +287,7 @@ class TestCleanupOldMetrics:
|
||||
"""Tests for cleanup_old_metrics()."""
|
||||
|
||||
async def test_deletes_old_data(self, db):
|
||||
old_date = (datetime.utcnow() - timedelta(days=400)).strftime("%Y-%m-%d")
|
||||
old_date = (datetime.now(UTC) - timedelta(days=400)).strftime("%Y-%m-%d")
|
||||
recent_date = _today()
|
||||
|
||||
await db.execute(
|
||||
|
||||
@@ -8,19 +8,16 @@ supervisor.py lives in src/padelnomics/ (not a uv workspace package), so we
|
||||
add src/ to sys.path before importing.
|
||||
"""
|
||||
|
||||
import sys
|
||||
# Load supervisor.py directly by path — avoids clashing with the web app's
|
||||
# 'padelnomics' namespace (which is the installed web package).
|
||||
import importlib.util as _ilu
|
||||
import textwrap
|
||||
import tomllib
|
||||
from datetime import UTC, datetime, timedelta
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
# Load supervisor.py directly by path — avoids clashing with the web app's
|
||||
# 'padelnomics' namespace (which is the installed web package).
|
||||
import importlib.util as _ilu
|
||||
|
||||
_SUP_PATH = Path(__file__).parent.parent.parent / "src" / "padelnomics" / "supervisor.py"
|
||||
_spec = _ilu.spec_from_file_location("padelnomics_supervisor", _SUP_PATH)
|
||||
sup = _ilu.module_from_spec(_spec)
|
||||
@@ -32,7 +29,6 @@ from padelnomics_extract.proxy import (
|
||||
make_sticky_selector,
|
||||
)
|
||||
|
||||
|
||||
# ── load_workflows ────────────────────────────────────────────────
|
||||
|
||||
|
||||
|
||||
@@ -5,11 +5,12 @@ POST real webhook payloads to /billing/webhook/paddle and verify DB state.
|
||||
Uses the existing client, db, sign_payload from conftest.
|
||||
"""
|
||||
import json
|
||||
from datetime import datetime
|
||||
from datetime import UTC, datetime
|
||||
from unittest.mock import AsyncMock, patch
|
||||
|
||||
import pytest
|
||||
from conftest import sign_payload
|
||||
from padelnomics.core import utcnow_iso
|
||||
|
||||
WEBHOOK_PATH = "/billing/webhook/paddle"
|
||||
SIG_HEADER = "Paddle-Signature"
|
||||
@@ -21,7 +22,7 @@ SIG_HEADER = "Paddle-Signature"
|
||||
@pytest.fixture
|
||||
async def supplier(db):
|
||||
"""Supplier with tier=free, credit_balance=0."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"""INSERT INTO suppliers
|
||||
(name, slug, country_code, region, category, tier,
|
||||
@@ -38,7 +39,7 @@ async def supplier(db):
|
||||
@pytest.fixture
|
||||
async def paddle_products(db):
|
||||
"""Insert paddle_products rows for all keys the handlers need."""
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
products = [
|
||||
("credits_25", "pri_credits25", "Credit Pack 25", 999, "one_time"),
|
||||
("credits_100", "pri_credits100", "Credit Pack 100", 3290, "one_time"),
|
||||
@@ -175,7 +176,7 @@ class TestStickyBoostPurchase:
|
||||
assert boosts[0][1] == "active"
|
||||
# expires_at should be ~7 days from now
|
||||
expires = datetime.fromisoformat(boosts[0][2])
|
||||
assert abs((expires - datetime.utcnow()).days - 7) <= 1
|
||||
assert abs((expires - datetime.now(UTC).replace(tzinfo=None)).days - 7) <= 1
|
||||
|
||||
# Verify sticky_until set on supplier
|
||||
sup = await db.execute_fetchall(
|
||||
@@ -202,7 +203,7 @@ class TestStickyBoostPurchase:
|
||||
assert len(boosts) == 1
|
||||
assert boosts[0][0] == "sticky_month"
|
||||
expires = datetime.fromisoformat(boosts[0][1])
|
||||
assert abs((expires - datetime.utcnow()).days - 30) <= 1
|
||||
assert abs((expires - datetime.now(UTC).replace(tzinfo=None)).days - 30) <= 1
|
||||
|
||||
async def test_sticky_boost_sets_country(self, client, db, supplier, paddle_products):
|
||||
payload = make_transaction_payload(
|
||||
@@ -387,7 +388,7 @@ class TestBusinessPlanPurchase:
|
||||
self, client, db, supplier, paddle_products, test_user,
|
||||
):
|
||||
# Need a scenario for the export
|
||||
now = datetime.utcnow().isoformat()
|
||||
now = utcnow_iso()
|
||||
async with db.execute(
|
||||
"""INSERT INTO scenarios (user_id, name, state_json, created_at)
|
||||
VALUES (?, 'Test Scenario', '{}', ?)""",
|
||||
|
||||
Reference in New Issue
Block a user