fix(generator): правки по двум линиям ревью — сторож состава и честные обещания
- Зачем:
- линия постановки: тест инвариантов обещал ловить дрейф колонок, но
переименование Referer или перенос колонки в другую группу проходили
все проверки; линия стандартов: докстринг говорил о contract-тесте
как о существующем и не нёс следа сверки API через Context7.
- Что:
- тест состава по разделу 1.2 мастер-спеки: группа, имя и тип всех 47
колонок записаны независимо от контракта, поэтому молчаливое
переименование или перестановка краснеют — проверено правкой
Referer → Referrer.
- контракт: contract-тест переведён в будущее время со ссылкой на
спеку; записана сверка записи типов ClickHouse (Context7 и запрос
к узлу стенда 26.3.17.56 — параметры входят в имя типа целиком).
- описание выгрузки самодостаточнее: расшифрованы коды
DeviceCategory, домен LastTrafficSource честно назван неполным,
«идентификатор» сведён к «id» ради одного слова на одну вещь.
- schema_doc: убраны неиспользуемые параметры render и main,
row → table_row; тест строки сверяет свойство, а не форму.
- Проверка:
- make test (249 тестов), make lint;
- make docs, затем git diff --exit-code docs/ — пусто.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -16,6 +16,60 @@ from clickstream_generator.schema import COLUMNS, Column, ColumnGroup
|
||||
# Состав решён мастер-спекой (раздел 1.2) и в этом тикете не переоткрывается.
|
||||
EXPECTED_COLUMN_COUNT = 47
|
||||
|
||||
# Тот же состав, переписанный с мастер-спеки отдельно от контракта: группа,
|
||||
# имя, тип. Дубль намеренный — только независимая запись ловит молчаливое
|
||||
# переименование колонки, подмену типа или перестановку. Правка контракта без
|
||||
# правки спеки краснеет здесь, и это единственный способ узнать о ней вовремя.
|
||||
MASTER_SPEC_COMPOSITION = (
|
||||
(ColumnGroup.IDENTIFIERS, "WatchID", "UInt64"),
|
||||
(ColumnGroup.IDENTIFIERS, "VisitID", "UInt64"),
|
||||
(ColumnGroup.IDENTIFIERS, "ClientID", "UInt64"),
|
||||
(ColumnGroup.IDENTIFIERS, "CounterID", "UInt32"),
|
||||
(ColumnGroup.IDENTIFIERS, "EventDate", "Date"),
|
||||
(ColumnGroup.IDENTIFIERS, "UTCEventTime", "DateTime"),
|
||||
(ColumnGroup.IDENTIFIERS, "ClientTimeZone", "Int16"),
|
||||
(ColumnGroup.IDENTIFIERS, "EventType", "LowCardinality(String)"),
|
||||
(ColumnGroup.IDENTIFIERS, "Sign", "Int8"),
|
||||
(ColumnGroup.PAGE, "URL", "String"),
|
||||
(ColumnGroup.PAGE, "Referer", "String"),
|
||||
(ColumnGroup.PAGE, "Title", "String"),
|
||||
(ColumnGroup.PAGE, "UTMSource", "String"),
|
||||
(ColumnGroup.PAGE, "UTMMedium", "String"),
|
||||
(ColumnGroup.PAGE, "UTMCampaign", "String"),
|
||||
(ColumnGroup.PAGE, "UTMContent", "String"),
|
||||
(ColumnGroup.PAGE, "UTMTerm", "String"),
|
||||
(ColumnGroup.PAGE, "LastTrafficSource", "String"),
|
||||
(ColumnGroup.PAGE, "HasGCLID", "UInt8"),
|
||||
(ColumnGroup.PAGE, "YCLID", "UInt64"),
|
||||
(ColumnGroup.CLIENT, "Browser", "String"),
|
||||
(ColumnGroup.CLIENT, "BrowserMajorVersion", "UInt16"),
|
||||
(ColumnGroup.CLIENT, "BrowserLanguage", "String"),
|
||||
(ColumnGroup.CLIENT, "OperatingSystem", "String"),
|
||||
(ColumnGroup.CLIENT, "OperatingSystemRoot", "String"),
|
||||
(ColumnGroup.CLIENT, "DeviceCategory", "UInt8"),
|
||||
(ColumnGroup.CLIENT, "MobilePhoneModel", "String"),
|
||||
(ColumnGroup.CLIENT, "ScreenWidth", "UInt16"),
|
||||
(ColumnGroup.CLIENT, "ScreenHeight", "UInt16"),
|
||||
(ColumnGroup.CLIENT, "IPAddress", "String"),
|
||||
(ColumnGroup.CLIENT, "RegionCountry", "String"),
|
||||
(ColumnGroup.CLIENT, "RegionCity", "String"),
|
||||
(ColumnGroup.CLIENT, "RegionCountryID", "UInt32"),
|
||||
(ColumnGroup.CLIENT, "RegionCityID", "UInt32"),
|
||||
(ColumnGroup.PARAMS, "GoalsReached", "Array(UInt32)"),
|
||||
(ColumnGroup.PARAMS, "ParsedParamsKey1", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "purchaseID", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "purchaseRevenue", "Array(Float64)"),
|
||||
(ColumnGroup.ECOMMERCE, "purchaseCurrency", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "purchaseCoupon", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "productID", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "productName", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "productCategory", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "productPrice", "Array(Int64)"),
|
||||
(ColumnGroup.ECOMMERCE, "productQuantity", "Array(UInt64)"),
|
||||
(ColumnGroup.ECOMMERCE, "productEventType", "Array(String)"),
|
||||
(ColumnGroup.ECOMMERCE, "ecommerce", "String"),
|
||||
)
|
||||
|
||||
# Соответствие «тип ClickHouse — тип numpy», записанное независимо от
|
||||
# контракта: если пара в контракте разъедется, сойтись они уже не смогут.
|
||||
NUMPY_BY_CLICKHOUSE_TYPE = {
|
||||
@@ -52,6 +106,14 @@ def test_column_count():
|
||||
assert len(COLUMNS) == EXPECTED_COLUMN_COUNT
|
||||
|
||||
|
||||
def test_composition_matches_master_spec():
|
||||
"""Состав, имена, типы и порядок — те же, что в разделе 1.2 мастер-спеки."""
|
||||
composition = tuple(
|
||||
(column.group, column.name, column.clickhouse_type) for column in COLUMNS
|
||||
)
|
||||
assert composition == MASTER_SPEC_COMPOSITION
|
||||
|
||||
|
||||
def test_metrica_names_are_unique():
|
||||
names = [column.name for column in COLUMNS]
|
||||
assert len(set(names)) == len(names)
|
||||
|
||||
@@ -42,14 +42,17 @@ def test_rows_are_numbered_in_contract_order(rendered: str):
|
||||
|
||||
@pytest.mark.parametrize("column", COLUMNS, ids=lambda column: column.name)
|
||||
def test_column_is_described_in_full(column: Column, rendered: str):
|
||||
cells = (
|
||||
f"`{column.name}`",
|
||||
f"`{column.clickhouse_type}`",
|
||||
f"`{column.numpy_dtype}`",
|
||||
f"`{column.dds_name}`",
|
||||
"""Колонку описывает одна строка, и в ней всё, что несёт контракт."""
|
||||
described = (
|
||||
column.name,
|
||||
column.clickhouse_type,
|
||||
column.numpy_dtype,
|
||||
column.dds_name,
|
||||
column.comment,
|
||||
)
|
||||
assert "| " + " | ".join(cells) + " |" in rendered
|
||||
assert any(
|
||||
all(value in line for value in described) for line in rendered.splitlines()
|
||||
)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("group", list(ColumnGroup), ids=lambda group: group.name)
|
||||
|
||||
Reference in New Issue
Block a user