DATAREST-774 - Separated integration tests from core project to avoid classpath overlap.

Extracted store specific tests into separate test modules to prevent classpath overlap between projects. Those tests are now executed in an "it" build profile to prevent the tests being packaged for distribution on release.

Use Map-based repositories and mapping contexts for test in the Core and WebMvc module.

Slightly changed the configuration API for lookup types on RepositoryRestConfiguration.

Related ticket: DATAREST-776.
This commit is contained in:
Oliver Gierke
2016-02-25 17:18:58 +01:00
parent 897bc88d69
commit 892409da2c
189 changed files with 1884 additions and 1506 deletions

View File

@@ -0,0 +1,27 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Oliver Gierke
*/
public class Address {
public String street;
public @JsonProperty(required = true) String zipCode;
}

View File

@@ -0,0 +1,85 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.mongodb.core.mapping.Document;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonProperty;
/**
* @author Jon Brisbin
*/
@Document
public class Profile {
@Id private String id;
private Long person;
private @JsonProperty(required = true) String type;
private @LastModifiedDate Date lastModifiedDate;
private @JsonProperty("renamed") String aliased;
private Map<String, String> metadata = new HashMap<String, String>();
public String getId() {
return id;
}
public Profile setId(String id) {
this.id = id;
return this;
}
public Long getPerson() {
return person;
}
public Profile setPerson(Long person) {
this.person = person;
return this;
}
public String getType() {
return type;
}
public Profile setType(String type) {
this.type = type;
return this;
}
@JsonIgnore
public Date getLastModifiedDate() {
return lastModifiedDate;
}
public String getAliased() {
return aliased;
}
public Map<String, String> getMetadata() {
return metadata;
}
public void setMetadata(Map<String, String> metadata) {
this.metadata = metadata;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import java.util.List;
import java.util.Optional;
import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.repository.query.Param;
/**
* @author Jon Brisbin
* @author Oliver Gierke
*/
public interface ProfileRepository extends PagingAndSortingRepository<Profile, String> {
List<Profile> findByType(String type);
/**
* @see DATAREST-247
*/
long countByType(@Param("type") String type);
/**
* @see DATAREST-511
*/
Optional<Profile> findById(@Param("id") String id);
}

View File

@@ -0,0 +1,42 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import java.math.BigDecimal;
import java.util.Date;
import org.springframework.data.annotation.Id;
import org.springframework.data.annotation.LastModifiedDate;
import org.springframework.data.annotation.Version;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* @author Pablo Lozano
*/
// tag::code[]
@Document
public class Receipt {
public @Id String id;
public @Version Long version;
public @LastModifiedDate Date date; // <1>
public String saleItem;
public BigDecimal amount;
}
// end::code[]

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import org.springframework.data.repository.CrudRepository;
/**
* A repository to manage {@link Receipt}s.
*
* @author Pablo Lozano
*/
public interface ReceiptRepository extends CrudRepository<Receipt, String> {
}

View File

@@ -0,0 +1,70 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import java.math.BigInteger;
import java.time.LocalDateTime;
import java.util.List;
import java.util.Set;
import org.springframework.data.annotation.ReadOnlyProperty;
import org.springframework.data.mongodb.core.mapping.DBRef;
import org.springframework.data.mongodb.core.mapping.Document;
import com.fasterxml.jackson.annotation.JsonValue;
/**
* @author Oliver Gierke
*/
@Document
public class User {
public static enum Gender {
MALE, FEMALE;
}
public BigInteger id;
public String firstname, lastname;
public Address address;
public Set<Address> shippingAddresses;
public List<String> nicknames;
public Gender gender;
public @ReadOnlyProperty EmailAddress email;
public LocalDateTime java8DateTime;
public org.joda.time.LocalDateTime jodaDateTime;
public TypeWithPattern pattern;
public @DBRef(lazy = true) List<User> colleagues;
public static class EmailAddress {
private final String value;
/**
* @param value
*/
public EmailAddress(String value) {
this.value = value;
}
@Override
@JsonValue
public String toString() {
return value;
}
}
public static class TypeWithPattern {}
}

View File

@@ -0,0 +1,33 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import java.math.BigInteger;
import java.util.List;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.repository.CrudRepository;
import org.springframework.data.repository.query.Param;
/**
* @author Oliver Gierke
*/
public interface UserRepository extends CrudRepository<User, BigInteger>, QueryDslPredicateExecutor<User> {
List<User> findByFirstname(String firstname);
List<User> findByColleaguesContains(@Param("colleagues") User colleague);
}

View File

@@ -0,0 +1,29 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import org.springframework.data.rest.core.config.Projection;
/**
* @author Oliver Gierke
*/
@Projection(types = User.class)
public interface UserSummary {
String getFirstname();
String getLastname();
}

View File

@@ -0,0 +1,77 @@
/*
* Copyright 2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb.groovy;
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.springframework.data.mongodb.core.mapping.Document;
/**
* Simulates a Groovy domain object by extending {@link GroovyObject}.
*
* @author Greg Turnquist
* @author Oliver Gierke
* @see DATAREST-754
*/
@Document
public class SimulatedGroovyDomainClass implements GroovyObject {
private String id, name;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//
// The following fields don't actually have to be implemented since the test cases don't
// make any Groovy calls. This just simulates the structure of a Groovy object to
// verify proper handling.
//
@Override
public Object invokeMethod(String s, Object o) {
return null;
}
@Override
public Object getProperty(String s) {
return null;
}
@Override
public void setProperty(String s, Object o) {}
@Override
public MetaClass getMetaClass() {
return null;
}
@Override
public void setMetaClass(MetaClass metaClass) {}
}

View File

@@ -0,0 +1,26 @@
/*
* Copyright 2016-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb.groovy;
import org.springframework.data.repository.CrudRepository;
/**
* Simulates a repository built on a Groovy domain object.
*
* @author Greg Turnquist
* @see DATAREST-754
*/
public interface SimulatedGroovyDomainClassRepository extends CrudRepository<SimulatedGroovyDomainClass, String> {}

View File

@@ -0,0 +1,61 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.mongodb.config.AbstractMongoConfiguration;
import org.springframework.data.mongodb.config.EnableMongoAuditing;
import org.springframework.data.mongodb.repository.config.EnableMongoRepositories;
import com.mongodb.Mongo;
import com.mongodb.MongoClient;
/**
* @author Jon Brisbin
* @author Oliver Gierke
*/
@Configuration
@EnableMongoRepositories
@EnableMongoAuditing
public class MongoDbRepositoryConfig extends AbstractMongoConfiguration {
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.config.AbstractMongoConfiguration#getDatabaseName()
*/
@Override
protected String getDatabaseName() {
return "spring-data-rest-sample";
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.config.AbstractMongoConfiguration#getMappingBasePackage()
*/
@Override
protected String getMappingBasePackage() {
return "";
}
/*
* (non-Javadoc)
* @see org.springframework.data.mongodb.config.AbstractMongoConfiguration#mongo()
*/
@Override
public Mongo mongo() throws Exception {
return new MongoClient();
}
}

View File

@@ -0,0 +1,352 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.springframework.http.HttpHeaders.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.math.BigDecimal;
import java.util.Arrays;
import java.util.Collections;
import org.junit.After;
import org.junit.Before;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.tests.CommonWebTests;
import org.springframework.data.rest.tests.mongodb.Address;
import org.springframework.data.rest.tests.mongodb.Profile;
import org.springframework.data.rest.tests.mongodb.ProfileRepository;
import org.springframework.data.rest.tests.mongodb.Receipt;
import org.springframework.data.rest.tests.mongodb.User;
import org.springframework.data.rest.tests.mongodb.UserRepository;
import org.springframework.data.rest.webmvc.RestMediaTypes;
import org.springframework.data.rest.webmvc.support.RepositoryEntityLinks;
import org.springframework.hateoas.Link;
import org.springframework.http.MediaType;
import org.springframework.mock.web.MockHttpServletResponse;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.web.util.UriComponentsBuilder;
import com.fasterxml.jackson.annotation.JsonInclude.Include;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
/**
* Integration tests for MongoDB repositories.
*
* @author Oliver Gierke
* @author Greg Turnquist
*/
@ContextConfiguration(classes = MongoDbRepositoryConfig.class)
public class MongoWebTests extends CommonWebTests {
@Autowired ProfileRepository repository;
@Autowired UserRepository userRepository;
@Autowired RepositoryEntityLinks entityLinks;
ObjectMapper mapper = new ObjectMapper();
@Before
public void populateProfiles() {
mapper.setSerializationInclusion(Include.NON_NULL);
Profile twitter = new Profile();
twitter.setPerson(1L);
twitter.setType("Twitter");
Profile linkedIn = new Profile();
linkedIn.setPerson(1L);
linkedIn.setType("LinkedIn");
repository.save(Arrays.asList(twitter, linkedIn));
Address address = new Address();
address.street = "ETagDoesntMatchExceptionUnitTests";
address.zipCode = "Bar";
User thomas = new User();
thomas.firstname = "Thomas";
thomas.lastname = "Darimont";
thomas.address = address;
userRepository.save(thomas);
User oliver = new User();
oliver.firstname = "Oliver";
oliver.lastname = "Gierke";
oliver.address = address;
oliver.colleagues = Arrays.asList(thomas);
userRepository.save(oliver);
thomas.colleagues = Arrays.asList(oliver);
userRepository.save(thomas);
}
@After
public void cleanUp() {
repository.deleteAll();
userRepository.deleteAll();
}
/*
* (non-Javadoc)
* @see org.springframework.data.rest.webmvc.AbstractWebIntegrationTests#expectedRootLinkRels()
*/
@Override
protected Iterable<String> expectedRootLinkRels() {
return Arrays.asList("profiles", "users");
}
@Test
public void foo() throws Exception {
Link profileLink = client.discoverUnique("profiles");
client.follow(profileLink).//
andExpect(jsonPath("$._embedded.profiles").value(hasSize(2)));
}
@Test
public void rendersEmbeddedDocuments() throws Exception {
Link usersLink = client.discoverUnique("users");
Link userLink = assertHasContentLinkWithRel("self", client.request(usersLink));
client.follow(userLink).//
andExpect(jsonPath("$.address.zipCode").value(is(notNullValue())));
}
/**
* @see DATAREST-247
*/
@Test
public void executeQueryMethodWithPrimitiveReturnType() throws Exception {
Link profiles = client.discoverUnique("profiles");
Link profileSearches = client.discoverUnique(profiles, "search");
Link countByTypeLink = client.discoverUnique(profileSearches, "countByType");
assertThat(countByTypeLink.isTemplated(), is(true));
assertThat(countByTypeLink.getVariableNames(), hasItem("type"));
MockHttpServletResponse response = client.request(countByTypeLink.expand("Twitter"));
assertThat(response.getContentAsString(), is("1"));
}
@Test
public void testname() throws Exception {
Link usersLink = client.discoverUnique("users");
Link userLink = assertHasContentLinkWithRel("self", client.request(usersLink));
MockHttpServletResponse response = patchAndGet(userLink,
"{\"lastname\" : null, \"address\" : { \"zipCode\" : \"ZIP\"}}", MediaType.APPLICATION_JSON);
assertThat(JsonPath.read(response.getContentAsString(), "$.lastname"), is(nullValue()));
assertThat(JsonPath.read(response.getContentAsString(), "$.address.zipCode"), is((Object) "ZIP"));
}
@Test
public void testname2() throws Exception {
Link usersLink = client.discoverUnique("users");
Link userLink = assertHasContentLinkWithRel("self", client.request(usersLink));
MockHttpServletResponse response = patchAndGet(userLink,
"[{ \"op\": \"replace\", \"path\": \"/address/zipCode\", \"value\": \"ZIP\" },"
// + "{ \"op\": \"replace\", \"path\": \"/lastname\", \"value\": null }]", //
+ "{ \"op\": \"remove\", \"path\": \"/lastname\" }]", //
RestMediaTypes.JSON_PATCH_JSON);
assertThat(JsonPath.read(response.getContentAsString(), "$.lastname"), is(nullValue()));
assertThat(JsonPath.read(response.getContentAsString(), "$.address.zipCode"), is((Object) "ZIP"));
}
/**
* @see DATAREST-160
*/
@Test
public void returnConflictWhenConcurrentlyEditingVersionedEntity() throws Exception {
Link receiptLink = client.discoverUnique("receipts");
Receipt receipt = new Receipt();
receipt.amount = new BigDecimal(50);
receipt.saleItem = "Springy Tacos";
String stringReceipt = mapper.writeValueAsString(receipt);
MockHttpServletResponse createdReceipt = postAndGet(receiptLink, stringReceipt, MediaType.APPLICATION_JSON);
Link tacosLink = client.assertHasLinkWithRel("self", createdReceipt);
assertJsonPathEquals("$.saleItem", "Springy Tacos", createdReceipt);
UriComponentsBuilder builder = UriComponentsBuilder.fromUriString(tacosLink.getHref());
String concurrencyTag = createdReceipt.getHeader("ETag");
mvc.perform(patch(builder.build().toUriString()).content("{ \"saleItem\" : \"SpringyBurritos\" }")
.contentType(MediaType.APPLICATION_JSON).header(IF_MATCH, concurrencyTag))
.andExpect(status().is2xxSuccessful());
mvc.perform(patch(builder.build().toUriString()).content("{ \"saleItem\" : \"SpringyTequila\" }")
.contentType(MediaType.APPLICATION_JSON).header(IF_MATCH, concurrencyTag))
.andExpect(status().isPreconditionFailed());
}
/**
* @see DATAREST-471
*/
@Test
public void auditableResourceHasLastModifiedHeaderSet() throws Exception {
Profile profile = repository.findAll().iterator().next();
String header = mvc.perform(get("/profiles/{id}", profile.getId())).//
andReturn().getResponse().getHeader("Last-Modified");
assertThat(header, not(isEmptyOrNullString()));
}
/**
* @see DATAREST-482
*/
@Test
public void putDoesNotRemoveAssociations() throws Exception {
Link usersLink = client.discoverUnique("users");
Link userLink = assertHasContentLinkWithRel("self", client.request(usersLink));
Link colleaguesLink = client.assertHasLinkWithRel("colleagues", client.request(userLink));
// Expect a user returned as colleague
client.follow(colleaguesLink).//
andExpect(jsonPath("$._embedded.users").exists());
User oliver = new User();
oliver.firstname = "Oliver";
oliver.lastname = "Gierke";
putAndGet(userLink, mapper.writeValueAsString(oliver), MediaType.APPLICATION_JSON);
// Expect colleague still present but address has been wiped
client.follow(colleaguesLink).//
andExpect(jsonPath("$._embedded.users").exists()).//
andExpect(jsonPath("$.embedded.users[0].address").doesNotExist());
}
/**
* @see DATAREST-482
*/
@Test
public void emptiesAssociationForEmptyUriList() throws Exception {
Link usersLink = client.discoverUnique("users");
Link userLink = assertHasContentLinkWithRel("self", client.request(usersLink));
Link colleaguesLink = client.assertHasLinkWithRel("colleagues", client.request(userLink));
putAndGet(colleaguesLink, "", MediaType.parseMediaType("text/uri-list"));
client.follow(colleaguesLink).//
andExpect(status().isOk()).//
andExpect(jsonPath("$").exists());
}
/**
* @see DATAREST-491
*/
@Test
public void updatesMapPropertyCorrectly() throws Exception {
Link profilesLink = client.discoverUnique("profiles");
Link profileLink = assertHasContentLinkWithRel("self", client.request(profilesLink));
Profile profile = new Profile();
profile.setMetadata(Collections.singletonMap("Key", "Value"));
putAndGet(profileLink, mapper.writeValueAsString(profile), MediaType.APPLICATION_JSON);
client.follow(profileLink).andExpect(jsonPath("$.metadata.Key").value("Value"));
}
/**
* @see DATAREST-506
*/
@Test
public void supportsConditionalGetsOnItemResource() throws Exception {
Receipt receipt = new Receipt();
receipt.amount = new BigDecimal(50);
receipt.saleItem = "Springy Tacos";
Link receiptsLink = client.discoverUnique("receipts");
MockHttpServletResponse response = postAndGet(receiptsLink, mapper.writeValueAsString(receipt),
MediaType.APPLICATION_JSON);
Link receiptLink = client.getDiscoverer(response).findLinkWithRel("self", response.getContentAsString());
mvc.perform(get(receiptLink.getHref()).header(IF_MODIFIED_SINCE, response.getHeader(LAST_MODIFIED))).//
andExpect(status().isNotModified()).//
andExpect(header().string(ETAG, is(notNullValue())));
mvc.perform(get(receiptLink.getHref()).header(IF_NONE_MATCH, response.getHeader(ETAG))).//
andExpect(status().isNotModified()).//
andExpect(header().string(ETAG, is(notNullValue())));
}
/**
* @see DATAREST-511
*/
@Test
public void invokesQueryResourceReturningAnOptional() throws Exception {
Profile profile = repository.findAll().iterator().next();
Link link = client.discoverUnique("profiles", "search", "findById");
mvc.perform(get(link.expand(profile.getId()).getHref())).//
andExpect(status().isOk());
}
/**
* @see DATAREST-517
*/
@Test
public void returnsNotFoundIfQueryExecutionDoesNotReturnResult() throws Exception {
Link link = client.discoverUnique("profiles", "search", "findById");
mvc.perform(get(link.expand("").getHref())).//
andExpect(status().isNotFound());
}
/**
* @see DATAREST-712
*/
@Test
public void invokesQueryMethodTakingAReferenceCorrectly() throws Exception {
Link link = client.discoverUnique("users", "search", "findByColleaguesContains");
User thomas = userRepository.findAll(QUser.user.firstname.eq("Thomas")).iterator().next();
Link thomasUri = entityLinks.linkToSingleResource(User.class, thomas.id).expand();
String href = link.expand(thomasUri.getHref()).getHref();
mvc.perform(get(href)).andExpect(status().isOk());
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* 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 org.springframework.data.rest.tests.mongodb;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.nio.charset.Charset;
import org.springframework.util.Assert;
/**
* Test helper methods.
*
* @author Oliver Gierke
* @author Christoph Strobl
*/
public class TestUtils {
private static final Charset UTF8 = Charset.forName("UTF-8");
/**
* Returns the given {@link String} as {@link InputStream}.
*
* @param source must not be {@literal null}.
* @return
*/
public static InputStream asStream(String source) {
Assert.notNull(source, "Source string must not be null!");
return new ByteArrayInputStream(source.getBytes(UTF8));
}
}

View File

@@ -0,0 +1,84 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import static org.mockito.Matchers.*;
import static org.mockito.Mockito.*;
import java.math.BigInteger;
import java.util.Collections;
import org.hamcrest.Matchers;
import org.junit.Test;
import org.mockito.internal.stubbing.answers.ReturnsArgumentAt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.rest.core.support.DefaultSelfLinkProvider;
import org.springframework.data.rest.core.support.EntityLookup;
import org.springframework.data.rest.tests.AbstractControllerIntegrationTests;
import org.springframework.data.rest.tests.AbstractControllerIntegrationTests.TestConfiguration;
import org.springframework.data.rest.tests.mongodb.MongoDbRepositoryConfig;
import org.springframework.data.rest.tests.mongodb.User;
import org.springframework.data.rest.webmvc.mapping.Associations;
import org.springframework.data.rest.webmvc.support.Projector;
import org.springframework.hateoas.EntityLinks;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.Links;
import org.springframework.test.context.ContextConfiguration;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Integration tests for {@link PersistentEntityResourceAssembler}.
*
* @author Oliver Gierke
*/
@ContextConfiguration(classes = { TestConfiguration.class, MongoDbRepositoryConfig.class })
public class PersistentEntityResourceAssemblerIntegrationTests extends AbstractControllerIntegrationTests {
@Autowired PersistentEntities entities;
@Autowired EntityLinks entityLinks;
@Autowired @Qualifier("objectMapper") ObjectMapper objectMapper;
@Autowired Associations associations;
/**
* @see DATAREST-609
*/
@Test
public void addsSelfAndSingleResourceLinkToResourceByDefault() throws Exception {
Projector projector = mock(Projector.class);
when(projector.projectExcerpt(anyObject())).thenAnswer(new ReturnsArgumentAt(0));
PersistentEntityResourceAssembler assembler = new PersistentEntityResourceAssembler(entities, projector,
associations, new DefaultSelfLinkProvider(entities, entityLinks, Collections.<EntityLookup<?>> emptyList()));
User user = new User();
user.id = BigInteger.valueOf(4711);
PersistentEntityResource resource = assembler.toResource(user);
Links links = new Links(resource.getLinks());
assertThat(links, is(Matchers.<Link> iterableWithSize(2)));
assertThat(links.getLink("self").getVariables(), is(Matchers.empty()));
assertThat(links.getLink("user").getVariableNames(), is(hasItem("projection")));
}
}

View File

@@ -0,0 +1,62 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import org.junit.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.rest.tests.AbstractControllerIntegrationTests;
import org.springframework.data.rest.tests.mongodb.MongoDbRepositoryConfig;
import org.springframework.data.rest.webmvc.support.DelegatingHandlerMapping;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExecutionChain;
/**
* Integration tests for {@link BasePathAwareHandlerMapping}.
*
* @author Oliver Gierke
* @soundtrack Elephants Crossing - Echo (Irrelephant)
*/
@ContextConfiguration(classes = MongoDbRepositoryConfig.class)
public class RepositoryRestHandlerMappingIntegrationTests extends AbstractControllerIntegrationTests {
@Autowired DelegatingHandlerMapping mapping;
/**
* @see DATAREST-617
*/
@Test
public void usesMethodsWithoutProducesClauseForGeneralJsonRequests() throws Exception {
MockHttpServletRequest mockRequest = new MockHttpServletRequest("GET", "/users");
mockRequest.addHeader("Accept", "application/*+json");
HandlerExecutionChain chain = mapping.getHandler(mockRequest);
assertThat(chain, is(notNullValue()));
Object handler = chain.getHandler();
assertThat(handler, is(instanceOf(HandlerMethod.class)));
HandlerMethod method = (HandlerMethod) handler;
assertThat(method.getMethod().getDeclaringClass(), is(typeCompatibleWith(RepositoryEntityController.class)));
assertThat(method.getMethod().getName(), is("getCollectionResource"));
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.config;
import org.junit.Before;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.context.web.WebAppConfiguration;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.web.context.WebApplicationContext;
/**
* Base class for integration tests that are run against a particular configuration defined by the subclass.
*
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@WebAppConfiguration
public abstract class AbstractRepositoryRestMvcConfigurationIntegrationTests {
@Autowired WebApplicationContext context;
protected MockMvc mvc;
@Before
public void setUp() {
this.mvc = MockMvcBuilders.webAppContextSetup(context).build();
}
}

View File

@@ -0,0 +1,143 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import static org.springframework.data.rest.tests.mongodb.TestUtils.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.data.rest.tests.mongodb.Address;
import org.springframework.data.rest.tests.mongodb.User;
import org.springframework.data.rest.webmvc.RestMediaTypes;
import org.springframework.data.rest.webmvc.json.DomainObjectReader;
import org.springframework.data.rest.webmvc.mapping.Associations;
import org.springframework.http.converter.HttpMessageNotReadableException;
import com.fasterxml.jackson.databind.ObjectMapper;
/**
* Unit tests for {@link JsonPatchHandler}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class JsonPatchHandlerUnitTests {
JsonPatchHandler handler;
User user;
@Mock ResourceMappings mappings;
public @Rule ExpectedException exception = ExpectedException.none();
@Before
public void setUp() {
MongoMappingContext context = new MongoMappingContext();
context.getPersistentEntity(User.class);
PersistentEntities entities = new PersistentEntities(Arrays.asList(context));
Associations associations = new Associations(mappings, mock(RepositoryRestConfiguration.class));
this.handler = new JsonPatchHandler(new ObjectMapper(), new DomainObjectReader(entities, associations));
Address address = new Address();
address.street = "Foo";
address.zipCode = "Bar";
this.user = new User();
this.user.firstname = "Oliver";
this.user.lastname = "Gierke";
this.user.address = address;
}
/**
* @see DATAREST-348
*/
@Test
public void appliesRemoveOperationCorrectly() throws Exception {
String input = "[{ \"op\": \"replace\", \"path\": \"/address/zipCode\", \"value\": \"ZIP\" },"
+ "{ \"op\": \"remove\", \"path\": \"/lastname\" }]";
User result = handler.applyPatch(asStream(input), user);
assertThat(result.lastname, is(nullValue()));
assertThat(result.address.zipCode, is("ZIP"));
}
/**
* @see DATAREST-348
*/
@Test
public void appliesMergePatchCorrectly() throws Exception {
String input = "{ \"address\" : { \"zipCode\" : \"ZIP\"}, \"lastname\" : null }";
User result = handler.applyMergePatch(asStream(input), user);
assertThat(result.lastname, is(nullValue()));
assertThat(result.address.zipCode, is("ZIP"));
}
/**
* DATAREST-537
*/
@Test
public void removesArrayItemCorrectly() throws Exception {
User thomas = new User();
thomas.firstname = "Thomas";
User christoph = new User();
christoph.firstname = "Christoph";
this.user.colleagues = Arrays.asList(thomas, christoph);
String input = "[{ \"op\": \"remove\", \"path\": \"/colleagues/0\" }]";
handler.applyPatch(asStream(input), user);
assertThat(user.colleagues, hasSize(1));
assertThat(user.colleagues.get(0).firstname, is(christoph.firstname));
}
/**
* @see DATAREST-609
*/
@Test
public void hintsToMediaTypeIfBodyCantBeRead() throws Exception {
exception.expect(HttpMessageNotReadableException.class);
exception.expectMessage(RestMediaTypes.JSON_PATCH_JSON.toString());
handler.applyPatch(asStream("{ \"foo\" : \"bar\" }"), new User());
}
}

View File

@@ -0,0 +1,76 @@
/*
* Copyright 2013-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.config;
import static org.hamcrest.CoreMatchers.*;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.tests.mongodb.MongoDbRepositoryConfig;
import org.springframework.http.MediaType;
import org.springframework.test.context.ContextConfiguration;
/**
* Integration tests to check the legacy representation is rendered if HAL is not the default media type.
*
* @author Oliver Gierke
*/
@ContextConfiguration
public class LegacyRepresentationConfigIntegrationTests extends AbstractRepositoryRestMvcConfigurationIntegrationTests {
@Configuration
@Import({ MongoDbRepositoryConfig.class, RepositoryRestMvcConfiguration.class })
static class Config extends RepositoryRestConfigurerAdapter {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.setDefaultMediaType(MediaType.APPLICATION_JSON);
config.useHalAsDefaultJsonMediaType(false);
}
}
/**
* @see DATAREST-213, DATAREST-617
*/
@Test
public void returnsJsonIfConfiguredAndRequested() throws Exception {
for (String resource : Arrays.asList("/", "/users")) {
mvc.perform(get(resource).accept(MediaType.APPLICATION_JSON)). //
andExpect(jsonPath("links", is(notNullValue())));
}
}
/**
* @see DATAREST-213, DATAREST-617
*/
@Test
public void returnsJsonIfConfigured() throws Exception {
for (String resource : Arrays.asList("/", "/users")) {
mvc.perform(get(resource).accept(MediaType.ALL)). //
andExpect(jsonPath("links", is(notNullValue())));
}
}
}

View File

@@ -0,0 +1,131 @@
/*
* Copyright 2015-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.config;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.Collections;
import java.util.Map;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.runners.MockitoJUnitRunner;
import org.springframework.core.MethodParameter;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.querydsl.QueryDslPredicateExecutor;
import org.springframework.data.querydsl.QuerydslRepositoryInvokerAdapter;
import org.springframework.data.querydsl.SimpleEntityPathResolver;
import org.springframework.data.querydsl.binding.QuerydslBinderCustomizer;
import org.springframework.data.querydsl.binding.QuerydslBindings;
import org.springframework.data.querydsl.binding.QuerydslBindingsFactory;
import org.springframework.data.querydsl.binding.QuerydslPredicate;
import org.springframework.data.querydsl.binding.QuerydslPredicateBuilder;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.repository.support.RepositoryInvoker;
import org.springframework.data.repository.support.RepositoryInvokerFactory;
import org.springframework.data.rest.tests.mongodb.QUser;
import org.springframework.data.rest.tests.mongodb.Receipt;
import org.springframework.data.rest.tests.mongodb.ReceiptRepository;
import org.springframework.data.rest.tests.mongodb.User;
import org.springframework.test.util.ReflectionTestUtils;
/**
* Unit tests for {@link QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver}.
*
* @author Oliver Gierke
*/
@RunWith(MockitoJUnitRunner.class)
public class QuerydslAwareRootResourceInformationHandlerMethodArgumentResolverUnitTests {
static final Map<String, String[]> NO_PARAMETERS = Collections.emptyMap();
@Mock Repositories repositories;
@Mock RepositoryInvokerFactory invokerFactory;
@Mock ResourceMetadataHandlerMethodArgumentResolver resourceMetadataResolver;
@Mock RepositoryInvoker invoker;
@Mock MethodParameter parameter;
QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver resolver;
@Before
public void setUp() {
QuerydslBindingsFactory factory = new QuerydslBindingsFactory(SimpleEntityPathResolver.INSTANCE);
ReflectionTestUtils.setField(factory, "repositories", repositories);
QuerydslPredicateBuilder builder = new QuerydslPredicateBuilder(new DefaultConversionService(),
factory.getEntityPathResolver());
this.resolver = new QuerydslAwareRootResourceInformationHandlerMethodArgumentResolver(repositories, invokerFactory,
resourceMetadataResolver, builder, factory);
when(parameter.hasParameterAnnotation(QuerydslPredicate.class)).thenReturn(true);
}
/**
* @see DATAREST-616
*/
@Test
public void returnsInvokerIfRepositoryIsNotQuerydslAware() {
ReceiptRepository repository = mock(ReceiptRepository.class);
when(repositories.getRepositoryFor(Receipt.class)).thenReturn(repository);
RepositoryInvoker result = resolver.postProcess(parameter, invoker, Receipt.class, NO_PARAMETERS);
assertThat(result, is(invoker));
}
/**
* @see DATAREST-616
*/
@Test
public void wrapsInvokerInQuerydslAdapter() {
Object repository = mock(QuerydslUserRepository.class);
when(repositories.getRepositoryFor(User.class)).thenReturn(repository);
RepositoryInvoker result = resolver.postProcess(parameter, invoker, User.class, NO_PARAMETERS);
assertThat(result, is(instanceOf(QuerydslRepositoryInvokerAdapter.class)));
}
/**
* @see DATAREST-616
*/
@Test
public void invokesCustomizationOnRepositoryIfItImplementsCustomizer() {
QuerydslCustomizingUserRepository repository = mock(QuerydslCustomizingUserRepository.class);
when(repositories.hasRepositoryFor(User.class)).thenReturn(true);
when(repositories.getRepositoryFor(User.class)).thenReturn(repository);
RepositoryInvoker result = resolver.postProcess(parameter, invoker, User.class, NO_PARAMETERS);
assertThat(result, is(instanceOf(QuerydslRepositoryInvokerAdapter.class)));
verify(repository, times(1)).customize(Mockito.any(QuerydslBindings.class), Mockito.any(QUser.class));
}
interface QuerydslUserRepository extends QueryDslPredicateExecutor<User> {}
interface QuerydslCustomizingUserRepository
extends QueryDslPredicateExecutor<User>, QuerydslBinderCustomizer<QUser> {}
}

View File

@@ -0,0 +1,127 @@
/*
* Copyright 2012-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.json;
import static org.hamcrest.MatcherAssert.*;
import static org.hamcrest.Matchers.*;
import java.util.Arrays;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.context.support.StaticMessageSource;
import org.springframework.data.projection.ProjectionFactory;
import org.springframework.data.projection.SpelAwareProxyProjectionFactory;
import org.springframework.data.repository.support.Repositories;
import org.springframework.data.rest.tests.RepositoryTestsConfig;
import org.springframework.data.rest.tests.mongodb.Address;
import org.springframework.data.rest.tests.mongodb.MongoDbRepositoryConfig;
import org.springframework.data.rest.tests.mongodb.User;
import org.springframework.data.rest.tests.mongodb.User.Gender;
import org.springframework.data.rest.webmvc.PersistentEntityResource;
import org.springframework.hateoas.Link;
import org.springframework.hateoas.LinkDiscoverer;
import org.springframework.hateoas.PagedResources;
import org.springframework.hateoas.PagedResources.PageMetadata;
import org.springframework.hateoas.hal.HalLinkDiscoverer;
import org.springframework.mock.web.MockHttpServletRequest;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletWebRequest;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
/**
* Integration tests for entity (de)serialization.
*
* @author Jon Brisbin
* @author Greg Turnquist
* @author Oliver Gierke
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MongoDbRepositoryConfig.class, RepositoryTestsConfig.class,
PersistentEntitySerializationTests.TestConfig.class })
@Transactional
public class PersistentEntitySerializationTests {
@Autowired ObjectMapper mapper;
@Autowired Repositories repositories;
@Configuration
static class TestConfig extends RepositoryTestsConfig {
@Bean
@Override
public ObjectMapper objectMapper() {
ObjectMapper objectMapper = super.objectMapper();
objectMapper.registerModule(
new JacksonSerializers(new EnumTranslator(new MessageSourceAccessor(new StaticMessageSource()))));
return objectMapper;
}
}
LinkDiscoverer linkDiscoverer;
ProjectionFactory projectionFactory;
@Before
public void setUp() {
RequestContextHolder.setRequestAttributes(new ServletWebRequest(new MockHttpServletRequest()));
this.linkDiscoverer = new HalLinkDiscoverer();
this.projectionFactory = new SpelAwareProxyProjectionFactory();
}
/**
* @see DATAREST-250
*/
@Test
public void serializesEmbeddedReferencesCorrectly() throws Exception {
User user = new User();
user.address = new Address();
user.address.street = "Street";
PersistentEntityResource userResource = PersistentEntityResource.//
build(user, repositories.getPersistentEntity(User.class)).//
withLink(new Link("/users/1")).//
build();
PagedResources<PersistentEntityResource> persistentEntityResource = new PagedResources<PersistentEntityResource>(
Arrays.asList(userResource), new PageMetadata(1, 0, 10));
String result = mapper.writeValueAsString(persistentEntityResource);
assertThat(JsonPath.read(result, "$_embedded.users[*].address"), is(notNullValue()));
}
/**
* @see DATAREST-654
*/
@Test
public void deserializesTranslatedEnumProperty() throws Exception {
assertThat(mapper.readValue("{ \"gender\" : \"Male\" }", User.class).gender, is(Gender.MALE));
}
}

View File

@@ -0,0 +1,217 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.json;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import static org.mockito.Mockito.*;
import java.util.ArrayList;
import java.util.List;
import org.hamcrest.Matcher;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.support.MessageSourceAccessor;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.rest.core.config.JsonSchemaFormat;
import org.springframework.data.rest.core.config.RepositoryRestConfiguration;
import org.springframework.data.rest.tests.TestMvcClient;
import org.springframework.data.rest.tests.mongodb.MongoDbRepositoryConfig;
import org.springframework.data.rest.tests.mongodb.Profile;
import org.springframework.data.rest.tests.mongodb.User;
import org.springframework.data.rest.tests.mongodb.User.EmailAddress;
import org.springframework.data.rest.tests.mongodb.User.TypeWithPattern;
import org.springframework.data.rest.tests.mongodb.groovy.SimulatedGroovyDomainClass;
import org.springframework.data.rest.webmvc.config.RepositoryRestConfigurerAdapter;
import org.springframework.data.rest.webmvc.config.RepositoryRestMvcConfiguration;
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverter.ValueTypeSchemaPropertyCustomizerFactory;
import org.springframework.data.rest.webmvc.json.PersistentEntityToJsonSchemaConverterUnitTests.TestConfiguration;
import org.springframework.data.rest.webmvc.mapping.Associations;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.jayway.jsonpath.JsonPath;
/**
* @author Oliver Gierke
* @author Greg Turnquist
*/
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = { MongoDbRepositoryConfig.class, TestConfiguration.class })
public class PersistentEntityToJsonSchemaConverterUnitTests {
@Autowired @Qualifier("resourceDescriptionMessageSourceAccessor") MessageSourceAccessor accessor;
@Autowired RepositoryRestConfiguration configuration;
@Autowired PersistentEntities entities;
@Autowired @Qualifier("objectMapper") ObjectMapper objectMapper;
@Autowired Associations associations;
@Configuration
@Import(RepositoryRestMvcConfiguration.class)
static class TestConfiguration extends RepositoryRestConfigurerAdapter {
@Override
public void configureRepositoryRestConfiguration(RepositoryRestConfiguration config) {
config.getMetadataConfiguration().registerJsonSchemaFormat(JsonSchemaFormat.EMAIL, EmailAddress.class);
config.getMetadataConfiguration().registerFormattingPatternFor("[A-Z]+", TypeWithPattern.class);
config.exposeIdsFor(Profile.class);
}
}
PersistentEntityToJsonSchemaConverter converter;
@Before
public void setUp() {
TestMvcClient.initWebTest();
ValueTypeSchemaPropertyCustomizerFactory customizerFactory = mock(ValueTypeSchemaPropertyCustomizerFactory.class);
converter = new PersistentEntityToJsonSchemaConverter(entities, associations, accessor, objectMapper, configuration,
customizerFactory);
}
/**
* @see DATAREST-631, DATAREST-632
*/
@Test
public void fulfillsConstraintsForProfile() {
List<Constraint> constraints = new ArrayList<Constraint>();
constraints.add(new Constraint("$.properties.id", is(notNullValue()), "Has descriptor for id property"));
constraints.add(new Constraint("$.description", is("Profile description"), "Adds description to schema root"));
constraints.add(new Constraint("$.properties.renamed", is(notNullValue()), "Has descriptor for renamed property"));
constraints.add(
new Constraint("$.properties.aliased", is(nullValue()), "No descriptor for original name of renamed property"));
assertConstraints(Profile.class, constraints);
}
/**
* @see DATAREST-632
*/
@Test
public void fulfillsConstraintsForUser() throws Exception {
List<Constraint> constraints = new ArrayList<Constraint>();
constraints.add(new Constraint("$.properties.id", is(nullValue()), "Does NOT have descriptor for id property"));
constraints.add(new Constraint("$.properties.firstname.type", is("string"), "Exposes firstname as String"));
constraints
.add(new Constraint("$.definitions.address", is(notNullValue()), "Exposes nested objects as definitions."));
constraints.add(new Constraint("$.definitions.address.type", is("object"), "Nested entity is of type 'object'"));
constraints.add(
new Constraint("$.definitions.address.properties.zipCode", is(notNullValue()), "Exposes nested properties"));
constraints.add(
new Constraint("$.definitions.address.requiredProperties[0]", is("zipCode"), "Lists nested required property"));
constraints.add(new Constraint("$.properties.gender.type", is("string"), "Enums are strings."));
constraints.add(new Constraint("$.properties.gender.enum", is(notNullValue()), "Exposes enum values."));
constraints
.add(new Constraint("$.properties.jodaDateTime.format", is("date-time"), "Exposes JodaTime dates in format."));
constraints
.add(new Constraint("$.properties.java8DateTime.format", is("date-time"), "Exposes Java 8 dates in format."));
constraints.add(new Constraint("$.properties.nicknames.type", is("array"), "Exposes collection of simple types."));
constraints.add(new Constraint("$.properties.nicknames.items.type", is("string"),
"Exposes element type of collection of simple types."));
constraints.add(new Constraint("$.properties.email.format", is("email"), "Uses manually configured format."));
constraints.add(new Constraint("$.properties.email.type", is("string"), "Treats types with format as String."));
constraints.add(
new Constraint("$.properties.shippingAddresses.type", is("array"), "Exposes collection of complex types."));
constraints
.add(new Constraint("$.properties.shippingAddresses.uniqueItems", is(true), "Exposes uniqueness for Sets."));
constraints.add(new Constraint("$.properties.shippingAddresses.items['$ref']", is("#/definitions/address"),
"References definition of complex element type."));
// DATAREST-531
constraints.add(new Constraint("$.properties.email.readOnly", is(true), "Email is read-only property"));
// DATAREST-644
constraints.add(new Constraint("$.properties.shippingAddresses.title", is("Shipping addresses"),
"Defaults titles correctly (split at camel case)"));
// DATAREST-665
constraints.add(new Constraint("$.properties.address.title", is("Adresse"), "I18n from simple property"));
constraints.add(new Constraint("$.properties.gender.title", is("Geschlecht"), "I18n from property on local type"));
constraints.add(
new Constraint("$.properties.firstname.title", is("Vorname"), "I18n from property on fully-qualified type"));
// DATAREST-690
constraints.add(new Constraint("$.properties.colleagues.items", is(nullValue()),
"Items must not appear for collection associations."));
assertConstraints(User.class, constraints);
}
/**
* @see DATAREST-754
*/
@Test
public void handlesGroovyDomainObjects() {
List<Constraint> constraints = new ArrayList<Constraint>();
constraints.add(new Constraint("$.properties.name", is(notNullValue()), "Has descriptor for name property"));
assertConstraints(SimulatedGroovyDomainClass.class, constraints);
}
@SuppressWarnings("unchecked")
private void assertConstraints(Class<?> type, Iterable<Constraint> constraints) {
String writeSchemaFor = writeSchemaFor(type);
for (Constraint constraint : constraints) {
try {
assertThat(constraint.description, JsonPath.read(writeSchemaFor, constraint.selector), constraint.matcher);
} catch (RuntimeException e) {
assertThat(e, constraint.matcher);
}
}
}
private String writeSchemaFor(Class<?> type) {
try {
return objectMapper.writeValueAsString(converter.convert(type));
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
}
}
@SuppressWarnings("rawtypes")
private static class Constraint {
String selector;
Matcher matcher;
String description;
public Constraint(String selector, Matcher matcher, String description) {
this.selector = selector;
this.matcher = matcher;
this.description = description;
}
}
}

View File

@@ -0,0 +1,72 @@
/*
* Copyright 2014-2016 the original author or authors.
*
* 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 org.springframework.data.rest.webmvc.support;
import static org.hamcrest.CoreMatchers.*;
import static org.junit.Assert.*;
import java.util.Arrays;
import org.junit.Test;
import org.springframework.data.mapping.context.PersistentEntities;
import org.springframework.data.mongodb.core.mapping.MongoMappingContext;
import org.springframework.data.rest.core.mapping.PersistentEntitiesResourceMappings;
import org.springframework.data.rest.core.mapping.ResourceMappings;
import org.springframework.data.rest.tests.TestMvcClient;
import org.springframework.data.rest.tests.mongodb.Profile;
import org.springframework.data.rest.webmvc.BaseUri;
import org.springframework.hateoas.Link;
/**
* Unit tests for {@link RepositoryLinkBuilder}.
*
* @author Oliver Gierke
*/
public class RepositoryLinkBuildUnitTests {
MongoMappingContext context = new MongoMappingContext();
/**
* @see DATAREST-292
*/
@Test
public void usesCurrentRequestsUriBaseForRelativeBaseUri() {
TestMvcClient.initWebTest();
assertRootUriFor("api", "http://localhost/api/profile");
}
/**
* @see DATAREST-292, DATAREST-296
*/
@Test
public void usesBaseUriOnlyIfItIsAbsolute() {
assertRootUriFor("http://foobar/api", "http://foobar/api/profile");
}
private void assertRootUriFor(String baseUri, String expectedUri) {
context.getPersistentEntity(Profile.class);
ResourceMappings mappings = new PersistentEntitiesResourceMappings(new PersistentEntities(Arrays.asList(context)));
RepositoryLinkBuilder builder = new RepositoryLinkBuilder(mappings.getMetadataFor(Profile.class),
new BaseUri(baseUri));
Link link = builder.withSelfRel();
assertThat(link.getHref(), is(expectedUri));
}
}

View File

@@ -0,0 +1,14 @@
rest.description.profile=Profile description
# User
# Local property
address._title=Adresse
# Property on simple type
User.gender._title=Geschlecht
# Property on fully-qualified property
org.springframework.data.rest.tests.mongodb.User.firstname._title=Vorname
# Local property that should be trumped by the more precise definition above
firstname._title=emanroV