# Copyright 2017 Google Inc.
#
# 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.

import random
import string
import unittest
from unittest import mock


class JsonRpcClientTestBase(unittest.TestCase):
  """Base class for tests of JSONRPC clients.

  Contains infrastructure for mocking responses.
  """

  MOCK_RESP = (
      b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1, '
      b'"callback": null}'
  )
  MOCK_RESP_WITHOUT_CALLBACK = (
      b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1}'
  )
  MOCK_RESP_TEMPLATE = (
      '{"id": %d, "result": 123, "error": null, "status": 1, "uid": 1, '
      '"callback": null}'
  )
  MOCK_RESP_UNKNOWN_STATUS = (
      b'{"id": 0, "result": 123, "error": null, "status": 0, "callback": null}'
  )
  MOCK_RESP_WITH_CALLBACK = (
      b'{"id": 0, "result": 123, "error": null, "status": 1, "uid": 1, '
      b'"callback": "1-0"}'
  )
  MOCK_RESP_WITH_ERROR = b'{"id": 0, "error": 1, "status": 1, "uid": 1}'
  MOCK_RESP_FLEXIABLE_RESULT_LENGTH = (
      '{"id": 0, "result": "%s", "error": null, "status": 0, "callback": null}'
  )

  class MockSocketFile:

    def __init__(self, resp):
      self.resp = resp
      self.last_write = None

    def write(self, msg):
      self.last_write = msg

    def readline(self):
      return self.resp

    def flush(self):
      pass

  def setup_mock_socket_file(self, mock_create_connection, resp=MOCK_RESP):
    """Sets up a fake socket file from the mock connection.

    Args:
      mock_create_connection: The mock method for creating a method.
      resp: (str) response to give. MOCK_RESP by default.

    Returns:
      The mock file that will be injected into the code.
    """
    fake_file = self.MockSocketFile(resp)
    fake_conn = mock.MagicMock()
    fake_conn.makefile.return_value = fake_file
    mock_create_connection.return_value = fake_conn
    return fake_file

  def generate_rpc_response(self, response_length=1024):
    length = response_length - len(self.MOCK_RESP_FLEXIABLE_RESULT_LENGTH) + 2
    chars = string.ascii_letters + string.digits
    random_msg = ''.join(random.choice(chars) for i in range(length))
    mock_response = self.MOCK_RESP_FLEXIABLE_RESULT_LENGTH % random_msg
    return bytes(mock_response, 'utf-8')
