package io.swagger;
import com.google.common.collect.ImmutableSet;
import io.swagger.annotations.ApiModelProperty;
import io.swagger.converter.ModelConverters;
import io.swagger.matchers.SerializationMatchers;
import io.swagger.models.Cat;
import io.swagger.models.ClientOptInput;
import io.swagger.models.Employee;
import io.swagger.models.EmptyModel;
import io.swagger.models.JacksonReadonlyModel;
import io.swagger.models.JodaDateTimeModel;
import io.swagger.models.Model;
import io.swagger.models.Model1155;
import io.swagger.models.ModelImpl;
import io.swagger.models.ModelPropertyName;
import io.swagger.models.ModelWithAltPropertyName;
import io.swagger.models.ModelWithApiModel;
import io.swagger.models.ModelWithEnumArray;
import io.swagger.models.ModelWithFormattedStrings;
import io.swagger.models.ModelWithNumbers;
import io.swagger.models.ModelWithOffset;
import io.swagger.models.ModelWithTuple2;
import io.swagger.models.Person;
import io.swagger.models.composition.AbstractModelWithApiModel;
import io.swagger.models.composition.ModelWithUrlProperty;
import io.swagger.models.composition.Pet;
import io.swagger.models.properties.ArrayProperty;
import io.swagger.models.properties.BaseIntegerProperty;
import io.swagger.models.properties.DecimalProperty;
import io.swagger.models.properties.DoubleProperty;
import io.swagger.models.properties.FloatProperty;
import io.swagger.models.properties.IntegerProperty;
import io.swagger.models.properties.LongProperty;
import io.swagger.models.properties.MapProperty;
import io.swagger.models.properties.Property;
import io.swagger.models.properties.RefProperty;
import io.swagger.models.properties.StringProperty;
import io.swagger.util.Json;
import io.swagger.util.ResourceUtils;
import org.testng.annotations.Test;
import java.io.IOException;
import java.lang.reflect.Type;
import java.net.URI;
import java.net.URL;
import java.util.Arrays;
import java.util.Date;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeSet;
import java.util.UUID;
import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertFalse;
import static org.testng.Assert.assertNotNull;
import static org.testng.Assert.assertNull;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
public class ModelConverterTest {
private Map<String, Model> read(Type type) {
return ModelConverters.getInstance().read(type);
}
private Map<String, Model> readAll(Type type) {
return ModelConverters.getInstance().readAll(type);
}
private void assertEqualsToJson( Object objectToSerialize, String fileName) throws IOException {
final String json = ResourceUtils.loadClassResource(getClass(), fileName);
SerializationMatchers.assertEqualsToJson(objectToSerialize, json);
}
@Test(description = "it should convert a model")
public void convertModel() throws IOException {
assertEqualsToJson(read(Person.class), "Person.json");
}
@Test(description = "it should convert a model with Joda DateTime")
public void convertModelWithJodaDateTime() throws IOException {
assertEqualsToJson(read(JodaDateTimeModel.class), "JodaDateTimeModel.json");
}
@Test(description = "read an interface")
public void readInterface() throws IOException {
assertEqualsToJson(readAll(Pet.class), "Pet.json");
}
@Test(description = "it should read an inherited interface")
public void readInheritedInterface() throws IOException {
assertEqualsToJson(readAll(Cat.class), "Cat.json");
}
@Test(description = "it should honor the ApiModel name")
public void honorApiModelName() {
final Map<String, Model> schemas = readAll(ModelWithApiModel.class);
assertEquals(schemas.size(), 1);
String model = schemas.keySet().iterator().next();
assertEquals(model, "MyModel");
}
@Test(description = "it should override an inherited model's name")
public void overrideInheritedModelName() {
final Map<String, Model> rootSchemas = readAll(AbstractModelWithApiModel.class);
assertEquals(rootSchemas.size(), 3);
assertTrue(rootSchemas.containsKey("MyProperty"));
assertTrue(rootSchemas.containsKey("ModelWithUrlProperty"));
assertTrue(rootSchemas.containsKey("ModelWithValueProperty"));
final Map<String, Model> nestedSchemas = readAll(ModelWithUrlProperty.class);
assertEquals(nestedSchemas.size(), 1);
assertTrue(nestedSchemas.containsKey("MyProperty"));
}
@Test(description = "it should maintain property names")
public void maintainPropertyNames() {
final Map<String, Model> schemas = readAll(ModelPropertyName.class);
assertEquals(schemas.size(), 1);
final String modelName = schemas.keySet().iterator().next();
assertEquals(modelName, "ModelPropertyName");
final Model model = schemas.get(modelName);
final Iterator<String> itr = new TreeSet(model.getProperties().keySet()).iterator();
assertEquals(itr.next(), "gettersAndHaters");
assertEquals(itr.next(), "is_persistent");
}
@Test(description = "it should serialize a parameterized type per 606")
public void serializeParameterizedType() {
final Map<String, Model> schemas = readAll(Employee.class);
final ModelImpl employee = (ModelImpl) schemas.get("employee");
final Map<String, Property> props = employee.getProperties();
final Iterator<String> et = props.keySet().iterator();
final Property id = props.get(et.next());
assertTrue(id instanceof IntegerProperty);
final Property firstName = props.get(et.next());
assertTrue(firstName instanceof StringProperty);
final Property lastName = props.get(et.next());
assertTrue(lastName instanceof StringProperty);
final Property department = props.get(et.next());
assertTrue(department instanceof RefProperty);
final Property manager = props.get(et.next());
assertTrue(manager instanceof RefProperty);
final Property team = props.get(et.next());
assertTrue(team instanceof ArrayProperty);
final ArrayProperty ap = (ArrayProperty) team;
assertTrue(ap.getUniqueItems());
assertNotNull(employee.getXml());
assertEquals(employee.getXml().getName(), "employee");
}
@Test(description = "it should ignore hidden fields")
public void ignoreHiddenFields() {
final Map<String, Model> schemas = readAll(ClientOptInput.class);
final Model model = schemas.get("ClientOptInput");
assertEquals(model.getProperties().size(), 2);
}
@Test(description = "it should set readOnly per #854")
public void setReadOnly() {
final Map<String, Model> schemas = readAll(JacksonReadonlyModel.class);
final ModelImpl model = (ModelImpl) schemas.get("JacksonReadonlyModel");
final Property prop = model.getProperties().get("count");
assertTrue(prop.getReadOnly());
}
@Test(description = "it should process a model with org.apache.commons.lang3.tuple.Pair properties")
public void processModelWithPairProperties() {
final ModelWithTuple2.TupleAsMapModelConverter asMapConverter = new ModelWithTuple2.TupleAsMapModelConverter(Json.mapper());
ModelConverters.getInstance().addConverter(asMapConverter);
final Map<String, Model> asMap = readAll(ModelWithTuple2.class);
ModelConverters.getInstance().removeConverter(asMapConverter);
assertEquals(asMap.size(), 4);
for (String item : Arrays.asList("MapOfString", "MapOfComplexLeft")) {
ModelImpl model = (ModelImpl) asMap.get(item);
assertEquals(model.getType(), "object");
assertNull(model.getProperties());
assertNotNull(model.getAdditionalProperties());
}
final ModelWithTuple2.TupleAsMapPropertyConverter asPropertyConverter = new ModelWithTuple2.TupleAsMapPropertyConverter(Json.mapper());
ModelConverters.getInstance().addConverter(asPropertyConverter);
final Map<String, Model> asProperty = readAll(ModelWithTuple2.class);
ModelConverters.getInstance().removeConverter(asPropertyConverter);
assertEquals(asProperty.size(), 2);
for (Map.Entry<String, Property> entry : asProperty.get("ModelWithTuple2").getProperties().entrySet()) {
String name = entry.getKey();
Property property = entry.getValue();
if ("timesheetStates".equals(name)) {
assertEquals(property.getClass(), MapProperty.class);
} else if ("manyPairs".equals(name)) {
assertEquals(property.getClass(), ArrayProperty.class);
Property items = ((ArrayProperty) property).getItems();
assertNotNull(items);
assertEquals(items.getClass(), MapProperty.class);
Property stringProperty = ((MapProperty) items).getAdditionalProperties();
assertNotNull(stringProperty);
assertEquals(stringProperty.getClass(), StringProperty.class);
} else if ("complexLeft".equals(name)) {
assertEquals(property.getClass(), ArrayProperty.class);
Property items = ((ArrayProperty) property).getItems();
assertNotNull(items);
assertEquals(items.getClass(), MapProperty.class);
Property additionalProperty = ((MapProperty) items).getAdditionalProperties();
assertNotNull(additionalProperty);
assertEquals(additionalProperty.getClass(), RefProperty.class);
assertEquals(((RefProperty) additionalProperty).getSimpleRef(), "ComplexLeft");
} else {
fail(String.format("Unexpected property: %s", name));
}
}
}
@Test(description = "it should scan an empty model per 499")
public void scanEmptyModel() {
final Map<String, Model> schemas = readAll(EmptyModel.class);
final ModelImpl model = (ModelImpl) schemas.get("EmptyModel");
assertNull(model.getProperties());
assertEquals(model.getType(), "object");
}
@Test(description = "it should override the property name")
public void overridePropertyName() {
final Map<String, Model> schemas = readAll(ModelWithAltPropertyName.class);
final Map<String, Property> properties = schemas.get("sample_model").getProperties();
assertNull(properties.get("id"));
assertNotNull(properties.get("the_id"));
}
@Test(description = "it should convert a model with enum array")
public void convertModelWithEnumArray() {
final Map<String, Model> schemas = readAll(ModelWithEnumArray.class);
assertEquals(schemas.size(), 1);
}
private Type getGenericType(Class<?> cls) throws Exception {
return getClass().getDeclaredMethod("getGenericType", Class.class).getGenericParameterTypes()[0];
}
@Test(description = "it should check handling of the Class<?> type")
public void checkHandlingClassType() throws Exception {
final Type type = getGenericType(null);
assertFalse(type instanceof Class<?>);
final Map<String, Model> schemas = readAll(type);
assertEquals(schemas.size(), 0);
}
@Test(description = "it should convert a model with Formatted strings")
public void convertModelWithFormattedStrings() throws IOException {
final Model model = readAll(ModelWithFormattedStrings.class).get("ModelWithFormattedStrings");
assertEqualsToJson(model, "ModelWithFormattedStrings.json");
}
@Test(description = "it should check handling of string types")
public void checkStringTypesHandling() {
for (Class<?> cls : Arrays.asList(URI.class, URL.class, UUID.class)) {
final Map<String, Model> schemas = readAll(cls);
assertEquals(schemas.size(), 0);
final Property property = ModelConverters.getInstance().readAsProperty(cls);
assertNotNull(property);
assertEquals(property.getType(), "string");
}
}
@Test(description = "it should scan a model per #1155")
public void scanModel() {
final Map<String, Model> model = read(Model1155.class);
assertEquals(model.get("Model1155").getProperties().keySet(), ImmutableSet.of("valid", "value", "is", "get",
"isA", "getA", "is_persistent", "gettersAndHaters"));
}
@Test(description = "it should scan a model with numbers")
public void scanModelWithNumbers() throws IOException {
final Map<String, Model> models = readAll(ModelWithNumbers.class);
assertEquals(models.size(), 1);
final Model model = models.get("ModelWithNumbers");
// Check if we get required properties after building models from classes.
checkModel(model);
// Check if we get required properties after deserialization from JSON
checkModel(Json.mapper().readValue(Json.pretty(model), Model.class));
}
@Test(description = "it tests a model with java offset")
public void scanModelWithOffset() throws IOException {
final Map<String, Model> models = readAll(ModelWithOffset.class);
assertEquals(models.size(), 1);
final Model model = models.get("ModelWithOffset");
Property property = model.getProperties().get("offset");
assertEquals(property.getType(), "string");
assertEquals(property.getFormat(), "date-time");
}
private void checkType(Property property, Class<?> cls, String type, String format) {
assertTrue(cls.isInstance(property));
assertEquals(property.getType(), type);
assertEquals(property.getFormat(), format);
}
private void checkModel(Model model) {
for (Map.Entry<String, Property> entry : model.getProperties().entrySet()) {
final String name = entry.getKey();
final Property property = entry.getValue();
if (Arrays.asList("shortPrimitive", "shortObject", "intPrimitive", "intObject").contains(name)) {
checkType(property, IntegerProperty.class, "integer", "int32");
} else if (Arrays.asList("longPrimitive", "longObject").contains(name)) {
checkType(property, LongProperty.class, "integer", "int64");
} else if (Arrays.asList("floatPrimitive", "floatObject").contains(name)) {
checkType(property, FloatProperty.class, "number", "float");
} else if (Arrays.asList("doublePrimitive", "doubleObject").contains(name)) {
checkType(property, DoubleProperty.class, "number", "double");
} else if ("bigInteger".equals(name)) {
checkType(property, BaseIntegerProperty.class, "integer", null);
} else if ("bigDecimal".equals(name)) {
checkType(property, DecimalProperty.class, "number", null);
} else {
fail(String.format("Unexpected property: %s", name));
}
}
}
@Test
public void formatDate() {
final Map<String, Model> models = ModelConverters.getInstance().read(DateModel.class);
final Model model = models.get("DateModel");
assertEquals(model.getProperties().size(), 5);
final String json = "{" +
" \"type\":\"object\"," +
" \"properties\":{" +
" \"date\":{" +
" \"type\":\"string\"," +
" \"format\":\"date-time\"," +
" \"position\":1" +
" }," +
" \"intValue\":{" +
" \"type\":\"integer\"," +
" \"format\":\"int32\"," +
" \"position\":2" +
" }," +
" \"longValue\":{" +
" \"type\":\"integer\"," +
" \"format\":\"int64\"," +
" \"position\":3" +
" }," +
" \"floatValue\":{" +
" \"type\":\"number\"," +
" \"format\":\"float\"," +
" \"position\":4" +
" }," +
" \"doubleValue\":{" +
" \"type\":\"number\"," +
" \"format\":\"double\"," +
" \"position\":5" +
" }" +
" }" +
"}";
SerializationMatchers.assertEqualsToJson(model, json);
}
class DateModel {
@ApiModelProperty(position = 1)
public Date date;
@ApiModelProperty(position=2)
public int intValue;
@ApiModelProperty(position=3)
public Long longValue;
@ApiModelProperty(position=4)
public Float floatValue;
@ApiModelProperty(position=5)
public Double doubleValue;
}
}