Fix news risk window query parsing
This commit is contained in:
1 parent
91bc65df75
commit
b801c83c60
3 files changed
+43
-3
No files matched your search
@@ -1,6 +1,7 @@
|
|||||||
"""Authenticated news risk research endpoints."""
|
"""Authenticated news risk research endpoints."""
|
||||||
from datetime import UTC, datetime, timedelta
|
from datetime import UTC, datetime, timedelta
|
||||||
from typing import Annotated, Literal
|
from enum import IntEnum
|
||||||
|
from typing import Annotated
|
||||||
from fastapi import APIRouter, Depends, HTTPException, Query
|
from fastapi import APIRouter, Depends, HTTPException, Query
|
||||||
from pydantic import BaseModel, Field
|
from pydantic import BaseModel, Field
|
||||||
from sqlalchemy import select, func
|
from sqlalchemy import select, func
|
||||||
@@ -17,6 +18,12 @@ router = APIRouter(prefix='/api/risk', tags=['risk'], dependencies=[Depends(requ
|
|||||||
DB = Annotated[AsyncSession, Depends(get_session)]
|
DB = Annotated[AsyncSession, Depends(get_session)]
|
||||||
|
|
||||||
|
|
||||||
|
class RiskWindow(IntEnum):
|
||||||
|
DAY = 24
|
||||||
|
TWO_DAYS = 48
|
||||||
|
WEEK = 168
|
||||||
|
|
||||||
|
|
||||||
class SettingsIn(BaseModel):
|
class SettingsIn(BaseModel):
|
||||||
enabled: bool = False
|
enabled: bool = False
|
||||||
provider_id: int = Field(gt=0)
|
provider_id: int = Field(gt=0)
|
||||||
@@ -72,7 +79,7 @@ async def coverage(session):
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/overview')
|
@router.get('/overview')
|
||||||
async def overview(session: DB, hours: Literal[24, 48, 168] = 24):
|
async def overview(session: DB, hours: RiskWindow = RiskWindow.DAY):
|
||||||
now = datetime.now(UTC); cutoff = now - timedelta(hours=hours)
|
now = datetime.now(UTC); cutoff = now - timedelta(hours=hours)
|
||||||
counts = dict((await session.execute(select(RiskEvent.stars, func.count()).where(
|
counts = dict((await session.execute(select(RiskEvent.stars, func.count()).where(
|
||||||
RiskEvent.updated_at >= cutoff, RiskEvent.result["verification"].astext != "insufficient").group_by(RiskEvent.stars))).all())
|
RiskEvent.updated_at >= cutoff, RiskEvent.result["verification"].astext != "insufficient").group_by(RiskEvent.stars))).all())
|
||||||
@@ -86,7 +93,7 @@ async def overview(session: DB, hours: Literal[24, 48, 168] = 24):
|
|||||||
|
|
||||||
|
|
||||||
@router.get('/events')
|
@router.get('/events')
|
||||||
async def events(session: DB, hours: Literal[24, 48, 168] | None = None, stars: int = Query(1, ge=0, le=5),
|
async def events(session: DB, hours: RiskWindow | None = None, stars: int = Query(1, ge=0, le=5),
|
||||||
q: str = '', start: datetime | None = None, end: datetime | None = None,
|
q: str = '', start: datetime | None = None, end: datetime | None = None,
|
||||||
page: int = Query(1, ge=1), limit: int = Query(20, ge=1, le=100)):
|
page: int = Query(1, ge=1), limit: int = Query(20, ge=1, le=100)):
|
||||||
conditions = [RiskEvent.stars >= stars]
|
conditions = [RiskEvent.stars >= stars]
|
||||||
|
|||||||
@@ -141,3 +141,30 @@ def test_context_budget_includes_unicode_and_tool_schema(risk):
|
|||||||
from types import SimpleNamespace
|
from types import SimpleNamespace
|
||||||
provider = SimpleNamespace(max_tokens=256, context_window=4096)
|
provider = SimpleNamespace(max_tokens=256, context_window=4096)
|
||||||
assert not risk.fits_context(provider, risk.SYSTEM, {'content': '风'*1000}, risk.Labels)
|
assert not risk.fits_context(provider, risk.SYSTEM, {'content': '风'*1000}, risk.Labels)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.mark.parametrize('endpoint', ['overview', 'events'])
|
||||||
|
@pytest.mark.parametrize('hours,status', [(None, 200), ('24', 200), ('48', 200), ('168', 200), ('25', 422), ('0', 422), ('abc', 422)])
|
||||||
|
def test_window_query_parameters_over_http(risk, monkeypatch, endpoint, hours, status):
|
||||||
|
from types import SimpleNamespace
|
||||||
|
from unittest.mock import AsyncMock
|
||||||
|
from fastapi import FastAPI
|
||||||
|
import httpx
|
||||||
|
from alphapulse.api import risk as api
|
||||||
|
|
||||||
|
session = SimpleNamespace(execute=AsyncMock(return_value=SimpleNamespace(all=lambda: [])),
|
||||||
|
scalar=AsyncMock(return_value=None), scalars=AsyncMock(return_value=SimpleNamespace(all=lambda: [])))
|
||||||
|
app = FastAPI()
|
||||||
|
app.include_router(api.router)
|
||||||
|
app.dependency_overrides[api.require_admin] = lambda: None
|
||||||
|
app.dependency_overrides[api.get_session] = lambda: session
|
||||||
|
monkeypatch.setattr(api, 'coverage', AsyncMock(return_value={}))
|
||||||
|
monkeypatch.setattr(api.redis, 'get', AsyncMock(return_value=None))
|
||||||
|
|
||||||
|
async def check():
|
||||||
|
async with httpx.AsyncClient(transport=httpx.ASGITransport(app), base_url='http://test') as client:
|
||||||
|
response = await client.get('/api/risk/'+endpoint, params={} if hours is None else {'hours': hours})
|
||||||
|
assert response.status_code == status, response.text
|
||||||
|
if status == 200:
|
||||||
|
assert response.json()['code'] == 0
|
||||||
|
asyncio.run(check())
|
||||||
@@ -46,6 +46,12 @@ async def main():
|
|||||||
response.raise_for_status()
|
response.raise_for_status()
|
||||||
assert response.json()['code'] == 0, path
|
assert response.json()['code'] == 0, path
|
||||||
assert 'CI synthetic news' in (await client.get('/api/news?q=CI')).text
|
assert 'CI synthetic news' in (await client.get('/api/news?q=CI')).text
|
||||||
|
for endpoint in ('overview', 'events'):
|
||||||
|
for hours in (24, 48, 168):
|
||||||
|
response = await client.get('/api/risk/'+endpoint, params={'hours': hours})
|
||||||
|
response.raise_for_status()
|
||||||
|
assert response.json()['code'] == 0
|
||||||
|
assert (await client.get('/api/risk/'+endpoint, params={'hours': 25})).status_code == 422
|
||||||
item = (await client.get('/api/news?q=CI')).json()['data']['items'][0]
|
item = (await client.get('/api/news?q=CI')).json()['data']['items'][0]
|
||||||
selected = await client.get('/api/news', params=[('ids', item['id']), ('ids', 2147483647)])
|
selected = await client.get('/api/news', params=[('ids', item['id']), ('ids', 2147483647)])
|
||||||
assert selected.status_code == 200 and [n['id'] for n in selected.json()['data']['items']] == [item['id']]
|
assert selected.status_code == 200 and [n['id'] for n in selected.json()['data']['items']] == [item['id']]
|
||||||
|
|||||||
Reference in new issue
Block a user