chore: import upstream snapshot with attribution
Continuous Integration / Pre-commit Linter (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.10) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.11) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.12) (push) Has been cancelled
Continuous Integration / Mypy Check (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.12) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / Unit Tests (Python 3.14) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.10) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.11) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.12) (push) Has been cancelled
Copybara PR Handler / close-imported-pr (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.13) (push) Has been cancelled
Continuous Integration / A2A v0.3 Tests (Python 3.14) (push) Has been cancelled

This commit is contained in:
wehub-resource-sync
2026-07-13 13:25:13 +08:00
commit ec2b666284
2231 changed files with 491535 additions and 0 deletions
+13
View File
@@ -0,0 +1,13 @@
# Copyright 2026 Google LLC
#
# Licensed 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.
@@ -0,0 +1,91 @@
# Copyright 2026 Google LLC
#
# Licensed 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.
from unittest import mock
from google.adk.tools.bigtable.bigtable_credentials import BIGTABLE_DEFAULT_SCOPE
from google.adk.tools.bigtable.bigtable_credentials import BigtableCredentialsConfig
from google.auth.credentials import Credentials
import google.oauth2.credentials
import pytest
class TestBigtableCredentials:
"""Test suite for Bigtable credentials configuration validation.
This class tests the credential configuration logic that ensures
either existing credentials or client ID/secret pairs are provided.
"""
def test_bigtable_credentials_config_client_id_secret(self):
"""Test BigtableCredentialsConfig with client_id and client_secret.
Ensures that when client_id and client_secret are provided, the config
object is created with the correct attributes.
"""
config = BigtableCredentialsConfig(client_id="abc", client_secret="def")
assert config.client_id == "abc"
assert config.client_secret == "def"
assert config.scopes == BIGTABLE_DEFAULT_SCOPE
assert config.credentials is None
def test_bigtable_credentials_config_existing_creds(self):
"""Test BigtableCredentialsConfig with existing generic credentials.
Ensures that when a generic Credentials object is provided, it is
stored correctly.
"""
mock_creds = mock.create_autospec(Credentials, instance=True)
config = BigtableCredentialsConfig(credentials=mock_creds)
assert config.credentials == mock_creds
assert config.client_id is None
assert config.client_secret is None
def test_bigtable_credentials_config_oauth2_creds(self):
"""Test BigtableCredentialsConfig with existing OAuth2 credentials.
Ensures that when a google.oauth2.credentials.Credentials object is
provided, the client_id, client_secret, and scopes are extracted
from the credentials object.
"""
mock_creds = mock.create_autospec(
google.oauth2.credentials.Credentials, instance=True
)
mock_creds.client_id = "oauth_client_id"
mock_creds.client_secret = "oauth_client_secret"
mock_creds.scopes = ["fake_scope"]
config = BigtableCredentialsConfig(credentials=mock_creds)
assert config.client_id == "oauth_client_id"
assert config.client_secret == "oauth_client_secret"
assert config.scopes == ["fake_scope"]
def test_bigtable_credentials_config_validation_errors(self):
"""Test BigtableCredentialsConfig validation errors.
Ensures that ValueError is raised under the following conditions:
- No arguments are provided.
- Only client_id is provided.
- Both credentials and client_id/client_secret are provided.
"""
with pytest.raises(ValueError):
BigtableCredentialsConfig()
with pytest.raises(ValueError):
BigtableCredentialsConfig(client_id="abc")
mock_creds = mock.create_autospec(Credentials, instance=True)
with pytest.raises(ValueError):
BigtableCredentialsConfig(
credentials=mock_creds, client_id="abc", client_secret="def"
)
@@ -0,0 +1,272 @@
# Copyright 2026 Google LLC
#
# Licensed 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 logging
from unittest import mock
from google.adk.tools.bigtable import client
from google.adk.tools.bigtable import metadata_tool
from google.auth.credentials import Credentials
from google.cloud.bigtable import enums
import pytest
@pytest.fixture
def mock_get_client():
with mock.patch.object(
client, "get_bigtable_admin_client"
) as mock_get_client:
mock_client = mock.MagicMock()
mock_get_client.return_value = mock_client
yield mock_get_client
def test_list_instances(mock_get_client):
mock_instance = mock.MagicMock()
mock_instance.instance_id = "test-instance"
mock_get_client.return_value.list_instances.return_value = (
[mock_instance],
[],
)
mock_instance.display_name = "Test Instance"
mock_instance.state = enums.Instance.State.READY
mock_instance.type_ = enums.Instance.Type.PRODUCTION
mock_instance.labels = {"env": "test"}
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.list_instances(
project_id="test-project", credentials=creds
)
expected_result = {
"project_id": "test-project",
"instance_id": "test-instance",
"display_name": "Test Instance",
"state": "READY",
"type": "PRODUCTION",
"labels": {"env": "test"},
}
assert result == {"status": "SUCCESS", "results": [expected_result]}
def test_list_instances_failed_locations(mock_get_client):
with mock.patch.object(logging, "warning") as mock_warning:
mock_instance = mock.MagicMock()
mock_instance.instance_id = "test-instance"
failed_locations = ["us-west1-a"]
mock_get_client.return_value.list_instances.return_value = (
[mock_instance],
failed_locations,
)
mock_instance.display_name = "Test Instance"
mock_instance.state = enums.Instance.State.READY
mock_instance.type_ = enums.Instance.Type.PRODUCTION
mock_instance.labels = {"env": "test"}
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.list_instances(
project_id="test-project", credentials=creds
)
expected_result = {
"project_id": "test-project",
"instance_id": "test-instance",
"display_name": "Test Instance",
"state": "READY",
"type": "PRODUCTION",
"labels": {"env": "test"},
}
assert result == {"status": "SUCCESS", "results": [expected_result]}
mock_warning.assert_called_once_with(
"Failed to list instances from the following locations: %s",
failed_locations,
)
def test_get_instance_info(mock_get_client):
mock_instance = mock.MagicMock()
mock_get_client.return_value.instance.return_value = mock_instance
mock_instance.instance_id = "test-instance"
mock_instance.display_name = "Test Instance"
mock_instance.state = enums.Instance.State.READY
mock_instance.type_ = enums.Instance.Type.PRODUCTION
mock_instance.labels = {"env": "test"}
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.get_instance_info(
project_id="test-project",
instance_id="test-instance",
credentials=creds,
)
expected_result = {
"project_id": "test-project",
"instance_id": "test-instance",
"display_name": "Test Instance",
"state": "READY",
"type": "PRODUCTION",
"labels": {"env": "test"},
}
assert result == {"status": "SUCCESS", "results": expected_result}
mock_instance.reload.assert_called_once()
def test_list_tables(mock_get_client):
mock_instance = mock.MagicMock()
mock_get_client.return_value.instance.return_value = mock_instance
mock_table = mock.MagicMock()
mock_table.table_id = "test-table"
mock_table.name = (
"projects/test-project/instances/test-instance/tables/test-table"
)
mock_instance.list_tables.return_value = [mock_table]
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.list_tables(
project_id="test-project",
instance_id="test-instance",
credentials=creds,
)
expected_result = [{
"project_id": "test-project",
"instance_id": "test-instance",
"table_id": "test-table",
"table_name": (
"projects/test-project/instances/test-instance/tables/test-table"
),
}]
assert result == {"status": "SUCCESS", "results": expected_result}
def test_get_table_info(mock_get_client):
mock_instance = mock.MagicMock()
mock_instance.instance_id = "test-instance"
mock_get_client.return_value.instance.return_value = mock_instance
mock_table = mock.MagicMock()
mock_instance.table.return_value = mock_table
mock_table.table_id = "test-table"
mock_table.list_column_families.return_value = {"cf1": mock.MagicMock()}
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.get_table_info(
project_id="test-project",
instance_id="test-instance",
table_id="test-table",
credentials=creds,
)
expected_result = {
"project_id": "test-project",
"instance_id": "test-instance",
"table_id": "test-table",
"column_families": ["cf1"],
}
assert result == {"status": "SUCCESS", "results": expected_result}
def test_list_clusters(mock_get_client):
mock_instance = mock.MagicMock()
mock_get_client.return_value.instance.return_value = mock_instance
mock_cluster = mock.MagicMock()
mock_cluster.cluster_id = "test-cluster"
mock_cluster.name = (
"projects/test-project/instances/test-instance/clusters/test-cluster"
)
mock_cluster.state = enums.Cluster.State.READY
mock_cluster.serve_nodes = 3
mock_cluster.default_storage_type = enums.StorageType.SSD
mock_cluster.location_id = "us-central1-a"
mock_instance.list_clusters.return_value = ([mock_cluster], [])
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.list_clusters(
project_id="test-project",
instance_id="test-instance",
credentials=creds,
)
expected_result = [{
"project_id": "test-project",
"instance_id": "test-instance",
"cluster_id": "test-cluster",
"cluster_name": mock_cluster.name,
"state": "READY",
"serve_nodes": 3,
"default_storage_type": "SSD",
"location_id": "us-central1-a",
}]
assert result == {"status": "SUCCESS", "results": expected_result}
def test_list_clusters_error(mock_get_client):
mock_get_client.side_effect = Exception("test-error")
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.list_clusters(
project_id="test-project",
instance_id="test-instance",
credentials=creds,
)
assert result == {
"status": "ERROR",
"error_details": "Exception('test-error')",
}
def test_get_cluster_info(mock_get_client):
mock_instance = mock.MagicMock()
mock_get_client.return_value.instance.return_value = mock_instance
mock_cluster = mock.MagicMock()
mock_instance.cluster.return_value = mock_cluster
mock_cluster.cluster_id = "test-cluster"
mock_cluster.state = enums.Cluster.State.READY
mock_cluster.serve_nodes = 3
mock_cluster.default_storage_type = enums.StorageType.SSD
mock_cluster.location_id = "us-central1-a"
mock_cluster.min_serve_nodes = 3
mock_cluster.max_serve_nodes = 10
mock_cluster.cpu_utilization_percent = 50
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.get_cluster_info(
project_id="test-project",
instance_id="test-instance",
cluster_id="test-cluster",
credentials=creds,
)
expected_results = {
"project_id": "test-project",
"instance_id": "test-instance",
"cluster_id": "test-cluster",
"state": "READY",
"serve_nodes": 3,
"default_storage_type": "SSD",
"location_id": "us-central1-a",
"min_serve_nodes": 3,
"max_serve_nodes": 10,
"cpu_utilization_percent": 50,
}
assert result == {"status": "SUCCESS", "results": expected_results}
mock_cluster.reload.assert_called_once()
def test_get_cluster_info_error(mock_get_client):
mock_get_client.side_effect = Exception("test-error")
creds = mock.create_autospec(Credentials, instance=True)
result = metadata_tool.get_cluster_info(
project_id="test-project",
instance_id="test-instance",
cluster_id="test-cluster",
credentials=creds,
)
assert result == {
"status": "ERROR",
"error_details": "Exception('test-error')",
}
@@ -0,0 +1,309 @@
# Copyright 2026 Google LLC
#
# Licensed 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.
from __future__ import annotations
from unittest import mock
from google.adk.tools.bigtable import client
from google.adk.tools.bigtable.query_tool import execute_sql
from google.adk.tools.bigtable.settings import BigtableToolSettings
from google.adk.tools.tool_context import ToolContext
from google.auth.credentials import Credentials
from google.cloud.bigtable.data.execute_query import ExecuteQueryIterator
import pytest
@pytest.mark.asyncio
@pytest.mark.parametrize(
(
"query",
"settings",
"parameters",
"parameter_types",
"execute_query_side_effect",
"iterator_yield_values",
"expected_result",
),
[
pytest.param(
"SELECT * FROM my_table",
BigtableToolSettings(),
None,
None,
None,
[{"col1": "val1", "col2": 123}],
{"status": "SUCCESS", "rows": [{"col1": "val1", "col2": 123}]},
id="basic",
),
pytest.param(
"SELECT * FROM my_table",
BigtableToolSettings(max_query_result_rows=1),
None,
None,
None,
[{"col1": "val1"}, {"col1": "val2"}],
{
"status": "SUCCESS",
"rows": [{"col1": "val1"}],
"result_is_likely_truncated": True,
},
id="truncated",
),
pytest.param(
"SELECT * FROM my_table",
BigtableToolSettings(),
None,
None,
Exception("Test error"),
None,
{"status": "ERROR", "error_details": "Test error"},
id="error",
),
pytest.param(
"SELECT * FROM my_table WHERE col1 = @param1",
BigtableToolSettings(),
{"param1": "val1"},
{"param1": "string"},
None,
[{"col1": "val1"}],
{"status": "SUCCESS", "rows": [{"col1": "val1"}]},
id="with_parameters",
),
pytest.param(
"SELECT * FROM my_table WHERE 1=0",
BigtableToolSettings(),
None,
None,
None,
[],
{"status": "SUCCESS", "rows": []},
id="empty_results",
),
pytest.param(
"SELECT * FROM my_table",
BigtableToolSettings(max_query_result_rows=10),
None,
None,
None,
[{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}],
{
"status": "SUCCESS",
"rows": [{"id": 1, "name": "Alice"}, {"id": 2, "name": "Bob"}],
},
id="multiple_rows",
),
pytest.param(
"SELECT * FROM my_table",
None,
None,
None,
None,
[{"id": i} for i in range(51)],
{
"status": "SUCCESS",
"rows": [{"id": i} for i in range(50)],
"result_is_likely_truncated": True,
},
id="settings_none_uses_default",
),
pytest.param(
"SELECT * FROM my_table",
BigtableToolSettings(),
None,
None,
None,
Exception("Iteration failed"),
{"status": "ERROR", "error_details": "Iteration failed"},
id="iteration_error_calls_close",
),
],
)
async def test_execute_sql(
query,
settings,
parameters,
parameter_types,
execute_query_side_effect,
iterator_yield_values,
expected_result,
):
"""Test execute_sql tool functionality."""
project = "my_project"
instance_id = "my_instance"
credentials = mock.create_autospec(Credentials, instance=True)
tool_context = mock.create_autospec(ToolContext, instance=True)
with mock.patch.object(client, "get_bigtable_data_client") as mock_get_client:
mock_client = mock.MagicMock()
mock_get_client.return_value = mock_client
if execute_query_side_effect:
mock_client.execute_query.side_effect = execute_query_side_effect
else:
mock_iterator = mock.create_autospec(ExecuteQueryIterator, instance=True)
mock_client.execute_query.return_value = mock_iterator
if isinstance(iterator_yield_values, Exception):
def raise_error():
yield mock.MagicMock()
raise iterator_yield_values
mock_iterator.__iter__.side_effect = raise_error
else:
mock_rows = []
for fields in iterator_yield_values:
mock_row = mock.MagicMock()
mock_row.fields = fields
mock_rows.append(mock_row)
mock_iterator.__iter__.return_value = mock_rows
result = await execute_sql(
project_id=project,
instance_id=instance_id,
credentials=credentials,
query=query,
settings=settings,
tool_context=tool_context,
parameters=parameters,
parameter_types=parameter_types,
)
if expected_result["status"] == "ERROR":
assert result["status"] == "ERROR"
assert expected_result["error_details"] in result["error_details"]
else:
assert result == expected_result
if not execute_query_side_effect:
mock_client.execute_query.assert_called_once_with(
query=query,
instance_id=instance_id,
parameters=parameters,
parameter_types=parameter_types,
view_parameters=None,
)
mock_iterator.close.assert_called_once()
@pytest.mark.asyncio
async def test_execute_sql_row_value_circular_reference_fallback():
"""Test execute_sql converts circular row values to strings."""
project = "my_project"
instance_id = "my_instance"
query = "SELECT * FROM my_table"
credentials = mock.create_autospec(Credentials, instance=True)
tool_context = mock.create_autospec(ToolContext, instance=True)
with mock.patch.object(client, "get_bigtable_data_client") as mock_get_client:
mock_client = mock.MagicMock()
mock_get_client.return_value = mock_client
mock_iterator = mock.create_autospec(ExecuteQueryIterator, instance=True)
mock_client.execute_query.return_value = mock_iterator
circular_value = []
circular_value.append(circular_value)
mock_row = mock.MagicMock()
mock_row.fields = {"col1": circular_value}
mock_iterator.__iter__.return_value = [mock_row]
result = await execute_sql(
project_id=project,
instance_id=instance_id,
credentials=credentials,
query=query,
settings=BigtableToolSettings(),
tool_context=tool_context,
)
assert result["status"] == "SUCCESS"
assert result["rows"][0]["col1"] == str(circular_value)
@pytest.mark.asyncio
async def test_execute_sql_with_view_parameters():
"""Test execute_sql with _view_parameters passed."""
project = "my_project"
instance_id = "my_instance"
query = "SELECT * FROM my_table"
credentials = mock.create_autospec(Credentials, instance=True)
tool_context = mock.create_autospec(ToolContext, instance=True)
view_parameters = {"user_id": "test-user-123"}
with mock.patch.object(client, "get_bigtable_data_client") as mock_get_client:
mock_client = mock.MagicMock()
mock_get_client.return_value = mock_client
mock_iterator = mock.create_autospec(ExecuteQueryIterator, instance=True)
mock_client.execute_query.return_value = mock_iterator
mock_iterator.__iter__.return_value = []
result = await execute_sql(
project_id=project,
instance_id=instance_id,
credentials=credentials,
query=query,
settings=BigtableToolSettings(),
tool_context=tool_context,
_view_parameters=view_parameters,
)
assert result["status"] == "SUCCESS"
mock_client.execute_query.assert_called_once_with(
query=query,
instance_id=instance_id,
parameters=None,
parameter_types=None,
view_parameters=view_parameters,
)
@pytest.mark.asyncio
async def test_execute_sql_with_multiple_view_parameters():
"""Test execute_sql with multiple view_parameters of different names."""
project = "my_project"
instance_id = "my_instance"
query = "SELECT * FROM my_table"
credentials = mock.create_autospec(Credentials, instance=True)
tool_context = mock.create_autospec(ToolContext, instance=True)
view_parameters = {
"user_id": "test-user-123",
"tenant_id": "tenant-xyz",
"role": "admin",
}
with mock.patch.object(client, "get_bigtable_data_client") as mock_get_client:
mock_client = mock.MagicMock()
mock_get_client.return_value = mock_client
mock_iterator = mock.create_autospec(ExecuteQueryIterator, instance=True)
mock_client.execute_query.return_value = mock_iterator
mock_iterator.__iter__.return_value = []
result = await execute_sql(
project_id=project,
instance_id=instance_id,
credentials=credentials,
query=query,
settings=BigtableToolSettings(),
tool_context=tool_context,
_view_parameters=view_parameters,
)
assert result["status"] == "SUCCESS"
mock_client.execute_query.assert_called_once_with(
query=query,
instance_id=instance_id,
parameters=None,
parameter_types=None,
view_parameters=view_parameters,
)
@@ -0,0 +1,340 @@
# Copyright 2026 Google LLC
#
# Licensed 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.
from __future__ import annotations
from unittest import mock
from google.adk.agents.context import Context
from google.adk.agents.invocation_context import InvocationContext
from google.adk.sessions.session import Session
from google.adk.tools.bigtable import BigtableCredentialsConfig
from google.adk.tools.bigtable import metadata_tool
from google.adk.tools.bigtable import query_tool
from google.adk.tools.bigtable.bigtable_toolset import BigtableParameterizedViewTool
from google.adk.tools.bigtable.bigtable_toolset import BigtableToolset
from google.adk.tools.bigtable.bigtable_toolset import DEFAULT_BIGTABLE_TOOL_NAME_PREFIX
from google.adk.tools.bigtable.settings import BigtableToolSettings
from google.adk.tools.google_tool import GoogleTool
from google.adk.tools.tool_context import ToolContext
from google.auth.credentials import Credentials
import pytest
def test_bigtable_toolset_name_prefix():
"""Test Bigtable toolset name prefix."""
credentials_config = BigtableCredentialsConfig(
client_id="abc", client_secret="def"
)
toolset = BigtableToolset(credentials_config=credentials_config)
assert toolset.tool_name_prefix == DEFAULT_BIGTABLE_TOOL_NAME_PREFIX
@pytest.mark.asyncio
async def test_bigtable_toolset_tools_default():
"""Test default Bigtable toolset."""
credentials_config = BigtableCredentialsConfig(
client_id="abc", client_secret="def"
)
toolset = BigtableToolset(credentials_config=credentials_config)
tools = await toolset.get_tools()
assert tools is not None
assert len(tools) == 7
assert all([isinstance(tool, GoogleTool) for tool in tools])
expected_tool_names = set([
"list_instances",
"get_instance_info",
"list_tables",
"get_table_info",
"execute_sql",
"list_clusters",
"get_cluster_info",
])
actual_tool_names = set([tool.name for tool in tools])
assert actual_tool_names == expected_tool_names
@pytest.mark.parametrize(
"selected_tools",
[
pytest.param([], id="None"),
pytest.param(
["list_instances", "get_instance_info"], id="instance-metadata"
),
pytest.param(["list_tables", "get_table_info"], id="table-metadata"),
pytest.param(["execute_sql"], id="query"),
],
)
@pytest.mark.asyncio
async def test_bigtable_toolset_tools_selective(selected_tools):
"""Test Bigtable toolset with filter.
This test verifies the behavior of the Bigtable toolset when filter is
specified. A use case for this would be when the agent builder wants to
use only a subset of the tools provided by the toolset.
"""
credentials_config = BigtableCredentialsConfig(
client_id="abc", client_secret="def"
)
toolset = BigtableToolset(
credentials_config=credentials_config, tool_filter=selected_tools
)
tools = await toolset.get_tools()
assert tools is not None
assert len(tools) == len(selected_tools)
assert all([isinstance(tool, GoogleTool) for tool in tools])
expected_tool_names = set(selected_tools)
actual_tool_names = set([tool.name for tool in tools])
assert actual_tool_names == expected_tool_names
@pytest.mark.parametrize(
("selected_tools", "returned_tools"),
[
pytest.param(["unknown"], [], id="all-unknown"),
pytest.param(
["unknown", "execute_sql"],
["execute_sql"],
id="mixed-known-unknown",
),
],
)
@pytest.mark.asyncio
async def test_bigtable_toolset_unknown_tool(selected_tools, returned_tools):
"""Test Bigtable toolset with filter.
This test verifies the behavior of the Bigtable toolset when filter is
specified with an unknown tool.
"""
credentials_config = BigtableCredentialsConfig(
client_id="abc", client_secret="def"
)
toolset = BigtableToolset(
credentials_config=credentials_config, tool_filter=selected_tools
)
tools = await toolset.get_tools()
assert tools is not None
assert len(tools) == len(returned_tools)
assert all([isinstance(tool, GoogleTool) for tool in tools])
expected_tool_names = set(returned_tools)
actual_tool_names = set([tool.name for tool in tools])
assert actual_tool_names == expected_tool_names
@pytest.mark.asyncio
async def test_bigtable_toolset_query_tool_wrapped():
"""Test that execute_sql is wrapped in BigtableQueryTool."""
credentials_config = BigtableCredentialsConfig(
client_id="abc", client_secret="def"
)
toolset = BigtableToolset(credentials_config=credentials_config)
tools = await toolset.get_tools()
query_tools = [tool for tool in tools if tool.name == "execute_sql"]
assert len(query_tools) == 1
parameterized_tools = [
tool for tool in tools if tool.name == "execute_sql_parameterized"
]
assert len(parameterized_tools) == 0
@pytest.mark.asyncio
async def test_bigtable_toolset_query_tool_wrapped_custom_mapping():
"""Test that BigtableParameterizedViewTool accepts custom mapping."""
credentials_config = BigtableCredentialsConfig(
client_id="abc", client_secret="def"
)
toolset = BigtableToolset(
credentials_config=credentials_config,
view_parameter_names=["user_id"],
)
tools = await toolset.get_tools()
parameterized_tools = [
tool for tool in tools if tool.name == "execute_sql_parameterized"
]
assert len(parameterized_tools) == 1
tool = parameterized_tools[0]
assert isinstance(tool, BigtableParameterizedViewTool)
assert tool.view_parameter_names == ["user_id"]
@pytest.mark.asyncio
async def test_bigtable_parameterized_view_tool_execution():
"""Test that BigtableParameterizedViewTool maps attributes from tool_context to view_parameters."""
# Define a dummy function to wrap that has '_view_parameters' parameter
def mock_execute_sql(_view_parameters=None):
return {"status": "SUCCESS", "_view_parameters": _view_parameters}
credentials = mock.create_autospec(Credentials, instance=True)
tool_settings = BigtableToolSettings()
tool_context = mock.create_autospec(ToolContext, instance=True)
tool_context.user_id = "test-user-123"
# Create tool with custom mapping
tool = BigtableParameterizedViewTool(
func=mock_execute_sql,
view_parameter_names=["user_id"],
)
# Run the tool
res = await tool._run_async_with_credential(
credentials=credentials,
tool_settings=tool_settings,
args={},
tool_context=tool_context,
)
assert res == {
"status": "SUCCESS",
"_view_parameters": {"user_id": "test-user-123"},
}
@pytest.mark.asyncio
async def test_bigtable_parameterized_view_tool_login_flow():
"""Test showing how user_id is updated in the session/context after login."""
def mock_execute_sql(_view_parameters=None):
return {"status": "SUCCESS", "_view_parameters": _view_parameters}
session = Session(id="session-1", app_name="test-app", user_id="anonymous")
invocation_context = mock.create_autospec(InvocationContext, instance=True)
invocation_context.session = session
type(invocation_context).user_id = property(lambda self: self.session.user_id)
tool_context = Context(invocation_context=invocation_context)
assert tool_context.user_id == "anonymous"
# Simulate login by updating user_id on session
session.user_id = "authenticated-user-999"
credentials = mock.create_autospec(Credentials, instance=True)
tool = BigtableParameterizedViewTool(
func=mock_execute_sql,
view_parameter_names=["user_id"],
)
res = await tool._run_async_with_credential(
credentials=credentials,
tool_settings=BigtableToolSettings(),
args={},
tool_context=tool_context,
)
assert res == {
"status": "SUCCESS",
"_view_parameters": {"user_id": "authenticated-user-999"},
}
@pytest.mark.asyncio
async def test_bigtable_parameterized_view_tool_execution_session_state_fallback():
"""Test that BigtableParameterizedViewTool falls back to tool_context.state for custom parameters."""
def mock_execute_sql(_view_parameters=None):
return {"status": "SUCCESS", "_view_parameters": _view_parameters}
# Create session with application-level state
session = Session(
id="session-1",
app_name="test-app",
user_id="user-123",
state={"tenant_id": "tenant-xyz"},
)
invocation_context = mock.create_autospec(InvocationContext, instance=True)
invocation_context.session = session
type(invocation_context).user_id = property(lambda self: self.session.user_id)
tool_context = Context(invocation_context=invocation_context)
# Ensure 'tenant_id' is NOT a top-level property or attribute on tool_context
assert not hasattr(tool_context, "tenant_id")
assert "tenant_id" in tool_context.state
credentials = mock.create_autospec(Credentials, instance=True)
tool = BigtableParameterizedViewTool(
func=mock_execute_sql,
view_parameter_names=["tenant_id"],
)
res = await tool._run_async_with_credential(
credentials=credentials,
tool_settings=BigtableToolSettings(),
args={},
tool_context=tool_context,
)
assert res == {
"status": "SUCCESS",
"_view_parameters": {"tenant_id": "tenant-xyz"},
}
@pytest.mark.asyncio
async def test_bigtable_parameterized_view_tool_execution_multiple_parameters():
"""Test that BigtableParameterizedViewTool maps multiple attributes to view_parameters."""
def mock_execute_sql(_view_parameters=None):
return {"status": "SUCCESS", "_view_parameters": _view_parameters}
session = Session(
id="session-1",
app_name="test-app",
user_id="user-123",
state={"tenant_id": "tenant-xyz", "agent_id": "agent-123"},
)
invocation_context = mock.create_autospec(InvocationContext, instance=True)
invocation_context.session = session
type(invocation_context).user_id = property(lambda self: self.session.user_id)
tool_context = Context(invocation_context=invocation_context)
credentials = mock.create_autospec(Credentials, instance=True)
# Pass list of parameter names to be matched
tool = BigtableParameterizedViewTool(
func=mock_execute_sql,
view_parameter_names=["user_id", "tenant_id", "agent_id"],
)
res = await tool._run_async_with_credential(
credentials=credentials,
tool_settings=BigtableToolSettings(),
args={},
tool_context=tool_context,
)
assert res == {
"status": "SUCCESS",
"_view_parameters": {
"user_id": "user-123",
"tenant_id": "tenant-xyz",
"agent_id": "agent-123",
},
}
@@ -0,0 +1,50 @@
# Copyright 2026 Google LLC
#
# Licensed 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.
from unittest import mock
from google.adk.tools.bigtable import client
from google.auth.credentials import Credentials
def test_get_bigtable_data_client():
"""Test get_bigtable_client function."""
with mock.patch(
"google.cloud.bigtable.data.BigtableDataClient"
) as MockBigtableDataClient:
mock_creds = mock.create_autospec(Credentials, instance=True)
client.get_bigtable_data_client(
project="test-project", credentials=mock_creds
)
MockBigtableDataClient.assert_called_once_with(
project="test-project",
credentials=mock_creds,
client_info=mock.ANY,
)
def test_get_bigtable_admin_client():
"""Test get_bigtable_admin_client function."""
with mock.patch("google.cloud.bigtable.Client") as BigtableDataClient:
mock_creds = mock.create_autospec(Credentials, instance=True)
client.get_bigtable_admin_client(
project="test-project", credentials=mock_creds
)
# Admin client is a BigtableDataClient created with admin=True.
BigtableDataClient.assert_called_once_with(
project="test-project",
admin=True,
credentials=mock_creds,
client_info=mock.ANY,
)