More tests (Dasboard API).

This commit is contained in:
Arik Fraimovich 2014-02-05 11:01:06 +02:00
parent 6ee3bc099d
commit 1e4f70747b
6 changed files with 154 additions and 50 deletions

View File

@ -1,5 +1,5 @@
NAME=redash NAME=redash
VERSION=0.2 VERSION=0.3
FULL_VERSION=$(VERSION).$(CIRCLE_BUILD_NUM) FULL_VERSION=$(VERSION).$(CIRCLE_BUILD_NUM)
FILENAME=$(CIRCLE_ARTIFACTS)/$(NAME).$(FULL_VERSION).tar.gz FILENAME=$(CIRCLE_ARTIFACTS)/$(NAME).$(FULL_VERSION).tar.gz

View File

@ -94,7 +94,7 @@ class DashboardListAPI(BaseResource):
return dashboards return dashboards
def post(self): def post(self):
dashboard_properties = json.loads(self.request.body) dashboard_properties = request.get_json(force=True)
dashboard = models.Dashboard(name=dashboard_properties['name'], dashboard = models.Dashboard(name=dashboard_properties['name'],
user=self.current_user, user=self.current_user,
layout='[]') layout='[]')
@ -104,13 +104,18 @@ class DashboardListAPI(BaseResource):
class DashboardAPI(BaseResource): class DashboardAPI(BaseResource):
def get(self, dashboard_slug=None): def get(self, dashboard_slug=None):
# TODO: prefetching? # TODO: prefetching of widgets and queries?
try:
dashboard = models.Dashboard.get_by_slug(dashboard_slug) dashboard = models.Dashboard.get_by_slug(dashboard_slug)
except models.Dashboard.DoesNotExist:
abort(404)
return dashboard.to_dict(with_widgets=True) return dashboard.to_dict(with_widgets=True)
def post(self, dashboard_id): def post(self, dashboard_slug):
dashboard_properties = request.json # TODO: either convert all requests to use slugs or ids
dashboard = models.Dashboard.get(models.Dashboard.id == dashboard_id) dashboard_properties = request.get_json(force=True)
dashboard = models.Dashboard.get(models.Dashboard.id == dashboard_slug)
dashboard.layout = dashboard_properties['layout'] dashboard.layout = dashboard_properties['layout']
dashboard.name = dashboard_properties['name'] dashboard.name = dashboard_properties['name']
dashboard.save() dashboard.save()
@ -128,7 +133,7 @@ api.add_resource(DashboardAPI, '/api/dashboards/<dashboard_slug>', endpoint='das
class WidgetListAPI(BaseResource): class WidgetListAPI(BaseResource):
def post(self): def post(self):
widget_properties = request.json widget_properties = request.get_json(force=True)
widget_properties['options'] = json.dumps(widget_properties['options']) widget_properties['options'] = json.dumps(widget_properties['options'])
widget = models.Widget(**widget_properties) widget = models.Widget(**widget_properties)
widget.save() widget.save()

View File

@ -1 +1,56 @@
from tests import controllers from unittest import TestCase
from redash import settings, db, app
import redash.models
# TODO: this isn't pretty... :-)
settings.DATABASE_CONFIG = {
'name': 'rd_test',
'engine': 'peewee.PostgresqlDatabase',
'threadlocals': True
}
app.config['DATABASE'] = settings.DATABASE_CONFIG
db.load_database()
def model_factory(model, **kwargs):
def factory(**properties):
kwargs.update(properties)
return model(**kwargs)
return factory
class ModelFactory(object):
def __init__(self, model, **kwargs):
self.model = model
self.kwargs = kwargs
def _get_kwargs(self, override_kwargs):
kwargs = self.kwargs.copy()
kwargs.update(override_kwargs)
return kwargs
def instance(self, **override_kwargs):
kwargs = self._get_kwargs(override_kwargs)
return self.model(**kwargs)
def create(self, **override_kwargs):
kwargs = self._get_kwargs(override_kwargs)
return self.model.create(**kwargs)
DashboardFactory = ModelFactory(redash.models.Dashboard,
name='test', user='test@everything.me', layout='[]')
dashboard_factory = model_factory(redash.models.Dashboard, name='test', user='test@everything.me', layout='[]')
class BaseTestCase(TestCase):
def setUp(self):
redash.models.create_db(True, True)
def tearDown(self):
db.close_db(None)
redash.models.create_db(False, True)

View File

@ -1,16 +1,31 @@
from contextlib import contextmanager from contextlib import contextmanager
import unittest import json
from redash import app from tests import BaseTestCase, DashboardFactory
from redash import app, models
@contextmanager @contextmanager
def authenticated_user(c): def authenticated_user(c, user='test@example.com', name='John Test'):
with c.session_transaction() as sess: with c.session_transaction() as sess:
sess['openid'] = {'email': 'test@example.com', 'name': 'John Test'} sess['openid'] = {'email': user, 'name': name}
yield yield
def json_request(method, path, data=None):
if data:
response = method(path, data=json.dumps(data))
else:
response = method(path)
if response.data:
response.json = json.loads(response.data)
else:
response.json = None
return response
class AuthenticationTestMixin(): class AuthenticationTestMixin():
def test_redirects_when_not_authenticated(self): def test_redirects_when_not_authenticated(self):
with app.test_client() as c: with app.test_client() as c:
@ -19,14 +34,13 @@ class AuthenticationTestMixin():
self.assertEquals(302, rv.status_code) self.assertEquals(302, rv.status_code)
def test_returns_content_when_authenticated(self): def test_returns_content_when_authenticated(self):
with app.test_client() as c: with app.test_client() as c, authenticated_user(c):
with authenticated_user(c):
for path in self.paths: for path in self.paths:
rv = c.get(path) rv = c.get(path)
self.assertEquals(200, rv.status_code) self.assertEquals(200, rv.status_code)
class PingTest(unittest.TestCase): class PingTest(BaseTestCase):
def test_ping(self): def test_ping(self):
with app.test_client() as c: with app.test_client() as c:
rv = c.get('/ping') rv = c.get('/ping')
@ -34,36 +48,83 @@ class PingTest(unittest.TestCase):
self.assertEquals('PONG.', rv.data) self.assertEquals('PONG.', rv.data)
class IndexTest(unittest.TestCase, AuthenticationTestMixin): class IndexTest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = ['/', '/dashboard/example', '/queries/1', '/admin/status'] self.paths = ['/', '/dashboard/example', '/queries/1', '/admin/status']
super(IndexTest, self).setUp()
class StatusTest(unittest.TestCase, AuthenticationTestMixin): class StatusTest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = ['/status.json'] self.paths = ['/status.json']
super(StatusTest, self).setUp()
class DashboardAPITest(unittest.TestCase, AuthenticationTestMixin): class DashboardAPITest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = ['/api/dashboards'] self.paths = ['/api/dashboards']
super(DashboardAPITest, self).setUp()
def test_get_dashboard(self):
d1 = DashboardFactory.create()
with app.test_client() as c, authenticated_user(c):
rv = c.get('/api/dashboards/{0}'.format(d1.slug))
self.assertEquals(rv.status_code, 200)
self.assertDictEqual(json.loads(rv.data), d1.to_dict(with_widgets=True))
def test_get_non_existint_dashbaord(self):
with app.test_client() as c, authenticated_user(c):
rv = c.get('/api/dashboards/not_existing')
self.assertEquals(rv.status_code, 404)
def test_create_new_dashboard(self):
user_email = 'test@everything.me'
with app.test_client() as c, authenticated_user(c, user=user_email):
dashboard_name = 'Test Dashboard'
rv = json_request(c.post, '/api/dashboards', data={'name': dashboard_name})
self.assertEquals(rv.status_code, 200)
self.assertEquals(rv.json['name'], 'Test Dashboard')
self.assertEquals(rv.json['user'], user_email)
self.assertEquals(rv.json['layout'], [])
def test_update_dashboard(self):
d = DashboardFactory.create()
new_name = 'New Name'
with app.test_client() as c, authenticated_user(c):
rv = json_request(c.post, '/api/dashboards/{0}'.format(d.id),
data={'name': new_name, 'layout': '[]'})
self.assertEquals(rv.status_code, 200)
self.assertEquals(rv.json['name'], new_name)
def test_delete_dashbaord(self):
d = DashboardFactory.create()
with app.test_client() as c, authenticated_user(c):
rv = json_request(c.delete, '/api/dashboards/{0}'.format(d.slug))
self.assertEquals(rv.status_code, 200)
d = models.Dashboard.get_by_slug(d.slug)
self.assertTrue(d.is_archived)
class QueryAPITest(unittest.TestCase, AuthenticationTestMixin): class QueryAPITest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = ['/api/queries'] self.paths = ['/api/queries']
super(QueryAPITest, self).setUp()
class QueryResultAPITest(unittest.TestCase, AuthenticationTestMixin): class QueryResultAPITest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = [] self.paths = []
super(QueryResultAPITest, self).setUp()
class JobAPITest(unittest.TestCase, AuthenticationTestMixin): class JobAPITest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = [] self.paths = []
super(JobAPITest, self).setUp()
class CsvQueryResultAPITest(unittest.TestCase, AuthenticationTestMixin): class CsvQueryResultAPITest(BaseTestCase, AuthenticationTestMixin):
def setUp(self): def setUp(self):
self.paths = [] self.paths = []
super(CsvQueryResultAPITest, self).setUp()

View File

@ -1,31 +1,14 @@
from unittest import TestCase from tests import BaseTestCase, DashboardFactory
from redash import settings, db, app, models
settings.DATABASE_CONFIG = {
'name': 'rd_test',
'engine': 'peewee.PostgresqlDatabase',
'threadlocals': True
}
app.config['DATABASE'] = settings.DATABASE_CONFIG
db.load_database()
class DatabaseTestCase(TestCase): class DashboardTest(BaseTestCase):
def setUp(self):
models.create_db(True, True)
def tearDown(self):
models.create_db(False, True)
class DashboardTest(DatabaseTestCase):
def test_appends_suffix_to_slug_when_duplicate(self): def test_appends_suffix_to_slug_when_duplicate(self):
d1 = models.Dashboard.create(name='test', user='arik', layout='') d1 = DashboardFactory.create()
self.assertEquals(d1.slug, 'test') self.assertEquals(d1.slug, 'test')
d2 = models.Dashboard.create(name='test', user='arik', layout='') d2 = DashboardFactory.create()
self.assertNotEquals(d1.slug, d2.slug) self.assertNotEquals(d1.slug, d2.slug)
d3 = models.Dashboard.create(name='test', user='arik', layout='') d3 = DashboardFactory.create()
self.assertNotEquals(d1.slug, d3.slug) self.assertNotEquals(d1.slug, d3.slug)
self.assertNotEquals(d2.slug, d3.slug) self.assertNotEquals(d2.slug, d3.slug)