diff --git a/moto/swf/exceptions.py b/moto/swf/exceptions.py index abeb348b7..a2e12fd73 100644 --- a/moto/swf/exceptions.py +++ b/moto/swf/exceptions.py @@ -119,3 +119,8 @@ class SWFDecisionValidationException(SWFClientError): prefix.format(count) + "; ".join(messages), "com.amazon.coral.validate#ValidationException" ) + + +class SWFWorkflowExecutionClosedError(Exception): + def __str__(self): + return repr("Cannot change this object because the WorkflowExecution is closed") diff --git a/moto/swf/models/activity_task.py b/moto/swf/models/activity_task.py index 91f6f0b21..1f011cb8d 100644 --- a/moto/swf/models/activity_task.py +++ b/moto/swf/models/activity_task.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from datetime import datetime import uuid +from ..exceptions import SWFWorkflowExecutionClosedError from ..utils import now_timestamp @@ -24,6 +25,10 @@ class ActivityTask(object): # but that shouldn't be a problem for tests self.scheduled_at = datetime.now() + def _check_workflow_execution_open(self): + if not self.workflow_execution.open: + raise SWFWorkflowExecutionClosedError() + @property def open(self): return self.state in ["SCHEDULED", "STARTED"] @@ -45,9 +50,11 @@ class ActivityTask(object): self.started_event_id = started_event_id def complete(self): + self._check_workflow_execution_open() self.state = "COMPLETED" def fail(self): + self._check_workflow_execution_open() self.state = "FAILED" def reset_heartbeat_clock(self): @@ -63,5 +70,9 @@ class ActivityTask(object): def process_timeouts(self): if self.has_timedout(): - self.state = "TIMED_OUT" - self.timeout_type = "HEARTBEAT" + self.timeout() + + def timeout(self): + self._check_workflow_execution_open() + self.state = "TIMED_OUT" + self.timeout_type = "HEARTBEAT" diff --git a/moto/swf/models/decision_task.py b/moto/swf/models/decision_task.py index d024ae118..23822976c 100644 --- a/moto/swf/models/decision_task.py +++ b/moto/swf/models/decision_task.py @@ -2,6 +2,7 @@ from __future__ import unicode_literals from datetime import datetime import uuid +from ..exceptions import SWFWorkflowExecutionClosedError from ..utils import now_timestamp @@ -21,6 +22,10 @@ class DecisionTask(object): self.scheduled_at = datetime.now() self.timeout_type = None + def _check_workflow_execution_open(self): + if not self.workflow_execution.open: + raise SWFWorkflowExecutionClosedError() + def to_full_dict(self, reverse_order=False): events = self.workflow_execution.events(reverse_order=reverse_order) hsh = { @@ -42,6 +47,7 @@ class DecisionTask(object): self.started_event_id = started_event_id def complete(self): + self._check_workflow_execution_open() self.state = "COMPLETED" def has_timedout(self): @@ -54,5 +60,9 @@ class DecisionTask(object): def process_timeouts(self): if self.has_timedout(): - self.state = "TIMED_OUT" - self.timeout_type = "START_TO_CLOSE" + self.timeout() + + def timeout(self): + self._check_workflow_execution_open() + self.state = "TIMED_OUT" + self.timeout_type = "START_TO_CLOSE" diff --git a/tests/test_swf/models/test_activity_task.py b/tests/test_swf/models/test_activity_task.py index f9f0e2ef7..8b81cbdd5 100644 --- a/tests/test_swf/models/test_activity_task.py +++ b/tests/test_swf/models/test_activity_task.py @@ -1,6 +1,7 @@ from freezegun import freeze_time from sure import expect +from moto.swf.exceptions import SWFWorkflowExecutionClosedError from moto.swf.models import ( ActivityTask, ActivityType, @@ -123,3 +124,21 @@ def test_activity_task_cannot_timeout_on_closed_workflow_execution(): wfe.has_timedout().should.equal(True) wfe.process_timeouts() task.has_timedout().should.equal(False) + +def test_activity_task_cannot_change_state_on_closed_workflow_execution(): + wfe = make_workflow_execution() + wfe.start() + + task = ActivityTask( + activity_id="my-activity-123", + activity_type="foo", + input="optional", + scheduled_event_id=117, + timeouts=ACTIVITY_TASK_TIMEOUTS, + workflow_execution=wfe, + ) + wfe.complete(123) + + task.timeout.when.called_with().should.throw(SWFWorkflowExecutionClosedError) + task.complete.when.called_with().should.throw(SWFWorkflowExecutionClosedError) + task.fail.when.called_with().should.throw(SWFWorkflowExecutionClosedError) diff --git a/tests/test_swf/models/test_decision_task.py b/tests/test_swf/models/test_decision_task.py index f0efb94c0..ae2b59fdc 100644 --- a/tests/test_swf/models/test_decision_task.py +++ b/tests/test_swf/models/test_decision_task.py @@ -1,6 +1,7 @@ from freezegun import freeze_time from sure import expect +from moto.swf.exceptions import SWFWorkflowExecutionClosedError from moto.swf.models import DecisionTask from ..utils import make_workflow_execution @@ -61,3 +62,13 @@ def test_decision_task_cannot_timeout_on_closed_workflow_execution(): wfe.has_timedout().should.equal(True) wfe.process_timeouts() dt.has_timedout().should.equal(False) + +def test_decision_task_cannot_change_state_on_closed_workflow_execution(): + wfe = make_workflow_execution() + wfe.start() + task = DecisionTask(wfe, 123) + + wfe.complete(123) + + task.timeout.when.called_with().should.throw(SWFWorkflowExecutionClosedError) + task.complete.when.called_with().should.throw(SWFWorkflowExecutionClosedError)