2020-06-15 14:52:36 +03:00
|
|
|
from threading import Thread, Lock
|
2020-12-10 12:21:58 +03:00
|
|
|
from typing import Any, Type, Callable, List
|
2021-01-05 11:31:05 +03:00
|
|
|
from unittest.mock import patch
|
2020-03-24 16:21:12 +03:00
|
|
|
|
2020-07-21 13:10:38 +03:00
|
|
|
from pykusto import PyKustoClient, Query
|
|
|
|
# noinspection PyProtectedMember
|
2021-03-24 21:56:25 +03:00
|
|
|
from pykusto._src.client import Database
|
2020-07-21 13:10:38 +03:00
|
|
|
# noinspection PyProtectedMember
|
|
|
|
from pykusto._src.expressions import _StringColumn, _NumberColumn, _AnyTypeColumn, _BooleanColumn
|
|
|
|
# noinspection PyProtectedMember
|
|
|
|
from pykusto._src.type_utils import _KustoType
|
2020-12-10 11:44:23 +03:00
|
|
|
from test.test_base import TestBase, MockKustoClient, RecordedQuery, mock_tables_response, mock_getschema_response, mock_databases_response
|
2020-03-24 16:21:12 +03:00
|
|
|
|
2020-12-10 12:21:58 +03:00
|
|
|
background_query_lock = Lock()
|
|
|
|
|
2020-03-24 16:21:12 +03:00
|
|
|
|
|
|
|
class TestClientFetch(TestBase):
|
2020-12-10 12:21:58 +03:00
|
|
|
query_thread: Thread = None
|
|
|
|
query_results: List = []
|
|
|
|
|
2020-12-10 11:31:10 +03:00
|
|
|
def assertType(self, obj: Any, expected_type: Type):
|
2020-12-10 11:44:23 +03:00
|
|
|
self.assertEqual(type(obj), expected_type)
|
2020-12-10 12:21:58 +03:00
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def query_in_background(query: Callable[[], Any]):
|
|
|
|
with background_query_lock:
|
|
|
|
assert TestClientFetch.query_thread is None
|
|
|
|
TestClientFetch.query_results.clear()
|
|
|
|
TestClientFetch.query_thread = Thread(target=lambda: TestClientFetch.query_results.extend(query()))
|
|
|
|
TestClientFetch.query_thread.start()
|
|
|
|
|
|
|
|
@staticmethod
|
|
|
|
def get_background_query_result():
|
|
|
|
with background_query_lock:
|
|
|
|
assert TestClientFetch.query_thread is not None
|
|
|
|
TestClientFetch.query_thread.join()
|
|
|
|
TestClientFetch.query_thread = None
|
|
|
|
return tuple(TestClientFetch.query_results)
|
|
|
|
|
2020-03-24 16:21:12 +03:00
|
|
|
def test_column_fetch(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(record_metadata=True)
|
|
|
|
table = PyKustoClient(mock_client, fetch_by_default=False)['test_db']['mock_table']
|
2020-03-24 16:21:12 +03:00
|
|
|
table.blocking_refresh()
|
|
|
|
# Fetch query
|
|
|
|
self.assertEqual(
|
2020-06-16 13:18:43 +03:00
|
|
|
[RecordedQuery('test_db', '.show table mock_table | project AttributeName, AttributeType | limit 10000')],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
|
|
|
# Dot notation
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo, _StringColumn)
|
|
|
|
self.assertType(table.bar, _NumberColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
# Bracket notation
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table['foo'], _StringColumn)
|
|
|
|
self.assertType(table['bar'], _NumberColumn)
|
|
|
|
self.assertType(table['baz'], _AnyTypeColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
|
2020-12-09 14:43:28 +03:00
|
|
|
def test_block_until_fetch_is_done(self):
|
2020-12-10 12:11:19 +03:00
|
|
|
mock_client = MockKustoClient(block=True, record_metadata=True)
|
|
|
|
client = PyKustoClient(mock_client)
|
2020-12-10 12:21:58 +03:00
|
|
|
self.query_in_background(client.get_databases_names)
|
2020-12-10 12:11:19 +03:00
|
|
|
mock_client.release()
|
2020-12-09 14:43:28 +03:00
|
|
|
client.wait_for_items()
|
|
|
|
# Make sure the fetch query was indeed called
|
2020-12-10 12:11:19 +03:00
|
|
|
assert not mock_client.blocked()
|
2020-12-10 12:21:58 +03:00
|
|
|
self.assertEqual(self.get_background_query_result(), ('test_db', ))
|
2020-12-09 14:43:28 +03:00
|
|
|
|
2020-12-10 11:21:58 +03:00
|
|
|
def test_dir_before_fetch_is_done(self):
|
2020-12-10 12:11:19 +03:00
|
|
|
mock_client = MockKustoClient(block=True, record_metadata=True)
|
|
|
|
client = PyKustoClient(mock_client)
|
2020-12-10 12:21:58 +03:00
|
|
|
self.query_in_background(lambda: dir(client))
|
2020-12-10 12:11:19 +03:00
|
|
|
# Return the fetch
|
|
|
|
mock_client.release()
|
2020-12-10 11:21:58 +03:00
|
|
|
client.wait_for_items()
|
|
|
|
# Make sure the fetch query was indeed called
|
2020-12-10 12:11:19 +03:00
|
|
|
assert not mock_client.blocked()
|
2020-12-10 12:21:58 +03:00
|
|
|
self.assertIn('test_db', self.get_background_query_result())
|
2020-12-10 11:21:58 +03:00
|
|
|
|
2020-03-24 16:21:12 +03:00
|
|
|
def test_column_fetch_slow(self):
|
2021-01-05 11:31:05 +03:00
|
|
|
mock_client = MockKustoClient(block=True)
|
2020-12-10 12:11:19 +03:00
|
|
|
table = PyKustoClient(mock_client, fetch_by_default=False)['test_db']['mock_table']
|
|
|
|
table.refresh()
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.wait_until_blocked()
|
2020-12-10 12:11:19 +03:00
|
|
|
self.assertType(table['foo'], _AnyTypeColumn)
|
|
|
|
self.assertType(table['bar'], _AnyTypeColumn)
|
|
|
|
self.assertType(table['baz'], _AnyTypeColumn)
|
|
|
|
# Return the fetch
|
|
|
|
mock_client.release()
|
2020-03-29 16:22:42 +03:00
|
|
|
table.wait_for_items()
|
|
|
|
# Make sure the fetch query was indeed called
|
2020-12-10 12:11:19 +03:00
|
|
|
assert not mock_client.blocked()
|
2020-03-29 16:22:42 +03:00
|
|
|
|
2021-01-05 11:31:05 +03:00
|
|
|
@patch("pykusto._src.item_fetcher._DEFAULT_GET_ITEM_TIMEOUT_SECONDS", 0.0001)
|
|
|
|
def test_table_fetch_slower_than_timeout(self):
|
|
|
|
mock_client = MockKustoClient(block=True)
|
|
|
|
try:
|
2021-01-26 10:55:10 +03:00
|
|
|
PyKustoClient(mock_client)['test_db']['mock_table']
|
2021-01-05 11:31:05 +03:00
|
|
|
finally:
|
|
|
|
# # Return the fetch
|
|
|
|
mock_client.release()
|
|
|
|
|
2020-03-29 16:22:42 +03:00
|
|
|
def test_query_before_fetch_returned(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(block=True, record_metadata=True)
|
|
|
|
table = PyKustoClient(mock_client, fetch_by_default=False)['test_db']['mock_table']
|
|
|
|
table.refresh()
|
|
|
|
mock_client.wait_until_blocked()
|
2021-01-26 10:55:10 +03:00
|
|
|
mock_client.do_not_block_next_requests()
|
2020-12-10 14:37:58 +03:00
|
|
|
self.query_in_background(Query(table).take(5).execute)
|
|
|
|
# Return the fetch
|
|
|
|
mock_client.release()
|
2020-03-29 16:22:42 +03:00
|
|
|
table.wait_for_items()
|
2020-12-10 12:21:58 +03:00
|
|
|
self.get_background_query_result()
|
2020-03-29 16:22:42 +03:00
|
|
|
# Make sure the fetch query was indeed called
|
2020-12-10 14:37:58 +03:00
|
|
|
assert not mock_client.blocked()
|
2020-04-21 13:22:03 +03:00
|
|
|
# Before the fix the order of returned query was reversed
|
2020-03-29 16:22:42 +03:00
|
|
|
self.assertEqual(
|
|
|
|
[
|
2020-06-16 13:18:43 +03:00
|
|
|
RecordedQuery('test_db', '.show table mock_table | project AttributeName, AttributeType | limit 10000'),
|
|
|
|
RecordedQuery('test_db', 'mock_table | take 5'),
|
2020-03-29 16:22:42 +03:00
|
|
|
],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-29 16:22:42 +03:00
|
|
|
)
|
2020-03-24 16:21:12 +03:00
|
|
|
|
|
|
|
def test_table_fetch(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(record_metadata=True)
|
|
|
|
db = PyKustoClient(mock_client, fetch_by_default=False)['test_db']
|
2020-03-24 16:21:12 +03:00
|
|
|
db.blocking_refresh()
|
|
|
|
self.assertEqual(
|
|
|
|
[RecordedQuery('test_db', '.show database schema | project TableName, ColumnName, ColumnType | limit 10000')],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
2020-06-16 13:18:43 +03:00
|
|
|
table = db.mock_table
|
2020-03-24 16:21:12 +03:00
|
|
|
# Table columns
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo, _StringColumn)
|
|
|
|
self.assertType(table.bar, _NumberColumn)
|
|
|
|
self.assertType(table['baz'], _AnyTypeColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
# Bracket notation
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(db['other_table']['foo'], _AnyTypeColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
# Dot notation error
|
|
|
|
self.assertRaises(
|
|
|
|
AttributeError("PyKustoClient(test_cluster.kusto.windows.net).Database(test_db) has no attribute 'test_table_1'"),
|
|
|
|
lambda: db.test_table_1
|
|
|
|
)
|
|
|
|
|
|
|
|
def test_two_tables_fetch(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(record_metadata=True)
|
|
|
|
db = PyKustoClient(mock_client, fetch_by_default=False)['test_db']
|
2020-03-24 16:21:12 +03:00
|
|
|
db.blocking_refresh()
|
|
|
|
self.assertEqual(
|
|
|
|
[RecordedQuery('test_db', '.show database schema | project TableName, ColumnName, ColumnType | limit 10000')],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
|
|
|
# Table columns
|
2020-12-10 11:44:23 +03:00
|
|
|
self.assertType(db.mock_table.foo, _StringColumn)
|
|
|
|
self.assertType(db.mock_table.bar, _NumberColumn)
|
|
|
|
self.assertType(db.mock_table_2['baz'], _BooleanColumn)
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(db['other_table']['foo'], _AnyTypeColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
# Union
|
2020-12-10 11:44:23 +03:00
|
|
|
table = db.get_table('mock_table', 'mock_table_2')
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo, _StringColumn)
|
|
|
|
self.assertType(table.bar, _NumberColumn)
|
|
|
|
self.assertType(table.baz, _BooleanColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
# Wildcard
|
2020-12-10 11:44:23 +03:00
|
|
|
table = db.get_table('mock_*')
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo, _StringColumn)
|
|
|
|
self.assertType(table.bar, _NumberColumn)
|
|
|
|
self.assertType(table.baz, _BooleanColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
|
|
|
|
def test_union_column_name_conflict(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(
|
2020-03-24 16:21:12 +03:00
|
|
|
tables_response=mock_tables_response([
|
2020-07-01 14:30:54 +03:00
|
|
|
('test_table_1', [('foo', _KustoType.STRING), ('bar', _KustoType.INT)]),
|
|
|
|
('test_table_2', [('foo', _KustoType.BOOL)])
|
2020-03-24 16:21:12 +03:00
|
|
|
]),
|
|
|
|
getschema_response=mock_getschema_response([
|
2020-07-01 14:30:54 +03:00
|
|
|
('foo_string', _KustoType.STRING), ('bar', _KustoType.INT), ('foo_bool', _KustoType.BOOL)
|
2020-03-24 16:21:12 +03:00
|
|
|
]),
|
|
|
|
record_metadata=True,
|
|
|
|
)
|
2020-12-10 14:37:58 +03:00
|
|
|
db = PyKustoClient(mock_client, fetch_by_default=False)['test_db']
|
2020-03-24 16:21:12 +03:00
|
|
|
db.blocking_refresh()
|
|
|
|
table = db.get_table('test_table_*')
|
|
|
|
table.blocking_refresh() # To trigger name conflict resolution
|
|
|
|
self.assertEqual(
|
|
|
|
[
|
|
|
|
# First trying the usual fetch
|
|
|
|
RecordedQuery('test_db', '.show database schema | project TableName, ColumnName, ColumnType | limit 10000'),
|
|
|
|
# Fallback for name conflict resolution
|
|
|
|
RecordedQuery('test_db', 'union test_table_* | getschema | project ColumnName, DataType | limit 10000')
|
|
|
|
],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo_string, _StringColumn)
|
|
|
|
self.assertType(table.bar, _NumberColumn)
|
|
|
|
self.assertType(table.foo_bool, _BooleanColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
|
|
|
|
def test_union_wildcard_one_table(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(record_metadata=True)
|
|
|
|
db = PyKustoClient(mock_client, fetch_by_default=False)['test_db']
|
2020-03-24 16:21:12 +03:00
|
|
|
db.blocking_refresh()
|
|
|
|
self.assertEqual(
|
|
|
|
[RecordedQuery('test_db', '.show database schema | project TableName, ColumnName, ColumnType | limit 10000')],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
2020-12-10 11:44:23 +03:00
|
|
|
table = db.get_table('mock_table_*')
|
|
|
|
self.assertType(table.foo, _AnyTypeColumn)
|
|
|
|
self.assertType(table.bar, _AnyTypeColumn)
|
|
|
|
self.assertType(table['baz'], _BooleanColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
|
|
|
|
def test_database_fetch(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(record_metadata=True)
|
|
|
|
client = PyKustoClient(mock_client)
|
2020-03-24 16:21:12 +03:00
|
|
|
client.wait_for_items()
|
|
|
|
self.assertEqual(
|
|
|
|
[RecordedQuery('', '.show databases schema | project DatabaseName, TableName, ColumnName, ColumnType | limit 100000')],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
|
|
|
# Table columns
|
2020-06-16 13:18:43 +03:00
|
|
|
table = client.test_db.mock_table
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo, _StringColumn)
|
|
|
|
self.assertType(table.bar, _NumberColumn)
|
|
|
|
self.assertType(table['baz'], _AnyTypeColumn)
|
|
|
|
self.assertType(client.test_db['other_table']['foo'], _AnyTypeColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
# Various utility methods
|
|
|
|
db = client.get_database('test_db')
|
2021-03-24 21:56:25 +03:00
|
|
|
self.assertType(db, Database)
|
2020-03-24 16:21:12 +03:00
|
|
|
self.assertEqual('test_db', db.get_name())
|
|
|
|
self.assertEqual(('test_db',), tuple(client.get_databases_names()))
|
2020-06-16 13:18:43 +03:00
|
|
|
self.assertEqual(('mock_table', 'other_table'), tuple(client.test_db.get_table_names()))
|
|
|
|
self.assertEqual(('foo', 'bar', 'baz'), tuple(client.test_db.mock_table.get_columns_names()))
|
|
|
|
self.assertTrue({'foo', 'bar'} < set(dir(client.test_db.mock_table)))
|
|
|
|
self.assertEqual('PyKustoClient(test_cluster.kusto.windows.net).Database(test_db).Table(mock_table)', repr(client.test_db.mock_table))
|
2020-03-24 16:21:12 +03:00
|
|
|
|
2020-04-26 18:11:29 +03:00
|
|
|
def test_autocomplete_with_dot(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(
|
2020-07-01 14:30:54 +03:00
|
|
|
databases_response=mock_databases_response([('test_db', [('mock_table', [('foo', _KustoType.STRING), ('bar.baz', _KustoType.INT)])])]),
|
2020-04-26 18:11:29 +03:00
|
|
|
)
|
2020-12-10 14:37:58 +03:00
|
|
|
client = PyKustoClient(mock_client)
|
2020-04-26 18:11:29 +03:00
|
|
|
client.wait_for_items()
|
|
|
|
# Table columns
|
2020-06-16 13:18:43 +03:00
|
|
|
table = client.test_db.mock_table
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(table.foo, _StringColumn)
|
|
|
|
self.assertType(table.bar, _AnyTypeColumn)
|
|
|
|
self.assertType(table['bar.baz'], _NumberColumn)
|
2020-06-16 13:18:43 +03:00
|
|
|
autocomplete_list = set(dir(client.test_db.mock_table))
|
2020-04-26 18:11:29 +03:00
|
|
|
self.assertIn('foo', autocomplete_list)
|
|
|
|
self.assertNotIn('bar.baz', autocomplete_list)
|
|
|
|
|
2021-01-26 10:55:10 +03:00
|
|
|
def test_exception_from_autocomplete(self):
|
|
|
|
mock_client = MockKustoClient(databases_response=self.raise_mock_exception)
|
|
|
|
client = PyKustoClient(mock_client, fetch_by_default=True)
|
|
|
|
autocomplete_list = set(dir(client))
|
|
|
|
self.assertNotIn('test_db', autocomplete_list)
|
|
|
|
|
2020-03-24 16:21:12 +03:00
|
|
|
def test_empty_database(self):
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client = MockKustoClient(
|
2020-03-24 16:21:12 +03:00
|
|
|
databases_response=mock_databases_response([
|
2020-07-01 14:30:54 +03:00
|
|
|
('test_db', [('mock_table', [('foo', _KustoType.STRING), ('bar', _KustoType.INT)])]),
|
|
|
|
('', [('test_table1', [('foo1', _KustoType.STRING), ('bar1', _KustoType.INT)])])
|
2020-03-24 16:21:12 +03:00
|
|
|
]),
|
|
|
|
record_metadata=True,
|
|
|
|
)
|
2020-12-10 14:37:58 +03:00
|
|
|
client = PyKustoClient(mock_client)
|
2020-03-24 16:21:12 +03:00
|
|
|
client.wait_for_items()
|
|
|
|
self.assertEqual(
|
|
|
|
[RecordedQuery('', '.show databases schema | project DatabaseName, TableName, ColumnName, ColumnType | limit 100000')],
|
2020-12-10 14:37:58 +03:00
|
|
|
mock_client.recorded_queries,
|
2020-03-24 16:21:12 +03:00
|
|
|
)
|
2020-12-10 11:31:10 +03:00
|
|
|
self.assertType(client.test_db.mock_table.foo, _StringColumn)
|
2020-03-24 16:21:12 +03:00
|
|
|
|
2020-12-10 11:16:17 +03:00
|
|
|
def test_client_database_names_not_fetched(self):
|
2020-12-10 11:44:23 +03:00
|
|
|
client = PyKustoClient(MockKustoClient(), fetch_by_default=False)
|
2020-12-10 11:16:17 +03:00
|
|
|
self.assertEqual(frozenset(['test_db']), set(client.get_databases_names()))
|
|
|
|
|
|
|
|
def test_client_databases_not_fetched(self):
|
2020-12-10 11:44:23 +03:00
|
|
|
client = PyKustoClient(MockKustoClient(), fetch_by_default=False)
|
2020-12-10 11:16:17 +03:00
|
|
|
self.assertEqual(frozenset(['test_db']), set(db.get_name() for db in client.get_databases()))
|
2021-01-26 10:55:10 +03:00
|
|
|
|
|
|
|
def test_exception_while_fetching(self):
|
|
|
|
client = PyKustoClient(MockKustoClient(databases_response=self.raise_mock_exception))
|
|
|
|
self.assertRaises(
|
|
|
|
Exception("Mock exception"),
|
|
|
|
lambda: set(client.get_databases_names()),
|
|
|
|
)
|