blob: 69516d74c1fcf2b58782befabf4e6a6b333056f3 [file]
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.
import pytest
from apache_iggy import IggyClient
from .utils import get_server_config, wait_for_ping, wait_for_server
class TestConnectivity:
"""Test basic connectivity and authentication."""
@pytest.mark.asyncio
async def test_client_not_none(self, iggy_client: IggyClient):
"""Test that client fixture is properly initialized."""
assert iggy_client is not None
@pytest.mark.parametrize(
"connection_string",
[
"iggy://iggy:iggy@127.0.0.1:8090",
"iggy+tcp://iggy:iggy@127.0.0.1:8090",
"iggy+tcp://iggy:iggy@127.0.0.1:8090?reconnection_retries=3&reconnection_interval=1s&reestablish_after=5s&heartbeat_interval=5s&nodelay=true&tls_domain=localhost&tls_ca_file=unused.pem&tls=false",
"iggy+http://iggy:iggy@127.0.0.1:3000",
"iggy+http://iggy:iggy@127.0.0.1:3000?heartbeat_interval=5s&retries=3",
"iggy+ws://iggy:iggy@127.0.0.1:8092",
"iggy+ws://iggy:iggy@127.0.0.1:8092?heartbeat_interval=5s&reconnection_retries=3&reconnection_interval=1s&reestablish_after=5s&read_buffer_size=4096&write_buffer_size=4096&max_write_buffer_size=8192&max_message_size=16384&max_frame_size=16384&accept_unmasked_frames=false&tls_domain=localhost&tls_ca_file=unused.pem&tls_validate_certificate=false&tls=false",
],
)
@pytest.mark.asyncio
async def test_valid_connection_string(self, connection_string: str):
"""Test that valid connection string formats can connect to the server."""
client = IggyClient.from_connection_string(connection_string)
await client.connect()
await wait_for_ping(client, timeout=5, interval=1)
@pytest.mark.parametrize(
("invalid_value", "expected_error"),
[
("", "Invalid connection string"),
("bad address", "Invalid connection string"),
("http://{host}:{port}", "Invalid connection string"),
("tcp://iggy:iggy@{host}:{port}", "Invalid connection string"),
("{host}:", "Invalid connection string"),
(":{port}", "Invalid connection string"),
("{host}:not-a-port", "Invalid connection string"),
("{host}:70000", "Invalid connection string"),
("iggy+tcp://", "Invalid connection string"),
("iggy+tcp://iggy:iggy@", "Invalid connection string"),
(
"iggy+tcp://iggy:iggy@{host}:not-a-port",
"Invalid connection string",
),
("iggy+tcp://iggy:iggy@{host}:-1", "Invalid connection string"),
("iggy+tcp://iggy:iggy@{host}:70000", "Invalid connection string"),
(
"iggy+tcp://iggy:bad:format@{host}:{port}",
"Invalid connection string",
),
(
"iggy+tcp://iggy:iggy@{host}:{port}?invalid_option=value",
"Invalid connection string",
),
("iggy+quic://iggy:iggy@127.0.0.1:8080", "Cannot create endpoint"),
],
)
def test_invalid_connection_string(self, invalid_value: str, expected_error: str):
"""Test malformed server addresses and connection strings are rejected."""
host, port = get_server_config()
value = invalid_value.format(host=host, port=port)
with pytest.raises(RuntimeError):
IggyClient(value)
with pytest.raises(RuntimeError, match=expected_error):
IggyClient.from_connection_string(value)
@pytest.mark.asyncio
async def test_repeated_connect_does_not_error(self):
"""Test calling connect twice on the same client succeeds."""
host, port = get_server_config()
wait_for_server(host, port)
client = IggyClient(f"{host}:{port}")
await client.connect()
await client.connect()
@pytest.mark.asyncio
async def test_login_before_connect_does_not_error(self):
"""Test login can establish authentication without an explicit connect call."""
host, port = get_server_config()
wait_for_server(host, port)
client = IggyClient(f"{host}:{port}")
await client.login_user("iggy", "iggy")
await wait_for_ping(client)
@pytest.mark.parametrize(
("username", "password", "expected_exception"),
[
("invalid-user", "iggy", RuntimeError),
("iggy", "invalid-password", RuntimeError),
("", "iggy", RuntimeError),
("iggy", "", RuntimeError),
(None, "iggy", TypeError),
("iggy", None, TypeError),
],
)
@pytest.mark.asyncio
async def test_login_with_invalid_credentials_fails(
self, username, password, expected_exception
):
"""Test login rejects invalid credentials and invalid argument values."""
host, port = get_server_config()
wait_for_server(host, port)
client = IggyClient(f"{host}:{port}")
await client.connect()
await wait_for_ping(client)
with pytest.raises(expected_exception):
result = client.login_user(username, password)
await result
@pytest.mark.asyncio
async def test_relogin_with_invalid_credentials_fails(self):
"""Test re-login with invalid credentials fails after a successful login."""
host, port = get_server_config()
wait_for_server(host, port)
client = IggyClient(f"{host}:{port}")
await client.connect()
await wait_for_ping(client)
await client.login_user("iggy", "iggy")
with pytest.raises(RuntimeError):
await client.login_user("iggy", "invalid-password")
@pytest.mark.asyncio
async def test_relogin_with_same_valid_credentials_does_not_error(self):
"""Test repeated login with the same valid credentials is allowed."""
host, port = get_server_config()
wait_for_server(host, port)
client = IggyClient(f"{host}:{port}")
await client.connect()
await wait_for_ping(client)
await client.login_user("iggy", "iggy")
await client.login_user("iggy", "iggy")
@pytest.mark.asyncio
async def test_ping(self, iggy_client: IggyClient):
"""Test server ping functionality."""
await iggy_client.ping()