From 48ce79faf29f14693b42a64506c37072e8abe4f4 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Wed, 24 Jan 2024 23:29:53 -0500 Subject: [PATCH 1/4] avoid pytest automatically running this TestCase instance on import --- dbt/tests/adapter/connection_manager.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dbt/tests/adapter/connection_manager.py b/dbt/tests/adapter/connection_manager.py index 7d40c829f..067dcc633 100644 --- a/dbt/tests/adapter/connection_manager.py +++ b/dbt/tests/adapter/connection_manager.py @@ -1,12 +1,15 @@ import sys from unittest import TestCase +import pytest + from dbt.adapters.base import BaseConnectionManager from dbt.adapters.contracts.connection import Connection from dbt.adapters.events.logging import AdapterLogger from dbt.adapters.exceptions import FailedToConnectError +@pytest.mark.skip("This gets run on import by pytest because it's an instance of TestCase.") class ConnectionManagerRetry(TestCase): def setUp(self): From f7617a334458be24c249ec0d62b9698d0d9169c3 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Thu, 25 Jan 2024 11:37:21 -0500 Subject: [PATCH 2/4] add starter project to include directory --- dbt/include/starter_project/.gitignore | 4 ++ dbt/include/starter_project/README.md | 15 ++++++++ dbt/include/starter_project/__init__.py | 3 ++ dbt/include/starter_project/analyses/.gitkeep | 0 dbt/include/starter_project/dbt_project.yml | 37 +++++++++++++++++++ dbt/include/starter_project/macros/.gitkeep | 0 .../models/example/my_first_dbt_model.sql | 27 ++++++++++++++ .../models/example/my_second_dbt_model.sql | 6 +++ .../starter_project/models/example/schema.yml | 21 +++++++++++ dbt/include/starter_project/seeds/.gitkeep | 0 .../starter_project/snapshots/.gitkeep | 0 dbt/include/starter_project/tests/.gitkeep | 0 12 files changed, 113 insertions(+) create mode 100644 dbt/include/starter_project/.gitignore create mode 100644 dbt/include/starter_project/README.md create mode 100644 dbt/include/starter_project/__init__.py create mode 100644 dbt/include/starter_project/analyses/.gitkeep create mode 100644 dbt/include/starter_project/dbt_project.yml create mode 100644 dbt/include/starter_project/macros/.gitkeep create mode 100644 dbt/include/starter_project/models/example/my_first_dbt_model.sql create mode 100644 dbt/include/starter_project/models/example/my_second_dbt_model.sql create mode 100644 dbt/include/starter_project/models/example/schema.yml create mode 100644 dbt/include/starter_project/seeds/.gitkeep create mode 100644 dbt/include/starter_project/snapshots/.gitkeep create mode 100644 dbt/include/starter_project/tests/.gitkeep diff --git a/dbt/include/starter_project/.gitignore b/dbt/include/starter_project/.gitignore new file mode 100644 index 000000000..49f147cb9 --- /dev/null +++ b/dbt/include/starter_project/.gitignore @@ -0,0 +1,4 @@ + +target/ +dbt_packages/ +logs/ diff --git a/dbt/include/starter_project/README.md b/dbt/include/starter_project/README.md new file mode 100644 index 000000000..7874ac842 --- /dev/null +++ b/dbt/include/starter_project/README.md @@ -0,0 +1,15 @@ +Welcome to your new dbt project! + +### Using the starter project + +Try running the following commands: +- dbt run +- dbt test + + +### Resources: +- Learn more about dbt [in the docs](https://docs.getdbt.com/docs/introduction) +- Check out [Discourse](https://discourse.getdbt.com/) for commonly asked questions and answers +- Join the [chat](https://community.getdbt.com/) on Slack for live discussions and support +- Find [dbt events](https://events.getdbt.com) near you +- Check out [the blog](https://blog.getdbt.com/) for the latest news on dbt's development and best practices diff --git a/dbt/include/starter_project/__init__.py b/dbt/include/starter_project/__init__.py new file mode 100644 index 000000000..b177e5d49 --- /dev/null +++ b/dbt/include/starter_project/__init__.py @@ -0,0 +1,3 @@ +import os + +PACKAGE_PATH = os.path.dirname(__file__) diff --git a/dbt/include/starter_project/analyses/.gitkeep b/dbt/include/starter_project/analyses/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/dbt/include/starter_project/dbt_project.yml b/dbt/include/starter_project/dbt_project.yml new file mode 100644 index 000000000..630001eed --- /dev/null +++ b/dbt/include/starter_project/dbt_project.yml @@ -0,0 +1,37 @@ + +# Name your project! Project names should contain only lowercase characters +# and underscores. A good package name should reflect your organization's +# name or the intended use of these models +name: '{project_name}' +version: '1.0.0' +config-version: 2 + +# This setting configures which "profile" dbt uses for this project. +profile: '{profile_name}' + +# These configurations specify where dbt should look for different types of files. +# The `model-paths` config, for example, states that models in this project can be +# found in the "models/" directory. You probably won't need to change these! +model-paths: ["models"] +analysis-paths: ["analyses"] +test-paths: ["tests"] +seed-paths: ["seeds"] +macro-paths: ["macros"] +snapshot-paths: ["snapshots"] + +clean-targets: # directories to be removed by `dbt clean` + - "target" + - "dbt_packages" + + +# Configuring models +# Full documentation: https://docs.getdbt.com/docs/configuring-models + +# In this example config, we tell dbt to build all models in the example/ +# directory as views. These settings can be overridden in the individual model +# files using the `{{{{ config(...) }}}}` macro. +models: + {project_name}: + # Config indicated by + and applies to all files under models/example/ + example: + +materialized: view diff --git a/dbt/include/starter_project/macros/.gitkeep b/dbt/include/starter_project/macros/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/dbt/include/starter_project/models/example/my_first_dbt_model.sql b/dbt/include/starter_project/models/example/my_first_dbt_model.sql new file mode 100644 index 000000000..f31a12d94 --- /dev/null +++ b/dbt/include/starter_project/models/example/my_first_dbt_model.sql @@ -0,0 +1,27 @@ + +/* + Welcome to your first dbt model! + Did you know that you can also configure models directly within SQL files? + This will override configurations stated in dbt_project.yml + + Try changing "table" to "view" below +*/ + +{{ config(materialized='table') }} + +with source_data as ( + + select 1 as id + union all + select null as id + +) + +select * +from source_data + +/* + Uncomment the line below to remove records with null `id` values +*/ + +-- where id is not null diff --git a/dbt/include/starter_project/models/example/my_second_dbt_model.sql b/dbt/include/starter_project/models/example/my_second_dbt_model.sql new file mode 100644 index 000000000..c91f8793a --- /dev/null +++ b/dbt/include/starter_project/models/example/my_second_dbt_model.sql @@ -0,0 +1,6 @@ + +-- Use the `ref` function to select from other models + +select * +from {{ ref('my_first_dbt_model') }} +where id = 1 diff --git a/dbt/include/starter_project/models/example/schema.yml b/dbt/include/starter_project/models/example/schema.yml new file mode 100644 index 000000000..2a5308171 --- /dev/null +++ b/dbt/include/starter_project/models/example/schema.yml @@ -0,0 +1,21 @@ + +version: 2 + +models: + - name: my_first_dbt_model + description: "A starter dbt model" + columns: + - name: id + description: "The primary key for this table" + tests: + - unique + - not_null + + - name: my_second_dbt_model + description: "A starter dbt model" + columns: + - name: id + description: "The primary key for this table" + tests: + - unique + - not_null diff --git a/dbt/include/starter_project/seeds/.gitkeep b/dbt/include/starter_project/seeds/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/dbt/include/starter_project/snapshots/.gitkeep b/dbt/include/starter_project/snapshots/.gitkeep new file mode 100644 index 000000000..e69de29bb diff --git a/dbt/include/starter_project/tests/.gitkeep b/dbt/include/starter_project/tests/.gitkeep new file mode 100644 index 000000000..e69de29bb From 0bb3213525196342d85201194e03431b6fcc51f5 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Fri, 26 Jan 2024 12:21:16 -0500 Subject: [PATCH 3/4] remove starter project from include directory --- dbt/include/starter_project/.gitignore | 4 -- dbt/include/starter_project/README.md | 15 -------- dbt/include/starter_project/__init__.py | 3 -- dbt/include/starter_project/analyses/.gitkeep | 0 dbt/include/starter_project/dbt_project.yml | 37 ------------------- dbt/include/starter_project/macros/.gitkeep | 0 .../models/example/my_first_dbt_model.sql | 27 -------------- .../models/example/my_second_dbt_model.sql | 6 --- .../starter_project/models/example/schema.yml | 21 ----------- dbt/include/starter_project/seeds/.gitkeep | 0 .../starter_project/snapshots/.gitkeep | 0 dbt/include/starter_project/tests/.gitkeep | 0 12 files changed, 113 deletions(-) delete mode 100644 dbt/include/starter_project/.gitignore delete mode 100644 dbt/include/starter_project/README.md delete mode 100644 dbt/include/starter_project/__init__.py delete mode 100644 dbt/include/starter_project/analyses/.gitkeep delete mode 100644 dbt/include/starter_project/dbt_project.yml delete mode 100644 dbt/include/starter_project/macros/.gitkeep delete mode 100644 dbt/include/starter_project/models/example/my_first_dbt_model.sql delete mode 100644 dbt/include/starter_project/models/example/my_second_dbt_model.sql delete mode 100644 dbt/include/starter_project/models/example/schema.yml delete mode 100644 dbt/include/starter_project/seeds/.gitkeep delete mode 100644 dbt/include/starter_project/snapshots/.gitkeep delete mode 100644 dbt/include/starter_project/tests/.gitkeep diff --git a/dbt/include/starter_project/.gitignore b/dbt/include/starter_project/.gitignore deleted file mode 100644 index 49f147cb9..000000000 --- a/dbt/include/starter_project/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ - -target/ -dbt_packages/ -logs/ diff --git a/dbt/include/starter_project/README.md b/dbt/include/starter_project/README.md deleted file mode 100644 index 7874ac842..000000000 --- a/dbt/include/starter_project/README.md +++ /dev/null @@ -1,15 +0,0 @@ -Welcome to your new dbt project! - -### Using the starter project - -Try running the following commands: -- dbt run -- dbt test - - -### Resources: -- Learn more about dbt [in the docs](https://docs.getdbt.com/docs/introduction) -- Check out [Discourse](https://discourse.getdbt.com/) for commonly asked questions and answers -- Join the [chat](https://community.getdbt.com/) on Slack for live discussions and support -- Find [dbt events](https://events.getdbt.com) near you -- Check out [the blog](https://blog.getdbt.com/) for the latest news on dbt's development and best practices diff --git a/dbt/include/starter_project/__init__.py b/dbt/include/starter_project/__init__.py deleted file mode 100644 index b177e5d49..000000000 --- a/dbt/include/starter_project/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -import os - -PACKAGE_PATH = os.path.dirname(__file__) diff --git a/dbt/include/starter_project/analyses/.gitkeep b/dbt/include/starter_project/analyses/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/dbt/include/starter_project/dbt_project.yml b/dbt/include/starter_project/dbt_project.yml deleted file mode 100644 index 630001eed..000000000 --- a/dbt/include/starter_project/dbt_project.yml +++ /dev/null @@ -1,37 +0,0 @@ - -# Name your project! Project names should contain only lowercase characters -# and underscores. A good package name should reflect your organization's -# name or the intended use of these models -name: '{project_name}' -version: '1.0.0' -config-version: 2 - -# This setting configures which "profile" dbt uses for this project. -profile: '{profile_name}' - -# These configurations specify where dbt should look for different types of files. -# The `model-paths` config, for example, states that models in this project can be -# found in the "models/" directory. You probably won't need to change these! -model-paths: ["models"] -analysis-paths: ["analyses"] -test-paths: ["tests"] -seed-paths: ["seeds"] -macro-paths: ["macros"] -snapshot-paths: ["snapshots"] - -clean-targets: # directories to be removed by `dbt clean` - - "target" - - "dbt_packages" - - -# Configuring models -# Full documentation: https://docs.getdbt.com/docs/configuring-models - -# In this example config, we tell dbt to build all models in the example/ -# directory as views. These settings can be overridden in the individual model -# files using the `{{{{ config(...) }}}}` macro. -models: - {project_name}: - # Config indicated by + and applies to all files under models/example/ - example: - +materialized: view diff --git a/dbt/include/starter_project/macros/.gitkeep b/dbt/include/starter_project/macros/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/dbt/include/starter_project/models/example/my_first_dbt_model.sql b/dbt/include/starter_project/models/example/my_first_dbt_model.sql deleted file mode 100644 index f31a12d94..000000000 --- a/dbt/include/starter_project/models/example/my_first_dbt_model.sql +++ /dev/null @@ -1,27 +0,0 @@ - -/* - Welcome to your first dbt model! - Did you know that you can also configure models directly within SQL files? - This will override configurations stated in dbt_project.yml - - Try changing "table" to "view" below -*/ - -{{ config(materialized='table') }} - -with source_data as ( - - select 1 as id - union all - select null as id - -) - -select * -from source_data - -/* - Uncomment the line below to remove records with null `id` values -*/ - --- where id is not null diff --git a/dbt/include/starter_project/models/example/my_second_dbt_model.sql b/dbt/include/starter_project/models/example/my_second_dbt_model.sql deleted file mode 100644 index c91f8793a..000000000 --- a/dbt/include/starter_project/models/example/my_second_dbt_model.sql +++ /dev/null @@ -1,6 +0,0 @@ - --- Use the `ref` function to select from other models - -select * -from {{ ref('my_first_dbt_model') }} -where id = 1 diff --git a/dbt/include/starter_project/models/example/schema.yml b/dbt/include/starter_project/models/example/schema.yml deleted file mode 100644 index 2a5308171..000000000 --- a/dbt/include/starter_project/models/example/schema.yml +++ /dev/null @@ -1,21 +0,0 @@ - -version: 2 - -models: - - name: my_first_dbt_model - description: "A starter dbt model" - columns: - - name: id - description: "The primary key for this table" - tests: - - unique - - not_null - - - name: my_second_dbt_model - description: "A starter dbt model" - columns: - - name: id - description: "The primary key for this table" - tests: - - unique - - not_null diff --git a/dbt/include/starter_project/seeds/.gitkeep b/dbt/include/starter_project/seeds/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/dbt/include/starter_project/snapshots/.gitkeep b/dbt/include/starter_project/snapshots/.gitkeep deleted file mode 100644 index e69de29bb..000000000 diff --git a/dbt/include/starter_project/tests/.gitkeep b/dbt/include/starter_project/tests/.gitkeep deleted file mode 100644 index e69de29bb..000000000 From 4e872abdef12a0d827c004ad39b6a0f2554d90c5 Mon Sep 17 00:00:00 2001 From: Mike Alfare Date: Fri, 26 Jan 2024 12:21:35 -0500 Subject: [PATCH 4/4] remove unused connection manager test --- dbt/tests/adapter/connection_manager.py | 446 ------------------------ 1 file changed, 446 deletions(-) delete mode 100644 dbt/tests/adapter/connection_manager.py diff --git a/dbt/tests/adapter/connection_manager.py b/dbt/tests/adapter/connection_manager.py deleted file mode 100644 index 067dcc633..000000000 --- a/dbt/tests/adapter/connection_manager.py +++ /dev/null @@ -1,446 +0,0 @@ -import sys -from unittest import TestCase - -import pytest - -from dbt.adapters.base import BaseConnectionManager -from dbt.adapters.contracts.connection import Connection -from dbt.adapters.events.logging import AdapterLogger -from dbt.adapters.exceptions import FailedToConnectError - - -@pytest.mark.skip("This gets run on import by pytest because it's an instance of TestCase.") -class ConnectionManagerRetry(TestCase): - - def setUp(self): - self.logger = AdapterLogger("test") - self.connection = self.get_connection() - - def get_connection(self) -> Connection: - raise NotImplementedError("Implement `ConnectionManagerRetry.get_connection` to use this test.") - - def test_retry_connection(self): - """Test a dummy handle is set on a connection on the first attempt. - - This test uses a Connection populated with test PostgresCredentials values, and - expects the Connection.handle attribute to be set to True and it's state to - "open", after calling retry_connection. - - Moreover, the attribute should be set in the first attempt as no exception would - be raised for retrying. A mock connect function is used to simulate a real connection - passing on the first attempt. - """ - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - return True - - conn = BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retryable_exceptions=[], - ) - - assert conn.state == "open" - assert conn.handle is True - assert attempts == 1 - - def test_retry_connection_fails_unhandled(self): - """Test setting a handle fails upon raising a non-handled exception. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a ValueError to be raised by a mock connect function. As a - result: - * The Connection state should be "fail" and the handle None. - * The resulting attempt count should be 1 as we are not explicitly configured to handle a - ValueError. - * retry_connection should raise a FailedToConnectError with the Exception message. - """ - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - raise ValueError("Something went horribly wrong") - - with self.assertRaisesRegex( - FailedToConnectError, - "Something went horribly wrong", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_limit=1, - retry_timeout=lambda attempt: 0, - retryable_exceptions=(TypeError,), - ) - - assert conn.state == "fail" - assert conn.handle is None - assert attempts == 1 - - def test_retry_connection_fails_handled(self): - """Test setting a handle fails upon raising a handled exception. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a ValueError to be raised by a mock connect function. - As a result: - * The Connection state should be "fail" and the handle None. - * The resulting attempt count should be 2 as we are configured to handle a ValueError. - * retry_connection should raise a FailedToConnectError with the Exception message. - """ - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - raise ValueError("Something went horribly wrong") - - with self.assertRaisesRegex( - FailedToConnectError, - "Something went horribly wrong", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=(ValueError,), - retry_limit=1, - ) - - assert conn.state == "fail" - assert conn.handle is None - - def test_retry_connection_passes_handled(self): - """Test setting a handle fails upon raising a handled exception. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a ValueError to be raised by a mock connect function only the first - time is called. Upon handling the exception once, connect should return. - As a result: - * The Connection state should be "open" and the handle True. - * The resulting attempt count should be 2 as we are configured to handle a ValueError. - """ - conn = self.connection - is_handled = False - attempts = 0 - - def connect(): - nonlocal is_handled - nonlocal attempts - - attempts += 1 - - if is_handled: - return True - - is_handled = True - raise ValueError("Something went horribly wrong") - - conn = BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=(ValueError,), - retry_limit=1, - ) - - assert conn.state == "open" - assert conn.handle is True - assert is_handled is True - assert attempts == 2 - - def test_retry_connection_attempts(self): - """Test setting a handle fails upon raising a handled exception multiple times. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a ValueError to be raised by a mock connect function. As a result: - * The Connection state should be "fail" and the handle None, as connect - never returns. - * The resulting attempt count should be 11 as we are configured to handle a ValueError. - * retry_connection should raise a FailedToConnectError with the Exception message. - """ - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - - raise ValueError("Something went horribly wrong") - - with self.assertRaisesRegex( - FailedToConnectError, - "Something went horribly wrong", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=(ValueError,), - retry_limit=10, - ) - - assert conn.state == "fail" - assert conn.handle is None - assert attempts == 11 - - def test_retry_connection_fails_handling_all_exceptions(self): - """Test setting a handle fails after exhausting all attempts. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a TypeError to be raised by a mock connect function. As a result: - * The Connection state should be "fail" and the handle None, as connect - never returns. - * The resulting attempt count should be 11 as we are configured to handle all Exceptions. - * retry_connection should raise a FailedToConnectError with the Exception message. - """ - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - - raise TypeError("An unhandled thing went horribly wrong") - - with self.assertRaisesRegex( - FailedToConnectError, - "An unhandled thing went horribly wrong", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=[Exception], - retry_limit=15, - ) - - assert conn.state == "fail" - assert conn.handle is None - assert attempts == 16 - - def test_retry_connection_passes_multiple_handled(self): - """Test setting a handle passes upon handling multiple exceptions. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a mock connect to raise a ValueError in the first invocation and a - TypeError in the second invocation. As a result: - * The Connection state should be "open" and the handle True, as connect - returns after both exceptions have been handled. - * The resulting attempt count should be 3. - """ - conn = self.connection - is_value_err_handled = False - is_type_err_handled = False - attempts = 0 - - def connect(): - nonlocal is_value_err_handled - nonlocal is_type_err_handled - nonlocal attempts - - attempts += 1 - - if is_value_err_handled and is_type_err_handled: - return True - elif is_type_err_handled: - is_value_err_handled = True - raise ValueError("Something went horribly wrong") - else: - is_type_err_handled = True - raise TypeError("An unhandled thing went horribly wrong") - - conn = BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=(ValueError, TypeError), - retry_limit=2, - ) - - assert conn.state == "open" - assert conn.handle is True - assert is_type_err_handled is True - assert is_value_err_handled is True - assert attempts == 3 - - def test_retry_connection_passes_none_excluded(self): - """Test setting a handle passes upon handling multiple exceptions. - - This test uses a Connection populated with test PostgresCredentials values, and - expects a mock connect to raise a ValueError in the first invocation and a - TypeError in the second invocation. As a result: - * The Connection state should be "open" and the handle True, as connect - returns after both exceptions have been handled. - * The resulting attempt count should be 3. - """ - conn = self.connection - is_value_err_handled = False - is_type_err_handled = False - attempts = 0 - - def connect(): - nonlocal is_value_err_handled - nonlocal is_type_err_handled - nonlocal attempts - - attempts += 1 - - if is_value_err_handled and is_type_err_handled: - return True - elif is_type_err_handled: - is_value_err_handled = True - raise ValueError("Something went horribly wrong") - else: - is_type_err_handled = True - raise TypeError("An unhandled thing went horribly wrong") - - conn = BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=(ValueError, TypeError), - retry_limit=2, - ) - - assert conn.state == "open" - assert conn.handle is True - assert is_type_err_handled is True - assert is_value_err_handled is True - assert attempts == 3 - - def test_retry_connection_retry_limit(self): - """Test retry_connection raises an exception with a negative retry limit.""" - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - return True - - with self.assertRaisesRegex( - FailedToConnectError, - "retry_limit cannot be negative", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=0, - retryable_exceptions=(ValueError,), - retry_limit=-2, - ) - - assert conn.state == "fail" - assert conn.handle is None - assert attempts == 0 - - def test_retry_connection_retry_timeout(self): - """Test retry_connection raises an exception with a negative timeout.""" - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - return True - - for retry_timeout in [-10, -2.5, lambda _: -100, lambda _: -10.1]: - with self.assertRaisesRegex( - FailedToConnectError, - "retry_timeout cannot be negative or return a negative time", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=-10, - retryable_exceptions=(ValueError,), - retry_limit=2, - ) - - assert conn.state == "init" - assert conn.handle is None - assert attempts == 0 - - def test_retry_connection_exceeds_recursion_limit(self): - """Test retry_connection raises an exception with retries that exceed recursion limit.""" - conn = self.connection - attempts = 0 - - def connect(): - nonlocal attempts - attempts += 1 - return True - - with self.assertRaisesRegex( - FailedToConnectError, - "retry_limit cannot be negative", - ): - BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=2, - retryable_exceptions=(ValueError,), - retry_limit=sys.getrecursionlimit() + 1, - ) - - assert conn.state == "fail" - assert conn.handle is None - assert attempts == 0 - - def test_retry_connection_with_exponential_backoff_timeout(self): - """Test retry_connection with an exponential backoff timeout. - - We assert the provided exponential backoff function gets passed the right attempt number - and produces the expected timeouts. - """ - conn = self.connection - attempts = 0 - timeouts = [] - - def connect(): - nonlocal attempts - attempts += 1 - - if attempts < 12: - raise ValueError("Keep trying!") - return True - - def exp_backoff(n): - nonlocal timeouts - computed = 2**n - # We store the computed values to ensure they match the expected backoff... - timeouts.append((n, computed)) - # but we return 0 as we don't want the test to go on forever. - return 0 - - conn = BaseConnectionManager.retry_connection( - conn, - connect, - self.logger, - retry_timeout=exp_backoff, - retryable_exceptions=(ValueError,), - retry_limit=12, - ) - - assert conn.state == "open" - assert conn.handle is True - assert attempts == 12 - assert timeouts == [(n, 2**n) for n in range(12)]