|
10 | 10 | import asynctnt |
11 | 11 | from asynctnt import Response |
12 | 12 | from asynctnt.connection import ConnectionState |
13 | | -from asynctnt.exceptions import TarantoolNotConnectedError |
| 13 | +from asynctnt.exceptions import ( |
| 14 | + ErrorCode, |
| 15 | + TarantoolDatabaseError, |
| 16 | + TarantoolNotConnectedError, |
| 17 | +) |
14 | 18 | from asynctnt.instance import TarantoolSyncInstance |
15 | | -from tests.conftest import read_applua |
| 19 | +from tests.conftest import create_tarantool_instance, read_applua |
16 | 20 |
|
17 | 21 |
|
18 | 22 | class TestConnect: |
@@ -484,6 +488,262 @@ async def test_connect_on_tnt_crash_with_reconnect( |
484 | 488 | finally: |
485 | 489 | await conn.disconnect() |
486 | 490 |
|
| 491 | + async def test_connect_wait_tnt_started(self, tnt: TarantoolSyncInstance) -> None: |
| 492 | + tnt.stop() |
| 493 | + conn = asynctnt.Connection( |
| 494 | + host=tnt.host, |
| 495 | + port=tnt.port, |
| 496 | + username="t1", |
| 497 | + password="t1", |
| 498 | + fetch_schema=True, |
| 499 | + reconnect_timeout=0.000000001, |
| 500 | + ) |
| 501 | + try: |
| 502 | + coro = asyncio.ensure_future(conn.connect()) |
| 503 | + await asyncio.sleep(0.3) |
| 504 | + tnt.start() |
| 505 | + await asyncio.sleep(1) |
| 506 | + while True: |
| 507 | + try: |
| 508 | + await coro |
| 509 | + break |
| 510 | + except TarantoolDatabaseError as e: |
| 511 | + if e.code == ErrorCode.ER_NO_SUCH_USER: |
| 512 | + # Try again |
| 513 | + coro = asyncio.ensure_future(conn.connect()) |
| 514 | + continue |
| 515 | + raise |
| 516 | + |
| 517 | + assert conn.state == ConnectionState.CONNECTED |
| 518 | + await conn.call("box.info") |
| 519 | + finally: |
| 520 | + await conn.disconnect() |
| 521 | + |
| 522 | + async def test_connect_waiting_for_spaces( |
| 523 | + self, tnt: TarantoolSyncInstance, in_docker: bool |
| 524 | + ) -> None: |
| 525 | + if in_docker: |
| 526 | + pytest.skip("not running in docker") |
| 527 | + |
| 528 | + with create_tarantool_instance(replication_source=["x:1"]) as instance: |
| 529 | + instance.start(wait=False) |
| 530 | + |
| 531 | + conn = asynctnt.Connection( |
| 532 | + host=instance.host, |
| 533 | + port=instance.port, |
| 534 | + fetch_schema=True, |
| 535 | + reconnect_timeout=0.1, |
| 536 | + connect_timeout=10, |
| 537 | + ) |
| 538 | + assert conn.connect_timeout == 10 |
| 539 | + try: |
| 540 | + states: dict[ConnectionState, bool] = {} |
| 541 | + |
| 542 | + async def state_checker() -> None: |
| 543 | + while True: |
| 544 | + states[conn.state] = True |
| 545 | + await asyncio.sleep(0.001) |
| 546 | + |
| 547 | + checker = asyncio.ensure_future(state_checker()) |
| 548 | + |
| 549 | + try: |
| 550 | + await asyncio.wait_for(conn.connect(), 1) |
| 551 | + except asyncio.TimeoutError: |
| 552 | + pass # connect cancelled as expected |
| 553 | + |
| 554 | + checker.cancel() |
| 555 | + |
| 556 | + assert states.get(ConnectionState.CONNECTING, False), "was in connecting" |
| 557 | + |
| 558 | + with pytest.raises(TarantoolNotConnectedError): |
| 559 | + await conn.call("box.info") |
| 560 | + finally: |
| 561 | + await conn.disconnect() |
| 562 | + |
| 563 | + @pytest.mark.min_bin_version((1, 7)) |
| 564 | + async def test_connect_waiting_for_spaces_no_reconnect( |
| 565 | + self, tnt: TarantoolSyncInstance, in_docker: bool |
| 566 | + ) -> None: |
| 567 | + if in_docker: |
| 568 | + pytest.skip("not running in docker") |
| 569 | + |
| 570 | + with create_tarantool_instance(replication_source=["x:1"]) as instance: |
| 571 | + instance.start(wait=False) |
| 572 | + await asyncio.sleep(1) |
| 573 | + |
| 574 | + conn = asynctnt.Connection( |
| 575 | + host=instance.host, |
| 576 | + port=instance.port, |
| 577 | + fetch_schema=True, |
| 578 | + reconnect_timeout=0, |
| 579 | + connect_timeout=10, |
| 580 | + ) |
| 581 | + try: |
| 582 | + with pytest.raises(TarantoolDatabaseError) as exc: |
| 583 | + await conn.connect() |
| 584 | + |
| 585 | + assert exc.value.code == ErrorCode.ER_NO_SUCH_SPACE |
| 586 | + finally: |
| 587 | + await conn.disconnect() |
| 588 | + |
| 589 | + async def test_connect_waiting_for_spaces_no_reconnect_1_6( |
| 590 | + self, tnt: TarantoolSyncInstance, in_docker: bool |
| 591 | + ) -> None: |
| 592 | + if in_docker: |
| 593 | + pytest.skip("not running in docker") |
| 594 | + |
| 595 | + with create_tarantool_instance(replication_source=["x:1"]) as instance: |
| 596 | + instance.start(wait=False) |
| 597 | + await asyncio.sleep(1) |
| 598 | + |
| 599 | + # Check if version < 1.7 |
| 600 | + async with asynctnt.Connection( |
| 601 | + host=instance.host, port=instance.port, fetch_schema=False |
| 602 | + ) as check_conn: |
| 603 | + if check_conn.version >= (1, 7): |
| 604 | + pytest.skip("Test only for Tarantool < 1.7") |
| 605 | + |
| 606 | + conn = asynctnt.Connection( |
| 607 | + host=instance.host, |
| 608 | + port=instance.port, |
| 609 | + fetch_schema=True, |
| 610 | + reconnect_timeout=0, |
| 611 | + connect_timeout=10, |
| 612 | + ) |
| 613 | + try: |
| 614 | + with pytest.raises(ConnectionRefusedError): |
| 615 | + await conn.connect() |
| 616 | + finally: |
| 617 | + await conn.disconnect() |
| 618 | + |
| 619 | + @pytest.mark.min_bin_version((1, 7)) |
| 620 | + async def test_connect_err_loading( |
| 621 | + self, tnt: TarantoolSyncInstance, in_docker: bool |
| 622 | + ) -> None: |
| 623 | + if in_docker: |
| 624 | + pytest.skip("not running in docker") |
| 625 | + |
| 626 | + with create_tarantool_instance(replication_source=["x:1"]) as instance: |
| 627 | + instance.start(wait=False) |
| 628 | + await asyncio.sleep(1) |
| 629 | + |
| 630 | + conn = asynctnt.Connection( |
| 631 | + host=instance.host, |
| 632 | + port=instance.port, |
| 633 | + username="t1", |
| 634 | + password="t1", |
| 635 | + fetch_schema=True, |
| 636 | + reconnect_timeout=0, |
| 637 | + connect_timeout=10, |
| 638 | + ) |
| 639 | + try: |
| 640 | + with pytest.raises(TarantoolDatabaseError) as exc: |
| 641 | + await conn.connect() |
| 642 | + |
| 643 | + assert exc.value.code == ErrorCode.ER_LOADING |
| 644 | + finally: |
| 645 | + await conn.disconnect() |
| 646 | + |
| 647 | + async def test_connect_err_loading_1_6( |
| 648 | + self, tnt: TarantoolSyncInstance, in_docker: bool |
| 649 | + ) -> None: |
| 650 | + if in_docker: |
| 651 | + pytest.skip("not running in docker") |
| 652 | + |
| 653 | + with create_tarantool_instance(replication_source=["x:1"]) as instance: |
| 654 | + instance.start(wait=False) |
| 655 | + await asyncio.sleep(1) |
| 656 | + |
| 657 | + # Check if version < 1.7 |
| 658 | + async with asynctnt.Connection( |
| 659 | + host=instance.host, port=instance.port, fetch_schema=False |
| 660 | + ) as check_conn: |
| 661 | + if check_conn.version >= (1, 7): |
| 662 | + pytest.skip("Test only for Tarantool < 1.7") |
| 663 | + |
| 664 | + conn = asynctnt.Connection( |
| 665 | + host=instance.host, |
| 666 | + port=instance.port, |
| 667 | + username="t1", |
| 668 | + password="t1", |
| 669 | + fetch_schema=True, |
| 670 | + reconnect_timeout=0, |
| 671 | + connect_timeout=10, |
| 672 | + ) |
| 673 | + try: |
| 674 | + with pytest.raises(ConnectionRefusedError): |
| 675 | + await conn.connect() |
| 676 | + finally: |
| 677 | + await conn.disconnect() |
| 678 | + |
| 679 | + async def test_connect_invalid_user_no_reconnect( |
| 680 | + self, tnt: TarantoolSyncInstance |
| 681 | + ) -> None: |
| 682 | + async with asynctnt.Connection(host=tnt.host, port=tnt.port) as check_conn: |
| 683 | + version = check_conn.version |
| 684 | + |
| 685 | + conn = asynctnt.Connection( |
| 686 | + host=tnt.host, |
| 687 | + port=tnt.port, |
| 688 | + username="fancy", |
| 689 | + password="man", |
| 690 | + connect_timeout=1, |
| 691 | + reconnect_timeout=0, |
| 692 | + ) |
| 693 | + with pytest.raises(TarantoolDatabaseError) as exc: |
| 694 | + await conn.connect() |
| 695 | + |
| 696 | + err_code = ErrorCode.ER_PASSWORD_MISMATCH |
| 697 | + if version < (2, 11): |
| 698 | + err_code = ErrorCode.ER_NO_SUCH_USER |
| 699 | + assert exc.value.code == err_code |
| 700 | + |
| 701 | + async def test_connect_invalid_user_with_reconnect( |
| 702 | + self, tnt: TarantoolSyncInstance |
| 703 | + ) -> None: |
| 704 | + conn = asynctnt.Connection( |
| 705 | + host=tnt.host, |
| 706 | + port=tnt.port, |
| 707 | + fetch_schema=True, |
| 708 | + reconnect_timeout=0.1, |
| 709 | + connect_timeout=10, |
| 710 | + ) |
| 711 | + await conn.connect() # first connect successfully |
| 712 | + |
| 713 | + # then change credentials |
| 714 | + conn._username = "fancy" |
| 715 | + conn._password = "man" |
| 716 | + |
| 717 | + tnt.stop() |
| 718 | + tnt.start() |
| 719 | + await asyncio.sleep(0.1) |
| 720 | + try: |
| 721 | + states: dict[ConnectionState, bool] = {} |
| 722 | + |
| 723 | + async def state_checker() -> None: |
| 724 | + while True: |
| 725 | + states[conn.state] = True |
| 726 | + await asyncio.sleep(0.001) |
| 727 | + |
| 728 | + checker = asyncio.ensure_future(state_checker()) |
| 729 | + |
| 730 | + try: |
| 731 | + await asyncio.wait_for(conn.connect(), 1) |
| 732 | + except asyncio.TimeoutError: |
| 733 | + pass # connect cancelled as expected |
| 734 | + |
| 735 | + checker.cancel() |
| 736 | + |
| 737 | + assert states.get(ConnectionState.CONNECTING, False), "was in connecting" |
| 738 | + assert states.get( |
| 739 | + ConnectionState.RECONNECTING, False |
| 740 | + ), "was in reconnecting" |
| 741 | + |
| 742 | + with pytest.raises(TarantoolNotConnectedError): |
| 743 | + await conn.call("box.info") |
| 744 | + finally: |
| 745 | + await conn.disconnect() |
| 746 | + |
487 | 747 | @pytest.mark.min_bin_version((2, 10)) |
488 | 748 | async def test_features(self, tnt: TarantoolSyncInstance) -> None: |
489 | 749 | async with asynctnt.Connection(host=tnt.host, port=tnt.port) as conn: |
|
0 commit comments