fix(translate): preserve ClickHouse datetime keys

This commit is contained in:
2026-07-23 12:42:56 +03:00
parent cd4b91daa5
commit eeb3a05e42
8 changed files with 167 additions and 81 deletions

View File

@@ -26,6 +26,7 @@ from src.plugins.translate.orchestrator_sql_rows import (
@pytest.fixture
def mock_job():
job = MagicMock()
job.source_key_cols = ["product_id", "store_id"]
job.target_key_cols = ["product_id", "store_id"]
job.target_language_column = "lang"
job.target_source_column = "source_text"
@@ -269,6 +270,21 @@ class TestBuildRows:
assert original["store_id"] is None
assert original["product_id"] == 1
def test_source_keys_map_to_target_keys(self, mock_job):
mock_job.source_key_cols = ["source_id", "source_event"]
mock_job.target_key_cols = ["id", "event_time"]
rec = self._make_record(
source_data={"source_id": 42, "source_event": "2024-01-15 12:00:00"},
)
rows = build_rows(
records=[rec], job=mock_job, effective_target="translated_name",
primary_language="en", context_keys=[],
)
original = [row for row in rows if row["is_original"] == 1][0]
assert original["id"] == 42
assert original["event_time"] == "2024-01-15 12:00:00"
assert "source_id" not in original
# ── Lines 96-106: translation language iteration ──
def test_translation_loop_creates_translated_rows(self, mock_job):

View File

@@ -11,6 +11,7 @@
# @TEST_EDGE: unknown_dialect -> ANSI fallback
# @TEST_EDGE: timestamp_normalization -> Converts Unix timestamps
from datetime import UTC, datetime
from decimal import Decimal
from typing import Any
import pytest
@@ -44,20 +45,20 @@ class TestNormalizeTimestampValue:
def test_unix_seconds(self):
# 2024-01-15 12:00:00 UTC = 1705320000
result = _normalize_timestamp_value(1705320000)
result = _normalize_timestamp_value(1705320000, "Date")
assert result == "2024-01-15"
def test_unix_milliseconds(self):
# 2024-01-15 12:00:00 UTC = 1705320000000 ms
result = _normalize_timestamp_value(1705320000000)
result = _normalize_timestamp_value(1705320000000, "Date")
assert result == "2024-01-15"
def test_string_unix_timestamp(self):
result = _normalize_timestamp_value("1705320000")
result = _normalize_timestamp_value("1705320000", "Date")
assert result == "2024-01-15"
def test_string_unix_millis(self):
result = _normalize_timestamp_value("1705320000000.0")
result = _normalize_timestamp_value("1705320000000.0", "Date")
assert result == "2024-01-15"
def test_out_of_range_small(self):
@@ -74,11 +75,11 @@ class TestNormalizeTimestampValue:
def test_overflow_error(self):
# datetime.fromtimestamp can raise OverflowError for extreme values
# 1e15 is still within range for most systems
result = _normalize_timestamp_value(9999999999) # ~2286
result = _normalize_timestamp_value(9999999999, "Date") # ~2286
assert result is not None
def test_float_milliseconds(self):
result = _normalize_timestamp_value(1705320000000.0)
result = _normalize_timestamp_value(1705320000000.0, "Date")
assert result == "2024-01-15"
def test_empty_string(self):
@@ -160,13 +161,33 @@ class TestEncodeSqlValue:
def test_string_with_quote(self):
assert _encode_sql_value("it's") == "'it''s'"
def test_clickhouse_with_timestamp_string(self):
result = _encode_sql_value("1705320000", dialect="clickhouse")
def test_epoch_zero_requires_explicit_date_type(self):
assert _normalize_timestamp_value(0, "Date") == "1970-01-01"
assert _normalize_timestamp_value(0) is None
def test_clickhouse_timestamp_requires_date_type(self):
result = _encode_sql_value("1705320000", dialect="clickhouse", target_type="Date")
assert result == "'2024-01-15'"
def test_clickhouse_with_timestamp_number(self):
def test_clickhouse_timestamp_like_id_is_preserved(self):
result = _encode_sql_value(1705320000, dialect="clickhouse")
assert result == "'2024-01-15'"
assert result == "1705320000"
def test_clickhouse_datetime_preserves_time(self):
result = _encode_sql_value(1705320000123, dialect="clickhouse", target_type="DateTime")
assert result == "'2024-01-15 12:00:00'"
def test_clickhouse_datetime64_preserves_fraction(self):
result = _encode_sql_value(1705320000123, dialect="clickhouse", target_type="DateTime64(3)")
assert result == "'2024-01-15 12:00:00.123'"
@pytest.mark.parametrize("value", [float("nan"), float("inf"), float("-inf")])
def test_non_finite_float_is_rejected(self, value):
with pytest.raises(ValueError, match="NaN and infinity"):
_encode_sql_value(value, dialect="clickhouse")
def test_decimal_is_numeric(self):
assert _encode_sql_value(Decimal("1.2300"), dialect="clickhouse") == "1.2300"
def test_clickhouse_regular_string(self):
result = _encode_sql_value("hello", dialect="clickhouse")
@@ -181,7 +202,28 @@ class TestEncodeSqlValue:
def test_non_clickhouse_passes_through(self):
result = _encode_sql_value("1705320000", dialect="postgresql")
assert result == "'1705320000'" # Not normalized
assert result == "'1705320000'"
def test_generate_batch_forwards_column_types_to_clickhouse(self):
statements = SQLGenerator.generate_batch(
dialect="clickhouse",
target_schema=None,
target_table="events",
columns=["created_at"],
rows=[{"created_at": 1705320000123}],
upsert_strategy="INSERT",
column_types={"created_at": "DateTime64(3)"},
)
assert "'2024-01-15 12:00:00.123'" in statements[0][0]
def test_values_clause_uses_column_types(self):
result = _build_values_clause(
["event_id", "created_at"],
[{"event_id": 1705320000, "created_at": 1705320000123}],
dialect="clickhouse",
column_types={"created_at": "DateTime64(3)"},
)
assert result == "(1705320000, '2024-01-15 12:00:00.123')"
# ---------------------------------------------------------------------------