diff --git a/web/tests/test_content.py b/web/tests/test_content.py index 015b3ff..4e5cbcf 100644 --- a/web/tests/test_content.py +++ b/web/tests/test_content.py @@ -9,6 +9,8 @@ import importlib import json import sqlite3 from datetime import date, datetime + +from padelnomics.core import utcnow_iso from pathlib import Path import pytest @@ -70,7 +72,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 +938,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), diff --git a/web/tests/test_credits.py b/web/tests/test_credits.py index 054dfbe..5599a7c 100644 --- a/web/tests/test_credits.py +++ b/web/tests/test_credits.py @@ -3,7 +3,7 @@ Tests for the credit system (credits.py). Pure SQL operations against real in-memory SQLite — no mocking needed. """ -from datetime import datetime +from padelnomics.core import utcnow_iso import pytest from padelnomics.credits import ( @@ -24,7 +24,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 +41,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 +154,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 +210,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 +247,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, diff --git a/web/tests/test_feature_flags.py b/web/tests/test_feature_flags.py index 4ab8768..b467c6b 100644 --- a/web/tests/test_feature_flags.py +++ b/web/tests/test_feature_flags.py @@ -7,7 +7,8 @@ Integration tests exercise full request/response flows via Quart test client. """ import sqlite3 -from datetime import datetime + +from padelnomics.core import utcnow_iso from pathlib import Path from unittest.mock import AsyncMock, patch @@ -30,7 +31,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), diff --git a/web/tests/test_seo.py b/web/tests/test_seo.py index 708ee51..64dc5b6 100644 --- a/web/tests/test_seo.py +++ b/web/tests/test_seo.py @@ -1,6 +1,8 @@ """Tests for the SEO metrics module: queries, sync functions, admin routes.""" -from datetime import datetime, timedelta +from datetime import UTC, datetime, timedelta + +from padelnomics.core import utcnow_iso from unittest.mock import AsyncMock, MagicMock, patch import pytest @@ -21,11 +23,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 +74,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 +93,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 +260,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 +288,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( diff --git a/web/tests/test_supplier_webhooks.py b/web/tests/test_supplier_webhooks.py index 2eba57c..2f1d1da 100644 --- a/web/tests/test_supplier_webhooks.py +++ b/web/tests/test_supplier_webhooks.py @@ -5,7 +5,9 @@ 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 padelnomics.core import utcnow_iso from unittest.mock import AsyncMock, patch import pytest @@ -21,7 +23,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 +40,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 +177,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 +204,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 +389,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', '{}', ?)""",