Files
Alexey MilovidovandClaude Opus 4.8 295ced78d1 Enable more Ruff checks (F401/F403/F405/F541/F841) and clean up tests
Follow-up to #105171, which added `ruff` with a deliberately narrow,
bug-class-only rule set and left the unused-name and star-import rules
off pending a dedicated cleanup pass. This enables that second batch in
`pyproject.toml` and fixes every violation across the tree (the bulk in
integration tests):

- `F401` unused-import and `F841` unused-variable: removed, preserving
  any side-effecting right-hand side (e.g. `result = node.query(...)`
  becomes `node.query(...)`). Two `try: import ...` feature probes are
  kept with `# noqa: F401`.
- `F541` f-string-missing-placeholders: dropped the redundant `f` prefix.
- `F403`/`F405` star imports: every `from module import *` is expanded
  into explicit imports of the names actually used, taken from each
  name's real origin, so stdlib/third-party names import from their real
  source rather than through a test "convenience bundle". The now-empty
  `tests/integration/helpers/kafka/common_direct.py` bundle is removed and
  its three remaining consumers import `_VarintBytes`/`KafkaAdminClient`
  directly.

Eliminating the star imports surfaced a real bug the wildcard had masked
from `F821`: `generate_presets.py` in `test_prometheus_protocols` called
an undefined `execute_instant_query_with_http_api`; fixed to the existing
`execute_query_via_http_api`.

No `black`/`isort` reformatting was applied (neither is enforced in the
style check), so the diff is limited to the lint fixes themselves.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
2026-06-18 23:22:25 +00:00

161 lines
5.4 KiB
Python

import logging
from kafka import KafkaAdminClient
import pytest
from helpers.cluster import ClickHouseCluster
import helpers.kafka.common as k
cluster = ClickHouseCluster(__file__)
instance = cluster.add_instance(
"instance",
main_configs=["configs/kafka.xml"],
with_kafka=True,
)
@pytest.fixture(scope="module")
def kafka_cluster():
try:
cluster.start()
kafka_id = instance.cluster.kafka_docker_id
print(("kafka_id is {}".format(kafka_id)))
yield cluster
finally:
cluster.shutdown()
def test_system_kafka_consumers_grant(kafka_cluster, max_retries=20):
admin_client = KafkaAdminClient(
bootstrap_servers="localhost:{}".format(kafka_cluster.kafka_port)
)
k.kafka_create_topic(admin_client, "visible")
k.kafka_create_topic(admin_client, "hidden")
instance.query(
"""
DROP TABLE IF EXISTS kafka_grant_visible;
DROP TABLE IF EXISTS kafka_grant_hidden;
CREATE TABLE kafka_grant_visible (key UInt64, value String)
ENGINE = Kafka
SETTINGS kafka_broker_list = 'kafka1:19092',
kafka_topic_list = 'visible',
kafka_group_name = 'visible',
kafka_format = 'JSONEachRow',
kafka_flush_interval_ms=1000,
kafka_num_consumers = 1;
CREATE TABLE kafka_grant_hidden (key UInt64, value String)
ENGINE = Kafka
SETTINGS kafka_broker_list = 'kafka1:19092',
kafka_topic_list = 'hidden',
kafka_group_name = 'hidden',
kafka_format = 'JSONEachRow',
kafka_flush_interval_ms=1000,
kafka_num_consumers = 1;
"""
)
instance.query_with_retry(
"""
SELECT count(1) FROM system.kafka_consumers WHERE table LIKE 'kafka_grant%'
""",
retry_count=max_retries,
sleep_time=1,
check_callback=lambda res: int(res) == 2,
)
# both kafka_grant_hidden and kafka_grant_visible tables are visible
instance.query(
"""
DROP USER IF EXISTS restricted;
CREATE USER restricted;
GRANT SHOW ON default.kafka_grant_visible TO restricted;
GRANT SELECT ON system.kafka_consumers TO restricted;
"""
)
restricted_result_system_kafka_consumers = instance.query(
"SELECT count(1) FROM system.kafka_consumers WHERE table LIKE 'kafka_grant%'",
user="restricted",
)
assert int(restricted_result_system_kafka_consumers) == 1
# only kafka_grant_visible is visible for user `restricted`
k.kafka_delete_topic(admin_client, "visible")
k.kafka_delete_topic(admin_client, "hidden")
instance.query(
"""
DROP TABLE IF EXISTS kafka_grant_visible;
DROP TABLE IF EXISTS kafka_grant_hidden;
DROP USER IF EXISTS restricted;
"""
)
def test_log_to_exceptions(kafka_cluster, max_retries=20):
non_existent_broker_port = 9876
instance.query(
f"""
DROP TABLE IF EXISTS foo_exceptions;
CREATE TABLE foo_exceptions(a String)
ENGINE = Kafka
SETTINGS kafka_broker_list = 'localhost:{non_existent_broker_port}', kafka_topic_list = 'foo', kafka_group_name = 'foo', kafka_format = 'RawBLOB';
"""
)
instance.query(
"SELECT * FROM foo_exceptions SETTINGS stream_like_engine_allow_direct_select=1"
)
instance.query("SYSTEM FLUSH LOGS")
# `librdkafka` emits several log lines when the broker is unreachable.
# Both flow into `system.kafka_consumers.exceptions` via
# `KafkaConsumer::setExceptionInfo`:
# - per-attempt `Connect to ... failed: Connection refused`
# - periodic `N/M brokers are down` summary
# There is no guarantee that any specific line shows up first or at all
# under unusual broker-thread timing, so accept either form.
thrd_prefix = (
f"[thrd:localhost:{non_existent_broker_port}/bootstrap]:"
)
broker_down_marker = f"{thrd_prefix} 1/1 brokers are down"
connect_refused_marker = (
f"{thrd_prefix} localhost:{non_existent_broker_port}/bootstrap: Connect to"
)
matching_count = instance.query_with_retry(
f"""
SELECT count()
FROM system.kafka_consumers
ARRAY JOIN exceptions
WHERE table = 'foo_exceptions'
AND (startsWith(exceptions.text, '{broker_down_marker}')
OR startsWith(exceptions.text, '{connect_refused_marker}'))
""",
check_callback=lambda res: int(res.strip()) >= 1,
retry_count=max_retries,
sleep_time=1,
)
if int(matching_count.strip()) < 1:
# Surface the full exceptions array on failure to make debugging easier.
all_exceptions = instance.query(
"SELECT exceptions.text FROM system.kafka_consumers ARRAY JOIN exceptions WHERE table = 'foo_exceptions'"
)
logging.debug(f"system.kafka_consumers content: {all_exceptions}")
raise AssertionError(
f"Expected at least one entry in system.kafka_consumers.exceptions starting with "
f"either {broker_down_marker!r} or {connect_refused_marker!r}, "
f"but none was found. Captured exceptions:\n{all_exceptions}"
)
instance.query("DROP TABLE foo_exceptions")
if __name__ == "__main__":
cluster.start()
input("Cluster created, press any key to destroy...")
cluster.shutdown()