feat: unify scope settings and catalog table interactions
Deploy production / deploy (push) Successful in 54s

This commit is contained in:
yuxuanhui
2026-09-12 12:11:53 +08:00
parent 9dee2de039
commit 8981c24fda
29 changed files with 2521 additions and 796 deletions
+33
View File
@@ -0,0 +1,33 @@
"""Preparation sorting applies before pagination and rejects unsupported keys."""
async def test_preparation_sort_pagination(logged_in):
client = logged_in
scope = {"instrument_type": "EQUITY", "region": "USA", "universe": "TOP3000", "delay": 1}
for name in ["sort-C", "sort-A", "sort-B", "unrelated"]:
response = await client.post("/api/v1/data-preparations", json={"name": name, "scope": scope})
assert response.status_code == 201
for direction, expected in [
("asc", ["sort-A", "sort-B", "sort-C"]),
("desc", ["sort-C", "sort-B", "sort-A"]),
]:
names = []
for offset in range(3):
response = await client.get(
"/api/v1/data-preparations",
params={
"q": "sort-",
"sort": "name",
"direction": direction,
"limit": 1,
"offset": offset,
},
)
assert response.status_code == 200
assert response.json()["total"] == 3
names.append(response.json()["items"][0]["name"])
assert names == expected
for params in [{"sort": "field_count"}, {"direction": "sideways"}]:
assert (await client.get("/api/v1/data-preparations", params=params)).status_code == 422
default = (await client.get("/api/v1/data-preparations")).json()
assert default["items"][0]["name"] == "unrelated"
+56
View File
@@ -0,0 +1,56 @@
"""Settings metadata retains platform choices, numeric bounds and scope dependencies."""
import pytest
from fastapi import HTTPException
from app.catalog.research_metadata import ResearchMetadata, setting_rows
from tests.catalog_fake import platform_response
def settings_response():
data = platform_response()
children = data["actions"]["POST"]["settings"]["children"]
children["neutralization"] = {"choices": [{"value": "INDUSTRY"}, {"value": "NONE"}]}
children["decay"] = {"min_value": 0, "max_value": 500}
children["truncation"] = {"choices": [{"value": 0.05}, {"value": 0.08}]}
children["visualization"] = {"choices": [{"value": False}, {"value": True}]}
children["nanHandling"] = {"choices": {"instrumentType": {"EQUITY": {"region": {
region: {"delay": {str(delay): [{"value": "OFF" if delay == 0 else "ON"}] for delay in [0, 1]}}
for region in ["USA", "CHN", "IND"]
}}}}}
return data
def test_metadata_preserves_types_and_resolves_delay():
rows = setting_rows(settings_response())
first = next(r for r in rows if r["region"] == "USA" and r["delay"] == 0)
second = next(r for r in rows if r["region"] == "USA" and r["delay"] == 1)
assert first["fields"]["decay"] == {"minimum": 0, "maximum": 500}
assert first["fields"]["truncation"]["choices"] == [0.05, 0.08]
assert first["fields"]["visualization"]["choices"] == [False, True]
assert first["fields"]["nanHandling"]["choices"] == ["OFF"]
assert second["fields"]["nanHandling"]["choices"] == ["ON"]
assert first["neutralizations"] == ["INDUSTRY", "NONE"]
async def test_refresh_publishes_fields_and_keeps_previous_snapshot_on_invalid_options(app):
class Client:
async def research_setting_options(self):
return settings_response()
async with app.state.sessions.begin() as db:
service = ResearchMetadata(db, Client())
previous = await service.refresh_settings()
assert previous["content"]["items"][0]["fields"]["visualization"]["choices"] == [False, True]
class InvalidClient:
async def research_setting_options(self):
data = settings_response()
data["actions"]["POST"]["settings"]["children"]["nanHandling"]["choices"] = {"unsupported": []}
return data
service.client = InvalidClient()
with pytest.raises(HTTPException) as error:
await service.refresh_settings()
assert error.value.status_code == 502
assert (await service.get("settings"))["content"] == previous["content"]