package com.stripe.model; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Lists; import com.google.common.reflect.ClassPath; import com.google.common.reflect.Invokable; import com.google.common.reflect.Parameter; import com.stripe.net.APIResource; import com.stripe.net.RequestOptions; import junit.framework.Assert; import org.junit.Test; import java.io.IOException; import java.lang.reflect.Method; import java.util.Arrays; import java.util.Collection; import java.util.HashSet; import java.util.List; import java.util.Map; /** * Simple test to make sure stripe-java provides consistent bindings. */ public class StandardizationTest { public Collection<Class> getAllModels() throws IOException { Class<Charge> chargeClass = Charge.class; ClassPath classPath = ClassPath.from(chargeClass.getClassLoader()); ImmutableSet<ClassPath.ClassInfo> topLevelClasses = classPath.getTopLevelClasses(chargeClass.getPackage().getName()); List<Class> classList = Lists.newArrayListWithExpectedSize(topLevelClasses.size()); for (ClassPath.ClassInfo classInfo : topLevelClasses) { Class c = classInfo.load(); // Skip things that aren't APIResources if (!APIResource.class.isAssignableFrom(c)) { continue; } // Skip the APIResource itself if (APIResource.class == c) { continue; } classList.add(classInfo.load()); } return classList; } @Test public void allNonDeprecatedMethodsTakeOptions() throws IOException, NoSuchMethodException { for (Class aClass : getAllModels()) { HashSet<Class<?>> interfaces = new HashSet<Class<?>>(Arrays.<Class<?>>asList(aClass.getInterfaces())); for (Method method : aClass.getMethods()) { // Skip methods not declared on the base class. if (method.getDeclaringClass() != aClass) { continue; } // Skip equals if (method.getName().equals("equals")) { continue; } // Skip setters if (method.getName().startsWith("set")) { continue; } // Skip getters if (method.getName().startsWith("get")) { continue; } // If more than one method with the same parameter types is declared in a class, and one of these // methods has a return type that is more specific than any of the others, that method is returned; // otherwise one of the methods is chosen arbitrarily. Method mostSpecificMethod = aClass.getDeclaredMethod(method.getName(), method.getParameterTypes()); if (!method.equals(mostSpecificMethod)) { continue; } Invokable<?, Object> invokable = Invokable.from(method); // Skip private methods. if (invokable.isPrivate()) { continue; } // Skip deprecated methods - we need to keep them around, but aren't asserting their type. if (invokable.isAnnotationPresent(Deprecated.class)) { continue; } ImmutableList<Parameter> parameters = invokable.getParameters(); // Skip empty parameter lists - assume the author is using default values for the RequestOptions if (parameters.isEmpty()) { continue; } Parameter lastParam = parameters.get(parameters.size() - 1); Class<?> finalParamType = lastParam.getType().getRawType(); // Skip methods that have exactly one param which is a map. if (Map.class.equals(finalParamType) && parameters.size() == 1) { continue; } // Skip `public static Foo retrieve(String id) {...` helper methods if (String.class.equals(finalParamType) && parameters.size() == 1 && "retrieve".equals(method.getName())) { continue; } // Skip the `public static Card createCard(String id) {...` helper method on Customer. if (String.class.equals(finalParamType) && parameters.size() == 1 && ("createCard".equals(method.getName()) || "createBankAccount".equals(method.getName()))) { continue; } if (RequestOptions.class.isAssignableFrom(finalParamType)) { continue; } Assert.assertTrue( String.format("Methods on %ss like %s.%s should take a final parameter as a %s parameter.%n", APIResource.class.getSimpleName(), aClass.getSimpleName(), method.getName(), RequestOptions.class.getSimpleName()), RequestOptions.class.isAssignableFrom(finalParamType)); } } } }