Skip to content

Commit

Permalink
Test against golden master testcases.yml (#29)
Browse files Browse the repository at this point in the history
[RFC 004](palantir/conjure#35) defines some common test cases to prove that all languages conform to a common wire spec.

This PR loads up the test-cases.json file and a known-good verification-server (published by the [palantir/conjure-verification](https://github.palantir.build/foundry/conjure-verification) repo) and runs the tests.
  • Loading branch information
iamdanfox authored Jul 12, 2018
1 parent 63654bf commit cb12eb5
Show file tree
Hide file tree
Showing 11 changed files with 670 additions and 0 deletions.
110 changes: 110 additions & 0 deletions conjure-java-verifier/build.gradle
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* 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 static org.apache.tools.ant.taskdefs.condition.Os.*

ext {
osClassifier = isFamily(FAMILY_MAC) ? "osx" : "linux"
}

// We locally-codegen verification-api.conjure.json into these source-sets
sourceSets {
generatedObjects
generatedJersey
}

idea {
module {
generatedSourceDirs += sourceSets.generatedObjects.java.srcDirs
generatedSourceDirs += sourceSets.generatedJersey.java.srcDirs
scopes.COMPILE.plus += [configurations.generatedObjectsCompile, configurations.generatedJerseyCompile]
}
}

configurations {
testCases
verificationApi
verificationServer
}

dependencies {
testCases 'com.palantir.conjure.verification:test-cases'
verificationApi 'com.palantir.conjure.verification:verification-api'
verificationServer "com.palantir.conjure.verification:verification-server::${osClassifier}@tgz"

generatedObjectsCompile project(':conjure-java-core')
generatedObjectsCompile project(':conjure-lib')

generatedJerseyCompile project(':conjure-java-core')
generatedJerseyCompile project(':conjure-lib')
generatedJerseyCompile sourceSets.generatedObjects.output

testCompile project(':conjure-java-core')
testCompile 'junit:junit'
testCompile 'org.assertj:assertj-core'
testCompile 'org.mockito:mockito-core'
testCompile 'com.palantir.remoting3:jaxrs-clients'
testCompile 'io.dropwizard:dropwizard-testing'
testCompile sourceSets.generatedObjects.output
testCompile sourceSets.generatedJersey.output

processor 'org.immutables:value'
}

tasks.compileTestJava {
options.compilerArgs += ['-Xep:Slf4jLogsafeArgs:OFF']
}

task unpackVerificationServer(type: Sync) {
from { tarTree(configurations.verificationServer.singleFile) }
into "${buildDir}/verification-server"
rename { "server" }
}

task copyTestCases(type: Sync) {
from configurations.testCases
into "$buildDir/test-cases"
rename { "test-cases.json" }
}

task conjureJavaObjects(type: JavaExec) {
main = "com.palantir.conjure.java.cli.ConjureJavaCli"
classpath = project(':conjure-java').sourceSets.main.runtimeClasspath
args 'generate', "${-> configurations.verificationApi.singleFile}", 'src/generatedObjects/java', '--objects'

inputs.file "${-> configurations.verificationApi.singleFile}"
outputs.files "src/generatedObjects/java"
doFirst { delete "src/generatedObjects/java/*" }
}

task conjureJavaJersey(type: JavaExec) {
main = "com.palantir.conjure.java.cli.ConjureJavaCli"
classpath = project(':conjure-java').sourceSets.main.runtimeClasspath
args 'generate', "${-> configurations.verificationApi.singleFile}", 'src/generatedJersey/java', '--jersey'

inputs.file "${-> configurations.verificationApi.singleFile}"
outputs.files "src/generatedJersey/java"
doFirst { delete "src/generatedJersey/java/*" }
}

compileGeneratedObjectsJava.dependsOn conjureJavaObjects
compileGeneratedObjectsJava.dependsOn conjureJavaJersey

tasks.idea.dependsOn conjureJavaObjects, conjureJavaJersey, unpackVerificationServer, copyTestCases
test.dependsOn unpackVerificationServer, copyTestCases

checkstyleGeneratedObjects.enabled = false
checkstyleGeneratedJersey.enabled = false
1 change: 1 addition & 0 deletions conjure-java-verifier/src/generatedJersey/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.java
1 change: 1 addition & 0 deletions conjure-java-verifier/src/generatedObjects/java/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
*.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* 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.
*/

package com.palantir.conjure.java.compliance;

import com.palantir.conjure.verification.AutoDeserializeConfirmService;
import com.palantir.conjure.verification.AutoDeserializeService;
import com.palantir.conjure.verification.EndpointName;
import com.palantir.remoting.api.errors.RemoteException;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.stream.IntStream;
import org.assertj.core.api.Assertions;
import org.junit.Assume;
import org.junit.ClassRule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

@RunWith(Parameterized.class)
public class AutoDeserializeTest {

@ClassRule
public static final VerificationServerRule server = new VerificationServerRule();

private static final Logger log = LoggerFactory.getLogger(AutoDeserializeTest.class);
private static final AutoDeserializeService testService = VerificationClients.autoDeserializeService(server);
private static final AutoDeserializeConfirmService confirmService = VerificationClients.confirmService(server);

@Parameterized.Parameter(0)
public EndpointName endpointName;

@Parameterized.Parameter(1)
public int index;

@Parameterized.Parameter(2)
public boolean shouldSucceed;

@Parameterized.Parameter(3)
public String jsonString;

@Parameterized.Parameters(name = "{0}({3}) -> should succeed {2}")
public static Collection<Object[]> data() throws IOException {

List<Object[]> objects = new ArrayList<>();
Cases.TEST_CASES.getAutoDeserialize().forEach((endpointName, positiveAndNegativeTestCases) -> {
int positiveSize = positiveAndNegativeTestCases.getPositive().size();
int negativeSize = positiveAndNegativeTestCases.getNegative().size();

IntStream.range(0, positiveSize).forEach(i -> objects.add(new Object[] {
endpointName, i, true,
positiveAndNegativeTestCases.getPositive().get(i)}));

IntStream.range(0, negativeSize).forEach(i -> objects.add(new Object[] {
endpointName, positiveSize + i, false,
positiveAndNegativeTestCases.getNegative().get(i)}));
});
return objects;
}

@Test
public void runTestCase() throws Exception {
Assume.assumeFalse(Cases.shouldIgnore(endpointName, jsonString));

Method method = testService.getClass().getMethod(endpointName.get(), int.class);
System.out.println(String.format("Invoking %s(%s), expected %s",
endpointName,
jsonString,
shouldSucceed ? "success" : "failure"));

if (shouldSucceed) {
expectSuccess(method);
} else {
expectFailure(method);
}
}

private void expectSuccess(Method method) throws Exception {
try {
Object resultFromServer = method.invoke(testService, index);
log.info("Received result for endpoint {} and index {}: {}", endpointName, index, resultFromServer);
confirmService.confirm(endpointName.get(), index, resultFromServer);
} catch (RemoteException e) {
log.error("Caught exception with params: {}", e.getError().parameters());
throw e;
}
}

private void expectFailure(Method method) {
Assertions.assertThatExceptionOfType(Exception.class).isThrownBy(() -> {
Object result = method.invoke(testService, index);
log.error("Result should have caused an exception but deserialized to: {}", result);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* (c) Copyright 2018 Palantir Technologies Inc. All rights reserved.
*
* 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.
*/

package com.palantir.conjure.java.compliance;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.dataformat.yaml.YAMLFactory;
import com.fasterxml.jackson.datatype.jdk8.Jdk8Module;
import com.palantir.conjure.verification.ClientTestCases;
import com.palantir.conjure.verification.EndpointName;
import com.palantir.conjure.verification.IgnoredClientTestCases;
import com.palantir.conjure.verification.IgnoredTestCases;
import java.io.File;
import java.io.IOException;
import java.util.Set;
import javax.annotation.Nullable;

public final class Cases {

public static final ClientTestCases TEST_CASES = deserializeTestCases(
new File("build/test-cases/test-cases.json"));
private static final IgnoredClientTestCases IGNORED_TEST_CASES = deserializeIgnoredClientTestCases(
new File("src/test/resources/ignored-test-cases.yml"));

private Cases() {}

private static ClientTestCases deserializeTestCases(File file) {
try {
return new ObjectMapper()
.registerModule(new Jdk8Module())
.readValue(file, com.palantir.conjure.verification.TestCases.class).getClient();
} catch (IOException e) {
throw new RuntimeException(
String.format("Unable to read %s, you may need to run ./gradlew copyTestCases", file),
e);
}
}

private static IgnoredClientTestCases deserializeIgnoredClientTestCases(File file) {
try {
return new ObjectMapper(new YAMLFactory())
.registerModule(new Jdk8Module())
.readValue(file, IgnoredTestCases.class).getClient();
} catch (IOException e) {
throw new RuntimeException(
String.format("Unable to read %s", file),
e);
}
}

public static boolean shouldIgnore(EndpointName endpointName, String json) {
return setContains(IGNORED_TEST_CASES.getAutoDeserialize().get(endpointName), json)
|| setContains(IGNORED_TEST_CASES.getSingleHeaderService().get(endpointName), json)
|| setContains(IGNORED_TEST_CASES.getSinglePathParamService().get(endpointName), json)
|| setContains(IGNORED_TEST_CASES.getSingleQueryParamService().get(endpointName), json);
}

private static boolean setContains(@Nullable Set<String> set, String item) {
if (set == null) {
return false;
}

return set.contains(item);
}
}
Loading

0 comments on commit cb12eb5

Please sign in to comment.