Skip to content

Commit 224a794

Browse files
FeilongHouEric Hou
andauthored
test: add e2e case for timestamptz on restful (#46254)
Issue: #46253 On branch feature/timestamps Changes to be committed: new file: testcases/test_timestamptz.py Signed-off-by: Eric Hou <[email protected]> Co-authored-by: Eric Hou <[email protected]>
1 parent a86b8b7 commit 224a794

File tree

1 file changed

+106
-0
lines changed

1 file changed

+106
-0
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import time
2+
import random
3+
import pytest
4+
from datetime import datetime, timezone, timedelta
5+
6+
from base.testbase import TestBase
7+
from utils.utils import gen_collection_name
8+
from utils.util_log import test_log as logger
9+
10+
11+
@pytest.mark.L1
12+
class TestTimestamptz(TestBase):
13+
"""
14+
RESTful e2e coverage for timestamptz field:
15+
- create collection with default timestamptz
16+
- describe schema to confirm defaultValue
17+
- insert rows (one missing timestamptz -> use default)
18+
- flush + load
19+
- get entities and validate timestamptz values are preserved/defaulted
20+
"""
21+
22+
def test_timestamptz_default_value_and_get(self):
23+
name = gen_collection_name()
24+
dim = 5
25+
default_time = "2025-01-01T00:00:00Z"
26+
27+
# 1. create collection with timestamptz default value and vector index
28+
payload = {
29+
"collectionName": name,
30+
"schema": {
31+
"autoId": False,
32+
"enableDynamicField": False,
33+
"fields": [
34+
{"fieldName": "id", "dataType": "Int64", "isPrimary": True},
35+
{"fieldName": "time", "dataType": "Timestamptz", "defaultValue": default_time, "nullable": True},
36+
{"fieldName": "color", "dataType": "VarChar", "elementTypeParams": {"max_length": "30"}},
37+
{"fieldName": "vector", "dataType": "FloatVector", "elementTypeParams": {"dim": f"{dim}"}},
38+
],
39+
},
40+
"indexParams": [
41+
{"fieldName": "vector", "indexName": "vector_index", "metricType": "L2"},
42+
],
43+
}
44+
logger.info(f"create collection {name} with payload: {payload}")
45+
rsp = self.collection_client.collection_create(payload)
46+
assert rsp["code"] == 0
47+
self.wait_load_completed(name)
48+
49+
# 2. describe collection and verify defaultValue is returned
50+
desc = self.collection_client.collection_describe(name)
51+
assert desc["code"] == 0
52+
fields = desc.get("data", {}).get("fields", [])
53+
time_field = next(
54+
(f for f in fields if f.get("fieldName") == "time" or f.get("name") == "time"),
55+
None,
56+
)
57+
assert time_field is not None, f"timestamptz field not found in describe: {desc}"
58+
# defaultValue is returned in protobuf-like structure, keep loose check on the string payload
59+
assert "defaultValue" in time_field
60+
61+
# 3. insert rows (one row omits timestamptz to trigger default)
62+
now_utc = datetime.now(timezone.utc)
63+
one_hour_ago = now_utc - timedelta(hours=1)
64+
rows = [
65+
{"id": 1, "time": now_utc.isoformat(), "color": "red_9392", "vector": [random.random() for _ in range(dim)]},
66+
{"id": 3, "time": one_hour_ago.isoformat(), "color": "pink_9298", "vector": [random.random() for _ in range(dim)]},
67+
{"id": 4, "color": "green_0004", "vector": [random.random() for _ in range(dim)]}, # default timestamptz
68+
{"id": 504, "time": one_hour_ago.isoformat(), "color": "blue_0000", "vector": [random.random() for _ in range(dim)]},
69+
]
70+
insert_payload = {"collectionName": name, "data": rows}
71+
insert_rsp = self.vector_client.vector_insert(insert_payload)
72+
assert insert_rsp["code"] == 0
73+
assert insert_rsp["data"]["insertCount"] == len(rows)
74+
75+
# 4. flush and load collection to make data queryable
76+
flush_rsp = self.collection_client.flush(name)
77+
assert flush_rsp["code"] == 0
78+
load_rsp = self.collection_client.collection_load(collection_name=name)
79+
assert load_rsp["code"] == 0
80+
# wait a moment for load state
81+
time.sleep(2)
82+
83+
# 5. get entities by id and validate timestamptz values
84+
get_payload = {
85+
"collectionName": name,
86+
"id": [1, 3, 4],
87+
"outputFields": ["color", "time"],
88+
}
89+
get_rsp = self.vector_client.vector_get(get_payload)
90+
assert get_rsp["code"] == 0
91+
result = {int(item["id"]): item for item in get_rsp["data"]}
92+
assert set(result.keys()) == {1, 3, 4}
93+
94+
def to_dt(ts: str) -> datetime:
95+
return datetime.fromisoformat(ts.replace("Z", "+00:00"))
96+
97+
# default applied for id=4
98+
assert to_dt(result[4]["time"]) == to_dt(default_time)
99+
# provided values preserved (allow small drift if server trims precision)
100+
assert abs((to_dt(result[1]["time"]) - now_utc).total_seconds()) < 1
101+
assert abs((to_dt(result[3]["time"]) - one_hour_ago).total_seconds()) < 1
102+
# colors round-trip
103+
assert result[1]["color"] == "red_9392"
104+
assert result[3]["color"] == "pink_9298"
105+
assert result[4]["color"] == "green_0004"
106+

0 commit comments

Comments
 (0)