Skip to content

Commit

Permalink
Mocks defaults can be changed in develop mode on the fly by running `…
Browse files Browse the repository at this point in the history
…mocks reset`
  • Loading branch information
shanejansen committed May 27, 2020
1 parent a05c9f8 commit 59980d0
Show file tree
Hide file tree
Showing 11 changed files with 74 additions and 67 deletions.
2 changes: 1 addition & 1 deletion docs/add-mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@ Contribute a New Mock
======
1. Create a new [Runnable](../touchstone/lib/mocks/runnables) or [Networked Runnable](../touchstone/lib/mocks/networked_runnables)
1. Add a new property to the [Mocks](../touchstone/lib/mocks/mocks.py) class, so your new mock is accessible in user test cases
1. Build a concrete instance of your new mock in the [Bootstrap](../touchstone/bootstrap.py) `__build_mocks` method with its required dependencies
1. Build a concrete instance of your new mock in the [Mock Factory](../touchstone/lib/mocks/mock_factory.py) with its required dependencies
1. Write [unit](../tests) and [Touchstone tests](../touchstone-tests) for your new mock
26 changes: 1 addition & 25 deletions tests/test_common.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,10 @@
import unittest
from unittest import TestCase, mock
from unittest import TestCase

from touchstone import common


class TestCommon(TestCase):
@mock.patch('touchstone.common.os')
def test_sanityCheckPasses_requirementsMet_ReturnsTrue(self, mock_os):
# Given
mock_os.getcwd.return_value = 'temp'
mock_os.path.exists.return_value = True

# When
result = common.sanity_check_passes()

# Then
self.assertTrue(result)

@mock.patch('touchstone.common.os')
def test_sanityCheckPasses_requirementsNotMet_ReturnsFalse(self, mock_os):
# Given
mock_os.getcwd.return_value = 'temp'
mock_os.path.exists.return_value = False

# When
result = common.sanity_check_passes()

# Then
self.assertFalse(result)

def test_dictMerge_emptyOverride_ReturnsBase(self):
# Given
base = {'foo': 'bar'}
Expand Down
7 changes: 3 additions & 4 deletions touchstone/bootstrap.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,15 +37,14 @@ def __build_touchstone_config(self, root) -> TouchstoneConfig:
return config

def __build_mocks(self, root, configs, host) -> Mocks:
defaults = {}
defaults_paths = {}
default_files = glob.glob(os.path.join(root, 'defaults') + '/*.yml')
for default_file in default_files:
with open(default_file, 'r') as file:
defaults[Path(default_file).stem] = yaml.safe_load(file)
defaults_paths[Path(default_file).stem] = default_file

mocks = Mocks()
for mock_name in configs:
mock_factory = MockFactory(self.is_dev_mode, root, defaults, configs, host, self.docker_manager)
mock_factory = MockFactory(self.is_dev_mode, root, defaults_paths, configs, host, self.docker_manager)
mock = mock_factory.get_mock(mock_name)
if not mock:
raise exceptions.MockNotSupportedException(f'Mock: {mock_name} is not supported.')
Expand Down
20 changes: 20 additions & 0 deletions touchstone/lib/mocks/configurers/FileConfigurer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import yaml

from touchstone import common
from touchstone.lib.mocks.configurers.i_configurable import IConfigurable


class FileConfigurer(IConfigurable):
def __init__(self, config_path: str = None):
self.__config_path = config_path
self.__override_config = {}

def get_config(self) -> dict:
if not self.__config_path:
return {}
with open(self.__config_path, 'r') as file:
config = yaml.safe_load(file)
return common.dict_merge(config, self.__override_config)

def merge_config(self, other: dict):
self.__override_config = other
26 changes: 17 additions & 9 deletions touchstone/lib/mocks/mock_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from touchstone.lib.docker_manager import DockerManager
from touchstone.lib.mocks.configurers.BasicConfigurer import BasicConfigurer
from touchstone.lib.mocks.configurers.FileConfigurer import FileConfigurer
from touchstone.lib.mocks.health_checks.http_health_check import HttpHealthCheck
from touchstone.lib.mocks.mockables.basic_mock import BasicMock
from touchstone.lib.mocks.mockables.i_mockable import IMockable
Expand Down Expand Up @@ -33,59 +34,66 @@


class MockFactory(object):
def __init__(self, is_dev_mode: bool, root: str, defaults: dict, configs: dict, host: str,
def __init__(self, is_dev_mode: bool, root: str, defaults_paths: dict, configs: dict, host: str,
docker_manager: DockerManager):
self.__is_dev_mode = is_dev_mode
self.__root = root
self.__defaults = defaults
self.__defaults_paths = defaults_paths
self.__configs = configs
self.__host = host
self.__docker_manager = docker_manager

def get_mock(self, mock_name: str) -> Optional[IMockable]:
config = self.__configs.get(mock_name, {})
mock_defaults = self.__defaults.get(mock_name, {})
mock_defaults_paths = self.__defaults_paths.get(mock_name, None)
mock = None

if mock_name == 'http':
runnable = DockerHttpRunnable(mock_defaults, HttpHealthCheck(), DockerHttpSetup(), DockerHttpVerify(),
defaults_configurer = FileConfigurer(mock_defaults_paths)
runnable = DockerHttpRunnable(defaults_configurer, HttpHealthCheck(), DockerHttpSetup(), DockerHttpVerify(),
self.__docker_manager)
mock = NetworkedMock('http', 'HTTP', self.__host, runnable)
elif mock_name == 'rabbitmq':
defaults_configurer = FileConfigurer(mock_defaults_paths)
configurer = BasicConfigurer(IRabbitmqBehavior.DEFAULT_CONFIG)
configurer.merge_config(config)
context = DockerRabbitmqContext()
setup = DockerRabbitmqSetup(context)
verify = DockerRabbitmqVerify(context)
runnable = DockerRabbitmqRunnable(mock_defaults, configurer, HttpHealthCheck(), setup, verify,
runnable = DockerRabbitmqRunnable(defaults_configurer, configurer, HttpHealthCheck(), setup, verify,
self.__docker_manager)
mock = NetworkedMock('rabbitmq', 'Rabbit MQ', self.__host, runnable)
elif mock_name == 'mongodb':
defaults_configurer = FileConfigurer(mock_defaults_paths)
context = DockerMongoContext()
setup = DockerMongodbSetup(context)
verify = DockerMongodbVerify(context)
runnable = DockerMongodbRunnable(mock_defaults, self.__is_dev_mode, setup, verify, self.__docker_manager)
runnable = DockerMongodbRunnable(defaults_configurer, self.__is_dev_mode, setup, verify,
self.__docker_manager)
mock = NetworkedMock('mongodb', 'Mongo DB', self.__host, runnable)
elif mock_name == 'mysql':
defaults_configurer = FileConfigurer(mock_defaults_paths)
configurer = BasicConfigurer(IMysqlBehavior.DEFAULT_CONFIG)
configurer.merge_config(config)
context = DockerMysqlContext()
setup = DockerMysqlSetup(context)
verify = DockerMysqlVerify(context)
runnable = DockerMysqlRunnable(self.__is_dev_mode, mock_defaults, configurer, setup, verify,
runnable = DockerMysqlRunnable(defaults_configurer, self.__is_dev_mode, configurer, setup, verify,
self.__docker_manager)
mock = NetworkedMock('mysql', 'MySQL', self.__host, runnable)
elif mock_name == 's3':
defaults_configurer = FileConfigurer(mock_defaults_paths)
base_objects_path = os.path.join(self.__root, 'defaults')
setup = DockerS3Setup()
verify = DockerS3Verify()
runnable = DockerS3Runnable(mock_defaults, base_objects_path, HttpHealthCheck(), setup, verify,
runnable = DockerS3Runnable(defaults_configurer, base_objects_path, HttpHealthCheck(), setup, verify,
self.__docker_manager)
mock = NetworkedMock('s3', 'S3', self.__host, runnable)
elif mock_name == 'filesystem':
defaults_configurer = FileConfigurer(mock_defaults_paths)
base_files_path = os.path.join(self.__root, 'defaults')
setup = LocalFilesystemSetup(base_files_path)
verify = LocalFilesystemVerify(base_files_path)
runnable = LocalFilesystemRunnable(mock_defaults, base_files_path, setup, verify)
runnable = LocalFilesystemRunnable(defaults_configurer, base_files_path, setup, verify)
mock = BasicMock('filesystem', 'Filesystem', runnable)
return mock
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from touchstone.lib import exceptions
from touchstone.lib.docker_manager import DockerManager
from touchstone.lib.mocks.configurers.i_configurable import IConfigurable
from touchstone.lib.mocks.health_checks.i_url_health_checkable import IUrlHealthCheckable
from touchstone.lib.mocks.network import Network
from touchstone.lib.mocks.networked_runnables.http.docker.docker_http_setup import DockerHttpSetup
Expand All @@ -9,9 +10,9 @@


class DockerHttpRunnable(INetworkedRunnable, IHttpBehavior):
def __init__(self, defaults: dict, health_check: IUrlHealthCheckable, setup: DockerHttpSetup,
def __init__(self, defaults_configurer: IConfigurable, health_check: IUrlHealthCheckable, setup: DockerHttpSetup,
verify: DockerHttpVerify, docker_manager: DockerManager):
self.__defaults = defaults
self.__defaults_configurer = defaults_configurer
self.__health_check = health_check
self.__setup = setup
self.__verify = verify
Expand All @@ -27,7 +28,7 @@ def get_network(self) -> Network:
def initialize(self):
self.__setup.set_url(self.get_network().external_url())
self.__verify.set_url(self.get_network().external_url())
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def start(self):
run_result = self.__docker_manager.run_image('holomekc/wiremock-gui:2.25.1', port=8080, exposed_port=9090)
Expand All @@ -44,7 +45,7 @@ def stop(self):
self.__docker_manager.stop_container(self.__container_id)

def reset(self):
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def services_available(self):
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from touchstone.lib import exceptions
from touchstone.lib.docker_manager import DockerManager
from touchstone.lib.mocks.configurers.i_configurable import IConfigurable
from touchstone.lib.mocks.network import Network
from touchstone.lib.mocks.networked_runnables.i_networked_runnable import INetworkedRunnable
from touchstone.lib.mocks.networked_runnables.mongodb.docker.docker_mongodb_setup import DockerMongodbSetup
Expand All @@ -11,9 +12,9 @@


class DockerMongodbRunnable(INetworkedRunnable, IMongodbBehavior):
def __init__(self, defaults: dict, is_dev_mode: bool, setup: DockerMongodbSetup, verify: DockerMongodbVerify,
docker_manager: DockerManager):
self.__defaults = defaults
def __init__(self, defaults_configurer: IConfigurable, is_dev_mode: bool, setup: DockerMongodbSetup,
verify: DockerMongodbVerify, docker_manager: DockerManager):
self.__defaults_configurer = defaults_configurer
self.__is_dev_mode = is_dev_mode
self.__setup = setup
self.__verify = verify
Expand All @@ -31,7 +32,7 @@ def initialize(self):
mongo_client = pymongo.MongoClient(self.get_network().external_host, self.get_network().external_port)
self.__setup.set_mongo_client(mongo_client)
self.__verify.set_mongo_client(mongo_client)
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def start(self):
run_result = self.__docker_manager.run_image('mongo:4.0.14', port=27017)
Expand Down Expand Up @@ -59,7 +60,7 @@ def stop(self):
self.__docker_manager.stop_container(self.__ui_container_id)

def reset(self):
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def services_available(self):
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,10 @@ class DockerMysqlRunnable(INetworkedRunnable, IMysqlBehavior):
__USERNAME = 'root'
__PASSWORD = 'root'

def __init__(self, is_dev_mode: bool, defaults: dict, configurer: IConfigurable, setup: DockerMysqlSetup,
verify: DockerMysqlVerify, docker_manager: DockerManager):
def __init__(self, defaults_configurer: IConfigurable, is_dev_mode: bool, configurer: IConfigurable,
setup: DockerMysqlSetup, verify: DockerMysqlVerify, docker_manager: DockerManager):
self.__defaults_configurer = defaults_configurer
self.__is_dev_mode = is_dev_mode
self.__defaults = defaults
self.__configurer = configurer
self.__setup = setup
self.__verify = verify
Expand Down Expand Up @@ -45,7 +45,7 @@ def initialize(self):
self.__setup.set_convert_camel_to_snake(convert_camel_to_snake)
self.__verify.set_cursor(cursor)
self.__verify.set_convert_camel_to_snake(convert_camel_to_snake)
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def start(self):
run_result = self.__docker_manager.run_image('mysql:5.7.29', port=3306,
Expand Down Expand Up @@ -75,7 +75,7 @@ def stop(self):
self.__docker_manager.stop_container(self.__ui_container_id)

def reset(self):
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def services_available(self):
pass
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,9 @@ class DockerRabbitmqRunnable(INetworkedRunnable, IRabbitmqBehavior):
__USERNAME = 'guest'
__PASSWORD = 'guest'

def __init__(self, defaults: dict, configurer: IConfigurable, health_check: IUrlHealthCheckable,
def __init__(self, defaults_configurer: IConfigurable, configurer: IConfigurable, health_check: IUrlHealthCheckable,
setup: DockerRabbitmqSetup, verify: DockerRabbitmqVerify, docker_manager: DockerManager):
self.__defaults = defaults
self.__defaults_configurer = defaults_configurer
self.__configurer = configurer
self.__health_check = health_check
self.__setup = setup
Expand All @@ -45,7 +45,7 @@ def initialize(self):
self.__setup.set_connection_params(connection_params)
self.__verify.set_blocking_channel(channel)
if self.__configurer.get_config()['autoCreate']:
self.__setup.create_all(self.__defaults)
self.__setup.create_all(self.__defaults_configurer.get_config())

def start(self):
run_result = self.__docker_manager.run_image('rabbitmq:3.7.22-management-alpine', port=5672, ui_port=15672)
Expand All @@ -67,7 +67,7 @@ def reset(self):

def services_available(self):
if not self.__configurer.get_config()['autoCreate']:
self.__setup.create_shadow_queues(self.__defaults)
self.__setup.create_shadow_queues(self.__defaults_configurer.get_config())

def is_healthy(self) -> bool:
self.__health_check.set_url(self.get_network().ui_url())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

from touchstone.lib import exceptions
from touchstone.lib.docker_manager import DockerManager
from touchstone.lib.mocks.configurers.i_configurable import IConfigurable
from touchstone.lib.mocks.health_checks.i_url_health_checkable import IUrlHealthCheckable
from touchstone.lib.mocks.network import Network
from touchstone.lib.mocks.networked_runnables.i_networked_runnable import INetworkedRunnable
Expand All @@ -14,9 +15,9 @@ class DockerS3Runnable(INetworkedRunnable, IS3Behavior):
__USERNAME = 'admin123'
__PASSWORD = 'admin123'

def __init__(self, defaults: dict, base_objects_path: str, health_check: IUrlHealthCheckable, setup: DockerS3Setup,
verify: DockerS3Verify, docker_manager: DockerManager):
self.__defaults = defaults
def __init__(self, defaults_configurer: IConfigurable, base_objects_path: str, health_check: IUrlHealthCheckable,
setup: DockerS3Setup, verify: DockerS3Verify, docker_manager: DockerManager):
self.__defaults_configurer = defaults_configurer
self.__base_objects_path = base_objects_path
self.__health_check = health_check
self.__setup = setup
Expand All @@ -37,7 +38,7 @@ def initialize(self):
secure=False)
self.__setup.set_s3_client(s3_client)
self.__verify.set_s3_client(s3_client)
self.__setup.init(self.__base_objects_path, self.__defaults)
self.__setup.init(self.__base_objects_path, self.__defaults_configurer.get_config())

def start(self):
run_result = self.__docker_manager.run_image('minio/minio:RELEASE.2020-02-27T00-23-05Z server /data',
Expand All @@ -57,7 +58,7 @@ def stop(self):
self.__docker_manager.stop_container(self.__container_id)

def reset(self):
self.__setup.init(self.__base_objects_path, self.__defaults)
self.__setup.init(self.__base_objects_path, self.__defaults_configurer.get_config())

def services_available(self):
pass
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from touchstone.lib import exceptions
from touchstone.lib.mocks.configurers.i_configurable import IConfigurable
from touchstone.lib.mocks.runnables.filesystem.i_filesystem_behavior import IFilesystemBehavior, IFilesystemVerify, \
IFilesystemSetup
from touchstone.lib.mocks.runnables.filesystem.local.local_filesystem_setup import LocalFilesystemSetup
Expand All @@ -7,21 +8,21 @@


class LocalFilesystemRunnable(IRunnable, IFilesystemBehavior):
def __init__(self, defaults: dict, base_files_path: str, setup: LocalFilesystemSetup,
def __init__(self, defaults_configurer: IConfigurable, base_files_path: str, setup: LocalFilesystemSetup,
verify: LocalFilesystemVerify):
self.__defaults = defaults
self.__defaults_configurer = defaults_configurer
self.__base_files_path = base_files_path
self.__setup = setup
self.__verify = verify

def start(self):
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def stop(self):
self.__setup.delete_defaults(self.__defaults)
self.__setup.delete_defaults(self.__defaults_configurer.get_config())

def reset(self):
self.__setup.init(self.__defaults)
self.__setup.init(self.__defaults_configurer.get_config())

def services_available(self):
pass
Expand Down

0 comments on commit 59980d0

Please sign in to comment.