/* * Copyright 2013-2017 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.batch.item.data; import java.util.ArrayList; import java.util.Collections; import java.util.List; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import org.mockito.MockitoAnnotations; import org.springframework.batch.support.transaction.ResourcelessTransactionManager; import org.springframework.data.mongodb.core.MongoOperations; import org.springframework.transaction.PlatformTransactionManager; import org.springframework.transaction.support.TransactionCallback; import org.springframework.transaction.support.TransactionTemplate; import static org.junit.Assert.assertEquals; import static org.junit.Assert.fail; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.doAnswer; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.verifyZeroInteractions; @SuppressWarnings("serial") public class MongoItemWriterTests { private MongoItemWriter<Object> writer; @Mock private MongoOperations template; private PlatformTransactionManager transactionManager = new ResourcelessTransactionManager(); @Before public void setUp() throws Exception { MockitoAnnotations.initMocks(this); writer = new MongoItemWriter<>(); writer.setTemplate(template); writer.afterPropertiesSet(); } @Test public void testAfterPropertiesSet() throws Exception { writer = new MongoItemWriter<>(); try { writer.afterPropertiesSet(); fail("Expected exception was not thrown"); } catch (IllegalStateException ignore) { } writer.setTemplate(template); writer.afterPropertiesSet(); } @Test public void testWriteNoTransactionNoCollection() throws Exception { List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.write(items); verify(template).save(items.get(0)); verify(template).save(items.get(1)); } @Test public void testWriteNoTransactionWithCollection() throws Exception { List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.setCollection("collection"); writer.write(items); verify(template).save(items.get(0), "collection"); verify(template).save(items.get(1), "collection"); } @Test public void testWriteNoTransactionNoItems() throws Exception { writer.write(null); verifyZeroInteractions(template); } @Test public void testWriteTransactionNoCollection() throws Exception { final List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; new TransactionTemplate(transactionManager).execute((TransactionCallback<Void>) status -> { try { writer.write(items); } catch (Exception e) { fail("An exception was thrown while writing: " + e.getMessage()); } return null; }); verify(template).save(items.get(0)); verify(template).save(items.get(1)); } @Test public void testWriteTransactionWithCollection() throws Exception { final List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.setCollection("collection"); new TransactionTemplate(transactionManager).execute((TransactionCallback<Void>) status -> { try { writer.write(items); } catch (Exception e) { fail("An exception was thrown while writing: " + e.getMessage()); } return null; }); verify(template).save(items.get(0), "collection"); verify(template).save(items.get(1), "collection"); } @Test public void testWriteTransactionFails() throws Exception { final List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.setCollection("collection"); try { new TransactionTemplate(transactionManager).execute((TransactionCallback<Void>) status -> { try { writer.write(items); } catch (Exception ignore) { fail("unexpected exception thrown"); } throw new RuntimeException("force rollback"); }); } catch (RuntimeException re) { assertEquals(re.getMessage(), "force rollback"); } catch (Throwable t) { fail("Unexpected exception was thrown"); } verifyZeroInteractions(template); } /** * A pointless use case but validates that the flag is still honored. * */ @Test public void testWriteTransactionReadOnly() throws Exception { final List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.setCollection("collection"); try { TransactionTemplate transactionTemplate = new TransactionTemplate(transactionManager); transactionTemplate.setReadOnly(true); transactionTemplate.execute((TransactionCallback<Void>) status -> { try { writer.write(items); } catch (Exception ignore) { fail("unexpected exception thrown"); } return null; }); } catch (Throwable t) { fail("Unexpected exception was thrown"); } verifyZeroInteractions(template); } @Test public void testRemoveNoTransactionNoCollection() throws Exception { writer.setDelete(true); List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.write(items); verify(template).remove(items.get(0)); verify(template).remove(items.get(1)); } @Test public void testRemoveNoTransactionWithCollection() throws Exception { writer.setDelete(true); List<Object> items = new ArrayList<Object>() {{ add(new Object()); add(new Object()); }}; writer.setCollection("collection"); writer.write(items); verify(template).remove(items.get(0), "collection"); verify(template).remove(items.get(1), "collection"); } // BATCH-2018 @Test public void testResourceKeyCollision() throws Exception { final int limit = 5000; @SuppressWarnings("unchecked") List<MongoItemWriter<String>> writers = new ArrayList<>(limit); final String[] results = new String[limit]; for(int i = 0; i< limit; i++) { final int index = i; MongoOperations mongoOperations = mock(MongoOperations.class); doAnswer(invocation -> { String val = (String) invocation.getArguments()[0]; if(results[index] == null) { results[index] = val; } else { results[index] += val; } return null; }).when(mongoOperations).save(any(String.class)); writers.add(i, new MongoItemWriter<>()); writers.get(i).setTemplate(mongoOperations); } new TransactionTemplate(transactionManager).execute((TransactionCallback<Void>) status -> { try { for(int i=0; i< limit; i++) { writers.get(i).write(Collections.singletonList(String.valueOf(i))); } } catch (Exception e) { throw new IllegalStateException("Unexpected Exception", e); } return null; }); for(int i=0; i< limit; i++) { assertEquals(String.valueOf(i), results[i]); } } }