Skip to content

Commit 44f02bd

Browse files
committed
SNOW-2000709 add further integ/unit tests to guard the behaviors of no-auth
### Description - Add more integ/unit tests to guard the behavior of no-auth - The goal is to avoid future changes accidentally and silently break the behavior of no-auth. (Eventually we want to have end-to-end tests to guard it when the Stored Procedure support is completed)
1 parent a3229c3 commit 44f02bd

File tree

2 files changed

+164
-4
lines changed

2 files changed

+164
-4
lines changed

test/integ/test_connection.py

+26-4
Original file line numberDiff line numberDiff line change
@@ -1575,6 +1575,23 @@ def test_is_valid(conn_cnx):
15751575
assert conn.is_valid() is False
15761576

15771577

1578+
@pytest.mark.skipolddriver
1579+
def test_no_auth_connection_basic():
1580+
"""Tests connection creation with AuthNoAuth."""
1581+
# AuthNoAuth does not exist in old drivers, so we import at test level to
1582+
# skip importing it for old driver tests.
1583+
from snowflake.connector.auth.no_auth import AuthNoAuth
1584+
1585+
no_auth = AuthNoAuth()
1586+
1587+
# Simulates how Snowpark server creates no-auth connection, such creation
1588+
# should succeed. Here we intentionally avoid using create_connection test
1589+
# utility. Because we want to validate that even with much information
1590+
# (like account, user etc.) missing, we should still be able to create a
1591+
# no-auth connection.
1592+
snowflake.connector.connect(auth_class=no_auth)
1593+
1594+
15781595
@pytest.mark.skipolddriver
15791596
def test_no_auth_connection_negative_case():
15801597
# AuthNoAuth does not exist in old drivers, so we import at test level to
@@ -1583,10 +1600,15 @@ def test_no_auth_connection_negative_case():
15831600

15841601
no_auth = AuthNoAuth()
15851602

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

15921614
# Make sure we are indeed passing the no-auth configuration to the

test/unit/test_auth_no_auth.py

+138
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,13 @@
44

55
from __future__ import annotations
66

7+
import unittest
8+
from unittest.mock import Mock
9+
710
import pytest
811

12+
import snowflake.connector
13+
914

1015
@pytest.mark.skipolddriver
1116
def test_auth_no_auth():
@@ -38,3 +43,136 @@ def test_auth_no_auth():
3843
assert (
3944
reauth_response == expected_reauth_response
4045
), f'reauthenticate(foo="bar") is expected to return {expected_reauth_response}, but returns {reauth_response}'
46+
47+
48+
@pytest.mark.skipolddriver
49+
def test_authenticate_for_no_auth():
50+
from snowflake.connector.auth import Auth
51+
from snowflake.connector.auth.no_auth import AuthNoAuth
52+
53+
rest = None
54+
auth = Auth(rest)
55+
56+
# Verify that when using AuthNoAuth, we can successfully call authenticate
57+
# even under these conditions
58+
# - None account is provided
59+
# - None user is provided
60+
# - restful client is not set up
61+
auth.authenticate(AuthNoAuth(), None, None)
62+
63+
64+
class TestHeartbeatExecutionFlowForNoAuth(unittest.TestCase):
65+
@pytest.mark.skipolddriver
66+
def test_hearbeat_execution_flow_for_no_auth(self):
67+
"""Tests the heartbeat execution flow no-auth connections.
68+
69+
No-auth connection relies on these facts
70+
- connection uses _heartbeat_tick method to perform heartbeat check
71+
- _heartbeat_tick method calls connection._rest._heartbeat method to
72+
send out the actual heartbeat request
73+
- client_session_keep_alive_heartbeat_frequency (setter) calls
74+
master_validity_in_seconds of the restful client to get the
75+
reasonable value range.
76+
- _validate_client_session_keep_alive_heartbeat_frequency calls
77+
master_validity_in_seconds of the restful client to get the
78+
reasonable value range.
79+
And this test serves as a simple indicator to tell developers when a
80+
code change breaks such assumptions.
81+
"""
82+
# AuthNoAuth does not exist in old drivers, so we import at test level
83+
# to skip importing it for old driver tests.
84+
from snowflake.connector.auth.no_auth import AuthNoAuth
85+
86+
no_auth = AuthNoAuth()
87+
conn = snowflake.connector.connect(auth_class=no_auth)
88+
# Simulate how we inject special restful client for no-auth connection.
89+
# And the tests verify that even with only the attributes listed below
90+
# available in conn._rest, the tested heartbeat functionalities are
91+
# still working as intended.
92+
conn._rest = Mock(spec=["_heartbeat", "master_validity_in_seconds"])
93+
conn._rest.master_validity_in_seconds = 100
94+
95+
breaking_change_error_message = """
96+
Unexpected execution flow for heartbeat, this means potential
97+
changes to heartbeat mechanism that will break no-auth connection
98+
feature. Please contact the owner of AuthNoAuth before proceeding.
99+
Details: {details}
100+
"""
101+
102+
# Check that _heartbeat_tick is working as intended.
103+
try:
104+
conn._heartbeat_tick()
105+
conn._rest._heartbeat.assert_called_once()
106+
except Exception as e:
107+
raise AssertionError(breaking_change_error_message.format(details=str(e)))
108+
109+
# Check that client_session_keep_alive_heartbeat_frequency setter is
110+
# working as intended with such a bare minimum set of interfaces
111+
# exposed in conn._rest.
112+
try:
113+
conn.client_session_keep_alive_heartbeat_frequency = 123
114+
except Exception as e:
115+
raise AssertionError(breaking_change_error_message.format(details=str(e)))
116+
117+
# Check that _validate_client_session_keep_alive_heartbeat_frequency is
118+
# working as intended with such a bare minimum set of interfaces
119+
# exposed in conn._rest.
120+
try:
121+
conn._validate_client_session_keep_alive_heartbeat_frequency()
122+
except Exception as e:
123+
raise AssertionError(breaking_change_error_message.format(details=str(e)))
124+
125+
# Indirect way to check that
126+
# client_session_keep_alive_heartbeat_frequency setter calls
127+
# master_validity_in_seconds in conn._rest.
128+
conn._rest = Mock(spec=["_heartbeat"])
129+
missing_master_validity_in_seconds_message = (
130+
"has no attribute 'master_validity_in_seconds'"
131+
)
132+
validity_dependency_change_template = (
133+
"{method} no longer relies on rest.master_validity_in_seconds"
134+
)
135+
try:
136+
# Verify that client_session_keep_alive_heartbeat_frequency setter
137+
# fails when conn._rest.master_validity_in_seconds method is
138+
# unavailable.
139+
with self.assertRaises(AttributeError) as context:
140+
conn.client_session_keep_alive_heartbeat_frequency = 123
141+
self.assertIn(
142+
missing_master_validity_in_seconds_message,
143+
str(context.exception),
144+
)
145+
except Exception:
146+
# This means there might be change breaking heartbeat mechanism for
147+
# no-auth connections.
148+
raise RuntimeError(
149+
breaking_change_error_message.format(
150+
details=validity_dependency_change_template.format(
151+
method="client_session_keep_alive_heartbeat_frequency (setter)"
152+
)
153+
)
154+
)
155+
156+
# Likewise, this is an indirect way to check that
157+
# _validate_client_session_keep_alive_heartbeat_frequency calls
158+
# master_validity_in_seconds in conn._rest.
159+
try:
160+
# Verify that _validate_client_session_keep_alive_heartbeat_frequency
161+
# fails when conn._rest.master_validity_in_seconds method is
162+
# unavailable.
163+
with self.assertRaises(AttributeError) as context:
164+
conn._validate_client_session_keep_alive_heartbeat_frequency()
165+
self.assertIn(
166+
missing_master_validity_in_seconds_message,
167+
str(context.exception),
168+
)
169+
except Exception:
170+
# This means there might be change breaking heartbeat mechanism for
171+
# no-auth connections.
172+
raise RuntimeError(
173+
breaking_change_error_message.format(
174+
details=validity_dependency_change_template.format(
175+
method="_validate_client_session_keep_alive_heartbeat_frequency"
176+
)
177+
)
178+
)

0 commit comments

Comments
 (0)