Skip to content

Commit

Permalink
FIX: Use service patcher when instantiating service in child emulator
Browse files Browse the repository at this point in the history
  • Loading branch information
cortadocodes committed Jul 27, 2023
1 parent baa6133 commit cd77b1e
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 58 deletions.
15 changes: 8 additions & 7 deletions octue/cloud/emulators/child.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,14 +31,15 @@ def __init__(self, id=None, backend=None, internal_service_name="local/local:loc
backend_type_name = backend.pop("name")
backend = service_backends.get_backend(backend_type_name)(**backend)

self._child = MockService(service_id=id, backend=backend, run_function=self._emulate_analysis)
self.id = self._child.id
with ServicePatcher():
self._child = MockService(service_id=id, backend=backend, run_function=self._emulate_analysis)
self.id = self._child.id

self._parent = MockService(
backend=backend,
service_id=internal_service_name,
children={self._child.id: self._child},
)
self._parent = MockService(
backend=backend,
service_id=internal_service_name,
children={self._child.id: self._child},
)

self._message_handlers = {
"delivery_acknowledgement": self._handle_delivery_acknowledgement,
Expand Down
5 changes: 2 additions & 3 deletions tests/cloud/emulators/test_child_emulator.py
Original file line number Diff line number Diff line change
Expand Up @@ -245,10 +245,9 @@ def test_messages_recorded_from_real_child_can_be_used_in_child_emulator(self):
def error_run_function(*args, **kwargs):
raise OSError("Oh no. Some error has been raised for testing.")

child = MockService(backend=backend, run_function=error_run_function)
parent = MockService(backend=backend, children={child.id: child})

with ServicePatcher():
child = MockService(backend=backend, run_function=error_run_function)
parent = MockService(backend=backend, children={child.id: child})
child.serve()

with self.assertRaises(OSError):
Expand Down
107 changes: 59 additions & 48 deletions tests/resources/test_child.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,26 @@

from google.auth.exceptions import DefaultCredentialsError

from octue.cloud.emulators._pub_sub import MockAnalysis, MockService, MockSubscriber, MockSubscription, MockTopic
from octue.cloud.emulators._pub_sub import MockAnalysis, MockService, MockSubscriber, MockSubscription
from octue.cloud.emulators.child import ServicePatcher
from octue.resources.child import Child
from octue.resources.service_backends import GCPPubSubBackend
from tests import MOCK_SERVICE_REVISION_TAG
from tests.base import BaseTestCase


service_patcher = ServicePatcher()


class TestChild(BaseTestCase):
@classmethod
def setUpClass(cls):
service_patcher.start()

@classmethod
def tearDownClass(cls):
service_patcher.stop()

def test_representation(self):
"""Test that children are represented correctly as a string."""
self.assertEqual(
Expand All @@ -39,18 +50,21 @@ def test_instantiating_child_without_credentials(self):
def test_child_cannot_be_asked_question_without_credentials(self):
"""Test that a child cannot be asked a question without Google Cloud credentials being available."""
with patch.dict(os.environ, clear=True):
with patch("octue.cloud.pub_sub.service.Topic", new=MockTopic):
with patch("octue.cloud.pub_sub.service.Subscription", new=MockSubscription):
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
with patch("google.cloud.pubsub_v1.SubscriberClient", new=MockSubscriber):
with patch("octue.cloud.pub_sub.service.Subscription", new=MockSubscription):
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
with patch("google.cloud.pubsub_v1.SubscriberClient", new=MockSubscriber):

child = Child(
id=f"octue/my-child:{MOCK_SERVICE_REVISION_TAG}",
backend={"name": "GCPPubSubBackend", "project_name": "blah"},
)
service_patcher.stop()

with self.assertRaises(DefaultCredentialsError):
child.ask({"some": "input"})
child = Child(
id=f"octue/my-child:{MOCK_SERVICE_REVISION_TAG}",
backend={"name": "GCPPubSubBackend", "project_name": "blah"},
)

with self.assertRaises(DefaultCredentialsError):
child.ask({"some": "input"})

service_patcher.start()

def test_child_can_be_asked_multiple_questions(self):
"""Test that a child can be asked multiple questions."""
Expand All @@ -64,16 +78,15 @@ def mock_run_function(analysis_id, input_values, *args, **kwargs):
run_function=mock_run_function,
)

with ServicePatcher():
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
responding_service.serve()
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
responding_service.serve()

child = Child(id=responding_service.id, backend={"name": "GCPPubSubBackend", "project_name": "blah"})
child = Child(id=responding_service.id, backend={"name": "GCPPubSubBackend", "project_name": "blah"})

# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service
self.assertEqual(child.ask([1, 2, 3, 4])["output_values"], [1, 2, 3, 4])
self.assertEqual(child.ask([5, 6, 7, 8])["output_values"], [5, 6, 7, 8])
# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service
self.assertEqual(child.ask([1, 2, 3, 4])["output_values"], [1, 2, 3, 4])
self.assertEqual(child.ask([5, 6, 7, 8])["output_values"], [5, 6, 7, 8])

def test_child_can_be_asked_questions_in_parallel(self):
"""Test that a child can be asked multiple questions in parallel and return the answers in the correct order."""
Expand All @@ -88,27 +101,26 @@ def mock_run_function(analysis_id, input_values, *args, **kwargs):
run_function=mock_run_function,
)

with ServicePatcher():
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
responding_service.serve()
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
responding_service.serve()

child = Child(id=responding_service.id, backend={"name": "GCPPubSubBackend", "project_name": "blah"})
child = Child(id=responding_service.id, backend={"name": "GCPPubSubBackend", "project_name": "blah"})

# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service
# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service

answers = child.ask_multiple(
{"input_values": [1, 2, 3, 4]},
{"input_values": [5, 6, 7, 8]},
)
answers = child.ask_multiple(
{"input_values": [1, 2, 3, 4]},
{"input_values": [5, 6, 7, 8]},
)

self.assertEqual(
answers,
[
{"output_values": [1, 2, 3, 4], "output_manifest": None},
{"output_values": [5, 6, 7, 8], "output_manifest": None},
],
)
self.assertEqual(
answers,
[
{"output_values": [1, 2, 3, 4], "output_manifest": None},
{"output_values": [5, 6, 7, 8], "output_manifest": None},
],
)

def test_error_raised_when_using_ask_multiple_and_one_question_fails(self):
"""Test that an error is raised if any of the questions given to `Child.ask_multiple` fail."""
Expand All @@ -128,18 +140,17 @@ def mock_run_function_that_sometimes_fails(analysis_id, input_values, *args, **k
run_function=functools.partial(mock_run_function_that_sometimes_fails, runs=Value("d", 0)),
)

with ServicePatcher():
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
responding_service.serve()
with patch("octue.resources.child.BACKEND_TO_SERVICE_MAPPING", {"GCPPubSubBackend": MockService}):
responding_service.serve()

child = Child(id=responding_service.id, backend={"name": "GCPPubSubBackend", "project_name": "blah"})
child = Child(id=responding_service.id, backend={"name": "GCPPubSubBackend", "project_name": "blah"})

# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service
# Make sure the child's underlying mock service knows how to access the mock responding service.
child._service.children[responding_service.id] = responding_service

with self.assertRaises(Exception):
child.ask_multiple(
{"input_values": [1, 2, 3, 4]},
{"input_values": [5, 6, 7, 8]},
{"input_values": [9, 10, 11, 12]},
)
with self.assertRaises(Exception):
child.ask_multiple(
{"input_values": [1, 2, 3, 4]},
{"input_values": [5, 6, 7, 8]},
{"input_values": [9, 10, 11, 12]},
)

0 comments on commit cd77b1e

Please sign in to comment.