From d7bad5dbead528dd369c37b94bb420f3bbb10f51 Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Wed, 9 Oct 2024 06:30:51 +0000 Subject: [PATCH 1/4] feat: Auth Testing Strategy Showcase --- noxfile.py | 172 ++++++++++++++++++++++++++++++++++- tests/showcase/test_unary.py | 56 ++++++++++++ 2 files changed, 227 insertions(+), 1 deletion(-) create mode 100644 tests/showcase/test_unary.py diff --git a/noxfile.py b/noxfile.py index 07cef9bcc..9b6025842 100644 --- a/noxfile.py +++ b/noxfile.py @@ -15,10 +15,14 @@ import os import pathlib import shutil - +import tempfile +import typing import nox +from contextlib import contextmanager + CURRENT_DIRECTORY = pathlib.Path(__file__).parent.absolute() +showcase_version = os.environ.get("SHOWCASE_VERSION", "0.35.0") # https://github.com/psf/black/issues/2964, pin click version to 8.0.4 to # avoid incompatiblity with black. @@ -155,3 +159,169 @@ def pypy(session): "tests", "tests_async", ) + +@contextmanager +def showcase_library( + session, templates="DEFAULT", other_opts: typing.Iterable[str] = (), + include_service_yaml=True, + retry_config=True, + rest_async_io_enabled=False +): + """Install the generated library into the session for showcase tests.""" + + session.log("-" * 70) + session.log("Note: Showcase must be running for these tests to work.") + session.log("See https://github.com/googleapis/gapic-showcase") + session.log("-" * 70) + + # Install gapic-generator-python + session.install("-e", ".") + + # Install grpcio-tools for protoc + session.install("grpcio-tools") + + # Install a client library for Showcase. + with tempfile.TemporaryDirectory() as tmp_dir: + # Download the Showcase descriptor. + session.run( + "curl", + "https://github.com/googleapis/gapic-showcase/releases/" + f"download/v{showcase_version}/" + f"gapic-showcase-{showcase_version}.desc", + "-L", + "--output", + os.path.join(tmp_dir, "showcase.desc"), + external=True, + silent=True, + ) + if include_service_yaml: + session.run( + "curl", + "https://github.com/googleapis/gapic-showcase/releases/" + f"download/v{showcase_version}/" + f"showcase_v1beta1.yaml", + "-L", + "--output", + os.path.join(tmp_dir, "showcase_v1beta1.yaml"), + external=True, + silent=True, + ) + # TODO(https://github.com/googleapis/gapic-generator-python/issues/2121): The section below updates the showcase service yaml + # to test experimental async rest transport. It must be removed once support for async rest is GA. + if rest_async_io_enabled: + # Install pyYAML for yaml. + session.install("pyYAML") + + python_settings = [ + { + 'version': 'google.showcase.v1beta1', + 'python_settings': { + 'experimental_features': { + 'rest_async_io_enabled': True + } + } + } + ] + update_service_yaml = _add_python_settings(tmp_dir, python_settings) + session.run("python", "-c" f"{update_service_yaml}") + # END TODO section to remove. + if retry_config: + session.run( + "curl", + "https://github.com/googleapis/gapic-showcase/releases/" + f"download/v{showcase_version}/" + f"showcase_grpc_service_config.json", + "-L", + "--output", + os.path.join(tmp_dir, "showcase_grpc_service_config.json"), + external=True, + silent=True, + ) + # Write out a client library for Showcase. + template_opt = f"python-gapic-templates={templates}" + opts = "--python_gapic_opt=" + if include_service_yaml and retry_config: + opts += ",".join(other_opts + (f"{template_opt}", "transport=grpc+rest", f"service-yaml={tmp_dir}/showcase_v1beta1.yaml", f"retry-config={tmp_dir}/showcase_grpc_service_config.json")) + else: + opts += ",".join(other_opts + (f"{template_opt}", "transport=grpc+rest",)) + cmd_tup = ( + "python", + "-m", + "grpc_tools.protoc", + f"--experimental_allow_proto3_optional", + f"--descriptor_set_in={tmp_dir}{os.sep}showcase.desc", + opts, + f"--python_gapic_out={tmp_dir}", + f"google/showcase/v1beta1/echo.proto", + f"google/showcase/v1beta1/identity.proto", + f"google/showcase/v1beta1/messaging.proto", + ) + session.run( + *cmd_tup, external=True, + ) + + # Install the generated showcase library. + if templates == "DEFAULT": + # Use the constraints file for the specific python runtime version. + # We do this to make sure that we're testing against the lowest + # supported version of a dependency. + # This is needed to recreate the issue reported in + # https://github.com/googleapis/google-cloud-python/issues/12254 + constraints_path = str( + f"{tmp_dir}/testing/constraints-{session.python}.txt" + ) + # Install the library with a constraints file. + session.install("-e", tmp_dir, "-r", constraints_path) + else: + # The ads templates do not have constraints files. + # See https://github.com/googleapis/gapic-generator-python/issues/1788 + # Install the library without a constraints file. + session.install("-e", tmp_dir) + + yield tmp_dir + + +@nox.session(python="3.8") +def showcase( + session, + templates="DEFAULT", + other_opts: typing.Iterable[str] = (), + env: typing.Optional[typing.Dict[str, str]] = {}, +): + """Run the Showcase test suite.""" + + with showcase_library(session, templates=templates, other_opts=other_opts): + session.install("pytest", "pytest-asyncio", "mock") + test_directory = os.path.join("tests", "showcase") + ignore_file = env.get("IGNORE_FILE") + pytest_command = [ + "py.test", + "--quiet", + *(session.posargs or [str(test_directory)]), + ] + if ignore_file: + ignore_path = test_directory / ignore_file + pytest_command.extend(["--ignore", str(ignore_path)]) + + session.run( + *pytest_command, + env=env, + ) + +# `_add_python_settings` consumes a path to a temporary directory (str; i.e. tmp_dir) and +# python settings (Dict; i.e. python settings) and modifies the service yaml within +# tmp_dir to include python settings. The primary purpose of this function is to modify +# the service yaml and include `rest_async_io_enabled=True` to test the async rest +# optional feature. +def _add_python_settings(tmp_dir, python_settings): + return f""" +import yaml +from pathlib import Path +temp_file_path = Path(f"{tmp_dir}/showcase_v1beta1.yaml") +with temp_file_path.open('r') as file: + data = yaml.safe_load(file) + data['publishing']['library_settings'] = {python_settings} + +with temp_file_path.open('w') as file: + yaml.safe_dump(data, file, default_flow_style=False, sort_keys=False) +""" \ No newline at end of file diff --git a/tests/showcase/test_unary.py b/tests/showcase/test_unary.py new file mode 100644 index 000000000..fc5bddb8e --- /dev/null +++ b/tests/showcase/test_unary.py @@ -0,0 +1,56 @@ +# Copyright 2018 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 +# +# https://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 re +import pytest +from google import showcase + +UUID4_RE = r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}" + +@pytest.fixture +def echo(): + # Create an instance of the Showcase Echo client + echo_client = showcase.EchoClient() + yield echo_client + # Optional: Clean up resources if needed after the test + # e.g., echo_client.close() + +def test_unary_with_request_object(echo): + response = echo.echo( + showcase.EchoRequest( + content="The hail in Wales falls mainly on the snails.", + request_id="some_value", + other_request_id="", + ) + ) + assert response.content == "The hail in Wales falls mainly on the snails." + assert response.request_id == "some_value" + assert response.other_request_id == "" + + # Repeat the same test but this time without `request_id`` set + # The `request_id` field should be automatically populated with + # a UUID4 value if it is not set. + # See https://google.aip.dev/client-libraries/4235 + response = echo.echo( + showcase.EchoRequest( + content="The hail in Wales falls mainly on the snails.", + ) + ) + assert response.content == "The hail in Wales falls mainly on the snails." + # Ensure that the uuid4 field is set according to AIP 4235 + assert re.match(UUID4_RE, response.request_id) + assert len(response.request_id) == 36 + # Ensure that the uuid4 field is set according to AIP 4235 + assert re.match(UUID4_RE, response.other_request_id) + assert len(response.other_request_id) == 36 \ No newline at end of file From f2f9358e23a90f308fcdcdca044bbf13de78ca4b Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Thu, 10 Oct 2024 23:50:15 +0000 Subject: [PATCH 2/4] unsecure grpc channel --- noxfile.py | 27 +++++++++++++++------------ tests/showcase/test_unary.py | 10 +++++++++- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/noxfile.py b/noxfile.py index 9b6025842..8872f4661 100644 --- a/noxfile.py +++ b/noxfile.py @@ -185,9 +185,10 @@ def showcase_library( # Download the Showcase descriptor. session.run( "curl", - "https://github.com/googleapis/gapic-showcase/releases/" - f"download/v{showcase_version}/" - f"gapic-showcase-{showcase_version}.desc", + "https://github.com/googleapis/gapic-showcase/blob/507a4cbdc45c8380aff29308ff2a1144ead9a7dc/test_gapic_showcase.desc" + # "https://github.com/googleapis/gapic-showcase/releases/" + # f"download/v{showcase_version}/" + # f"gapic-showcase-{showcase_version}.desc", "-L", "--output", os.path.join(tmp_dir, "showcase.desc"), @@ -197,9 +198,10 @@ def showcase_library( if include_service_yaml: session.run( "curl", - "https://github.com/googleapis/gapic-showcase/releases/" - f"download/v{showcase_version}/" - f"showcase_v1beta1.yaml", + "https://github.com/googleapis/gapic-showcase/blob/507a4cbdc45c8380aff29308ff2a1144ead9a7dc/schema/google/showcase/v1beta1/showcase_v1beta1.yaml", + # "https://github.com/googleapis/gapic-showcase/releases/" + # f"download/v{showcase_version}/" + # f"showcase_v1beta1.yaml", "-L", "--output", os.path.join(tmp_dir, "showcase_v1beta1.yaml"), @@ -228,9 +230,10 @@ def showcase_library( if retry_config: session.run( "curl", - "https://github.com/googleapis/gapic-showcase/releases/" - f"download/v{showcase_version}/" - f"showcase_grpc_service_config.json", + "https://github.com/googleapis/gapic-showcase/blob/507a4cbdc45c8380aff29308ff2a1144ead9a7dc/schema/google/showcase/v1beta1/showcase_grpc_service_config.json", + # "https://github.com/googleapis/gapic-showcase/releases/" + # f"download/v{showcase_version}/" + # f"showcase_grpc_service_config.json", "-L", "--output", os.path.join(tmp_dir, "showcase_grpc_service_config.json"), @@ -241,7 +244,7 @@ def showcase_library( template_opt = f"python-gapic-templates={templates}" opts = "--python_gapic_opt=" if include_service_yaml and retry_config: - opts += ",".join(other_opts + (f"{template_opt}", "transport=grpc+rest", f"service-yaml={tmp_dir}/showcase_v1beta1.yaml", f"retry-config={tmp_dir}/showcase_grpc_service_config.json")) + opts += ",".join(other_opts + (f"{template_opt}", "transport=grpc+rest", f"service-yaml=/usr/local/google/home/saisunder/data/playground/TestingStrategy/gapic-showcase/schema/google/showcase/v1beta1/showcase_v1beta1.yaml", f"retry-config=/usr/local/google/home/saisunder/data/playground/TestingStrategy/gapic-showcase/schema/google/showcase/v1beta1/showcase_grpc_service_config.json")) else: opts += ",".join(other_opts + (f"{template_opt}", "transport=grpc+rest",)) cmd_tup = ( @@ -249,7 +252,7 @@ def showcase_library( "-m", "grpc_tools.protoc", f"--experimental_allow_proto3_optional", - f"--descriptor_set_in={tmp_dir}{os.sep}showcase.desc", + f"--descriptor_set_in=/usr/local/google/home/saisunder/data/playground/TestingStrategy/gapic-showcase/test_gapic_showcase.desc", opts, f"--python_gapic_out={tmp_dir}", f"google/showcase/v1beta1/echo.proto", @@ -317,7 +320,7 @@ def _add_python_settings(tmp_dir, python_settings): return f""" import yaml from pathlib import Path -temp_file_path = Path(f"{tmp_dir}/showcase_v1beta1.yaml") +temp_file_path = Path(f"/usr/local/google/home/saisunder/data/playground/TestingStrategy/gapic-showcase/schema/google/showcase/v1beta1/showcase_v1beta1.yaml") with temp_file_path.open('r') as file: data = yaml.safe_load(file) data['publishing']['library_settings'] = {python_settings} diff --git a/tests/showcase/test_unary.py b/tests/showcase/test_unary.py index fc5bddb8e..5c1486ea2 100644 --- a/tests/showcase/test_unary.py +++ b/tests/showcase/test_unary.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import grpc +from google.auth import credentials import re import pytest from google import showcase @@ -21,7 +23,13 @@ @pytest.fixture def echo(): # Create an instance of the Showcase Echo client - echo_client = showcase.EchoClient() + transport_cls = showcase.EchoClient.get_transport_class("grpc") + transport = transport_cls( + credentials=credentials.AnonymousCredentials(), + channel=grpc.insecure_channel("localhost:7469"), + host="localhost:7469", + ) + echo_client = showcase.EchoClient(transport=transport) yield echo_client # Optional: Clean up resources if needed after the test # e.g., echo_client.close() From 96756c0796b87f215ab87e5679af3aa44a56e747 Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Mon, 14 Oct 2024 23:36:18 +0000 Subject: [PATCH 3/4] Add test for ssj --- tests/showcase/test_service_account.py | 44 ++++++++++++++++++++++++++ tests/showcase/test_unary.py | 4 +-- 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 tests/showcase/test_service_account.py diff --git a/tests/showcase/test_service_account.py b/tests/showcase/test_service_account.py new file mode 100644 index 000000000..12ebf0155 --- /dev/null +++ b/tests/showcase/test_service_account.py @@ -0,0 +1,44 @@ +# Copyright 2018 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 +# +# https://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 grpc +from google.auth import credentials, default +import re +import pytest +from google import showcase + +UUID4_RE = r"[a-f0-9]{8}-?[a-f0-9]{4}-?4[a-f0-9]{3}-?[89ab][a-f0-9]{3}-?[a-f0-9]{12}" + +@pytest.fixture +def echo(): + # Create an instance of the Showcase Echo client + transport_cls = showcase.EchoClient.get_transport_class("grpc") + transport = transport_cls( + credentials=default()[0], + channel=grpc.insecure_channel("localhost:7469"), + host="localhost:7469", + ) + echo_client = showcase.EchoClient(transport=transport) + yield echo_client + # Optional: Clean up resources if needed after the test + # e.g., echo_client.close() + +def test_ssj_with_scopes(echo): + response = echo.echo_authentication( + showcase.EchoAuthenticationRequest( + ) + ) + + # Handle the response + print(response) \ No newline at end of file diff --git a/tests/showcase/test_unary.py b/tests/showcase/test_unary.py index 5c1486ea2..ce959d054 100644 --- a/tests/showcase/test_unary.py +++ b/tests/showcase/test_unary.py @@ -13,7 +13,7 @@ # limitations under the License. import grpc -from google.auth import credentials +from google.auth import credentials, default import re import pytest from google import showcase @@ -25,7 +25,7 @@ def echo(): # Create an instance of the Showcase Echo client transport_cls = showcase.EchoClient.get_transport_class("grpc") transport = transport_cls( - credentials=credentials.AnonymousCredentials(), + credentials=default()[0], channel=grpc.insecure_channel("localhost:7469"), host="localhost:7469", ) From 4c723c84cd390400f11ee62b2011c7f0adbbeab4 Mon Sep 17 00:00:00 2001 From: Sai Sunder Srinivasan Date: Wed, 16 Oct 2024 18:32:37 +0000 Subject: [PATCH 4/4] .. --- tests/showcase/test_service_account.py | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/tests/showcase/test_service_account.py b/tests/showcase/test_service_account.py index 12ebf0155..dead5dffe 100644 --- a/tests/showcase/test_service_account.py +++ b/tests/showcase/test_service_account.py @@ -23,12 +23,20 @@ @pytest.fixture def echo(): # Create an instance of the Showcase Echo client - transport_cls = showcase.EchoClient.get_transport_class("grpc") + # transport_cls = showcase.EchoClient.get_transport_class("grpc") + # transport = transport_cls( + # credentials=default()[0], + # channel=grpc.insecure_channel("localhost:7469"), + # host="localhost:7469", + # ) + + transport_cls = showcase.EchoClient.get_transport_class("rest") transport = transport_cls( credentials=default()[0], - channel=grpc.insecure_channel("localhost:7469"), host="localhost:7469", + url_scheme="http", ) + echo_client = showcase.EchoClient(transport=transport) yield echo_client # Optional: Clean up resources if needed after the test