def testEnqueueCommandEvents_malformedEvents(self):
    """A malformed event should not lose all events."""
    _, request_id, _, command_id = self.command.key.flat()
    command_event_test_util.CreateCommandAttempt(
        self.command, "aid", common.CommandState.QUEUED)
    event = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_id, "aid", "InvocationStarted")
    malformed_event = {
        "data": {"total_test_count": 1, "exec_test_count": 1},
        "time": TIMESTAMP_INT,
        "type": "TestRunInProgress",
    }

    command_event_handler.EnqueueCommandEvents([event, malformed_event])
    tasks = self.mock_task_scheduler.GetTasks()
    self.assertEqual(len(tasks), 2)
    self.testapp.post(
        command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[0].payload)
    with self.assertRaises(webtest.app.AppError):
      self.testapp.post(
          command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[1].payload)

    command_attempts = command_manager.GetCommandAttempts(
        request_id, command_id)
    self.assertEqual(len(command_attempts), 1)
    self.assertEqual(common.CommandState.RUNNING, command_attempts[0].state)
  def testEnqueueCommandEvents_multipleEvents(self):
    self.request = request_manager.CreateRequest(
        request_id="9999",
        user="******",
        command_infos=[
            datastore_entities.CommandInfo(
                command_line="command_line",
                cluster="cluster",
                run_target="run_target",
                shard_count=2)
        ])
    command_1, command_2 = command_manager.CreateCommands(
        request_id=self.request.key.id(),
        command_infos=[
            datastore_entities.CommandInfo(
                command_line="long command line 0",
                cluster="foobar",
                run_target="foo",
                run_count=1,
                shard_count=2),
            datastore_entities.CommandInfo(
                command_line="long command line 1",
                cluster="foobar",
                run_target="foo",
                run_count=1,
                shard_count=2)
        ],
        shard_indexes=list(range(2)))
    _, request_id, _, command_1_id = command_1.key.flat()
    _, _, _, command_2_id = command_2.key.flat()
    command_event_test_util.CreateCommandAttempt(
        command_1, "aid", common.CommandState.QUEUED)
    command_event_test_util.CreateCommandAttempt(
        command_2, "aid", common.CommandState.QUEUED)

    event = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_1_id, "aid", "InvocationStarted")
    event2 = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_2_id, "aid", "InvocationStarted")
    event3 = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_1_id, "aid", "InvocationCompleted")
    event4 = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_2_id, "aid", "InvocationCompleted")
    command_event_handler.EnqueueCommandEvents([event, event2, event3, event4])

    tasks = self.mock_task_scheduler.GetTasks()
    self.assertEqual(len(tasks), 4)
    for task in tasks:
      self.testapp.post(
          command_event_handler.COMMAND_EVENT_HANDLER_PATH, task.payload)

    command_attempts = command_manager.GetCommandAttempts(
        request_id, command_1_id)
    self.assertEqual(len(command_attempts), 1)
    self.assertEqual(common.CommandState.COMPLETED, command_attempts[0].state)
    command_attempts = command_manager.GetCommandAttempts(
        request_id, command_2_id)
    self.assertEqual(len(command_attempts), 1)
    self.assertEqual(common.CommandState.COMPLETED, command_attempts[0].state)
  def SubmitCommandEvents(self, request):
    """Submit a bundle of cluster command events for processing.

    Args:
      request: a CommandEventList
    Returns:
      a VoidMessage
    """

    # Convert the request message to json for the taskqueue payload
    json_message = json.loads(protojson.encode_message(request))  # pytype: disable=module-attr
    command_event_handler.EnqueueCommandEvents(json_message["command_events"])
    return message_types.VoidMessage()
  def testEnqueueCommandEvents(self):
    _, request_id, _, command_id = self.command.key.flat()
    command_event_test_util.CreateCommandAttempt(
        self.command, "aid", common.CommandState.QUEUED)
    event = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_id, "aid", "InvocationCompleted")

    command_event_handler.EnqueueCommandEvents([event])
    tasks = self.mock_task_scheduler.GetTasks()
    self.assertEqual(len(tasks), 1)
    self.testapp.post(
        command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[0].payload)

    command_attempts = command_manager.GetCommandAttempts(
        request_id, command_id)
    self.assertEqual(len(command_attempts), 1)
    self.assertEqual(common.CommandState.COMPLETED, command_attempts[0].state)
  def testEnqueueCommandEvents_oldCommandEvents(self, mock_process):
    _, request_id, _, command_id = self.command.key.flat()
    command_event_test_util.CreateCommandAttempt(
        self.command, "aid", common.CommandState.QUEUED)
    event = command_event_test_util.CreateTestCommandEventJson(
        request_id, command_id, "aid", "InvocationCompleted")
    self.mock_now.return_value = (
        TIMESTAMP + datetime.timedelta(
            days=command_event_handler.COMMAND_EVENT_TIMEOUT_DAYS + 1))

    command_event_handler.EnqueueCommandEvents([event])
    tasks = self.mock_task_scheduler.GetTasks()
    self.assertEqual(len(tasks), 1)
    self.testapp.post(
        command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[0].payload)
    # Old command event is ignored.
    self.assertFalse(mock_process.called)
  def testEnqueueCommandEvents_transactionError(self, mock_process):
    event = command_event_test_util.CreateTestCommandEventJson(
        "rid", "cid", "aid", "InvocationStarted")
    event2 = command_event_test_util.CreateTestCommandEventJson(
        "rid", "cid", "aid", "InvocationCompleted")

    # for the first time, the second event failed due to TransactionFailedError
    # the second event will be reschedule to the queue.
    mock_process.side_effect = [None, ndb.exceptions.ContextError(), None]

    command_event_handler.EnqueueCommandEvents([event, event2])
    tasks = self.mock_task_scheduler.GetTasks()
    self.assertEqual(len(tasks), 2)
    self.testapp.post(
        command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[0].payload)
    with self.assertRaises(webtest.app.AppError):
      self.testapp.post(
          command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[1].payload)
    # Simulate a task retry.
    self.testapp.post(
        command_event_handler.COMMAND_EVENT_HANDLER_PATH, tasks[1].payload)

    mock_process.assert_has_calls([
        mock.call(
            hamcrest.match_equality(
                hamcrest.all_of(
                    hamcrest.has_property("task_id", event["task_id"]),
                    hamcrest.has_property("type", event["type"])))),
        mock.call(
            hamcrest.match_equality(
                hamcrest.all_of(
                    hamcrest.has_property("task_id", event2["task_id"]),
                    hamcrest.has_property("type", event2["type"])))),
        # this is the retry.
        mock.call(
            hamcrest.match_equality(
                hamcrest.all_of(
                    hamcrest.has_property("task_id", event2["task_id"]),
                    hamcrest.has_property("type", event2["type"]))))
    ])