Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions tests/integration/test_live_nodes.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,15 @@

from __future__ import annotations

from collections.abc import Sequence
from urllib.parse import urlsplit

import pytest

from alternator import AlternatorConfigBuilder, Auth, Helper, KeyRouteAffinityMode
from alternator._http import create_sync_http_fetcher
from alternator.async_client import AsyncHelper
from tests.conftest import FakeAlternatorServer
from tests.integration import SCYLLA_HOST, SCYLLA_PORT, SKIP_INTEGRATION

pytestmark = [
Expand All @@ -27,6 +32,19 @@ def _affinity_config() -> object:
)


def _live_cluster_localnodes() -> Sequence[str]:
fetcher = create_sync_http_fetcher(timeout_seconds=5.0)
return fetcher(f"http://{SCYLLA_HOST}:{SCYLLA_PORT}/localnodes")


def _dns_entrypoint_config(fake_url: str) -> object:
parsed = urlsplit(fake_url)
assert parsed.port is not None
return (
AlternatorConfigBuilder().with_seeds("localhost").with_port(parsed.port).build()
)


class TestLiveNodeHelperDiagnostics:
"""Verify sync helper diagnostics and shared-client lifecycle."""

Expand All @@ -49,6 +67,19 @@ def test_helper_exposes_nodes_clients_and_partition_keys(self) -> None:
resource = helper.resource()
assert "TableNames" in resource.meta.client.list_tables()

def test_helper_discovers_live_cluster_nodes_from_dns_entrypoint(
self,
fake_alternator_server: FakeAlternatorServer,
) -> None:
fake_alternator_server.set_localnodes(_live_cluster_localnodes())

with Helper(
_dns_entrypoint_config(fake_alternator_server.url("/")),
auth=Auth.disabled(),
) as helper:
assert helper.update_live_nodes()
assert helper.get_nodes()


class TestAsyncLiveNodeHelperDiagnostics:
"""Verify async helper diagnostics and shared-client lifecycle."""
Expand All @@ -73,3 +104,17 @@ async def test_helper_exposes_nodes_clients_and_partition_keys(self) -> None:

client = await helper.client()
assert "TableNames" in await client.list_tables()

@pytest.mark.asyncio
async def test_helper_discovers_live_cluster_nodes_from_dns_entrypoint(
self,
fake_alternator_server: FakeAlternatorServer,
) -> None:
fake_alternator_server.set_localnodes(_live_cluster_localnodes())

async with AsyncHelper(
_dns_entrypoint_config(fake_alternator_server.url("/")),
auth=Auth.disabled(),
) as helper:
assert await helper.update_live_nodes()
assert helper.get_nodes()
26 changes: 26 additions & 0 deletions tests/unit/test_sync_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,16 @@

import time
from collections.abc import Sequence
from urllib.parse import urlsplit

import pytest

from alternator._http import create_sync_http_fetcher
from alternator.config import Config
from alternator.core.live_nodes import NoNodesAvailableError, SyncLiveNodesManager
from alternator.core.routing_scope import ClusterScope, DatacenterScope, RackScope
from alternator.exceptions import ConfigurationError
from tests.conftest import FakeAlternatorServer


class TestSyncLiveNodesManager:
Expand Down Expand Up @@ -146,6 +149,29 @@ def mock_fetch(url: str) -> Sequence[str]:
assert manager.refresh_nodes() is True
assert manager.nodes.nodes == ("dc2-node1", "dc2-node2")

def test_dns_entrypoint_discovers_dns_node_records(
self,
fake_alternator_server: FakeAlternatorServer,
) -> None:
"""Discovery reaches a resolver-backed seed host and keeps DNS node records."""
fake_alternator_server.set_localnodes(["localhost", "node-a.internal"])
parsed = urlsplit(fake_alternator_server.url("/"))
assert parsed.port is not None
config = Config(
seed_hosts=["localhost"],
port=parsed.port,
routing_scope=ClusterScope(),
)
manager = SyncLiveNodesManager(
config,
create_sync_http_fetcher(timeout_seconds=1.0),
)

assert manager.refresh_nodes() is True

assert fake_alternator_server.requested_paths() == ["/localnodes"]
assert manager.nodes.nodes == ("localhost", "node-a.internal")

def test_url_construction(self, config: Config) -> None:
"""Test URL is constructed correctly."""
captured_url: list[str] = []
Expand Down
Loading