fix: replace datetime.UTC with timezone.utc for Python 3.12+ compatibility
Some checks failed
API Endpoint Tests / test-api-endpoints (push) Successful in 22s
Blockchain Synchronization Verification / sync-verification (push) Successful in 3s
CLI Tests / test-cli (push) Failing after 13s
Cross-Chain Functionality Tests / test-cross-chain-sync (push) Failing after 3s
Cross-Chain Functionality Tests / test-cross-chain-transactions (push) Successful in 3s
Cross-Chain Functionality Tests / test-cross-chain-bridge (push) Has been skipped
Cross-Chain Functionality Tests / test-multi-chain-consensus (push) Failing after 3s
Cross-Chain Functionality Tests / aggregate-results (push) Has been skipped
Cross-Node Transaction Testing / transaction-test (push) Successful in 2s
Deploy to Testnet / deploy-testnet (push) Successful in 1m34s
Documentation Validation / validate-docs (push) Failing after 10s
Documentation Validation / validate-policies-strict (push) Successful in 3s
Multi-Node Stress Testing / stress-test (push) Has been cancelled
Node Failover Simulation / failover-test (push) Has been cancelled
Python Tests / test-python (push) Has been cancelled
Integration Tests / test-service-integration (push) Successful in 2m42s
Multi-Chain Island Architecture Tests / test-multi-chain-island (push) Successful in 3s
Multi-Node Blockchain Health Monitoring / health-check (push) Successful in 5s
P2P Network Verification / p2p-verification (push) Successful in 3s
Package Tests / Python package - aitbc-agent-sdk (push) Failing after 33s
Package Tests / Python package - aitbc-core (push) Successful in 17s
Package Tests / Python package - aitbc-crypto (push) Successful in 11s
Security Scanning / security-scan (push) Has been cancelled
Package Tests / Python package - aitbc-sdk (push) Successful in 13s
Package Tests / JavaScript package - aitbc-sdk-js (push) Successful in 9s
Package Tests / JavaScript package - aitbc-token (push) Successful in 17s
Staking Tests / test-staking-service (push) Failing after 6s
Staking Tests / test-staking-integration (push) Has been skipped
Staking Tests / test-staking-contract (push) Has been skipped
Staking Tests / run-staking-test-runner (push) Has been skipped

This commit is contained in:
aitbc
2026-05-09 12:03:26 +02:00
parent 14449b0758
commit d26e6d3772
152 changed files with 848 additions and 848 deletions

View File

@@ -1,7 +1,7 @@
"""Miner Registry Implementation"""
from typing import List, Optional, Dict, Any
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from dataclasses import dataclass, field
import asyncio
@@ -22,8 +22,8 @@ class MinerInfo:
jobs_completed: int = 0
jobs_failed: int = 0
uptime_percent: float = 100.0
registered_at: datetime = field(default_factory=datetime.now(datetime.UTC))
last_heartbeat: datetime = field(default_factory=datetime.now(datetime.UTC))
registered_at: datetime = field(default_factory=datetime.now(timezone.utc))
last_heartbeat: datetime = field(default_factory=datetime.now(timezone.utc))
gpu_utilization: float = 0.0
memory_used_gb: float = 0.0
@@ -43,7 +43,7 @@ class PoolInfo:
total_hashrate: float = 0.0
jobs_completed_24h: int = 0
earnings_24h: float = 0.0
created_at: datetime = field(default_factory=datetime.now(datetime.UTC))
created_at: datetime = field(default_factory=datetime.now(timezone.utc))
@dataclass
@@ -55,7 +55,7 @@ class JobAssignment:
pool_id: str
model: str
status: str = "assigned"
assigned_at: datetime = field(default_factory=datetime.now(datetime.UTC))
assigned_at: datetime = field(default_factory=datetime.now(timezone.utc))
deadline: Optional[datetime] = None
completed_at: Optional[datetime] = None
@@ -142,7 +142,7 @@ class MinerRegistry:
miner.current_jobs = current_jobs
miner.gpu_utilization = gpu_utilization
miner.memory_used_gb = memory_used_gb
miner.last_heartbeat = datetime.now(datetime.UTC)
miner.last_heartbeat = datetime.now(timezone.utc)
async def update_capabilities(self, miner_id: str, capabilities: List[str]):
"""Update miner capabilities."""
@@ -271,7 +271,7 @@ class MinerRegistry:
if job_id in self._jobs:
job = self._jobs[job_id]
job.status = status
job.completed_at = datetime.now(datetime.UTC)
job.completed_at = datetime.now(timezone.utc)
if miner_id in self._miners:
miner = self._miners[miner_id]
@@ -314,7 +314,7 @@ class MinerRegistry:
# Update job
job.miner_id = new_miner_id
job.status = "assigned"
job.assigned_at = datetime.now(datetime.UTC)
job.assigned_at = datetime.now(timezone.utc)
# Update new miner
if new_miner_id in self._miners:

View File

@@ -1,7 +1,7 @@
"""Health check routes for Pool Hub"""
from fastapi import APIRouter
from datetime import datetime, UTC
from datetime import datetime, timezone
from sqlalchemy import text
router = APIRouter(tags=["health"])
@@ -13,7 +13,7 @@ async def health_check():
return {
"status": "ok",
"service": "pool-hub",
"timestamp": datetime.now(datetime.UTC).isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
@@ -28,7 +28,7 @@ async def readiness_check():
return {
"ready": all_ready,
"checks": checks,
"timestamp": datetime.now(datetime.UTC).isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat(),
}

View File

@@ -2,7 +2,7 @@
from fastapi import APIRouter, HTTPException, Depends, Query
from typing import List, Optional
from datetime import datetime, UTC
from datetime import datetime, timezone
from pydantic import BaseModel
from ..registry import MinerRegistry
@@ -86,7 +86,7 @@ async def assign_job(
job_id=job.job_id,
miner_id=best_miner.miner_id,
pool_id=best_miner.pool_id,
assigned_at=datetime.now(datetime.UTC),
assigned_at=datetime.now(timezone.utc),
deadline=job.deadline
)

View File

@@ -2,7 +2,7 @@
from typing import List, Dict, Any, Optional
from dataclasses import dataclass
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
import math
@@ -74,7 +74,7 @@ class ScoringEngine:
success_rate = 100.0 # New miners start with perfect score
# Heartbeat freshness penalty
heartbeat_age = (datetime.now(datetime.UTC) - miner.last_heartbeat).total_seconds()
heartbeat_age = (datetime.now(timezone.utc) - miner.last_heartbeat).total_seconds()
if heartbeat_age > 300: # 5 minutes
freshness_penalty = min(20, heartbeat_age / 60)
else:
@@ -137,7 +137,7 @@ class ScoringEngine:
weight_total = 0
for record in history:
age_days = (datetime.now(datetime.UTC) - record["timestamp"]).days
age_days = (datetime.now(timezone.utc) - record["timestamp"]).days
weight = math.exp(-age_days / self.DECAY_HALF_LIFE_DAYS)
if record["success"]:
@@ -153,7 +153,7 @@ class ScoringEngine:
def _get_hours_active(self, miner) -> float:
"""Get hours since miner registered."""
delta = datetime.now(datetime.UTC) - miner.registered_at
delta = datetime.now(timezone.utc) - miner.registered_at
return max(1, delta.total_seconds() / 3600)
def _parse_memory(self, memory_str: str) -> float:
@@ -204,7 +204,7 @@ class ScoringEngine:
self._history[miner_id] = []
self._history[miner_id].append({
"timestamp": datetime.now(datetime.UTC),
"timestamp": datetime.now(timezone.utc),
"success": True,
"metrics": metrics or {}
})
@@ -219,7 +219,7 @@ class ScoringEngine:
self._history[miner_id] = []
self._history[miner_id].append({
"timestamp": datetime.now(datetime.UTC),
"timestamp": datetime.now(timezone.utc),
"success": False,
"error": error
})

View File

@@ -3,7 +3,7 @@ SLA and Billing API Endpoints for Pool-Hub
Provides endpoints for SLA metrics, capacity planning, and billing integration.
"""
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from typing import Dict, List, Optional, Any
from decimal import Decimal
@@ -165,7 +165,7 @@ async def get_capacity_snapshots(
):
"""Get capacity planning snapshots"""
try:
cutoff = datetime.now(datetime.UTC) - timedelta(hours=hours)
cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
stmt = (
db.query(CapacitySnapshot)
.filter(CapacitySnapshot.timestamp >= cutoff)
@@ -236,7 +236,7 @@ async def configure_capacity_alerts(
return {
"status": "configured",
"alert_config": alert_config,
"timestamp": datetime.now(datetime.UTC).isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
except Exception as e:
logger.error(f"Error configuring capacity alerts: {e}")
@@ -270,7 +270,7 @@ async def sync_billing_usage(
try:
if request.miner_id:
# Sync specific miner
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=request.hours_back)
result = await billing_integration.sync_miner_usage(
miner_id=request.miner_id, start_date=start_date, end_date=end_date
@@ -350,7 +350,7 @@ async def get_sla_status(db: Session = Depends(get_db)):
"status": status,
"active_violations": len(active_violations),
"recent_metrics_count": len(recent_metrics),
"timestamp": datetime.now(datetime.UTC).isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat(),
}
except Exception as e:
logger.error(f"Error getting SLA status: {e}")

View File

@@ -40,7 +40,7 @@ class Miner(Base):
miner_id: Mapped[str] = mapped_column(String(64), primary_key=True)
api_key_hash: Mapped[str] = mapped_column(String(128), nullable=False)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
last_seen_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
addr: Mapped[str] = mapped_column(String(256))
@@ -78,7 +78,7 @@ class MinerStatus(Base):
uptime_pct: Mapped[Optional[float]] = mapped_column(Float) # SLA metric
last_heartbeat_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
updated_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC), onupdate=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc), onupdate=dt.datetime.now(timezone.utc)
)
miner: Mapped[Miner] = relationship(back_populates="status")
@@ -95,7 +95,7 @@ class MatchRequest(Base):
hints: Mapped[Dict[str, object]] = mapped_column(JSON, default=dict)
top_k: Mapped[int] = mapped_column(Integer, default=1)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
results: Mapped[List["MatchResult"]] = relationship(
@@ -119,7 +119,7 @@ class MatchResult(Base):
price: Mapped[Optional[float]] = mapped_column(Float)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
request: Mapped[MatchRequest] = relationship(back_populates="results")
@@ -140,7 +140,7 @@ class Feedback(Base):
fail_code: Mapped[Optional[str]] = mapped_column(String(64))
tokens_spent: Mapped[Optional[float]] = mapped_column(Float)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
miner: Mapped[Miner] = relationship(back_populates="feedback")
@@ -164,10 +164,10 @@ class ServiceConfig(Base):
capabilities: Mapped[List[str]] = mapped_column(JSON, default=list)
max_concurrent: Mapped[int] = mapped_column(Integer, default=1)
created_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
updated_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC), onupdate=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc), onupdate=dt.datetime.now(timezone.utc)
)
# Add unique constraint for miner_id + service_type
@@ -192,7 +192,7 @@ class SLAMetric(Base):
threshold: Mapped[float] = mapped_column(Float, nullable=False)
is_violation: Mapped[bool] = mapped_column(Boolean, default=False)
timestamp: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
meta_data: Mapped[Dict[str, str]] = mapped_column(JSON, default=dict)
@@ -217,7 +217,7 @@ class SLAViolation(Base):
violation_duration_ms: Mapped[Optional[int]] = mapped_column(Integer)
resolved_at: Mapped[Optional[dt.datetime]] = mapped_column(DateTime(timezone=True))
created_at: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
meta_data: Mapped[Dict[str, str]] = mapped_column(JSON, default=dict)
@@ -241,6 +241,6 @@ class CapacitySnapshot(Base):
recommended_scaling: Mapped[str] = mapped_column(String(32), nullable=False)
scaling_reason: Mapped[str] = mapped_column(Text)
timestamp: Mapped[dt.datetime] = mapped_column(
DateTime(timezone=True), default=dt.datetime.now(datetime.UTC)
DateTime(timezone=True), default=dt.datetime.now(timezone.utc)
)
meta_data: Mapped[Dict[str, Any]] = mapped_column(JSON, default=dict)

View File

@@ -40,7 +40,7 @@ class FeedbackRepository:
latency_ms=latency_ms,
fail_code=fail_code,
tokens_spent=tokens_spent,
created_at=dt.datetime.now(datetime.UTC),
created_at=dt.datetime.now(timezone.utc),
)
self._session.add(feedback)
await self._session.flush()

View File

@@ -34,7 +34,7 @@ class MatchRepository:
requirements=requirements,
hints=hints or {},
top_k=top_k,
created_at=dt.datetime.now(datetime.UTC),
created_at=dt.datetime.now(timezone.utc),
)
self._session.add(request)
await self._session.flush()
@@ -58,7 +58,7 @@ class MatchRepository:
publish: bool = True,
) -> List[MatchResult]:
results: List[MatchResult] = []
created_at = dt.datetime.now(datetime.UTC)
created_at = dt.datetime.now(timezone.utc)
for candidate in candidates:
result = MatchResult(
request_id=request_id,

View File

@@ -69,7 +69,7 @@ class MinerRepository:
miner.capabilities = capabilities
miner.region = region
miner.last_seen_at = dt.datetime.now(datetime.UTC)
miner.last_seen_at = dt.datetime.now(timezone.utc)
await self._session.flush()
await self._sync_miner_to_redis(miner_id)
@@ -97,7 +97,7 @@ class MinerRepository:
"avg_latency_ms": avg_latency_ms,
"temp_c": temp_c,
"mem_free_gb": mem_free_gb,
"updated_at": dt.datetime.now(datetime.UTC),
"updated_at": dt.datetime.now(timezone.utc),
}.items()
if v is not None
}
@@ -107,7 +107,7 @@ class MinerRepository:
miner = await self._session.get(Miner, miner_id)
if miner:
miner.last_seen_at = dt.datetime.now(datetime.UTC)
miner.last_seen_at = dt.datetime.now(timezone.utc)
await self._session.flush()
await self._sync_miner_to_redis(miner_id)
@@ -115,7 +115,7 @@ class MinerRepository:
miner = await self._session.get(Miner, miner_id)
if miner is None:
return
miner.last_seen_at = dt.datetime.now(datetime.UTC)
miner.last_seen_at = dt.datetime.now(timezone.utc)
await self._session.flush()
await self._sync_miner_to_redis(miner_id)

View File

@@ -4,7 +4,7 @@ Integrates pool-hub usage data with coordinator-api's billing system.
"""
import asyncio
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from typing import Dict, List, Optional, Any
@@ -76,7 +76,7 @@ class BillingIntegration:
"unit_price": float(unit_price),
"total_amount": float(total_cost),
"currency": "USD",
"timestamp": datetime.now(datetime.UTC).isoformat(),
"timestamp": datetime.now(timezone.utc).isoformat(),
"metadata": metadata or {},
}
@@ -139,7 +139,7 @@ class BillingIntegration:
) -> Dict[str, Any]:
"""Sync usage data for all miners to coordinator-api billing"""
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=hours_back)
# Get all miners

View File

@@ -4,7 +4,7 @@ Collects and tracks SLA metrics for miners including uptime, response time, job
"""
import asyncio
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from typing import Dict, List, Optional, Any
@@ -57,7 +57,7 @@ class SLACollector:
metric_value=metric_value,
threshold=threshold,
is_violation=is_violation,
timestamp=datetime.now(datetime.UTC),
timestamp=datetime.now(timezone.utc),
meta_data=metadata or {},
)
@@ -90,7 +90,7 @@ class SLACollector:
# Calculate uptime based on last heartbeat
if miner_status.last_heartbeat_at:
time_since_heartbeat = (
datetime.now(datetime.UTC) - miner_status.last_heartbeat_at
datetime.now(timezone.utc) - miner_status.last_heartbeat_at
).total_seconds()
# Consider miner down if no heartbeat for 5 minutes
@@ -153,7 +153,7 @@ class SLACollector:
stmt = (
select(Feedback)
.where(Feedback.miner_id == miner_id)
.where(Feedback.created_at >= datetime.now(datetime.UTC) - timedelta(days=7))
.where(Feedback.created_at >= datetime.now(timezone.utc) - timedelta(days=7))
.order_by(Feedback.created_at.desc())
.limit(100)
)
@@ -206,7 +206,7 @@ class SLACollector:
forecast_capacity=total_miners, # Would be calculated from forecasting
recommended_scaling="stable",
scaling_reason="Capacity within normal range",
timestamp=datetime.now(datetime.UTC),
timestamp=datetime.now(timezone.utc),
meta_data={"method": "real_time_collection"},
)
@@ -266,7 +266,7 @@ class SLACollector:
stmt = (
select(func.count(SLAViolation.id))
.where(SLAViolation.resolved_at.is_(None))
.where(SLAViolation.created_at >= datetime.now(datetime.UTC) - timedelta(hours=1))
.where(SLAViolation.created_at >= datetime.now(timezone.utc) - timedelta(hours=1))
)
results["violations_detected"] = self.db.execute(stmt).scalar() or 0
@@ -282,7 +282,7 @@ class SLACollector:
) -> List[SLAMetric]:
"""Get SLA metrics for a miner or all miners"""
cutoff = datetime.now(datetime.UTC) - timedelta(hours=hours)
cutoff = datetime.now(timezone.utc) - timedelta(hours=hours)
stmt = select(SLAMetric).where(SLAMetric.timestamp >= cutoff)
@@ -349,7 +349,7 @@ class SLACollector:
metric_value=metric_value,
threshold=threshold,
violation_duration_ms=None, # Will be updated when resolved
created_at=datetime.now(datetime.UTC),
created_at=datetime.now(timezone.utc),
meta_data=metadata or {},
)

View File

@@ -4,7 +4,7 @@ Tests for Billing Integration Service
import sys
import pytest
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from unittest.mock import AsyncMock, patch
from sqlalchemy.orm import Session
@@ -84,7 +84,7 @@ async def test_record_usage_with_fallback_pricing(billing_integration: BillingIn
@pytest.mark.asyncio
async def test_sync_miner_usage(billing_integration: BillingIntegration, sample_miner: Miner):
"""Test syncing usage for a specific miner"""
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=24)
with patch("poolhub.services.billing_integration.httpx.AsyncClient") as mock_client:
@@ -123,7 +123,7 @@ async def test_sync_all_miners_usage(billing_integration: BillingIntegration, sa
def test_collect_miner_usage(billing_integration: BillingIntegration, sample_miner: Miner):
"""Test collecting usage data for a miner"""
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=24)
usage_data = billing_integration.db.run_sync(
@@ -169,7 +169,7 @@ async def test_trigger_invoice_generation(billing_integration: BillingIntegratio
mock_client.return_value.__aenter__.return_value.post = AsyncMock(return_value=mock_response)
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(days=30)
result = await billing_integration.trigger_invoice_generation(

View File

@@ -5,7 +5,7 @@ Tests the integration between pool-hub and coordinator-api's billing system.
import sys
import pytest
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from sqlalchemy.orm import Session
@@ -70,7 +70,7 @@ def test_end_to_end_sla_to_billing_workflow(
assert len(metrics) > 0
# Step 3: Collect usage data for billing
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=1)
usage_data = sla_collector.db.run_sync(
lambda sess: billing_integration._collect_miner_usage(
@@ -126,7 +126,7 @@ def test_sla_violation_billing_correlation(
assert len(violations) > 0
# Usage should still be recorded even with violations
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=1)
usage_data = sla_collector.db.run_sync(
lambda sess: billing_integration._collect_miner_usage(
@@ -172,7 +172,7 @@ def test_billing_sync_with_coordinator_api(
"""Test billing sync with coordinator-api (mocked)"""
from unittest.mock import AsyncMock, patch
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(hours=1)
with patch("poolhub.services.billing_integration.httpx.AsyncClient") as mock_client:

View File

@@ -4,7 +4,7 @@ Tests for SLA Collector Service
import sys
import pytest
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from sqlalchemy.orm import Session
@@ -48,7 +48,7 @@ def sample_miner_status(db_session: Session, sample_miner: Miner) -> MinerStatus
avg_latency_ms=150,
temp_c=65,
mem_free_gb=32.0,
last_heartbeat_at=datetime.now(datetime.UTC),
last_heartbeat_at=datetime.now(timezone.utc),
)
db_session.add(status)
db_session.commit()

View File

@@ -4,7 +4,7 @@ Tests for SLA API Endpoints
import sys
import pytest
from datetime import datetime, UTC, timedelta
from datetime import datetime, timezone, timedelta
from decimal import Decimal
from fastapi.testclient import TestClient
from sqlalchemy.orm import Session
@@ -65,7 +65,7 @@ def sample_sla_metric(db_session: Session, sample_miner: Miner) -> SLAMetric:
metric_value=98.5,
threshold=95.0,
is_violation=False,
timestamp=datetime.now(datetime.UTC),
timestamp=datetime.now(timezone.utc),
metadata={"test": "true"},
)
db_session.add(metric)
@@ -191,7 +191,7 @@ def test_record_usage(test_client: TestClient):
def test_generate_invoice(test_client: TestClient):
"""Test triggering invoice generation"""
end_date = datetime.now(datetime.UTC)
end_date = datetime.now(timezone.utc)
start_date = end_date - timedelta(days=30)
request_data = {