From a9347e280c6c1ed8d6ccc346caeb01167eddba1a Mon Sep 17 00:00:00 2001 From: Dustin Frisch Date: Thu, 5 Feb 2015 23:21:52 +0100 Subject: [PATCH] Added query API --- backend/src/logzen/db/streams.py | 3 +- backend/src/logzen/es.py | 19 +- backend/src/logzen/logs.py | 113 ++++++---- backend/src/logzen/web/api/user/logs.py | 24 ++- backend/tests/test_api.py | 269 +----------------------- backend/tests/test_api_admin.py | 106 ++++++++++ backend/tests/test_api_user.py | 226 ++++++++++++++++++++ backend/tests/test_db.py | 10 +- backend/tests/test_logs.py | 214 +++++++++++++++++++ backend/tests/util.py | 17 +- 10 files changed, 679 insertions(+), 322 deletions(-) create mode 100644 backend/tests/test_api_admin.py create mode 100644 backend/tests/test_api_user.py create mode 100644 backend/tests/test_logs.py diff --git a/backend/src/logzen/db/streams.py b/backend/src/logzen/db/streams.py index 73a2a66..2b408d5 100644 --- a/backend/src/logzen/db/streams.py +++ b/backend/src/logzen/db/streams.py @@ -53,7 +53,8 @@ class Stream(Entity): default='') filter = sqlalchemy.Column(JSONDict, - nullable=False) + nullable=True, + default=None) @export() diff --git a/backend/src/logzen/es.py b/backend/src/logzen/es.py index cb3a6a6..068ddf8 100644 --- a/backend/src/logzen/es.py +++ b/backend/src/logzen/es.py @@ -42,6 +42,10 @@ def ElasticsearchConfigDecl(config_decl): section_decl('index', default='syslog') + # The doc_type of the the syslog messages + section_decl('type', + default='event') + @export() @@ -64,10 +68,18 @@ def __init__(self): else: auth = None - self.__connection = Elasticsearch(self.config.es.host, + self.__connection = Elasticsearch(self.config.es.hosts, connection_class=Urllib3HttpConnection, http_auth=auth) + if not self.__connection.indices.exists(self.config.es.index): + self.logger.warn('Specified index does not exist: %s', self.config.es.index) + + elif not self.__connection.indices.exists_type(self.config.es.index, + self.config.es.type): + self.logger.warn('Specified mapping does not exist: %s/%s', self.config.es.index, self.config.es.type) + + def search(self, body): """ Execute a search query. @@ -79,5 +91,6 @@ def search(self, self.logger.debug('Execute search: %s', JSONSerializer().dumps(body)) - return self.__connection.search(body=body, - index=self.config.es.index) + return self.__connection.search(self.config.es.index, + self.config.es.type, + body) diff --git a/backend/src/logzen/logs.py b/backend/src/logzen/logs.py index af02176..fcd17e3 100644 --- a/backend/src/logzen/logs.py +++ b/backend/src/logzen/logs.py @@ -29,54 +29,83 @@ class Logs(object): es = require('logzen.es:Connection') - users = require('logzen.db.users:Users') - streams = require('logzen.db.streams:Streams') + def queryWithFilter(self, + filter, + query=None): + """ Search for logs using the passed filter and an optional query. + """ - def query(self, - stream, - query=None): - """ Search for logs using the passed stream and query. + request = {} - The returned log list is filtered by the filter assigned to the - user owning the stream and by an optional filter assigned to a stream. + # Add the filter to the request - if any + if filter: + request['filter'] = filter - The returned value is the unmodified result of the executed - ElasticSearch query. - """ + # Add the query to the request - if any + if query: + request['query'] = query + + else: + request['query'] = self.MATCH_ALL + + # Execute the request + result = self.es.search(request) - request = { - } + return result + + + def queryWithFilters(self, + filters, + query=None): + """ Search for logs using the passed filters and an optional query. + + If the filters does contain more than one filter, the filters are concatinated using the 'and' operation. + """ # Add the filters to the request - if stream.user.filter and stream.filter: - # Create a combined filter using the user filter and stream filter - request.update({ - 'filter': { - 'and': [ - stream.user.filter, - stream.filter - ] - } - }) - - elif stream.user.filter: - # Use only the user filter - request.update({ - 'filter': stream.user.filter - }) - - elif stream.filter is not None: - # Use only the stream filter - request.update({ - 'filter': stream.filter - }) + if not filters: + # Do not use a filter + filter = None - # Add the query to the request - if any - if query: - request.update({ - 'query': query, - }) + elif len(filters) == 1: + # Use the single filter as-is + filter = filters[0] + + else: + # Concatenate all filters using 'and' operation + filter = {'and': filters} + + # Execute the query + return self.queryWithFilter(filter, + query) + + + def queryWithUser(self, + user, + query=None): + """ Search for logs using the passed users filter and an optional query. + """ + + return self.queryWithFilter(user.filter, + query) + + + def queryWithStream(self, + stream, + query=None): + """ Search for logs using the passed streams filter and an optional query. + """ + + filters = [] + + # Add the user filter to the list of filters - if any + if stream.user.filter: + filters.append(stream.user.filter) + + # Add the stream filter to the list of filters - if any + if stream.filter: + filters.append(stream.filter) - # Execute the search - return self.es.search(request) + return self.queryWithFilters(filters, + query) diff --git a/backend/src/logzen/web/api/user/logs.py b/backend/src/logzen/web/api/user/logs.py index f38ea80..0eef1d5 100644 --- a/backend/src/logzen/web/api/user/logs.py +++ b/backend/src/logzen/web/api/user/logs.py @@ -24,21 +24,31 @@ +@resource('/logs/*', ['GET', 'POST']) +@require(user='logzen.web.api.auth:User', + request='logzen.web.api:Request', + logs='logzen.logs:Logs') +def query(user, + request, + logs): + return logs.queryWithUser(user=user, + query=request.json) + + + @resource('/logs/', ['GET', 'POST']) @require(user='logzen.web.api.auth:User', request='logzen.web.api:Request', logs='logzen.logs:Logs') -def query(name, +def query(stream, user, request, logs): - # Resolve the stream entity try: - stream = user.streams[name] + stream = user.streams[stream] except KeyError: - raise bottle.HTTPError(404, 'Stream not found: %s' % name) + raise bottle.HTTPError(404, 'Stream not found: %s' % stream) - # Execute the query and return the result - return logs.query(stream=stream, - query=request.json) + return logs.queryWithStream(stream=stream, + query=request.json) diff --git a/backend/tests/test_api.py b/backend/tests/test_api.py index b0818f0..2775e4f 100644 --- a/backend/tests/test_api.py +++ b/backend/tests/test_api.py @@ -2,12 +2,15 @@ import json from hamcrest import * + import werkzeug.test import werkzeug.wrappers -from require import * from logzen.db import session +from util import * + + class ApiTestCase(unittest.TestCase): testConnection = require('util:TestConnection') @@ -18,6 +21,8 @@ class ApiTestCase(unittest.TestCase): def setUp(self): + mockConfigFile.start() + # Begin a manual transaction self.transaction = self.testConnection.begin() @@ -37,13 +42,15 @@ def tearDown(self): # Rollback all changes self.transaction.rollback() + mockConfigFile.stop() + class AuthenticationApiTestCase(ApiTestCase): users = require('logzen.db.users:Users') def setUp(self): - super(AuthenticationApiTestCase, self).setUp() + super().setUp() with session(): self.user = self.users.createUser(username='admin', @@ -75,261 +82,3 @@ def testAuthenticationWithWrongPasswordFails(self): assert_that(resp.status_code, is_(401)) assert_that(resp.data, is_(b'Wrong username or password')) - - -class UserApiTestCase(ApiTestCase): - users = require('logzen.db.users:Users') - - - def setUp(self): - super(UserApiTestCase, self).setUp() - - with session(): - self.user = self.users.createUser(username='user', - password='user') - - - def testAuthenticatedAccessSucceeds(self): - self.client.post('/api/v1/token', - data=json.dumps({'username': 'user', - 'password': 'user'})) - - resp = self.client.get('/api/v1/user') - - assert_that(resp.status_code, is_(200)) - - - def testAnonymousAccessFails(self): - resp = self.client.get('/api/v1/user') - - assert_that(resp.status_code, is_(401)) - assert_that(resp.data, is_(b'Authentication required')) - - -class UserAccountApiTestCase(ApiTestCase): - users = require('logzen.db.users:Users') - - - def setUp(self): - super(UserAccountApiTestCase, self).setUp() - - with session(): - self.user = self.users.createUser(username='user', - password='user') - - self.client.post('/api/v1/token', - data=json.dumps({'username': 'user', - 'password': 'user'})) - - - def testFetchingAccountInfoSucceeds(self): - resp = self.client.get('/api/v1/user/account') - - assert_that(resp.status_code, is_(200)) - assert_that(json.loads(resp.data.decode('utf8')), is_({'username': 'user'})) - - -class UserStreamsApiTestCase(ApiTestCase): - users = require('logzen.db.users:Users') - streams = require('logzen.db.streams:Streams') - - - def setUp(self): - super(UserStreamsApiTestCase, self).setUp() - - with session(): - self.user = self.users.createUser(username='user', - password='user') - - self.client.post('/api/v1/token', - data=json.dumps({'username': 'user', - 'password': 'user'})) - - - def testCreatingStreamSucceeds(self): - resp = self.client.post('/api/v1/user/streams', - data=json.dumps({'name': 'test', - 'description': 'For testing purposes only', - 'filter': {'foo': 42, - 'bar': 23}})) - - assert_that(resp.status_code, is_(200)) - - with session(): - user = self.users.getUserByName('user') - - assert_that(user.streams, has_length(1)) - assert_that(user.streams, has_items('test')) - - - def testDeletingStreamSucceeds(self): - with session(): - self.streams.createStream(user=self.user, - name='test', - filter={}) - - resp = self.client.delete('/api/v1/user/streams/test') - - assert_that(resp.status_code, is_(200)) - - with session(): - user = self.users.getUserByName('user') - - assert_that(user.streams, is_(empty())) - - - def testListingStreamsSucceeds(self): - with session(): - self.streams.createStream(user=self.user, - name='test1', - filter={}) - self.streams.createStream(user=self.user, - name='test2', - filter={}) - self.streams.createStream(user=self.user, - name='test3', - filter={}) - - resp = self.client.get('/api/v1/user/streams') - - assert_that(resp.status_code, is_(200)) - assert_that(json.loads(resp.data.decode('utf8')), is_({'test1': {'description': '', - 'filter': {}}, - 'test2': {'description': '', - 'filter': {}}, - 'test3': {'description': '', - 'filter': {}}})) - - - def testFetchingStreamSucceeds(self): - with session(): - self.streams.createStream(user=self.user, - name='test', - filter={}) - - resp = self.client.get('/api/v1/user/streams/test') - - assert_that(resp.status_code, is_(200)) - assert_that(json.loads(resp.data.decode('utf8')), is_({'name': 'test', - 'description': '', - 'filter': {}})) - - - def testUpdatingStreamSucceeds(self): - with session(): - self.streams.createStream(user=self.user, - name='test', - filter={}) - - resp = self.client.put('/api/v1/user/streams/test', - data=json.dumps({'name': 'toast', - 'description': 'x', - 'filter': {'foo': 'bar'}})) - - assert_that(resp.status_code, is_(200)) - - with session(): - user = self.users.getUserByName('user') - stream = user.streams['toast'] - - assert_that(stream.name, is_('toast')) - assert_that(stream.description, is_('x')) - assert_that(stream.filter, is_({'foo': 'bar'})) - - -class AdminApiTestCase(ApiTestCase): - users = require('logzen.db.users:Users') - - - def setUp(self): - super(AdminApiTestCase, self).setUp() - - with session(): - self.user = self.users.createUser(username='user', - password='user') - - self.admin = self.users.createUser(username='admin', - password='admin', - admin=True) - - - def testAuthorizedAccessSucceeds(self): - self.client.post('/api/v1/token', - data=json.dumps({'username': 'admin', - 'password': 'admin'})) - - resp = self.client.get('/api/v1/admin/users') - - assert_that(resp.status_code, is_(200)) - - - def testAuthenticatedNotAuthorizedAccessFails(self): - self.client.post('/api/v1/token', - data=json.dumps({'username': 'user', - 'password': 'user'})) - - resp = self.client.get('/api/v1/admin/users') - - assert_that(resp.status_code, is_(401)) - assert_that(resp.data, is_(b'Authentication required')) - - - def testAnonymousAccessFails(self): - resp = self.client.get('/api/v1/admin/users') - - assert_that(resp.status_code, is_(401)) - assert_that(resp.data, is_(b'Authentication required')) - - -class AdminUsersApiTestCase(ApiTestCase): - users = require('logzen.db.users:Users') - - - def setUp(self): - super(AdminUsersApiTestCase, self).setUp() - - with session(): - self.admin = self.users.createUser(username='admin', - password='admin', - admin=True) - - self.user1 = self.users.createUser(username='user1', - password='test') - self.user2 = self.users.createUser(username='user2', - password='test') - self.user3 = self.users.createUser(username='user3', - password='test') - - self.client.post('/api/v1/token', - data=json.dumps({'username': 'admin', - 'password': 'admin'})) - - - def testFetchingUserListSucceeds(self): - resp = self.client.get('/api/v1/admin/users') - - assert_that(resp.status_code, is_(200)) - assert_that(json.loads(resp.data.decode('utf8')), is_({'admin': {'username': 'admin', - 'admin': True}, - 'user1': {'username': 'user1', - 'admin': False}, - 'user2': {'username': 'user2', - 'admin': False}, - 'user3': {'username': 'user3', - 'admin': False}})) - - - def testCreatingUserSucceeds(self): - resp = self.client.post('/api/v1/admin/users', - data=json.dumps({'username': 'marvin', - 'password': 'test123', - 'admin': True})) - - assert_that(resp.status_code, is_(200)) - assert_that(resp.data, is_(b'')) - - user = self.users.getUserByName('marvin') - - assert_that(user, is_(not_none())) - assert_that(user, has_properties(username='marvin', - admin=True)) diff --git a/backend/tests/test_api_admin.py b/backend/tests/test_api_admin.py new file mode 100644 index 0000000..3b4d1e4 --- /dev/null +++ b/backend/tests/test_api_admin.py @@ -0,0 +1,106 @@ +import json + +from hamcrest import * + +from require import * +from logzen.db import session + +from test_api import ApiTestCase + + +class AdminApiTestCase(ApiTestCase): + users = require('logzen.db.users:Users') + + + def setUp(self): + super().setUp() + + with session(): + self.user = self.users.createUser(username='user', + password='user') + + self.admin = self.users.createUser(username='admin', + password='admin', + admin=True) + + + def testAuthorizedAccessSucceeds(self): + self.client.post('/api/v1/token', + data=json.dumps({'username': 'admin', + 'password': 'admin'})) + + resp = self.client.get('/api/v1/admin/users') + + assert_that(resp.status_code, is_(200)) + + + def testAuthenticatedNotAuthorizedAccessFails(self): + self.client.post('/api/v1/token', + data=json.dumps({'username': 'user', + 'password': 'user'})) + + resp = self.client.get('/api/v1/admin/users') + + assert_that(resp.status_code, is_(401)) + assert_that(resp.data, is_(b'Authentication required')) + + + def testAnonymousAccessFails(self): + resp = self.client.get('/api/v1/admin/users') + + assert_that(resp.status_code, is_(401)) + assert_that(resp.data, is_(b'Authentication required')) + + +class AdminUsersApiTestCase(ApiTestCase): + users = require('logzen.db.users:Users') + + + def setUp(self): + super().setUp() + + with session(): + self.admin = self.users.createUser(username='admin', + password='admin', + admin=True) + + self.user1 = self.users.createUser(username='user1', + password='test') + self.user2 = self.users.createUser(username='user2', + password='test') + self.user3 = self.users.createUser(username='user3', + password='test') + + self.client.post('/api/v1/token', + data=json.dumps({'username': 'admin', + 'password': 'admin'})) + + + def testFetchingUserListSucceeds(self): + resp = self.client.get('/api/v1/admin/users') + + assert_that(resp.status_code, is_(200)) + assert_that(json.loads(resp.data.decode('utf8')), is_({'admin': {'username': 'admin', + 'admin': True}, + 'user1': {'username': 'user1', + 'admin': False}, + 'user2': {'username': 'user2', + 'admin': False}, + 'user3': {'username': 'user3', + 'admin': False}})) + + + def testCreatingUserSucceeds(self): + resp = self.client.post('/api/v1/admin/users', + data=json.dumps({'username': 'marvin', + 'password': 'test123', + 'admin': True})) + + assert_that(resp.status_code, is_(200)) + assert_that(resp.data, is_(b'')) + + user = self.users.getUserByName('marvin') + + assert_that(user, is_(not_none())) + assert_that(user, has_properties(username='marvin', + admin=True)) diff --git a/backend/tests/test_api_user.py b/backend/tests/test_api_user.py new file mode 100644 index 0000000..ed27cfd --- /dev/null +++ b/backend/tests/test_api_user.py @@ -0,0 +1,226 @@ +import json + +from hamcrest import * + +from require import * +from require.mock import * + +from logzen.db import session + +from test_api import ApiTestCase + +from util import * + + + +class UserApiTestCase(ApiTestCase): + users = require('logzen.db.users:Users') + + + def setUp(self): + super().setUp() + + with session(): + self.user = self.users.createUser(username='user', + password='user') + + + def testAuthenticatedAccessSucceeds(self): + self.client.post('/api/v1/token', + data=json.dumps({'username': 'user', + 'password': 'user'})) + + resp = self.client.get('/api/v1/user') + + assert_that(resp.status_code, is_(200)) + + + def testAnonymousAccessFails(self): + resp = self.client.get('/api/v1/user') + + assert_that(resp.status_code, is_(401)) + assert_that(resp.data, is_(b'Authentication required')) + + +class UserAccountApiTestCase(ApiTestCase): + users = require('logzen.db.users:Users') + + + def setUp(self): + super().setUp() + + with session(): + self.user = self.users.createUser(username='user', + password='user') + + self.client.post('/api/v1/token', + data=json.dumps({'username': 'user', + 'password': 'user'})) + + + def testFetchingAccountInfoSucceeds(self): + resp = self.client.get('/api/v1/user/account') + + assert_that(resp.status_code, is_(200)) + assert_that(json.loads(resp.data.decode('utf8')), is_({'username': 'user'})) + + +class UserStreamsApiTestCase(ApiTestCase): + users = require('logzen.db.users:Users') + streams = require('logzen.db.streams:Streams') + + + def setUp(self): + super().setUp() + + with session(): + self.user = self.users.createUser(username='user', + password='user') + + self.client.post('/api/v1/token', + data=json.dumps({'username': 'user', + 'password': 'user'})) + + + def testCreatingStreamSucceeds(self): + resp = self.client.post('/api/v1/user/streams', + data=json.dumps({'name': 'test', + 'description': 'For testing purposes only', + 'filter': {'foo': 42, + 'bar': 23}})) + + assert_that(resp.status_code, is_(200)) + + with session(): + user = self.users.getUserByName('user') + + assert_that(user.streams, has_length(1)) + assert_that(user.streams, has_items('test')) + + + def testDeletingStreamSucceeds(self): + with session(): + self.streams.createStream(user=self.user, + name='test', + filter={}) + + resp = self.client.delete('/api/v1/user/streams/test') + + assert_that(resp.status_code, is_(200)) + + with session(): + user = self.users.getUserByName('user') + + assert_that(user.streams, is_(empty())) + + + def testListingStreamsSucceeds(self): + with session(): + self.streams.createStream(user=self.user, + name='test1', + filter={}) + self.streams.createStream(user=self.user, + name='test2', + filter={}) + self.streams.createStream(user=self.user, + name='test3', + filter={}) + + resp = self.client.get('/api/v1/user/streams') + + assert_that(resp.status_code, is_(200)) + assert_that(json.loads(resp.data.decode('utf8')), is_({'test1': {'description': '', + 'filter': {}}, + 'test2': {'description': '', + 'filter': {}}, + 'test3': {'description': '', + 'filter': {}}})) + + + def testFetchingStreamSucceeds(self): + with session(): + self.streams.createStream(user=self.user, + name='test', + filter={}) + + resp = self.client.get('/api/v1/user/streams/test') + + assert_that(resp.status_code, is_(200)) + assert_that(json.loads(resp.data.decode('utf8')), is_({'name': 'test', + 'description': '', + 'filter': {}})) + + + def testUpdatingStreamSucceeds(self): + with session(): + self.streams.createStream(user=self.user, + name='test', + filter={}) + + resp = self.client.put('/api/v1/user/streams/test', + data=json.dumps({'name': 'toast', + 'description': 'x', + 'filter': {'foo': 'bar'}})) + + assert_that(resp.status_code, is_(200)) + + with session(): + user = self.users.getUserByName('user') + stream = user.streams['toast'] + + assert_that(stream.name, is_('toast')) + assert_that(stream.description, is_('x')) + assert_that(stream.filter, is_({'foo': 'bar'})) + + +class UserLogsApiTestCase(ApiTestCase): + users = require('logzen.db.users:Users') + streams = require('logzen.db.streams:Streams') + + + def setUp(self): + mockElasticsearch.start() + + super().setUp() + + with session(): + self.user = self.users.createUser(username='user', + password='user') + + self.stream = self.streams.createStream(self.user, + name='foo', + filter={}) + + self.client.post('/api/v1/token', + data=json.dumps({'username': 'user', + 'password': 'user'})) + + def tearDown(self): + super().tearDown() + + mockElasticsearch.stop() + + + def testFetchWithMatchAllSucceeds(self): + resp = self.client.post('/api/v1/user/logs/*', + data=json.dumps(None)) + + assert_that(resp.status_code, is_(200)) + assert_that(json.loads(resp.data.decode('utf8')), + is_({'query': {'match_all': {}}})) + + + def testFetchWithStreamSucceeds(self): + resp = self.client.post('/api/v1/user/logs/foo', + data=json.dumps(None)) + + assert_that(resp.status_code, is_(200)) + assert_that(json.loads(resp.data.decode('utf8')), + is_({'query': {'match_all': {}}})) + + + def testFetchWithUnknownStreamFails(self): + resp = self.client.post('/api/v1/user/logs/unknown', + data=json.dumps(None)) + + assert_that(resp.status_code, is_(404)) diff --git a/backend/tests/test_db.py b/backend/tests/test_db.py index 105155c..1812096 100644 --- a/backend/tests/test_db.py +++ b/backend/tests/test_db.py @@ -2,11 +2,11 @@ from hamcrest import * -from require import * - from logzen.db import session from logzen.db.users import User, Password +from util import * + class DatabaseTestCase(unittest.TestCase): @@ -16,6 +16,8 @@ class DatabaseTestCase(unittest.TestCase): def setUp(self): + mockConfigFile.start() + # Begin a manual transaction self.transaction = self.testConnection.begin() @@ -28,6 +30,8 @@ def tearDown(self): # Rollback all changes self.transaction.rollback() + mockConfigFile.stop() + class UserDatabaseTestCase(DatabaseTestCase): @@ -36,7 +40,7 @@ class UserDatabaseTestCase(DatabaseTestCase): def setUp(self): - super(UserDatabaseTestCase, self).setUp() + super().setUp() with session(): self.users.createUser(username='test', diff --git a/backend/tests/test_logs.py b/backend/tests/test_logs.py new file mode 100644 index 0000000..d141b73 --- /dev/null +++ b/backend/tests/test_logs.py @@ -0,0 +1,214 @@ +from hamcrest import * + +from logzen.db import session +from test_db import DatabaseTestCase +from util import * + + +class LogsTestCase(DatabaseTestCase): + users = require('logzen.db.users:Users') + streams = require('logzen.db.streams:Streams') + + logs = require('logzen.logs:Logs') + + + def setUp(self): + mockElasticsearch.start() + + super().setUp() + + + def tearDown(self): + super().tearDown() + + mockElasticsearch.stop() + + + def testQueryWithQuerySucceeds(self): + result = self.logs.queryWithFilter(filter=None, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithoutQuerySucceeds(self): + result = self.logs.queryWithFilter(filter=None) + + assert_that(result, is_({'query': {'match_all': {}}})) + + + def testQueryWithFullUserSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + result = self.logs.queryWithUser(user=user, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithEmptyUserSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + result = self.logs.queryWithUser(user=user, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithRestrictedUserSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user', + filter={'term': {'host': 'localhost'}}) + + result = self.logs.queryWithUser(user=user, + query={'q': 'a'}) + + assert_that(result, is_({'filter': {'term': {'host': 'localhost'}}, + 'query': {'q': 'a'}})) + + + def testQueryWithFullUserAndFullStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + stream = self.streams.createStream(user=user, + name='stream') + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithEmptyUserAndFullStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + stream = self.streams.createStream(user=user, + name='stream') + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithRestrictedUserAndFullStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user', + filter={'term': {'host': 'localhost'}}) + + stream = self.streams.createStream(user=user, + name='stream') + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'filter': {'term': {'host': 'localhost'}}, + 'query': {'q': 'a'}})) + + + def testQueryWithFullUserAndEmptyStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + stream = self.streams.createStream(user=user, + name='stream', + filter={}) + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithEmptyUserAndEmptyStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + stream = self.streams.createStream(user=user, + name='stream', + filter={}) + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'query': {'q': 'a'}})) + + + def testQueryWithRestrictedUserAndEmptyStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user', + filter={'term': {'host': 'localhost'}}) + + stream = self.streams.createStream(user=user, + name='stream', + filter={}) + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'filter': {'term': {'host': 'localhost'}}, + 'query': {'q': 'a'}})) + + + def testQueryWithFullUserAndRestrictedStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + stream = self.streams.createStream(user=user, + name='stream', + filter={'term': {'severity': 'fatal'}}) + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'filter': {'term': {'severity': 'fatal'}}, + 'query': {'q': 'a'}})) + + + def testQueryWithEmptyUserAndRestrictedStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user') + + stream = self.streams.createStream(user=user, + name='stream', + filter={'term': {'severity': 'fatal'}}) + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'filter': {'term': {'severity': 'fatal'}}, + 'query': {'q': 'a'}})) + + + def testQueryWithRestrictedUserAndRestrictedStreamSucceeds(self): + with session(): + user = self.users.createUser(username='user', + password='user', + filter={'term': {'host': 'localhost'}}) + + stream = self.streams.createStream(user=user, + name='stream', + filter={'term': {'severity': 'fatal'}}) + + result = self.logs.queryWithStream(stream=stream, + query={'q': 'a'}) + + assert_that(result, is_({'filter': {'and': [{'term': {'host': 'localhost'}}, + {'term': {'severity': 'fatal'}}]}, + 'query': {'q': 'a'}})) + diff --git a/backend/tests/util.py b/backend/tests/util.py index fd6ed9c..417e36b 100644 --- a/backend/tests/util.py +++ b/backend/tests/util.py @@ -1,11 +1,10 @@ - - - from require import * +from require.mock import * + -@extend('logzen.config:ConfigFile') -def TestConfigFile(file): +@mock('logzen.config:ConfigFile') +def mockConfigFile(file): from configparser import ConfigParser config_file = ConfigParser() @@ -18,7 +17,13 @@ def TestConfigFile(file): config_file.set('es', 'hosts', '') config_file.set('log', 'level', 'DEBUG') - return config_file + file.return_value = config_file + + + +@mock('logzen.es:Connection') +def mockElasticsearch(connection): + connection.return_value.search.side_effect = lambda request: request