--- /dev/null
+# Copyright 2019 Canonical
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+# For those usages not covered by the Apache License, Version 2.0 please
+# contact: eduardo.sousa@canonical.com
+##
+
+import logging
+import pytest
+import tempfile
+
+from pymongo import MongoClient
+from gridfs import GridFSBucket
+
+from osm_common.fsbase import FsException
+from osm_common.fsmongo import FsMongo
+
+__author__ = "Eduardo Sousa <eduardo.sousa@canonical.com>"
+
+
+def valid_path():
+ return tempfile.gettempdir() + '/'
+
+
+def invalid_path():
+ return '/#tweeter/'
+
+
+@pytest.fixture(scope="function", params=[True, False])
+def fs_mongo(request, monkeypatch):
+ def mock_mongoclient_constructor(a, b, c):
+ pass
+
+ def mock_mongoclient_getitem(a, b):
+ pass
+
+ def mock_gridfs_constructor(a, b):
+ pass
+
+ monkeypatch.setattr(MongoClient, '__init__', mock_mongoclient_constructor)
+ monkeypatch.setattr(MongoClient, '__getitem__', mock_mongoclient_getitem)
+ monkeypatch.setattr(GridFSBucket, '__init__', mock_gridfs_constructor)
+ fs = FsMongo(lock=request.param)
+ fs.fs_connect({
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'})
+ return fs
+
+
+def generic_fs_exception_message(message):
+ return "storage exception {}".format(message)
+
+
+def fs_connect_exception_message(path):
+ return "storage exception Invalid configuration param at '[storage]': path '{}' does not exist".format(path)
+
+
+def file_open_file_not_found_exception(storage):
+ f = storage if isinstance(storage, str) else '/'.join(storage)
+ return "storage exception File {} does not exist".format(f)
+
+
+def file_open_io_exception(storage):
+ f = storage if isinstance(storage, str) else '/'.join(storage)
+ return "storage exception File {} cannot be opened".format(f)
+
+
+def dir_ls_not_a_directory_exception(storage):
+ f = storage if isinstance(storage, str) else '/'.join(storage)
+ return "storage exception File {} does not exist".format(f)
+
+
+def dir_ls_io_exception(storage):
+ f = storage if isinstance(storage, str) else '/'.join(storage)
+ return "storage exception File {} cannot be opened".format(f)
+
+
+def file_delete_exception_message(storage):
+ return "storage exception File {} does not exist".format(storage)
+
+
+def test_constructor_without_logger():
+ fs = FsMongo()
+ assert fs.logger == logging.getLogger('fs')
+ assert fs.path is None
+ assert fs.client is None
+ assert fs.fs is None
+
+
+def test_constructor_with_logger():
+ logger_name = 'fs_mongo'
+ fs = FsMongo(logger_name=logger_name)
+ assert fs.logger == logging.getLogger(logger_name)
+ assert fs.path is None
+ assert fs.client is None
+ assert fs.fs is None
+
+
+def test_get_params(fs_mongo, monkeypatch):
+ def mock_gridfs_find(self, search_query, **kwargs):
+ return []
+
+ monkeypatch.setattr(GridFSBucket, 'find', mock_gridfs_find)
+ params = fs_mongo.get_params()
+ assert len(params) == 2
+ assert "fs" in params
+ assert "path" in params
+ assert params["fs"] == "mongo"
+ assert params["path"] == valid_path()
+
+
+@pytest.mark.parametrize("config, exp_logger, exp_path", [
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ 'fs_mongo', valid_path()
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ 'fs_mongo', valid_path()
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path()[:-1],
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ 'fs_mongo', valid_path()
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path()[:-1],
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ 'fs_mongo', valid_path()
+ ),
+ (
+ {
+ 'path': valid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ 'fs', valid_path()
+ ),
+ (
+ {
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ 'fs', valid_path()
+ ),
+ (
+ {
+ 'path': valid_path()[:-1],
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ 'fs', valid_path()
+ ),
+ (
+ {
+ 'path': valid_path()[:-1],
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ 'fs', valid_path()
+ )])
+def test_fs_connect_with_valid_config(config, exp_logger, exp_path):
+ fs = FsMongo()
+ fs.fs_connect(config)
+ assert fs.logger == logging.getLogger(exp_logger)
+ assert fs.path == exp_path
+ assert type(fs.client) == MongoClient
+ assert type(fs.fs) == GridFSBucket
+
+
+@pytest.mark.parametrize("config, exp_exception_message", [
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': invalid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path())
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': invalid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path())
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': invalid_path()[:-1],
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path()[:-1])
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': invalid_path()[:-1],
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path()[:-1])
+ ),
+ (
+ {
+ 'path': invalid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path())
+ ),
+ (
+ {
+ 'path': invalid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path())
+ ),
+ (
+ {
+ 'path': invalid_path()[:-1],
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path()[:-1])
+ ),
+ (
+ {
+ 'path': invalid_path()[:-1],
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ fs_connect_exception_message(invalid_path()[:-1])
+ ),
+ (
+ {
+ 'path': '/',
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ generic_fs_exception_message(
+ "Invalid configuration param at '[storage]': path '/' is not writable"
+ )
+ )])
+def test_fs_connect_with_invalid_path(config, exp_exception_message):
+ fs = FsMongo()
+ with pytest.raises(FsException) as excinfo:
+ fs.fs_connect(config)
+ assert str(excinfo.value) == exp_exception_message
+
+
+@pytest.mark.parametrize("config, exp_exception_message", [
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ "Missing parameter \"path\""
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ "Missing parameter \"path\""
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'collection': 'files'
+ },
+ "Missing parameters: \"uri\" or \"host\" + \"port\""
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ "Missing parameters: \"uri\" or \"host\" + \"port\""
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'collection': 'files'
+ },
+ "Missing parameters: \"uri\" or \"host\" + \"port\""
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'uri': 'mongo:27017'
+ },
+ "Missing parameter \"collection\""
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ },
+ "Missing parameter \"collection\""
+ )])
+def test_fs_connect_with_missing_parameters(config, exp_exception_message):
+ fs = FsMongo()
+ with pytest.raises(FsException) as excinfo:
+ fs.fs_connect(config)
+ assert str(excinfo.value) == generic_fs_exception_message(exp_exception_message)
+
+
+@pytest.mark.parametrize("config, exp_exception_message", [
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ "MongoClient crashed"
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ "MongoClient crashed"
+ )])
+def test_fs_connect_with_invalid_mongoclient(config, exp_exception_message, monkeypatch):
+ def generate_exception(a, b, c=None):
+ raise Exception(exp_exception_message)
+
+ monkeypatch.setattr(MongoClient, '__init__', generate_exception)
+
+ fs = FsMongo()
+ with pytest.raises(FsException) as excinfo:
+ fs.fs_connect(config)
+ assert str(excinfo.value) == generic_fs_exception_message(exp_exception_message)
+
+
+@pytest.mark.parametrize("config, exp_exception_message", [
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ "Collection unavailable"
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ "Collection unavailable"
+ )])
+def test_fs_connect_with_invalid_mongo_collection(config, exp_exception_message, monkeypatch):
+ def mock_mongoclient_constructor(a, b, c=None):
+ pass
+
+ def generate_exception(a, b):
+ raise Exception(exp_exception_message)
+
+ monkeypatch.setattr(MongoClient, '__init__', mock_mongoclient_constructor)
+ monkeypatch.setattr(MongoClient, '__getitem__', generate_exception)
+
+ fs = FsMongo()
+ with pytest.raises(FsException) as excinfo:
+ fs.fs_connect(config)
+ assert str(excinfo.value) == generic_fs_exception_message(exp_exception_message)
+
+
+@pytest.mark.parametrize("config, exp_exception_message", [
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'uri': 'mongo:27017',
+ 'collection': 'files'
+ },
+ "GridFsBucket crashed"
+ ),
+ (
+ {
+ 'logger_name': 'fs_mongo',
+ 'path': valid_path(),
+ 'host': 'mongo',
+ 'port': 27017,
+ 'collection': 'files'
+ },
+ "GridFsBucket crashed"
+ )])
+def test_fs_connect_with_invalid_gridfsbucket(config, exp_exception_message, monkeypatch):
+ def mock_mongoclient_constructor(a, b, c=None):
+ pass
+
+ def mock_mongoclient_getitem(a, b):
+ pass
+
+ def generate_exception(a, b):
+ raise Exception(exp_exception_message)
+
+ monkeypatch.setattr(MongoClient, '__init__', mock_mongoclient_constructor)
+ monkeypatch.setattr(MongoClient, '__getitem__', mock_mongoclient_getitem)
+ monkeypatch.setattr(GridFSBucket, '__init__', generate_exception)
+
+ fs = FsMongo()
+ with pytest.raises(FsException) as excinfo:
+ fs.fs_connect(config)
+ assert str(excinfo.value) == generic_fs_exception_message(exp_exception_message)
+
+
+def test_fs_disconnect(fs_mongo):
+ fs_mongo.fs_disconnect()