superset/tests/celery_tests.py

274 lines
10 KiB
Python
Raw Normal View History

2018-02-25 18:06:11 -05:00
# -*- coding: utf-8 -*-
"""Unit tests for Superset Celery worker"""
2016-08-30 01:11:36 -04:00
from __future__ import absolute_import
from __future__ import division
from __future__ import print_function
from __future__ import unicode_literals
import json
import os
import subprocess
import time
import unittest
import pandas as pd
2017-11-07 23:23:40 -05:00
from past.builtins import basestring
2016-08-30 01:11:36 -04:00
from superset import app, cli, dataframe, db, security_manager
from superset.models.helpers import QueryStatus
from superset.models.sql_lab import Query
from superset.sql_parse import SupersetQuery
from .base_tests import SupersetTestCase
2016-08-30 01:11:36 -04:00
2017-11-07 23:23:40 -05:00
2016-08-30 01:11:36 -04:00
BASE_DIR = app.config.get('BASE_DIR')
class CeleryConfig(object):
BROKER_URL = 'sqla+sqlite:///' + app.config.get('SQL_CELERY_DB_FILE_PATH')
CELERY_IMPORTS = ('superset.sql_lab', )
2017-11-12 14:09:22 -05:00
CELERY_RESULT_BACKEND = (
'db+sqlite:///' + app.config.get('SQL_CELERY_RESULTS_DB_FILE_PATH'))
2016-08-30 01:11:36 -04:00
CELERY_ANNOTATIONS = {'sql_lab.add': {'rate_limit': '10/s'}}
CONCURRENCY = 1
2017-11-10 20:52:34 -05:00
2016-08-30 01:11:36 -04:00
app.config['CELERY_CONFIG'] = CeleryConfig
class UtilityFunctionTests(SupersetTestCase):
2016-08-30 01:11:36 -04:00
# TODO(bkyryliuk): support more cases in CTA function.
def test_create_table_as(self):
2017-11-14 00:06:51 -05:00
q = SupersetQuery('SELECT * FROM outer_space;')
2016-08-30 01:11:36 -04:00
self.assertEqual(
2017-11-14 00:06:51 -05:00
'CREATE TABLE tmp AS \nSELECT * FROM outer_space',
q.as_create_table('tmp'))
2016-08-30 01:11:36 -04:00
self.assertEqual(
2017-11-14 00:06:51 -05:00
'DROP TABLE IF EXISTS tmp;\n'
'CREATE TABLE tmp AS \nSELECT * FROM outer_space',
q.as_create_table('tmp', overwrite=True))
2016-08-30 01:11:36 -04:00
# now without a semicolon
2017-11-14 00:06:51 -05:00
q = SupersetQuery('SELECT * FROM outer_space')
2016-08-30 01:11:36 -04:00
self.assertEqual(
2017-11-14 00:06:51 -05:00
'CREATE TABLE tmp AS \nSELECT * FROM outer_space',
q.as_create_table('tmp'))
2016-08-30 01:11:36 -04:00
# now a multi-line query
2016-08-30 01:11:36 -04:00
multi_line_query = (
2017-11-14 00:06:51 -05:00
'SELECT * FROM planets WHERE\n'
"Luke_Father = 'Darth Vader'")
q = SupersetQuery(multi_line_query)
2016-08-30 01:11:36 -04:00
self.assertEqual(
2017-11-14 00:06:51 -05:00
'CREATE TABLE tmp AS \nSELECT * FROM planets WHERE\n'
"Luke_Father = 'Darth Vader'",
2017-11-14 00:06:51 -05:00
q.as_create_table('tmp'),
)
2016-08-30 01:11:36 -04:00
class CeleryTestCase(SupersetTestCase):
2016-08-30 01:11:36 -04:00
def __init__(self, *args, **kwargs):
super(CeleryTestCase, self).__init__(*args, **kwargs)
self.client = app.test_client()
def get_query_by_name(self, sql):
session = db.session
query = session.query(Query).filter_by(sql=sql).first()
2016-08-30 01:11:36 -04:00
session.close()
return query
def get_query_by_id(self, id):
session = db.session
query = session.query(Query).filter_by(id=id).first()
2016-08-30 01:11:36 -04:00
session.close()
return query
@classmethod
def setUpClass(cls):
try:
os.remove(app.config.get('SQL_CELERY_DB_FILE_PATH'))
except OSError as e:
app.logger.warn(str(e))
try:
os.remove(app.config.get('SQL_CELERY_RESULTS_DB_FILE_PATH'))
except OSError as e:
app.logger.warn(str(e))
security_manager.sync_role_definitions()
2016-08-30 01:11:36 -04:00
worker_command = BASE_DIR + '/bin/superset worker'
2016-08-30 01:11:36 -04:00
subprocess.Popen(
worker_command, shell=True, stdout=subprocess.PIPE)
admin = security_manager.find_user('admin')
2016-08-30 01:11:36 -04:00
if not admin:
security_manager.add_user(
2016-08-30 01:11:36 -04:00
'admin', 'admin', ' user', 'admin@fab.org',
security_manager.find_role('Admin'),
2016-08-30 01:11:36 -04:00
password='general')
cli.load_examples(load_test_data=True)
@classmethod
def tearDownClass(cls):
subprocess.call(
"ps auxww | grep 'celeryd' | awk '{print $2}' | xargs kill -9",
2017-11-08 00:32:45 -05:00
shell=True,
2016-08-30 01:11:36 -04:00
)
subprocess.call(
2017-11-14 00:06:51 -05:00
"ps auxww | grep 'superset worker' | awk '{print $2}' | xargs kill -9",
2017-11-08 00:32:45 -05:00
shell=True,
2016-08-30 01:11:36 -04:00
)
def run_sql(self, db_id, sql, client_id, cta='false', tmp_table='tmp',
2016-08-30 01:11:36 -04:00
async='false'):
self.login()
resp = self.client.post(
'/superset/sql_json/',
2016-08-30 01:11:36 -04:00
data=dict(
database_id=db_id,
2016-08-30 01:11:36 -04:00
sql=sql,
async=async,
select_as_cta=cta,
tmp_table_name=tmp_table,
client_id=client_id,
2016-08-30 01:11:36 -04:00
),
)
self.logout()
return json.loads(resp.data.decode('utf-8'))
def test_run_sync_query_dont_exist(self):
main_db = self.get_main_database(db.session)
db_id = main_db.id
2016-08-30 01:11:36 -04:00
sql_dont_exist = 'SELECT name FROM table_dont_exist'
2017-11-14 00:06:51 -05:00
result1 = self.run_sql(db_id, sql_dont_exist, '1', cta='true')
2016-08-30 01:11:36 -04:00
self.assertTrue('error' in result1)
def test_run_sync_query_cta(self):
main_db = self.get_main_database(db.session)
db_id = main_db.id
eng = main_db.get_sqla_engine()
perm_name = 'can_sql_json'
sql_where = (
"SELECT name FROM ab_permission WHERE name='{}'".format(perm_name))
2016-08-30 01:11:36 -04:00
result2 = self.run_sql(
2017-11-14 00:06:51 -05:00
db_id, sql_where, '2', tmp_table='tmp_table_2', cta='true')
2016-08-30 01:11:36 -04:00
self.assertEqual(QueryStatus.SUCCESS, result2['query']['state'])
self.assertEqual([], result2['data'])
2017-03-10 17:49:11 -05:00
self.assertEqual([], result2['columns'])
2016-08-30 01:11:36 -04:00
query2 = self.get_query_by_id(result2['query']['serverId'])
# Check the data in the tmp table.
df2 = pd.read_sql_query(sql=query2.select_sql, con=eng)
data2 = df2.to_dict(orient='records')
self.assertEqual([{'name': perm_name}], data2)
2016-08-30 01:11:36 -04:00
def test_run_sync_query_cta_no_data(self):
main_db = self.get_main_database(db.session)
db_id = main_db.id
2016-08-30 01:11:36 -04:00
sql_empty_result = 'SELECT * FROM ab_user WHERE id=666'
result3 = self.run_sql(
2017-11-14 00:06:51 -05:00
db_id, sql_empty_result, '3', tmp_table='tmp_table_3', cta='true')
2016-08-30 01:11:36 -04:00
self.assertEqual(QueryStatus.SUCCESS, result3['query']['state'])
self.assertEqual([], result3['data'])
2017-03-10 17:49:11 -05:00
self.assertEqual([], result3['columns'])
2016-08-30 01:11:36 -04:00
query3 = self.get_query_by_id(result3['query']['serverId'])
self.assertEqual(QueryStatus.SUCCESS, query3.status)
def test_run_async_query(self):
main_db = self.get_main_database(db.session)
2016-08-30 01:11:36 -04:00
eng = main_db.get_sqla_engine()
sql_where = "SELECT name FROM ab_role WHERE name='Admin'"
result = self.run_sql(
2017-11-14 00:06:51 -05:00
main_db.id, sql_where, '4', async='true', tmp_table='tmp_async_1',
cta='true')
assert result['query']['state'] in (
QueryStatus.PENDING, QueryStatus.RUNNING, QueryStatus.SUCCESS)
2016-08-30 01:11:36 -04:00
time.sleep(1)
query = self.get_query_by_id(result['query']['serverId'])
df = pd.read_sql_query(query.select_sql, con=eng)
self.assertEqual(QueryStatus.SUCCESS, query.status)
self.assertEqual([{'name': 'Admin'}], df.to_dict(orient='records'))
self.assertEqual(QueryStatus.SUCCESS, query.status)
2017-11-14 00:06:51 -05:00
self.assertTrue('FROM tmp_async_1' in query.select_sql)
2018-05-18 20:57:21 -04:00
self.assertTrue('LIMIT 666' in query.select_sql)
2016-08-30 01:11:36 -04:00
self.assertEqual(
2017-11-14 00:06:51 -05:00
'CREATE TABLE tmp_async_1 AS \nSELECT name FROM ab_role '
"WHERE name='Admin'", query.executed_sql)
self.assertEqual(sql_where, query.sql)
self.assertEqual(0, query.rows)
2018-05-18 20:57:21 -04:00
self.assertEqual(666, query.limit)
self.assertEqual(False, query.limit_used)
self.assertEqual(True, query.select_as_cta)
self.assertEqual(True, query.select_as_cta_used)
2016-08-30 01:11:36 -04:00
@staticmethod
def de_unicode_dict(d):
def str_if_basestring(o):
if isinstance(o, basestring):
return str(o)
return o
return {str_if_basestring(k): str_if_basestring(d[k]) for k in d}
@classmethod
def dictify_list_of_dicts(cls, l, k):
return {str(o[k]): cls.de_unicode_dict(o) for o in l}
2017-03-10 17:49:11 -05:00
def test_get_columns(self):
main_db = self.get_main_database(db.session)
2017-11-14 00:06:51 -05:00
df = main_db.get_df('SELECT * FROM multiformat_time_series', None)
cdf = dataframe.SupersetDataFrame(df)
# Making ordering non-deterministic
cols = self.dictify_list_of_dicts(cdf.columns, 'name')
if main_db.sqlalchemy_uri.startswith('sqlite'):
self.assertEqual(self.dictify_list_of_dicts([
{'is_date': True, 'type': 'STRING', 'name': 'ds',
'is_dim': False},
{'is_date': True, 'type': 'STRING', 'name': 'ds2',
'is_dim': False},
{'agg': 'sum', 'is_date': False, 'type': 'INT',
'name': 'epoch_ms', 'is_dim': False},
{'agg': 'sum', 'is_date': False, 'type': 'INT',
'name': 'epoch_s', 'is_dim': False},
{'is_date': True, 'type': 'STRING', 'name': 'string0',
'is_dim': False},
{'is_date': False, 'type': 'STRING',
'name': 'string1', 'is_dim': True},
{'is_date': True, 'type': 'STRING', 'name': 'string2',
'is_dim': False},
{'is_date': False, 'type': 'STRING',
2017-11-08 23:34:23 -05:00
'name': 'string3', 'is_dim': True}], 'name'),
cols,
)
else:
self.assertEqual(self.dictify_list_of_dicts([
{'is_date': True, 'type': 'DATETIME', 'name': 'ds',
'is_dim': False},
{'is_date': True, 'type': 'DATETIME',
'name': 'ds2', 'is_dim': False},
{'agg': 'sum', 'is_date': False, 'type': 'INT',
'name': 'epoch_ms', 'is_dim': False},
{'agg': 'sum', 'is_date': False, 'type': 'INT',
'name': 'epoch_s', 'is_dim': False},
{'is_date': True, 'type': 'STRING', 'name': 'string0',
'is_dim': False},
{'is_date': False, 'type': 'STRING',
'name': 'string1', 'is_dim': True},
{'is_date': True, 'type': 'STRING', 'name': 'string2',
'is_dim': False},
{'is_date': False, 'type': 'STRING',
2017-11-08 23:34:23 -05:00
'name': 'string3', 'is_dim': True}], 'name'),
cols,
)
2016-08-30 01:11:36 -04:00
if __name__ == '__main__':
unittest.main()