|
19 | 19 | from datetime import timezone |
20 | 20 | import os |
21 | 21 | import pickle |
| 22 | +import time |
22 | 23 |
|
23 | 24 | from fastapi.openapi.models import HTTPBearer |
24 | 25 | from google.adk.auth.auth_tool import AuthConfig |
@@ -367,6 +368,38 @@ def test_migrate_from_sqlalchemy_pickle_ignores_non_object_json_fields(): |
367 | 368 | assert event.content is None |
368 | 369 |
|
369 | 370 |
|
| 371 | +def test_migrate_from_sqlalchemy_pickle_reads_naive_timestamp_as_local( |
| 372 | + monkeypatch, |
| 373 | +): |
| 374 | + """Naive v0 event timestamps must migrate as local time, not UTC. |
| 375 | +
|
| 376 | + The v0 schema stored the event ``timestamp`` column as a naive datetime in |
| 377 | + local time (``StorageEvent.from_event`` uses ``datetime.fromtimestamp`` and |
| 378 | + ``to_event`` reads it back with naive ``.timestamp()``). Forcing UTC on that |
| 379 | + naive value shifted every migrated timestamp by the host's UTC offset. Pin a |
| 380 | + fixed non-UTC zone so the round trip is exact regardless of the host. |
| 381 | + """ |
| 382 | + monkeypatch.setenv("TZ", "Asia/Kolkata") |
| 383 | + time.tzset() |
| 384 | + try: |
| 385 | + original_epoch = 1000000.0 |
| 386 | + # Exactly what v0.StorageEvent.from_event persisted: naive local time. |
| 387 | + naive_local_timestamp = datetime.fromtimestamp(original_epoch) |
| 388 | + assert naive_local_timestamp.tzinfo is None |
| 389 | + |
| 390 | + event = mfsp._row_to_event({ |
| 391 | + "id": "event-naive-timestamp", |
| 392 | + "invocation_id": "invoke1", |
| 393 | + "author": "user", |
| 394 | + "actions": EventActions(), |
| 395 | + "timestamp": naive_local_timestamp, |
| 396 | + }) |
| 397 | + |
| 398 | + assert event.timestamp == original_epoch |
| 399 | + finally: |
| 400 | + time.tzset() |
| 401 | + |
| 402 | + |
370 | 403 | def test_migrate_from_sqlalchemy_pickle_blocks_unsafe_actions_pickle( |
371 | 404 | tmp_path, monkeypatch |
372 | 405 | ): |
|
0 commit comments