1# Copyright 2020 The Cirq Developers 2# Licensed under the Apache License, Version 2.0 (the "License"); 3# you may not use this file except in compliance with the License. 4# You may obtain a copy of the License at 5# 6# https://www.apache.org/licenses/LICENSE-2.0 7# 8# Unless required by applicable law or agreed to in writing, software 9# distributed under the License is distributed on an "AS IS" BASIS, 10# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 11# See the License for the specific language governing permissions and 12# limitations under the License. 13 14import contextlib 15import datetime 16import io 17from unittest import mock 18 19import requests 20import pytest 21 22import cirq_ionq as ionq 23 24 25def test_ionq_exception_str(): 26 ex = ionq.IonQException('err', status_code=501) 27 assert str(ex) == 'Status code: 501, Message: \'err\'' 28 29 30def test_ionq_not_found_exception_str(): 31 ex = ionq.IonQNotFoundException('err') 32 assert str(ex) == 'Status code: 404, Message: \'err\'' 33 34 35def test_ionq_client_invalid_remote_host(): 36 for invalid_url in ('', 'url', 'http://', 'ftp://', 'http://'): 37 with pytest.raises(AssertionError, match='not a valid url'): 38 _ = ionq.ionq_client._IonQClient(remote_host=invalid_url, api_key='a') 39 with pytest.raises(AssertionError, match=invalid_url): 40 _ = ionq.ionq_client._IonQClient(remote_host=invalid_url, api_key='a') 41 42 43def test_ionq_client_invalid_api_version(): 44 with pytest.raises(AssertionError, match='is accepted'): 45 _ = ionq.ionq_client._IonQClient( 46 remote_host='http://example.com', api_key='a', api_version='v0.0' 47 ) 48 with pytest.raises(AssertionError, match='0.0'): 49 _ = ionq.ionq_client._IonQClient( 50 remote_host='http://example.com', api_key='a', api_version='v0.0' 51 ) 52 53 54def test_ionq_client_invalid_target(): 55 with pytest.raises(AssertionError, match='the store'): 56 _ = ionq.ionq_client._IonQClient( 57 remote_host='http://example.com', api_key='a', default_target='the store' 58 ) 59 with pytest.raises(AssertionError, match='Target'): 60 _ = ionq.ionq_client._IonQClient( 61 remote_host='http://example.com', api_key='a', default_target='the store' 62 ) 63 64 65def test_ionq_client_time_travel(): 66 with pytest.raises(AssertionError, match='time machine'): 67 _ = ionq.ionq_client._IonQClient( 68 remote_host='http://example.com', api_key='a', max_retry_seconds=-1 69 ) 70 71 72def test_ionq_client_attributes(): 73 client = ionq.ionq_client._IonQClient( 74 remote_host='http://example.com', 75 api_key='to_my_heart', 76 default_target='qpu', 77 max_retry_seconds=10, 78 verbose=True, 79 ) 80 assert client.url == 'http://example.com/v0.1' 81 assert client.headers == { 82 'Authorization': 'apiKey to_my_heart', 83 'Content-Type': 'application/json', 84 } 85 assert client.default_target == 'qpu' 86 assert client.max_retry_seconds == 10 87 assert client.verbose == True 88 89 90@mock.patch('requests.post') 91def test_ionq_client_create_job(mock_post): 92 mock_post.return_value.status_code.return_value = requests.codes.ok 93 mock_post.return_value.json.return_value = {'foo': 'bar'} 94 95 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 96 program = ionq.SerializedProgram(body={'job': 'mine'}, metadata={'a': '0,1'}) 97 response = client.create_job( 98 serialized_program=program, repetitions=200, target='qpu', name='bacon' 99 ) 100 assert response == {'foo': 'bar'} 101 102 expected_json = { 103 'target': 'qpu', 104 'body': {'job': 'mine'}, 105 'lang': 'json', 106 'name': 'bacon', 107 'shots': '200', 108 'metadata': {'shots': '200', 'a': '0,1'}, 109 } 110 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 111 mock_post.assert_called_with( 112 'http://example.com/v0.1/jobs', json=expected_json, headers=expected_headers 113 ) 114 115 116@mock.patch('requests.post') 117def test_ionq_client_create_job_default_target(mock_post): 118 mock_post.return_value.status_code.return_value = requests.codes.ok 119 mock_post.return_value.json.return_value = {'foo'} 120 121 client = ionq.ionq_client._IonQClient( 122 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 123 ) 124 _ = client.create_job(ionq.SerializedProgram(body={'job': 'mine'}, metadata={})) 125 assert mock_post.call_args[1]['json']['target'] == 'simulator' 126 127 128@mock.patch('requests.post') 129def test_ionq_client_create_job_target_overrides_default_target(mock_post): 130 mock_post.return_value.status_code.return_value = requests.codes.ok 131 mock_post.return_value.json.return_value = {'foo'} 132 133 client = ionq.ionq_client._IonQClient( 134 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 135 ) 136 _ = client.create_job( 137 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}), 138 target='qpu', 139 repetitions=1, 140 ) 141 assert mock_post.call_args[1]['json']['target'] == 'qpu' 142 143 144def test_ionq_client_create_job_no_targets(): 145 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 146 with pytest.raises(AssertionError, match='neither were set'): 147 _ = client.create_job( 148 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 149 ) 150 151 152@mock.patch('requests.post') 153def test_ionq_client_create_job_unauthorized(mock_post): 154 mock_post.return_value.ok = False 155 mock_post.return_value.status_code = requests.codes.unauthorized 156 157 client = ionq.ionq_client._IonQClient( 158 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 159 ) 160 with pytest.raises(ionq.IonQException, match='Not authorized'): 161 _ = client.create_job( 162 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 163 ) 164 165 166@mock.patch('requests.post') 167def test_ionq_client_create_job_not_found(mock_post): 168 mock_post.return_value.ok = False 169 mock_post.return_value.status_code = requests.codes.not_found 170 171 client = ionq.ionq_client._IonQClient( 172 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 173 ) 174 with pytest.raises(ionq.IonQNotFoundException, match='not find'): 175 _ = client.create_job( 176 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 177 ) 178 179 180@mock.patch('requests.post') 181def test_ionq_client_create_job_not_retriable(mock_post): 182 mock_post.return_value.ok = False 183 mock_post.return_value.status_code = requests.codes.not_implemented 184 185 client = ionq.ionq_client._IonQClient( 186 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 187 ) 188 with pytest.raises(ionq.IonQException, match='Status: 501'): 189 _ = client.create_job( 190 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 191 ) 192 193 194@mock.patch('requests.post') 195def test_ionq_client_create_job_retry(mock_post): 196 response1 = mock.MagicMock() 197 response2 = mock.MagicMock() 198 mock_post.side_effect = [response1, response2] 199 response1.ok = False 200 response1.status_code = requests.codes.service_unavailable 201 response2.ok = True 202 client = ionq.ionq_client._IonQClient( 203 remote_host='http://example.com', 204 api_key='to_my_heart', 205 default_target='simulator', 206 verbose=True, 207 ) 208 test_stdout = io.StringIO() 209 with contextlib.redirect_stdout(test_stdout): 210 _ = client.create_job( 211 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 212 ) 213 assert test_stdout.getvalue().strip() == 'Waiting 0.1 seconds before retrying.' 214 assert mock_post.call_count == 2 215 216 217@mock.patch('requests.post') 218def test_ionq_client_create_job_retry_request_error(mock_post): 219 response2 = mock.MagicMock() 220 mock_post.side_effect = [requests.exceptions.ConnectionError(), response2] 221 response2.ok = True 222 client = ionq.ionq_client._IonQClient( 223 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 224 ) 225 _ = client.create_job( 226 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 227 ) 228 assert mock_post.call_count == 2 229 230 231@mock.patch('requests.post') 232def test_ionq_client_create_job_timeout(mock_post): 233 mock_post.return_value.ok = False 234 mock_post.return_value.status_code = requests.codes.service_unavailable 235 236 client = ionq.ionq_client._IonQClient( 237 remote_host='http://example.com', 238 api_key='to_my_heart', 239 default_target='simulator', 240 max_retry_seconds=0.2, 241 ) 242 with pytest.raises(TimeoutError): 243 _ = client.create_job( 244 serialized_program=ionq.SerializedProgram(body={'job': 'mine'}, metadata={}) 245 ) 246 247 248@mock.patch('requests.get') 249def test_ionq_client_get_job(mock_get): 250 mock_get.return_value.ok = True 251 mock_get.return_value.json.return_value = {'foo': 'bar'} 252 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 253 response = client.get_job(job_id='job_id') 254 assert response == {'foo': 'bar'} 255 256 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 257 mock_get.assert_called_with('http://example.com/v0.1/jobs/job_id', headers=expected_headers) 258 259 260@mock.patch('requests.get') 261def test_ionq_client_get_job_unauthorized(mock_get): 262 mock_get.return_value.ok = False 263 mock_get.return_value.status_code = requests.codes.unauthorized 264 265 client = ionq.ionq_client._IonQClient( 266 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 267 ) 268 with pytest.raises(ionq.IonQException, match='Not authorized'): 269 _ = client.get_job('job_id') 270 271 272@mock.patch('requests.get') 273def test_ionq_client_get_job_not_found(mock_get): 274 (mock_get.return_value).ok = False 275 (mock_get.return_value).status_code = requests.codes.not_found 276 277 client = ionq.ionq_client._IonQClient( 278 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 279 ) 280 with pytest.raises(ionq.IonQNotFoundException, match='not find'): 281 _ = client.get_job('job_id') 282 283 284@mock.patch('requests.get') 285def test_ionq_client_get_job_not_retriable(mock_get): 286 mock_get.return_value.ok = False 287 mock_get.return_value.status_code = requests.codes.not_implemented 288 289 client = ionq.ionq_client._IonQClient( 290 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 291 ) 292 with pytest.raises(ionq.IonQException, match='Status: 501'): 293 _ = client.get_job('job_id') 294 295 296@mock.patch('requests.get') 297def test_ionq_client_get_job_retry(mock_get): 298 response1 = mock.MagicMock() 299 response2 = mock.MagicMock() 300 mock_get.side_effect = [response1, response2] 301 response1.ok = False 302 response1.status_code = requests.codes.service_unavailable 303 response2.ok = True 304 client = ionq.ionq_client._IonQClient( 305 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 306 ) 307 _ = client.get_job('job_id') 308 assert mock_get.call_count == 2 309 310 311@mock.patch('requests.get') 312def test_ionq_client_list_jobs(mock_get): 313 mock_get.return_value.ok = True 314 mock_get.return_value.json.return_value = {'jobs': [{'id': '1'}, {'id': '2'}]} 315 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 316 response = client.list_jobs() 317 assert response == [{'id': '1'}, {'id': '2'}] 318 319 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 320 mock_get.assert_called_with( 321 'http://example.com/v0.1/jobs', headers=expected_headers, json={'limit': 1000}, params={} 322 ) 323 324 325@mock.patch('requests.get') 326def test_ionq_client_list_jobs_status(mock_get): 327 mock_get.return_value.ok = True 328 mock_get.return_value.json.return_value = {'jobs': [{'id': '1'}, {'id': '2'}]} 329 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 330 response = client.list_jobs(status='canceled') 331 assert response == [{'id': '1'}, {'id': '2'}] 332 333 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 334 mock_get.assert_called_with( 335 'http://example.com/v0.1/jobs', 336 headers=expected_headers, 337 json={'limit': 1000}, 338 params={'status': 'canceled'}, 339 ) 340 341 342@mock.patch('requests.get') 343def test_ionq_client_list_jobs_limit(mock_get): 344 mock_get.return_value.ok = True 345 mock_get.return_value.json.return_value = {'jobs': [{'id': '1'}, {'id': '2'}, {'id': 3}]} 346 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 347 response = client.list_jobs(limit=2) 348 assert response == [{'id': '1'}, {'id': '2'}] 349 350 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 351 mock_get.assert_called_with( 352 'http://example.com/v0.1/jobs', headers=expected_headers, json={'limit': 1000}, params={} 353 ) 354 355 356@mock.patch('requests.get') 357def test_ionq_client_list_jobs_batches(mock_get): 358 mock_get.return_value.ok = True 359 mock_get.return_value.json.side_effect = [ 360 {'jobs': [{'id': '1'}], 'next': 'a'}, 361 {'jobs': [{'id': '2'}], 'next': 'b'}, 362 {'jobs': [{'id': '3'}]}, 363 ] 364 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 365 response = client.list_jobs(batch_size=1) 366 assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}] 367 368 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 369 url = 'http://example.com/v0.1/jobs' 370 mock_get.assert_has_calls( 371 [ 372 mock.call(url, headers=expected_headers, json={'limit': 1}, params={}), 373 mock.call().json(), 374 mock.call(url, headers=expected_headers, json={'limit': 1}, params={'next': 'a'}), 375 mock.call().json(), 376 mock.call(url, headers=expected_headers, json={'limit': 1}, params={'next': 'b'}), 377 mock.call().json(), 378 ] 379 ) 380 381 382@mock.patch('requests.get') 383def test_ionq_client_list_jobs_batches_does_not_divide_total(mock_get): 384 mock_get.return_value.ok = True 385 mock_get.return_value.json.side_effect = [ 386 {'jobs': [{'id': '1'}, {'id': '2'}], 'next': 'a'}, 387 {'jobs': [{'id': '3'}]}, 388 ] 389 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 390 response = client.list_jobs(batch_size=2) 391 assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}] 392 393 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 394 url = 'http://example.com/v0.1/jobs' 395 mock_get.assert_has_calls( 396 [ 397 mock.call(url, headers=expected_headers, json={'limit': 2}, params={}), 398 mock.call().json(), 399 mock.call(url, headers=expected_headers, json={'limit': 2}, params={'next': 'a'}), 400 mock.call().json(), 401 ] 402 ) 403 404 405@mock.patch('requests.get') 406def test_ionq_client_list_jobs_unauthorized(mock_get): 407 mock_get.return_value.ok = False 408 mock_get.return_value.status_code = requests.codes.unauthorized 409 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 410 with pytest.raises(ionq.IonQException, match='Not authorized'): 411 _ = client.list_jobs() 412 413 414@mock.patch('requests.get') 415def test_ionq_client_list_jobs_not_retriable(mock_get): 416 mock_get.return_value.ok = False 417 mock_get.return_value.status_code = requests.codes.not_implemented 418 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 419 with pytest.raises(ionq.IonQException, match='Status: 501'): 420 _ = client.list_jobs() 421 422 423@mock.patch('requests.get') 424def test_ionq_client_list_jobs_retry(mock_get): 425 response1 = mock.MagicMock() 426 response2 = mock.MagicMock() 427 mock_get.side_effect = [response1, response2] 428 response1.ok = False 429 response1.status_code = requests.codes.service_unavailable 430 response2.ok = True 431 client = ionq.ionq_client._IonQClient( 432 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 433 ) 434 client.list_jobs() 435 assert mock_get.call_count == 2 436 437 438@mock.patch('requests.put') 439def test_ionq_client_cancel_job(mock_put): 440 mock_put.return_value.ok = True 441 mock_put.return_value.json.return_value = {'foo': 'bar'} 442 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 443 response = client.cancel_job(job_id='job_id') 444 assert response == {'foo': 'bar'} 445 446 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 447 mock_put.assert_called_with( 448 'http://example.com/v0.1/jobs/job_id/status/cancel', headers=expected_headers 449 ) 450 451 452@mock.patch('requests.put') 453def test_ionq_client_cancel_job_unauthorized(mock_put): 454 mock_put.return_value.ok = False 455 mock_put.return_value.status_code = requests.codes.unauthorized 456 457 client = ionq.ionq_client._IonQClient( 458 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 459 ) 460 with pytest.raises(ionq.IonQException, match='Not authorized'): 461 client.cancel_job('job_id') 462 463 464@mock.patch('requests.put') 465def test_ionq_client_cancel_job_not_found(mock_put): 466 (mock_put.return_value).ok = False 467 (mock_put.return_value).status_code = requests.codes.not_found 468 469 client = ionq.ionq_client._IonQClient( 470 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 471 ) 472 with pytest.raises(ionq.IonQNotFoundException, match='not find'): 473 client.cancel_job('job_id') 474 475 476@mock.patch('requests.put') 477def test_ionq_client_cancel_job_not_retriable(mock_get): 478 mock_get.return_value.ok = False 479 mock_get.return_value.status_code = requests.codes.not_implemented 480 481 client = ionq.ionq_client._IonQClient( 482 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 483 ) 484 with pytest.raises(ionq.IonQException, match='Status: 501'): 485 client.cancel_job('job_id') 486 487 488@mock.patch('requests.put') 489def test_ionq_client_cancel_job_retry(mock_put): 490 response1 = mock.MagicMock() 491 response2 = mock.MagicMock() 492 mock_put.side_effect = [response1, response2] 493 response1.ok = False 494 response1.status_code = requests.codes.service_unavailable 495 response2.ok = True 496 client = ionq.ionq_client._IonQClient( 497 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 498 ) 499 client.cancel_job('job_id') 500 assert mock_put.call_count == 2 501 502 503@mock.patch('requests.delete') 504def test_ionq_client_delete_job(mock_delete): 505 mock_delete.return_value.ok = True 506 mock_delete.return_value.json.return_value = {'foo': 'bar'} 507 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 508 response = client.delete_job(job_id='job_id') 509 assert response == {'foo': 'bar'} 510 511 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 512 mock_delete.assert_called_with('http://example.com/v0.1/jobs/job_id', headers=expected_headers) 513 514 515@mock.patch('requests.delete') 516def test_ionq_client_delete_job_unauthorized(mock_delete): 517 mock_delete.return_value.ok = False 518 mock_delete.return_value.status_code = requests.codes.unauthorized 519 520 client = ionq.ionq_client._IonQClient( 521 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 522 ) 523 with pytest.raises(ionq.IonQException, match='Not authorized'): 524 client.delete_job('job_id') 525 526 527@mock.patch('requests.delete') 528def test_ionq_client_delete_job_not_found(mock_put): 529 (mock_put.return_value).ok = False 530 (mock_put.return_value).status_code = requests.codes.not_found 531 532 client = ionq.ionq_client._IonQClient( 533 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 534 ) 535 with pytest.raises(ionq.IonQNotFoundException, match='not find'): 536 client.delete_job('job_id') 537 538 539@mock.patch('requests.delete') 540def test_ionq_client_delete_job_not_retriable(mock_delete): 541 mock_delete.return_value.ok = False 542 mock_delete.return_value.status_code = requests.codes.not_implemented 543 544 client = ionq.ionq_client._IonQClient( 545 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 546 ) 547 with pytest.raises(ionq.IonQException, match='Status: 501'): 548 client.delete_job('job_id') 549 550 551@mock.patch('requests.delete') 552def test_ionq_client_delete_job_retry(mock_put): 553 response1 = mock.MagicMock() 554 response2 = mock.MagicMock() 555 mock_put.side_effect = [response1, response2] 556 response1.ok = False 557 response1.status_code = requests.codes.service_unavailable 558 response2.ok = True 559 client = ionq.ionq_client._IonQClient( 560 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 561 ) 562 client.delete_job('job_id') 563 assert mock_put.call_count == 2 564 565 566@mock.patch('requests.get') 567def test_ionq_client_get_current_calibrations(mock_get): 568 mock_get.return_value.ok = True 569 mock_get.return_value.json.return_value = {'foo': 'bar'} 570 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 571 response = client.get_current_calibration() 572 assert response == {'foo': 'bar'} 573 574 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 575 mock_get.assert_called_with( 576 'http://example.com/v0.1/calibrations/current', headers=expected_headers 577 ) 578 579 580@mock.patch('requests.get') 581def test_ionq_client_get_current_calibration_unauthorized(mock_get): 582 mock_get.return_value.ok = False 583 mock_get.return_value.status_code = requests.codes.unauthorized 584 585 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 586 with pytest.raises(ionq.IonQException, match='Not authorized'): 587 _ = client.get_current_calibration() 588 589 590@mock.patch('requests.get') 591def test_ionq_client_get_current_calibration_not_found(mock_get): 592 (mock_get.return_value).ok = False 593 (mock_get.return_value).status_code = requests.codes.not_found 594 595 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 596 with pytest.raises(ionq.IonQNotFoundException, match='not find'): 597 _ = client.get_current_calibration() 598 599 600@mock.patch('requests.get') 601def test_ionq_client_get_current_calibration_not_retriable(mock_get): 602 mock_get.return_value.ok = False 603 mock_get.return_value.status_code = requests.codes.not_implemented 604 605 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 606 with pytest.raises(ionq.IonQException, match='Status: 501'): 607 _ = client.get_current_calibration() 608 609 610@mock.patch('requests.get') 611def test_ionq_client_get_calibration_retry(mock_get): 612 response1 = mock.MagicMock() 613 response2 = mock.MagicMock() 614 mock_get.side_effect = [response1, response2] 615 response1.ok = False 616 response1.status_code = requests.codes.service_unavailable 617 response2.ok = True 618 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 619 _ = client.get_current_calibration() 620 assert mock_get.call_count == 2 621 622 623@mock.patch('requests.get') 624def test_ionq_client_list_calibrations(mock_get): 625 mock_get.return_value.ok = True 626 mock_get.return_value.json.return_value = {'calibrations': [{'id': '1'}, {'id': '2'}]} 627 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 628 response = client.list_calibrations() 629 assert response == [{'id': '1'}, {'id': '2'}] 630 631 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 632 mock_get.assert_called_with( 633 'http://example.com/v0.1/calibrations', 634 headers=expected_headers, 635 json={'limit': 1000}, 636 params={}, 637 ) 638 639 640@mock.patch('requests.get') 641def test_ionq_client_list_calibrations_dates(mock_get): 642 mock_get.return_value.ok = True 643 mock_get.return_value.json.return_value = {'calibrations': [{'id': '1'}, {'id': '2'}]} 644 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 645 response = client.list_calibrations( 646 start=datetime.datetime.utcfromtimestamp(1284286794), 647 end=datetime.datetime.utcfromtimestamp(1284286795), 648 ) 649 assert response == [{'id': '1'}, {'id': '2'}] 650 651 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 652 mock_get.assert_called_with( 653 'http://example.com/v0.1/calibrations', 654 headers=expected_headers, 655 json={'limit': 1000}, 656 params={'start': 1284286794000, 'end': 1284286795000}, 657 ) 658 659 660@mock.patch('requests.get') 661def test_ionq_client_list_calibrations_limit(mock_get): 662 mock_get.return_value.ok = True 663 mock_get.return_value.json.return_value = { 664 'calibrations': [{'id': '1'}, {'id': '2'}, {'id': 3}] 665 } 666 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 667 response = client.list_calibrations(limit=2) 668 assert response == [{'id': '1'}, {'id': '2'}] 669 670 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 671 mock_get.assert_called_with( 672 'http://example.com/v0.1/calibrations', 673 headers=expected_headers, 674 json={'limit': 1000}, 675 params={}, 676 ) 677 678 679@mock.patch('requests.get') 680def test_ionq_client_list_calibrations_batches(mock_get): 681 mock_get.return_value.ok = True 682 mock_get.return_value.json.side_effect = [ 683 {'calibrations': [{'id': '1'}], 'next': 'a'}, 684 {'calibrations': [{'id': '2'}], 'next': 'b'}, 685 {'calibrations': [{'id': '3'}]}, 686 ] 687 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 688 response = client.list_calibrations(batch_size=1) 689 assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}] 690 691 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 692 url = 'http://example.com/v0.1/calibrations' 693 mock_get.assert_has_calls( 694 [ 695 mock.call(url, headers=expected_headers, json={'limit': 1}, params={}), 696 mock.call().json(), 697 mock.call(url, headers=expected_headers, json={'limit': 1}, params={'next': 'a'}), 698 mock.call().json(), 699 mock.call(url, headers=expected_headers, json={'limit': 1}, params={'next': 'b'}), 700 mock.call().json(), 701 ] 702 ) 703 704 705@mock.patch('requests.get') 706def test_ionq_client_list_calibrations_batches_does_not_divide_total(mock_get): 707 mock_get.return_value.ok = True 708 mock_get.return_value.json.side_effect = [ 709 {'calibrations': [{'id': '1'}, {'id': '2'}], 'next': 'a'}, 710 {'calibrations': [{'id': '3'}]}, 711 ] 712 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 713 response = client.list_calibrations(batch_size=2) 714 assert response == [{'id': '1'}, {'id': '2'}, {'id': '3'}] 715 716 expected_headers = {'Authorization': 'apiKey to_my_heart', 'Content-Type': 'application/json'} 717 url = 'http://example.com/v0.1/calibrations' 718 mock_get.assert_has_calls( 719 [ 720 mock.call(url, headers=expected_headers, json={'limit': 2}, params={}), 721 mock.call().json(), 722 mock.call(url, headers=expected_headers, json={'limit': 2}, params={'next': 'a'}), 723 mock.call().json(), 724 ] 725 ) 726 727 728@mock.patch('requests.get') 729def test_ionq_client_list_calibrations_unauthorized(mock_get): 730 mock_get.return_value.ok = False 731 mock_get.return_value.status_code = requests.codes.unauthorized 732 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 733 with pytest.raises(ionq.IonQException, match='Not authorized'): 734 _ = client.list_calibrations() 735 736 737@mock.patch('requests.get') 738def test_ionq_client_list_calibrations_not_retriable(mock_get): 739 mock_get.return_value.ok = False 740 mock_get.return_value.status_code = requests.codes.not_implemented 741 client = ionq.ionq_client._IonQClient(remote_host='http://example.com', api_key='to_my_heart') 742 with pytest.raises(ionq.IonQException, match='Status: 501'): 743 _ = client.list_calibrations() 744 745 746@mock.patch('requests.get') 747def test_ionq_client_list_calibrations_retry(mock_get): 748 response1 = mock.MagicMock() 749 response2 = mock.MagicMock() 750 mock_get.side_effect = [response1, response2] 751 response1.ok = False 752 response1.status_code = requests.codes.service_unavailable 753 response2.ok = True 754 client = ionq.ionq_client._IonQClient( 755 remote_host='http://example.com', api_key='to_my_heart', default_target='simulator' 756 ) 757 client.list_calibrations() 758 assert mock_get.call_count == 2 759