Skip to content
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

feat: Auth Testing Strategy Showcase #1603

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
175 changes: 174 additions & 1 deletion noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -155,3 +159,172 @@ 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/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"),
external=True,
silent=True,
)
if include_service_yaml:
session.run(
"curl",
"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"),
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/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"),
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=/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 = (
"python",
"-m",
"grpc_tools.protoc",
f"--experimental_allow_proto3_optional",
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",
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"/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}

with temp_file_path.open('w') as file:
yaml.safe_dump(data, file, default_flow_style=False, sort_keys=False)
"""
52 changes: 52 additions & 0 deletions tests/showcase/test_service_account.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# 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",
# )

transport_cls = showcase.EchoClient.get_transport_class("rest")
transport = transport_cls(
credentials=default()[0],
host="localhost:7469",
url_scheme="http",
)

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)
64 changes: 64 additions & 0 deletions tests/showcase/test_unary.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# 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_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