diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml index 07c4a188e1..349ac5011a 100644 --- a/.github/workflows/sonar.yaml +++ b/.github/workflows/sonar.yaml @@ -1,6 +1,6 @@ name: SonarCloud Build env: - SHOWCASE_VERSION: 0.25.0 + SHOWCASE_VERSION: 0.26.0 on: push: branches: diff --git a/.gitignore b/.gitignore index 585e94f980..07604728ff 100644 --- a/.gitignore +++ b/.gitignore @@ -15,4 +15,4 @@ target/ # Vscode Settings .vscode/settings.json -*.iml +*.iml \ No newline at end of file diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml index 96b4045ebd..016885ebef 100644 --- a/showcase/gapic-showcase/pom.xml +++ b/showcase/gapic-showcase/pom.xml @@ -18,6 +18,10 @@ 0.0.1-SNAPSHOT + + 0.26.0 + + enable-golden-tests @@ -106,6 +110,28 @@ (IT.*\.java)|(.*Test.java) + + com.googlecode.maven-download-plugin + download-maven-plugin + 1.6.8 + + + download-compliance-suite + generate-test-resources + + wget + + + + https://raw.githubusercontent.com/googleapis/gapic-showcase/v${gapic-showcase.version}/server/services/compliance_suite.json + + src/test/resources + + true + + + + diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITHttpAnnotation.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITHttpAnnotation.java new file mode 100644 index 0000000000..79431fbc68 --- /dev/null +++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITHttpAnnotation.java @@ -0,0 +1,141 @@ +/* + * Copyright 2023 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. + */ +package com.google.showcase.v1beta1.it; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.client.http.javanet.NetHttpTransport; +import com.google.api.gax.core.NoCredentialsProvider; +import com.google.common.collect.ImmutableMap; +import com.google.protobuf.util.JsonFormat; +import com.google.showcase.v1beta1.ComplianceClient; +import com.google.showcase.v1beta1.ComplianceData; +import com.google.showcase.v1beta1.ComplianceGroup; +import com.google.showcase.v1beta1.ComplianceSettings; +import com.google.showcase.v1beta1.ComplianceSuite; +import com.google.showcase.v1beta1.RepeatRequest; +import com.google.showcase.v1beta1.RepeatResponse; +import java.io.IOException; +import java.io.InputStreamReader; +import java.security.GeneralSecurityException; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.function.Function; +import java.util.stream.Collectors; +import org.junit.After; +import org.junit.Before; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; + +// This test runs from the parameters in the compliance_suite.json file +// The file is downloaded from the gapic-showcase repo. Each compliance +// group is a set of HttpJson behaviors we want to test for. Each group +// tests the product of the rpc list and requests list. +@RunWith(Parameterized.class) +public class ITHttpAnnotation { + + @Parameterized.Parameters(name = "Compliance Group Name: {0}") + public static String[] data() { + return new String[] { + "Fully working conversions, resources", + "Binding selection testing", + "Cases that apply to non-path requests", + "Fully working conversions, no resources" + }; + } + + @Parameterized.Parameter(0) + public String groupName; + + private ComplianceSuite complianceSuite; + private ComplianceClient complianceClient; + private Map> validComplianceRpcMap; + + @Before + public void createClient() throws IOException, GeneralSecurityException { + ComplianceSuite.Builder builder = ComplianceSuite.newBuilder(); + JsonFormat.parser() + .merge( + new InputStreamReader( + ITHttpAnnotation.class + .getClassLoader() + .getResourceAsStream("compliance_suite.json")), + builder); + complianceSuite = builder.build(); + + ComplianceSettings httpjsonComplianceSettings = + ComplianceSettings.newHttpJsonBuilder() + .setCredentialsProvider(NoCredentialsProvider.create()) + .setTransportChannelProvider( + ComplianceSettings.defaultHttpJsonTransportProviderBuilder() + .setHttpTransport( + new NetHttpTransport.Builder().doNotValidateCertificate().build()) + .setEndpoint("http://localhost:7469") + .build()) + .build(); + complianceClient = ComplianceClient.create(httpjsonComplianceSettings); + + // Mapping of Compliance Suite file RPC Names to ComplianceClient methods + validComplianceRpcMap = + ImmutableMap.of( + "Compliance.RepeatDataBody", + complianceClient::repeatDataBody, + "Compliance.RepeatDataBodyInfo", + complianceClient::repeatDataBodyInfo, + "Compliance.RepeatDataQuery", + complianceClient::repeatDataQuery, + "Compliance.RepeatDataSimplePath", + complianceClient::repeatDataSimplePath, + "Compliance.RepeatDataBodyPut", + complianceClient::repeatDataBodyPut, + "Compliance.RepeatDataBodyPatch", + complianceClient::repeatDataBodyPatch, + "Compliance.RepeatDataPathResource", + complianceClient::repeatDataPathResource); + } + + @After + public void destroyClient() { + complianceClient.close(); + } + + // Verify that the input's info is the same as the response's info + // This ensures that the entire group's behavior over HttpJson + // works as intended + @Test + public void testComplianceGroup() { + Optional complianceGroupOptional = + complianceSuite.getGroupList().stream() + .filter(x -> x.getName().equals(groupName)) + .findFirst(); + assertThat(complianceGroupOptional.isPresent()).isTrue(); + ComplianceGroup complianceGroup = complianceGroupOptional.get(); + List validRpcList = + complianceGroup.getRpcsList().stream() + .filter(validComplianceRpcMap::containsKey) + .collect(Collectors.toList()); + for (String rpcName : validRpcList) { + Function rpc = validComplianceRpcMap.get(rpcName); + for (RepeatRequest repeatRequest : complianceGroup.getRequestsList()) { + ComplianceData expectedData = repeatRequest.getInfo(); + RepeatResponse response = rpc.apply(repeatRequest); + assertThat(response.getRequest().getInfo()).isEqualTo(expectedData); + } + } + } +} diff --git a/showcase/gapic-showcase/src/test/resources/META-INF/native-image/resource-config.json b/showcase/gapic-showcase/src/test/resources/META-INF/native-image/resource-config.json new file mode 100644 index 0000000000..80d7bc0ea2 --- /dev/null +++ b/showcase/gapic-showcase/src/test/resources/META-INF/native-image/resource-config.json @@ -0,0 +1,3 @@ +{ + "resources":[{"pattern": ".*.json"}] +} \ No newline at end of file diff --git a/showcase/gapic-showcase/src/test/resources/compliance_suite.json b/showcase/gapic-showcase/src/test/resources/compliance_suite.json new file mode 100644 index 0000000000..a458819b35 --- /dev/null +++ b/showcase/gapic-showcase/src/test/resources/compliance_suite.json @@ -0,0 +1,240 @@ +{ + "group": [ + { + "name": "Fully working conversions, no resources", + "rpcs": ["Compliance.RepeatDataBody", "Compliance.RepeatDataBodyPut", "Compliance.RepeatDataBodyPatch", "Compliance.RepeatDataQuery", "Compliance.RepeatDataSimplePath", "Compliance.RepeatDataBodyInfo"], + "requests": [ + { + "name": "Basic data types", + "serverVerify": true, + "info": { + "fString": "Hello", + "fInt32": -1, + "fSint32" : -2, + "fSfixed32": -3, + "fUint32": 5, + "fFixed32": 7, + "fInt64": -11, + "fSint64": -13, + "fSfixed64": -17, + "fUint64": 19, + "fFixed64":23, + "fDouble": -29e4, + "fFloat": -31, + "fBool": true, + "fKingdom": "ANIMALIA", + + "pString": "Goodbye", + "pInt32": -37, + "pDouble": -41.43, + "pBool": true, + "pKingdom": "PLANTAE", + + "fChild": { + "fString": "second/bool/salutation" + } + }, + "fInt32": -10, + "fInt64": -110, + "fDouble": -54e4, + + "pInt32": -47, + "pInt64": -477, + "pDouble": -61.73 + }, + { + "name": "Basic types, no optional fields", + "serverVerify": true, + "info": { + "fString": "Hello", + "fInt32": -1, + "fSint32" : -2, + "fSfixed32": -3, + "fUint32": 5, + "fFixed32": 7, + "fInt64": -11, + "fSint64": -13, + "fSfixed64": -17, + "fUint64": 19, + "fFixed64":23, + "fDouble": -29e4, + "fFloat": -31, + "fBool": true, + "fKingdom": "ANIMALIA", + + "fChild": { + "fString": "second/bool/salutation" + } + } + }, + + { + "name": "Zero values for non-string fields", + "serverVerify": true, + "info": { + "fString": "Hello", + "fInt32": 0, + "fSint32" : 0, + "fSfixed32": 0, + "fUint32": 0, + "fFixed32": 0, + "fInt64": 0, + "fSint64": 0, + "fSfixed64": 0, + "fUint64": 0, + "fFixed64": 0, + "fDouble": 0, + "fFloat": 0, + "fBool": false, + "fKingdom": "LIFE_KINGDOM_UNSPECIFIED", + + "pString": "Goodbye", + "pInt32": 0, + "pDouble": 0, + "pBool": false, + "pKingdom": "LIFE_KINGDOM_UNSPECIFIED" + } + }, + { + "name": "Extreme values", + "serverVerify": true, + "info": { + "fString": "non-ASCII+non-printable string ☺ → ← \"\\\/\b\f\r\t\u1234 works, not newlines yet", + "fInt32": 2147483647, + "fSint32" : 2147483647, + "fSfixed32": 2147483647, + "fUint32": 4294967295, + "fFixed32": 4294967295, + "fInt64": "9223372036854775807", + "fSint64": "9223372036854775807", + "fSfixed64": "9223372036854775807", + "fUint64": "18446744073709551615", + "fFixed64": "18446744073709551615", + "fDouble": 1.797693134862315708145274237317043567981e+308, + "fFloat": 3.40282346638528859811704183484516925440e+38, + "fBool": false, + + "pString": "Goodbye", + "pInt32": 2147483647, + "pDouble": 1.797693134862315708145274237317043567981e+308, + "pBool": false + } + }, + { + "name": "Strings with spaces", + "serverVerify": true, + "info": { + "fString": "Hello there" + } + }, + { + "name": "Strings with quotes", + "serverVerify": true, + "info": { + "fString": "Hello \"You\"" + } + }, + { + "name": "Strings with percents", + "serverVerify": true, + "info": { + "fString": "Hello 100%" + } + } + ] + }, + { + "name": "Fully working conversions, resources", + "rpcs": ["Compliance.RepeatDataBody", "Compliance.RepeatDataBodyPut", "Compliance.RepeatDataBodyPatch", "Compliance.RepeatDataQuery"], + "requests": [ + { + "name": "Strings with slashes and values that resemble subsequent resource templates", + "serverVerify": true, + "info": { + "fString": "first/hello/second/greetings", + "pBool": true, + + "fChild": { + "fString": "second/zzz/bool/true" + } + } + } + ] + }, + { + "name": "Binding selection testing", + "rpcs": ["Compliance.RepeatDataPathResource"], + "requests": [ + { + "name": "Binding testing baseline no Uri verification", + "serverVerify": true, + "info": { + "fString": "first/hello", + "pBool": true, + + "fChild": { + "fString": "second/greetings" + } + } + }, + { + "name": "Binding testing first binding", + "serverVerify": true, + "info": { + "fString": "first/hello", + "pBool": true, + + "fChild": { + "fString": "second/greetings" + } + }, + "intendedBindingUri": "/v1beta1/repeat/{info.f_string=first/*}/{info.f_child.f_string=second/*}/bool/{info.f_bool}:pathresource" + }, + { + "name": "Binding testing additional binding", + "serverVerify": true, + "info": { + "fString": "second/greetings", + "pBool": true, + + "fChild": { + "fString": "first/hello" + } + }, + "intendedBindingUri": "/v1beta1/repeat/{info.f_child.f_string=first/*}/{info.f_string=second/*}/bool/{info.f_bool}:childfirstpathresource" + } + ] + }, + { + "name": "Cases that apply to non-path requests", + "rpcs": ["Compliance.RepeatDataBody", "Compliance.RepeatDataBodyPut", "Compliance.RepeatDataBodyPatch", "Compliance.RepeatDataQuery"], + "requests": [ + { + "name": "Zero values for all fields", + "serverVerify": true, + "info": { + "fString": "", + "fInt32": 0, + "fSint32" : 0, + "fSfixed32": 0, + "fUint32": 0, + "fFixed32": 0, + "fInt64": 0, + "fSint64": 0, + "fSfixed64": 0, + "fUint64": 0, + "fFixed64":20, + "fDouble": 0, + "fFloat": 0, + "fBool": false, + + "pString": "", + "pInt32": 0, + "pDouble": 0, + "pBool": false + } + } + ] + } + ] +} diff --git a/showcase/scripts/verify.sh b/showcase/scripts/verify.sh index 41b72565c7..48026e9600 100755 --- a/showcase/scripts/verify.sh +++ b/showcase/scripts/verify.sh @@ -57,6 +57,6 @@ case $1 in unzip -q -c "$BAZEL_ROOT/$GAPIC_JAR" temp-codegen.srcjar | jar x delete_unneeded - diff -ru "$SHOWCASE_DIR/$GAPIC_PROJECT_DIR"/src "$GAPIC_UNPACK_DIR"/src --exclude=it + diff -ru "$SHOWCASE_DIR/$GAPIC_PROJECT_DIR"/src "$GAPIC_UNPACK_DIR"/src --exclude=it --exclude=resources ;; esac