def test_send_auth_token_required(monkeypatch):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 4, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )

    mock_post = mock.MagicMock(side_effect=Exception())
    monkeypatch.setattr('requests.sessions.Session.post', mock_post)

    def user_password_input(prompt):
        if prompt == 'IonQ apiKey > ':
            return None

    monkeypatch.setattr('getpass.getpass', user_password_input)

    # Code to test:
    info = {
        'nq': 1,
        'shots': 1,
        'meas_mapped': [],
        'meas_qubit_ids': [],
        'circuit': [],
    }
    with pytest.raises(RuntimeError) as excinfo:
        _ionq_http_client.send(info, device='dummy')
    mock_post.assert_not_called()
    assert 'An authentication token is required!' == str(excinfo.value)
def test_send_auth_errors_reraise(monkeypatch):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 4, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )

    mock_response = mock.MagicMock()
    mock_response.status_code = 401
    auth_error = requests.exceptions.HTTPError(response=mock_response)
    mock_post = mock.MagicMock(side_effect=auth_error)
    monkeypatch.setattr('requests.sessions.Session.post', mock_post)

    def user_password_input(prompt):
        if prompt == 'IonQ apiKey > ':
            return 'NotNone'

    monkeypatch.setattr('getpass.getpass', user_password_input)

    # Code to test:
    info = {
        'nq': 1,
        'shots': 1,
        'meas_mapped': [],
        'meas_qubit_ids': [],
        'circuit': [],
    }
    with pytest.raises(requests.exceptions.HTTPError) as excinfo:
        _ionq_http_client.send(info, device='dummy')
    mock_post.assert_called_once()
    assert auth_error is excinfo.value
def test_send_requests_errors_are_caught(monkeypatch, error_type):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 4, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )
    mock_post = mock.MagicMock(side_effect=error_type())
    monkeypatch.setattr('requests.sessions.Session.post', mock_post)

    def user_password_input(prompt):
        if prompt == 'IonQ apiKey > ':
            return 'NotNone'

    monkeypatch.setattr('getpass.getpass', user_password_input)

    # Code to test:
    info = {
        'nq': 1,
        'shots': 1,
        'meas_mapped': [],
        'meas_qubit_ids': [],
        'circuit': [],
    }
    _ionq_http_client.send(info, device='dummy')
    mock_post.assert_called_once()
def test_timeout_exception(monkeypatch):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 4, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )

    def mock_post(_self, path, *args, **kwargs):
        assert path == 'https://api.ionq.co/v0.2/jobs'
        mock_response = mock.MagicMock()
        mock_response.json = mock.MagicMock(return_value={
            'id': 'new-job-id',
            'status': 'ready',
        })
        return mock_response

    def mock_get(_self, path, *args, **kwargs):
        assert path == 'https://api.ionq.co/v0.2/jobs/new-job-id'
        mock_response = mock.MagicMock()
        mock_response.json = mock.MagicMock(return_value={
            'id': 'new-job-id',
            'status': 'running',
        })
        return mock_response

    monkeypatch.setattr('requests.sessions.Session.post', mock_post)
    monkeypatch.setattr('requests.sessions.Session.get', mock_get)

    def user_password_input(prompt):
        if prompt == 'IonQ apiKey > ':
            return 'NotNone'

    monkeypatch.setattr('getpass.getpass', user_password_input)

    # Called once per loop in _get_result while the job is not ready.
    mock_sleep = mock.MagicMock()
    monkeypatch.setattr(_ionq_http_client.time, 'sleep', mock_sleep)

    # RequestTimeoutErrors are not caught, and so will raise out.
    with pytest.raises(RequestTimeoutError) as excinfo:
        info = {
            'nq': 1,
            'shots': 1,
            'meas_mapped': [],
            'meas_qubit_ids': [],
            'circuit': [],
        }
        _ionq_http_client.send(info, device='dummy', num_retries=1)
    mock_sleep.assert_called_once()
    assert 'Timeout. The ID of your submitted job is new-job-id.' == str(
        excinfo.value)
def test_send_too_many_qubits(monkeypatch):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 3, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )
    info = {
        'nq':
        4,
        'shots':
        1,
        'meas_mapped': [2, 3],
        'circuit': [
            {
                'gate': 'x',
                'targets': [0]
            },
            {
                'gate': 'x',
                'targets': [1]
            },
            {
                'controls': [0],
                'gate': 'cnot',
                'targets': [2]
            },
            {
                'controls': [1],
                'gate': 'cnot',
                'targets': [2]
            },
            {
                'controls': [0, 1],
                'gate': 'cnot',
                'targets': [3]
            },
        ],
    }
    with pytest.raises(_ionq_http_client.DeviceTooSmall):
        _ionq_http_client.send(
            info,
            device='dummy',
            token='NotNone',
            verbose=True,
        )
def test_send_api_errors_are_raised(monkeypatch, expected_err, err_data):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 4, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )

    def mock_post(_self, path, **kwargs):
        assert path == 'https://api.ionq.co/v0.2/jobs'
        mock_response = mock.MagicMock()
        mock_response.json = mock.MagicMock(return_value=err_data)
        return mock_response

    monkeypatch.setattr('requests.sessions.Session.post', mock_post)

    def user_password_input(prompt):
        if prompt == 'IonQ apiKey > ':
            return 'NotNone'

    monkeypatch.setattr('getpass.getpass', user_password_input)

    # Code to test:
    info = {
        'nq': 1,
        'shots': 1,
        'meas_mapped': [],
        'meas_qubit_ids': [],
        'circuit': [],
    }
    with pytest.raises(JobSubmissionError) as excinfo:
        _ionq_http_client.send(info, device='dummy')

    assert expected_err == str(excinfo.value)
def test_send_real_device_online_verbose(monkeypatch):
    # Patch the method to give back dummy devices
    def _dummy_update(_self):
        _self.backends = {'dummy': {'nq': 4, 'target': 'dummy'}}

    monkeypatch.setattr(
        _ionq_http_client.IonQ,
        'update_devices_list',
        _dummy_update.__get__(None, _ionq_http_client.IonQ),
    )
    # What the IonQ JSON API request should look like.
    expected_request = {
        'target': 'dummy',
        'metadata': {
            'sdk': 'ProjectQ',
            'meas_qubit_ids': '[2, 3]'
        },
        'shots': 1,
        'registers': {
            'meas_mapped': [2, 3]
        },
        'lang': 'json',
        'body': {
            'qubits':
            4,
            'circuit': [
                {
                    'gate': 'x',
                    'targets': [0]
                },
                {
                    'gate': 'x',
                    'targets': [1]
                },
                {
                    'controls': [0],
                    'gate': 'cnot',
                    'targets': [2]
                },
                {
                    'controls': [1],
                    'gate': 'cnot',
                    'targets': [2]
                },
                {
                    'controls': [0, 1],
                    'gate': 'cnot',
                    'targets': [3]
                },
            ],
        },
    }

    def mock_post(_self, path, *args, **kwargs):
        assert path == 'https://api.ionq.co/v0.2/jobs'
        assert 'json' in kwargs
        assert expected_request == kwargs['json']
        mock_response = mock.MagicMock()
        mock_response.status_code = 200
        mock_response.json = mock.MagicMock(return_value={
            'id': 'new-job-id',
            'status': 'ready',
        })
        return mock_response

    def mock_get(_self, path, *args, **kwargs):
        assert path == 'https://api.ionq.co/v0.2/jobs/new-job-id'
        mock_response = mock.MagicMock()
        mock_response.json = mock.MagicMock(
            return_value={
                'id': 'new-job-id',
                'status': 'completed',
                'qubits': 4,
                'metadata': {
                    'meas_qubit_ids': '[2, 3]'
                },
                'registers': {
                    'meas_mapped': [2, 3]
                },
                'data': {
                    'registers': {
                        'meas_mapped': {
                            '2': 1
                        }
                    },
                },
            })
        return mock_response

    monkeypatch.setattr('requests.sessions.Session.post', mock_post)
    monkeypatch.setattr('requests.sessions.Session.get', mock_get)

    def user_password_input(prompt):
        if prompt == 'IonQ apiKey > ':
            return 'NotNone'

    monkeypatch.setattr('getpass.getpass', user_password_input)

    # Code to test:
    info = {
        'nq':
        4,
        'shots':
        1,
        'meas_mapped': [2, 3],
        'meas_qubit_ids': [2, 3],
        'circuit': [
            {
                'gate': 'x',
                'targets': [0]
            },
            {
                'gate': 'x',
                'targets': [1]
            },
            {
                'controls': [0],
                'gate': 'cnot',
                'targets': [2]
            },
            {
                'controls': [1],
                'gate': 'cnot',
                'targets': [2]
            },
            {
                'controls': [0, 1],
                'gate': 'cnot',
                'targets': [3]
            },
        ],
    }
    expected = {
        'nq': 4,
        'output_probs': {
            '2': 1
        },
        'meas_mapped': [2, 3],
        'meas_qubit_ids': [2, 3],
    }
    actual = _ionq_http_client.send(info, device='dummy')
    assert expected == actual