package org.molgenis.integrationtest.platform;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import org.molgenis.data.DataService;
import org.molgenis.data.Entity;
import org.molgenis.data.elasticsearch.index.job.IndexService;
import org.molgenis.test.data.OneToManyTestHarness;
import org.molgenis.test.data.staticentity.bidirectional.authorbook1.AuthorMetaData1;
import org.molgenis.test.data.staticentity.bidirectional.authorbook1.BookMetaData1;
import org.molgenis.test.data.staticentity.bidirectional.person1.PersonMetaData1;
import org.molgenis.test.data.staticentity.bidirectional.person2.PersonMetaData2;
import org.molgenis.test.data.staticentity.bidirectional.person3.PersonMetaData3;
import org.molgenis.test.data.staticentity.bidirectional.person4.PersonMetaData4;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.TestingAuthenticationToken;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;
import org.springframework.transaction.annotation.Transactional;
import org.testng.annotations.AfterMethod;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.stream.Stream;
import java.util.stream.StreamSupport;
import static com.google.common.collect.Lists.newArrayList;
import static com.google.common.collect.Sets.newHashSet;
import static java.util.stream.Collectors.toList;
import static java.util.stream.Collectors.toSet;
import static org.molgenis.data.meta.model.Package.PACKAGE_SEPARATOR;
import static org.molgenis.integrationtest.platform.PlatformIT.*;
import static org.molgenis.security.core.runas.RunAsSystemProxy.runAsSystem;
import static org.molgenis.test.data.OneToManyTestHarness.*;
import static org.molgenis.test.data.OneToManyTestHarness.TestCaseType.*;
import static org.testng.Assert.assertEquals;
@ContextConfiguration(classes = { PlatformITConfig.class })
public class OneToManyIT extends AbstractTestNGSpringContextTests
{
private final Logger LOG = LoggerFactory.getLogger(OneToManyIT.class);
@Autowired
private IndexService indexService;
@Autowired
private OneToManyTestHarness oneToManyTestHarness;
@Autowired
private DataService dataService;
@BeforeClass
public void setUp()
{
List<GrantedAuthority> authorities = newArrayList();
for (int i = 1; i <= ONE_TO_MANY_CASES; i++)
{
authorities.addAll(makeAuthorities("sys" + PACKAGE_SEPARATOR + "Author" + i, true, true, true));
authorities.addAll(makeAuthorities("sys" + PACKAGE_SEPARATOR + "Book" + i, true, true, true));
authorities.addAll(makeAuthorities("sys" + PACKAGE_SEPARATOR + "Person" + i, true, true, true));
}
SecurityContextHolder.getContext()
.setAuthentication(new TestingAuthenticationToken("user", "user", authorities));
waitForWorkToBeFinished(indexService, LOG);
}
@AfterMethod
public void afterMethod()
{
runAsSystem(() ->
{
deleteBooksThenAuthors(1);
deleteBooksThenAuthors(2);
deleteBooksThenAuthors(3);
deleteBooksThenAuthors(4);
dataService.deleteAll(PersonMetaData1.NAME);
dataService.deleteAll(PersonMetaData2.NAME);
dataService.deleteAll(PersonMetaData3.NAME);
dataService.deleteAll(PersonMetaData4.NAME);
});
waitForWorkToBeFinished(indexService, LOG);
}
@Test(singleThreaded = true, dataProvider = "allTestCaseDataProvider")
public void testAuthorAndBookInsert(TestCaseType testCase)
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(testCase);
String bookEntityName = authorsAndBooks.getBookMetaData().getName();
assertEquals(dataService.findOneById(bookEntityName, BOOK_1).getEntity(ATTR_AUTHOR).getIdValue(), AUTHOR_1);
assertEquals(dataService.findOneById(bookEntityName, BOOK_2).getEntity(ATTR_AUTHOR).getIdValue(), AUTHOR_2);
assertEquals(dataService.findOneById(bookEntityName, BOOK_3).getEntity(ATTR_AUTHOR).getIdValue(), AUTHOR_3);
String authorEntityName = authorsAndBooks.getAuthorMetaData().getName();
assertEquals(dataService.findOneById(authorEntityName, AUTHOR_1).getEntities(ATTR_BOOKS).iterator().next()
.getIdValue(), BOOK_1);
assertEquals(dataService.findOneById(authorEntityName, AUTHOR_2).getEntities(ATTR_BOOKS).iterator().next()
.getIdValue(), BOOK_2);
assertEquals(dataService.findOneById(authorEntityName, AUTHOR_3).getEntities(ATTR_BOOKS).iterator().next()
.getIdValue(), BOOK_3);
}
@Test(singleThreaded = true, dataProvider = "allTestCaseDataProvider")
public void testPersonInsert(TestCaseType testCase)
{
List<Entity> persons = importPersons(testCase);
String personEntityName = persons.get(0).getEntityType().getName();
Entity person1 = dataService.findOneById(personEntityName, PERSON_1);
Entity person2 = dataService.findOneById(personEntityName, PERSON_2);
Entity person3 = dataService.findOneById(personEntityName, PERSON_3);
assertEquals(person1.getEntity(ATTR_PARENT).getIdValue(), PERSON_3);
assertEquals(person2.getEntity(ATTR_PARENT).getIdValue(), PERSON_1);
assertEquals(person3.getEntity(ATTR_PARENT).getIdValue(), PERSON_2);
assertEquals(
StreamSupport.stream(person1.getEntities(ATTR_CHILDREN).spliterator(), false).map(Entity::getIdValue)
.collect(toSet()), newHashSet(PERSON_2));
assertEquals(
StreamSupport.stream(person2.getEntities(ATTR_CHILDREN).spliterator(), false).map(Entity::getIdValue)
.collect(toSet()), newHashSet(PERSON_3));
assertEquals(
StreamSupport.stream(person3.getEntities(ATTR_CHILDREN).spliterator(), false).map(Entity::getIdValue)
.collect(toSet()), newHashSet(PERSON_1));
}
@Test(singleThreaded = true)
@Transactional
public void testL1SingleEntityUpdate()
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(XREF_NULLABLE);
try
{
Entity book1 = dataService.findOneById(authorsAndBooks.getBookMetaData().getName(), BOOK_1);
Entity author1 = dataService.findOneById(authorsAndBooks.getAuthorMetaData().getName(), AUTHOR_1);
Entity author2 = dataService.findOneById(authorsAndBooks.getAuthorMetaData().getName(), AUTHOR_2);
book1.set(BookMetaData1.AUTHOR, author2);
dataService.update(book1.getEntityType().getName(), book1);
Entity author1RetrievedAgain = dataService
.findOneById(authorsAndBooks.getAuthorMetaData().getName(), author1.getIdValue());
assertEquals(Collections.emptyList(),
Lists.newArrayList(author1RetrievedAgain.getEntities(AuthorMetaData1.ATTR_BOOKS)));
Entity author2Retrieved = dataService
.findOneById(authorsAndBooks.getAuthorMetaData().getName(), author2.getIdValue());
Iterable<Entity> author2Books = author2Retrieved.getEntities(AuthorMetaData1.ATTR_BOOKS);
// expected behavior: book.author changed, new author.books order is undefined
Set<Object> retrievedAuthor2BookIds = StreamSupport.stream(author2Books.spliterator(), false)
.map(Entity::getIdValue).collect(toSet());
assertEquals(retrievedAuthor2BookIds, newHashSet(BOOK_2, BOOK_1));
}
finally
{
dataService.deleteAll(authorsAndBooks.getBookMetaData().getName());
dataService.deleteAll(authorsAndBooks.getAuthorMetaData().getName());
}
}
@Test(singleThreaded = true)
@Transactional
public void testL1StreamingEntityUpdate()
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(XREF_NULLABLE);
try
{
Entity book1 = dataService.findOneById(authorsAndBooks.getBookMetaData().getName(), BOOK_1);
Entity author1 = dataService.findOneById(authorsAndBooks.getAuthorMetaData().getName(), AUTHOR_1);
Entity author2 = dataService.findOneById(authorsAndBooks.getAuthorMetaData().getName(), AUTHOR_2);
book1.set(BookMetaData1.AUTHOR, author2);
dataService.update(book1.getEntityType().getName(), Stream.of(book1));
Entity author1RetrievedAgain = dataService
.findOneById(authorsAndBooks.getAuthorMetaData().getName(), author1.getIdValue());
assertEquals(Collections.emptyList(),
Lists.newArrayList(author1RetrievedAgain.getEntities(AuthorMetaData1.ATTR_BOOKS)));
Entity author2Retrieved = dataService
.findOneById(authorsAndBooks.getAuthorMetaData().getName(), author2.getIdValue());
Iterable<Entity> author2Books = author2Retrieved.getEntities(AuthorMetaData1.ATTR_BOOKS);
// expected behavior: book.author changed, new author.books order is undefined
Set<Object> retrievedAuthor2BookIds = StreamSupport.stream(author2Books.spliterator(), false)
.map(Entity::getIdValue).collect(toSet());
assertEquals(retrievedAuthor2BookIds, newHashSet(BOOK_2, BOOK_1));
}
finally
{
dataService.deleteAll(authorsAndBooks.getBookMetaData().getName());
dataService.deleteAll(authorsAndBooks.getAuthorMetaData().getName());
}
}
@Test(singleThreaded = true)
@Transactional
public void testL1EntitySingleEntityDelete()
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(XREF_NULLABLE);
try
{
Entity book1 = dataService.findOneById(authorsAndBooks.getBookMetaData().getName(), BOOK_1);
Entity author1 = dataService.findOneById(authorsAndBooks.getAuthorMetaData().getName(), AUTHOR_1);
dataService.delete(book1.getEntityType().getName(), book1);
Entity author1RetrievedAgain = dataService
.findOneById(authorsAndBooks.getAuthorMetaData().getName(), author1.getIdValue());
assertEquals(Collections.emptyList(),
Lists.newArrayList(author1RetrievedAgain.getEntities(AuthorMetaData1.ATTR_BOOKS)));
}
finally
{
dataService.deleteAll(authorsAndBooks.getBookMetaData().getName());
dataService.deleteAll(authorsAndBooks.getAuthorMetaData().getName());
}
}
@Test(singleThreaded = true)
@Transactional
public void testL1EntityStreamingEntityDelete()
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(XREF_NULLABLE);
try
{
Entity book1 = dataService.findOneById(authorsAndBooks.getBookMetaData().getName(), BOOK_1);
Entity author1 = dataService.findOneById(authorsAndBooks.getAuthorMetaData().getName(), AUTHOR_1);
dataService.delete(book1.getEntityType().getName(), Stream.of(book1));
Entity author1RetrievedAgain = dataService
.findOneById(authorsAndBooks.getAuthorMetaData().getName(), author1.getIdValue());
assertEquals(Collections.emptyList(),
Lists.newArrayList(author1RetrievedAgain.getEntities(AuthorMetaData1.ATTR_BOOKS)));
}
finally
{
dataService.deleteAll(authorsAndBooks.getBookMetaData().getName());
dataService.deleteAll(authorsAndBooks.getAuthorMetaData().getName());
}
}
@Test(singleThreaded = true, dataProvider = "allTestCaseDataProvider")
public void testUpdateAuthorValue(TestCaseType testCase)
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(testCase);
String bookName = authorsAndBooks.getBookMetaData().getName();
String authorName = authorsAndBooks.getAuthorMetaData().getName();
Entity author1 = dataService.findOneById(authorName, AUTHOR_1);
Entity author2 = dataService.findOneById(authorName, AUTHOR_2);
Entity book1 = dataService.findOneById(bookName, BOOK_1);
Entity book2 = dataService.findOneById(bookName, BOOK_2);
book1.set(ATTR_AUTHOR, author2); // switch authors
book2.set(ATTR_AUTHOR, author1);
dataService.update(bookName, Stream.of(book1, book2));
assertEquals(dataService.findOneById(bookName, BOOK_1).getEntity(ATTR_AUTHOR).getIdValue(), AUTHOR_2);
assertEquals(dataService.findOneById(bookName, BOOK_2).getEntity(ATTR_AUTHOR).getIdValue(), AUTHOR_1);
Entity updatedAuthor1 = dataService.findOneById(authorName, AUTHOR_1);
assertEquals(StreamSupport.stream(updatedAuthor1.getEntities(ATTR_BOOKS).spliterator(), false)
.map(Entity::getIdValue).collect(toSet()), newHashSet(BOOK_2));
Entity updatedAuthor2 = dataService.findOneById(authorName, AUTHOR_2);
assertEquals(StreamSupport.stream(updatedAuthor2.getEntities(ATTR_BOOKS).spliterator(), false)
.map(Entity::getIdValue).collect(toSet()), newHashSet(BOOK_1));
}
@Test(singleThreaded = true, dataProvider = "allTestCaseDataProvider")
public void testUpdateParentValue(TestCaseType testCase)
{
List<Entity> persons = importPersons(testCase);
String personName = persons.get(0).getEntityType().getName();
Entity person1 = dataService.findOneById(personName, PERSON_1);
Entity person2 = dataService.findOneById(personName, PERSON_2);
Entity person3 = dataService.findOneById(personName, PERSON_3);
person1.set(ATTR_PARENT, person2); // switch parents
person2.set(ATTR_PARENT, person3);
person3.set(ATTR_PARENT, person1);
dataService.update(personName, Stream.of(person1, person2, person3));
assertEquals(dataService.findOneById(personName, PERSON_1).getEntity(ATTR_PARENT).getIdValue(), PERSON_2);
assertEquals(dataService.findOneById(personName, PERSON_2).getEntity(ATTR_PARENT).getIdValue(), PERSON_3);
assertEquals(dataService.findOneById(personName, PERSON_3).getEntity(ATTR_PARENT).getIdValue(), PERSON_1);
Entity updatedPerson1 = dataService.findOneById(personName, PERSON_1);
assertEquals(StreamSupport.stream(updatedPerson1.getEntities(ATTR_CHILDREN).spliterator(), false)
.map(Entity::getIdValue).collect(toSet()), newHashSet(PERSON_3));
Entity updatedPerson2 = dataService.findOneById(personName, PERSON_2);
assertEquals(StreamSupport.stream(updatedPerson2.getEntities(ATTR_CHILDREN).spliterator(), false)
.map(Entity::getIdValue).collect(toSet()), newHashSet(PERSON_1));
Entity updatedPerson3 = dataService.findOneById(personName, PERSON_3);
assertEquals(StreamSupport.stream(updatedPerson3.getEntities(ATTR_CHILDREN).spliterator(), false)
.map(Entity::getIdValue).collect(toSet()), newHashSet(PERSON_2));
}
@Test(singleThreaded = true)
public void testUpdateAuthorOrderAscending()
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(ASCENDING_ORDER);
String bookName = authorsAndBooks.getBookMetaData().getName();
String authorName = authorsAndBooks.getAuthorMetaData().getName();
Entity book1 = dataService.findOneById(bookName, BOOK_1);
Entity book2 = dataService.findOneById(bookName, BOOK_2);
Entity author3 = dataService.findOneById(authorName, AUTHOR_3);
book1.set(ATTR_AUTHOR, author3);
book2.set(ATTR_AUTHOR, author3);
dataService.update(bookName, Stream.of(book2, book1));
Entity updatedAuthor3 = dataService.findOneById(authorName, AUTHOR_3);
assertEquals(
StreamSupport.stream(updatedAuthor3.getEntities(ATTR_BOOKS).spliterator(), false).map(Entity::getIdValue)
.collect(toList()), newArrayList(BOOK_1, BOOK_2, BOOK_3));
Entity updatedAuthor1 = dataService.findOneById(authorName, AUTHOR_1);
Entity updatedAuthor2 = dataService.findOneById(authorName, AUTHOR_2);
assertEquals(Iterables.size(updatedAuthor1.getEntities(bookName)), 0);
assertEquals(Iterables.size(updatedAuthor2.getEntities(bookName)), 0);
}
@Test(singleThreaded = true)
public void testUpdateAuthorOrderDescending()
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks = importAuthorsAndBooks(DESCENDING_ORDER);
String bookName = authorsAndBooks.getBookMetaData().getName();
String authorName = authorsAndBooks.getAuthorMetaData().getName();
Entity book2 = dataService.findOneById(bookName, BOOK_2);
Entity book3 = dataService.findOneById(bookName, BOOK_3);
Entity author1 = dataService.findOneById(authorName, AUTHOR_1);
book2.set(ATTR_AUTHOR, author1);
book3.set(ATTR_AUTHOR, author1);
dataService.update(bookName, Stream.of(book2, book3));
Entity updatedAuthor1 = dataService.findOneById(authorName, AUTHOR_1);
assertEquals(
StreamSupport.stream(updatedAuthor1.getEntities(ATTR_BOOKS).spliterator(), false).map(Entity::getIdValue)
.collect(toList()), newArrayList(BOOK_3, BOOK_2, BOOK_1));
Entity updatedAuthor2 = dataService.findOneById(authorName, AUTHOR_2);
Entity updatedAuthor3 = dataService.findOneById(authorName, AUTHOR_3);
assertEquals(Iterables.size(updatedAuthor2.getEntities(ATTR_BOOKS)), 0);
assertEquals(Iterables.size(updatedAuthor3.getEntities(ATTR_BOOKS)), 0);
}
@Test(singleThreaded = true)
public void testUpdateParentOrderAscending()
{
List<Entity> persons = importPersons(ASCENDING_ORDER);
String personName = persons.get(0).getEntityType().getName();
Entity person1 = dataService.findOneById(personName, PERSON_1);
Entity person2 = dataService.findOneById(personName, PERSON_2);
Entity person3 = dataService.findOneById(personName, PERSON_3);
person1.set(ATTR_PARENT, person3);
person2.set(ATTR_PARENT, person3);
person3.set(ATTR_PARENT, person3);
dataService.update(personName, Stream.of(person2, person1, person3));
Entity updatedPerson3 = dataService.findOneById(personName, PERSON_3);
assertEquals(
StreamSupport.stream(updatedPerson3.getEntities(ATTR_CHILDREN).spliterator(), false).map(Entity::getIdValue)
.collect(toList()), newArrayList(PERSON_1, PERSON_2, PERSON_3));
Entity updatedPerson1 = dataService.findOneById(personName, PERSON_1);
Entity updatedPerson2 = dataService.findOneById(personName, PERSON_2);
assertEquals(Iterables.size(updatedPerson1.getEntities(ATTR_CHILDREN)), 0);
assertEquals(Iterables.size(updatedPerson2.getEntities(ATTR_CHILDREN)), 0);
}
@Test(singleThreaded = true)
public void testUpdateParentOrderDescending()
{
List<Entity> persons = importPersons(DESCENDING_ORDER);
String personName = persons.get(0).getEntityType().getName();
Entity person1 = dataService.findOneById(personName, PERSON_1);
Entity person2 = dataService.findOneById(personName, PERSON_2);
Entity person3 = dataService.findOneById(personName, PERSON_3);
person1.set(ATTR_PARENT, person1);
person2.set(ATTR_PARENT, person1);
person3.set(ATTR_PARENT, person1);
dataService.update(personName, Stream.of(person2, person1, person3));
Entity updatedPerson1 = dataService.findOneById(personName, PERSON_1);
assertEquals(
StreamSupport.stream(updatedPerson1.getEntities(ATTR_CHILDREN).spliterator(), false).map(Entity::getIdValue)
.collect(toList()), newArrayList(PERSON_3, PERSON_2, PERSON_1));
Entity updatedPerson2 = dataService.findOneById(personName, PERSON_2);
Entity updatedPerson3 = dataService.findOneById(personName, PERSON_3);
assertEquals(Iterables.size(updatedPerson2.getEntities(ATTR_CHILDREN)), 0);
assertEquals(Iterables.size(updatedPerson3.getEntities(ATTR_CHILDREN)), 0);
}
private void deleteBooksThenAuthors(int testCase)
{
dataService.deleteAll("sys" + PACKAGE_SEPARATOR + "Book" + testCase);
dataService.deleteAll("sys" + PACKAGE_SEPARATOR + "Author" + testCase);
}
private OneToManyTestHarness.AuthorsAndBooks importAuthorsAndBooks(TestCaseType testCase)
{
OneToManyTestHarness.AuthorsAndBooks authorsAndBooks;
authorsAndBooks = oneToManyTestHarness.createAuthorAndBookEntities(testCase);
runAsSystem(() ->
{
dataService.add(authorsAndBooks.getAuthorMetaData().getName(), authorsAndBooks.getAuthors().stream());
dataService.add(authorsAndBooks.getBookMetaData().getName(), authorsAndBooks.getBooks().stream());
waitForIndexToBeStable(authorsAndBooks.getAuthorMetaData().getName(), indexService, LOG);
waitForIndexToBeStable(authorsAndBooks.getBookMetaData().getName(), indexService, LOG);
});
return authorsAndBooks;
}
private List<Entity> importPersons(TestCaseType testCase)
{
List<Entity> persons = oneToManyTestHarness.createPersonEntities(testCase);
runAsSystem(() ->
{
dataService.add(persons.get(0).getEntityType().getName(), persons.stream());
waitForIndexToBeStable(persons.get(0).getEntityType().getName(), indexService, LOG);
});
return persons;
}
/**
* Serves all test case numbers.
*/
@DataProvider(name = "allTestCaseDataProvider")
private Object[][] allTestCaseDataProvider()
{
return new Object[][] { { XREF_NULLABLE }, { XREF_REQUIRED }, { ASCENDING_ORDER }, { DESCENDING_ORDER } };
}
}