deer-flow/backend/tests/test_json_integer_matching.py
wgwfn3 97b26b0704
fix(persistence): match integer metadata without overflowing stored values (#6000)
Co-authored-by: Willem Jiang <willem.jiang@gmail.com>
2026-10-07 14:34:24 +08:00

65 lines
3.0 KiB
Python

"""Execute integer predicates against JSON values, including oversized numbers."""
import json
import os
import pytest
from sqlalchemy import JSON, Column, MetaData, String, Table, select, text
from sqlalchemy.ext.asyncio import create_async_engine
from deerflow.persistence.json_compat import json_match
@pytest.fixture(params=["sqlite", "postgresql"])
async def json_table(request):
if request.param == "postgresql":
url = os.getenv("DEERFLOW_TEST_POSTGRES_URL")
if not url:
pytest.skip("set DEERFLOW_TEST_POSTGRES_URL to exercise real PostgreSQL integer matching")
if url.startswith("postgresql://"):
url = url.replace("postgresql://", "postgresql+asyncpg://", 1)
else:
url = "sqlite+aiosqlite:///:memory:"
engine = create_async_engine(url)
# A connection-local table keeps opt-in PostgreSQL runs isolated from any
# application schema and is discarded even when an assertion fails.
table = Table("json_integer_matching", MetaData(), Column("id", String), Column("data", JSON), prefixes=["TEMPORARY"])
try:
async with engine.connect() as connection:
async with connection.begin():
await connection.run_sync(table.create)
yield connection, table
finally:
await engine.dispose()
@pytest.mark.anyio
@pytest.mark.parametrize("expected", [-(2**63), -(2**63) + 1, -42, -1, 0, 1, 42, 2**63 - 2, 2**63 - 1])
async def test_integer_filter_ignores_unrepresentable_stored_numbers(json_table, expected):
connection, table = json_table
integers = [-(2**63), -(2**63) + 1, -42, -1, 0, 1, 42, 2**63 - 2, 2**63 - 1]
rows = [{"id": str(value), "data": json.dumps({"x": value})} for value in integers]
rows += [
{"id": "below-min", "data": json.dumps({"x": -(2**63) - 1})},
{"id": "above-max", "data": json.dumps({"x": 2**63})},
{"id": "large-negative", "data": json.dumps({"x": -(10**100)})},
{"id": "large-positive", "data": json.dumps({"x": 10**100})},
# PostgreSQL JSON accepts numbers beyond even NUMERIC's precision.
{"id": "beyond-numeric", "data": '{"x": ' + "9" * 131073 + "}"},
{"id": "negative-zero", "data": '{"x": -0}'},
{"id": "float", "data": json.dumps({"x": float(expected)})},
{"id": "exponent", "data": '{"x": 42e0}'},
{"id": "string", "data": json.dumps({"x": str(expected)})},
{"id": "boolean", "data": '{"x": true}'},
{"id": "null", "data": '{"x": null}'},
{"id": "array", "data": '{"x": [42]}'},
{"id": "object", "data": '{"x": {}}'},
{"id": "missing", "data": "{}"},
]
# Insert raw JSON to retain -0 and exponent spellings on both backends.
await connection.execute(text("INSERT INTO json_integer_matching (id, data) VALUES (:id, :data)"), rows)
result = await connection.execute(select(table.c.id).where(json_match(table.c.data, "x", expected)))
expected_ids = {str(expected)} | ({"negative-zero"} if expected == 0 else set())
assert set(result.scalars()) == expected_ids