Skip to content

SNOW-2000709 add further integ/unit tests to guard the behaviors of n… #2230

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
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
30 changes: 26 additions & 4 deletions test/integ/test_connection.py
Original file line number Diff line number Diff line change
Expand Up @@ -1575,6 +1575,23 @@ def test_is_valid(conn_cnx):
assert conn.is_valid() is False


@pytest.mark.skipolddriver
def test_no_auth_connection_basic():
"""Tests connection creation with AuthNoAuth."""
# AuthNoAuth does not exist in old drivers, so we import at test level to
# skip importing it for old driver tests.
from snowflake.connector.auth.no_auth import AuthNoAuth

no_auth = AuthNoAuth()

# Simulates how Snowpark server creates no-auth connection, such creation
# should succeed. Here we intentionally avoid using create_connection test
# utility. Because we want to validate that even with much information
# (like account, user etc.) missing, we should still be able to create a
# no-auth connection.
snowflake.connector.connect(auth_class=no_auth)


@pytest.mark.skipolddriver
def test_no_auth_connection_negative_case():
# AuthNoAuth does not exist in old drivers, so we import at test level to
Expand All @@ -1583,10 +1600,15 @@ def test_no_auth_connection_negative_case():

no_auth = AuthNoAuth()

# Create a no-auth connection in an invalid way.
# We do not fail connection establishment because there is no validated way
# to tell whether the no-auth is a valid use case or not. But it is
# effectively protected because invalid no-auth will fail to run any query.
# Create a no-auth connection in user code (and we create connection as we
# would for other tests, to provide all the needed information like
# account, user etc.). Such connection can be successfully created, but it
# is not fully functional because it lacks certain setup that is on the
# server side.
# We do not fail connection establishment in user code because there is no
# validated way to tell whether the no-auth is a valid use case or not. But
# it is effectively protected because invalid no-auth will fail to run any
# query.
conn = create_connection("default", auth_class=no_auth)

# Make sure we are indeed passing the no-auth configuration to the
Expand Down
138 changes: 138 additions & 0 deletions test/unit/test_auth_no_auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

from __future__ import annotations

import unittest
from unittest.mock import Mock

import pytest

import snowflake.connector


@pytest.mark.skipolddriver
def test_auth_no_auth():
Expand Down Expand Up @@ -38,3 +43,136 @@ def test_auth_no_auth():
assert (
reauth_response == expected_reauth_response
), f'reauthenticate(foo="bar") is expected to return {expected_reauth_response}, but returns {reauth_response}'


@pytest.mark.skipolddriver
def test_authenticate_for_no_auth():
from snowflake.connector.auth import Auth
from snowflake.connector.auth.no_auth import AuthNoAuth

rest = None
auth = Auth(rest)

# Verify that when using AuthNoAuth, we can successfully call authenticate
# even under these conditions
# - None account is provided
# - None user is provided
# - restful client is not set up
auth.authenticate(AuthNoAuth(), account=None, user=None)


class TestHeartbeatExecutionFlowForNoAuth(unittest.TestCase):
@pytest.mark.skipolddriver
def test_hearbeat_execution_flow_for_no_auth(self):
"""Tests the heartbeat execution flow no-auth connections.

No-auth connection relies on these facts
- connection uses _heartbeat_tick method to perform heartbeat check
- _heartbeat_tick method calls connection._rest._heartbeat method to
send out the actual heartbeat request
- client_session_keep_alive_heartbeat_frequency (setter) calls
master_validity_in_seconds of the restful client to get the
reasonable value range.
- _validate_client_session_keep_alive_heartbeat_frequency calls
master_validity_in_seconds of the restful client to get the
reasonable value range.
And this test serves as a simple indicator to tell developers when a
code change breaks such assumptions.
"""
# AuthNoAuth does not exist in old drivers, so we import at test level
# to skip importing it for old driver tests.
from snowflake.connector.auth.no_auth import AuthNoAuth

no_auth = AuthNoAuth()
conn = snowflake.connector.connect(auth_class=no_auth)
# Simulate how we inject special restful client for no-auth connection.
# And the tests verify that even with only the attributes listed below
# available in conn._rest, the tested heartbeat functionalities are
# still working as intended.
conn._rest = Mock(spec=["_heartbeat", "master_validity_in_seconds"])
conn._rest.master_validity_in_seconds = 100

breaking_change_error_message = """
Unexpected execution flow for heartbeat, this means potential
changes to heartbeat mechanism that will break no-auth connection
feature. Please contact the owner of AuthNoAuth before proceeding.
Details: {details}
"""

# Check that _heartbeat_tick is working as intended.
try:
conn._heartbeat_tick()
conn._rest._heartbeat.assert_called_once()
except Exception as e:
raise AssertionError(breaking_change_error_message.format(details=str(e)))

# Check that client_session_keep_alive_heartbeat_frequency setter is
# working as intended with such a bare minimum set of interfaces
# exposed in conn._rest.
try:
conn.client_session_keep_alive_heartbeat_frequency = 123
except Exception as e:
raise AssertionError(breaking_change_error_message.format(details=str(e)))

# Check that _validate_client_session_keep_alive_heartbeat_frequency is
# working as intended with such a bare minimum set of interfaces
# exposed in conn._rest.
try:
conn._validate_client_session_keep_alive_heartbeat_frequency()
except Exception as e:
raise AssertionError(breaking_change_error_message.format(details=str(e)))

# Indirect way to check that
# client_session_keep_alive_heartbeat_frequency setter calls
# master_validity_in_seconds in conn._rest.
conn._rest = Mock(spec=["_heartbeat"])
missing_master_validity_in_seconds_message = (
"has no attribute 'master_validity_in_seconds'"
)
validity_dependency_change_template = (
"{method} no longer relies on rest.master_validity_in_seconds"
)
try:
# Verify that client_session_keep_alive_heartbeat_frequency setter
# fails when conn._rest.master_validity_in_seconds method is
# unavailable.
with self.assertRaises(AttributeError) as context:
conn.client_session_keep_alive_heartbeat_frequency = 123
self.assertIn(
missing_master_validity_in_seconds_message,
str(context.exception),
)
except Exception:
# This means there might be change breaking heartbeat mechanism for
# no-auth connections.
raise RuntimeError(
breaking_change_error_message.format(
details=validity_dependency_change_template.format(
method="client_session_keep_alive_heartbeat_frequency (setter)"
)
)
)

# Likewise, this is an indirect way to check that
# _validate_client_session_keep_alive_heartbeat_frequency calls
# master_validity_in_seconds in conn._rest.
try:
# Verify that _validate_client_session_keep_alive_heartbeat_frequency
# fails when conn._rest.master_validity_in_seconds method is
# unavailable.
with self.assertRaises(AttributeError) as context:
conn._validate_client_session_keep_alive_heartbeat_frequency()
self.assertIn(
missing_master_validity_in_seconds_message,
str(context.exception),
)
except Exception:
# This means there might be change breaking heartbeat mechanism for
# no-auth connections.
raise RuntimeError(
breaking_change_error_message.format(
details=validity_dependency_change_template.format(
method="_validate_client_session_keep_alive_heartbeat_frequency"
)
)
)
Loading