diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 000000000..ff2c165e2 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +/.github/CODEOWNERS @neo4j/drivers +/.github/workflows/ @neo4j/drivers diff --git a/.github/workflows/tests.yaml b/.github/workflows/tests.yaml new file mode 100644 index 000000000..d0a77d21e --- /dev/null +++ b/.github/workflows/tests.yaml @@ -0,0 +1,58 @@ +name: Tests + +on: + push: + branches: + - '6.x' + pull_request: + branches: + - '6.x' + +jobs: + win-unit-tests: + name: Windows Unit Tests + runs-on: windows-latest + strategy: + matrix: + python-version: + - semver: '3.7' + tox-factor: 'py37' + - semver: '3.8' + tox-factor: 'py38' + - semver: '3.9' + tox-factor: 'py39' + - semver: '3.10' + tox-factor: 'py310' + - semver: '3.11' + tox-factor: 'py311' + - semver: '3.12' + tox-factor: 'py312' + - semver: '3.13' + tox-factor: 'py313' + steps: + - uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 + + - name: Set up Python ${{ matrix.python-version.semver }} + uses: actions/setup-python@e797f83bcb11b83ae66e0230d6156d7c80228e7c # v6.0.0 + with: + python-version: ${{ matrix.python-version.semver }} + cache: 'pip' + + - name: Run install tox + run: python -m pip install -U --group tox + - name: Run unit tests + run: python -m tox -vv -f unit ${{ matrix.python-version.tox-factor }} + + gha-conclusion: + name: gha-conclusion + needs: win-unit-tests + runs-on: ubuntu-latest + steps: + - name: Signal failure + if: ${{ cancelled() || contains(needs.*.result, 'cancelled') || contains(needs.*.result, 'failure') }} + run: | + echo "Some workflows have failed!" + exit 1 + - name: Signal success + if: ${{ !cancelled() && !contains(needs.*.result, 'cancelled') && !contains(needs.*.result, 'failure') }} + run: echo "All done!" diff --git a/src/neo4j/_async_compat/network/_util.py b/src/neo4j/_async_compat/network/_util.py index 1ac1c38b0..56ed9559d 100644 --- a/src/neo4j/_async_compat/network/_util.py +++ b/src/neo4j/_async_compat/network/_util.py @@ -14,6 +14,8 @@ # limitations under the License. +from __future__ import annotations + import asyncio import logging import socket diff --git a/tests/unit/async_/io/test_neo4j_pool.py b/tests/unit/async_/io/test_neo4j_pool.py index e1549fd06..3924689e1 100644 --- a/tests/unit/async_/io/test_neo4j_pool.py +++ b/tests/unit/async_/io/test_neo4j_pool.py @@ -15,6 +15,7 @@ import inspect +import time import pytest @@ -28,6 +29,7 @@ AsyncBolt, AsyncNeo4jPool, ) +from neo4j._async_compat import async_sleep from neo4j._async_compat.util import AsyncUtil from neo4j._conf import ( RoutingConfig, @@ -45,6 +47,8 @@ from ...._async_compat import mark_async_test +MONOTONIC_TIME_RESOLUTION = time.get_clock_info("monotonic").resolution + ROUTER1_ADDRESS = ResolvedAddress(("1.2.3.1", 9000), host_name="host") ROUTER2_ADDRESS = ResolvedAddress(("1.2.3.1", 9001), host_name="host") ROUTER3_ADDRESS = ResolvedAddress(("1.2.3.1", 9002), host_name="host") @@ -193,6 +197,8 @@ async def test_acquires_new_routing_table_if_stale( old_value = pool.routing_tables[db.name].last_updated_time pool.routing_tables[db.name].ttl = 0 + await async_sleep(MONOTONIC_TIME_RESOLUTION * 2) + cx = await pool.acquire(READ_ACCESS, 30, db, None, None, None) await pool.release(cx) assert pool.routing_tables[db.name].last_updated_time > old_value @@ -214,6 +220,8 @@ async def test_removes_old_routing_table(opener): db2_rt = pool.routing_tables[TEST_DB2.name] db2_rt.ttl = -RoutingConfig.routing_table_purge_delay + await async_sleep(MONOTONIC_TIME_RESOLUTION * 2) + cx = await pool.acquire(READ_ACCESS, 30, TEST_DB1, None, None, None) await pool.release(cx) assert pool.routing_tables[TEST_DB1.name].last_updated_time > old_value diff --git a/tests/unit/async_/test_addressing.py b/tests/unit/async_/test_addressing.py index 5a0a940b5..7c76724be 100644 --- a/tests/unit/async_/test_addressing.py +++ b/tests/unit/async_/test_addressing.py @@ -53,8 +53,12 @@ async def test_address_resolve_with_custom_resolver_none() -> None: @pytest.mark.parametrize( ("test_input", "expected"), [ + (Address(("example.invalid", "7687")), ValueError), + (Address(("example.invalid", 7687)), ValueError), + (Address(("example.invalid", None)), ValueError), (Address(("127.0.0.1", "abcd")), ValueError), (Address((None, None)), ValueError), + (Address((1234, "7687")), TypeError), ], ) @mark_async_test @@ -67,6 +71,21 @@ async def test_address_resolve_with_unresolvable_address( ) +@pytest.mark.parametrize( + "test_input", + [ + Address((None, 7687)), + Address(("example.com", None)), + ], +) +@mark_async_test +async def test_address_resolves_with_none(test_input) -> None: + resolved = await AsyncUtil.list( + AsyncNetworkUtil.resolve_address(test_input, resolver=None) + ) + assert resolved + + @mark_async_test @pytest.mark.parametrize("resolver_type", ("sync", "async")) async def test_address_resolve_with_custom_resolver(resolver_type) -> None: diff --git a/tests/unit/sync/io/test_neo4j_pool.py b/tests/unit/sync/io/test_neo4j_pool.py index 13b9be4e2..30fc908f6 100644 --- a/tests/unit/sync/io/test_neo4j_pool.py +++ b/tests/unit/sync/io/test_neo4j_pool.py @@ -15,6 +15,7 @@ import inspect +import time import pytest @@ -22,6 +23,7 @@ READ_ACCESS, WRITE_ACCESS, ) +from neo4j._async_compat import sleep from neo4j._async_compat.util import Util from neo4j._conf import ( RoutingConfig, @@ -45,6 +47,8 @@ from ...._async_compat import mark_sync_test +MONOTONIC_TIME_RESOLUTION = time.get_clock_info("monotonic").resolution + ROUTER1_ADDRESS = ResolvedAddress(("1.2.3.1", 9000), host_name="host") ROUTER2_ADDRESS = ResolvedAddress(("1.2.3.1", 9001), host_name="host") ROUTER3_ADDRESS = ResolvedAddress(("1.2.3.1", 9002), host_name="host") @@ -193,6 +197,8 @@ def test_acquires_new_routing_table_if_stale( old_value = pool.routing_tables[db.name].last_updated_time pool.routing_tables[db.name].ttl = 0 + sleep(MONOTONIC_TIME_RESOLUTION * 2) + cx = pool.acquire(READ_ACCESS, 30, db, None, None, None) pool.release(cx) assert pool.routing_tables[db.name].last_updated_time > old_value @@ -214,6 +220,8 @@ def test_removes_old_routing_table(opener): db2_rt = pool.routing_tables[TEST_DB2.name] db2_rt.ttl = -RoutingConfig.routing_table_purge_delay + sleep(MONOTONIC_TIME_RESOLUTION * 2) + cx = pool.acquire(READ_ACCESS, 30, TEST_DB1, None, None, None) pool.release(cx) assert pool.routing_tables[TEST_DB1.name].last_updated_time > old_value diff --git a/tests/unit/sync/test_addressing.py b/tests/unit/sync/test_addressing.py index fc741716e..3f48247be 100644 --- a/tests/unit/sync/test_addressing.py +++ b/tests/unit/sync/test_addressing.py @@ -53,8 +53,12 @@ def test_address_resolve_with_custom_resolver_none() -> None: @pytest.mark.parametrize( ("test_input", "expected"), [ + (Address(("example.invalid", "7687")), ValueError), + (Address(("example.invalid", 7687)), ValueError), + (Address(("example.invalid", None)), ValueError), (Address(("127.0.0.1", "abcd")), ValueError), (Address((None, None)), ValueError), + (Address((1234, "7687")), TypeError), ], ) @mark_sync_test @@ -67,6 +71,21 @@ def test_address_resolve_with_unresolvable_address( ) +@pytest.mark.parametrize( + "test_input", + [ + Address((None, 7687)), + Address(("example.com", None)), + ], +) +@mark_sync_test +def test_address_resolves_with_none(test_input) -> None: + resolved = Util.list( + NetworkUtil.resolve_address(test_input, resolver=None) + ) + assert resolved + + @mark_sync_test @pytest.mark.parametrize("resolver_type", ("sync", "async")) def test_address_resolve_with_custom_resolver(resolver_type) -> None: