package com.constellio.model.services.batch.controller; import static com.constellio.model.entities.schemas.Schemas.IDENTIFIER; import static com.constellio.model.services.search.query.logical.LogicalSearchQueryOperators.fromAllSchemasIn; import static junit.framework.Assert.fail; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyString; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.doThrow; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.junit.Before; import org.junit.Test; import com.constellio.data.dao.dto.records.TransactionDTO; import com.constellio.data.dao.services.DataStoreTypesFactory; import com.constellio.data.dao.services.bigVault.RecordDaoException; import com.constellio.data.dao.services.idGenerator.UniqueIdGenerator; import com.constellio.data.dao.services.records.RecordDao; import com.constellio.model.entities.batchprocess.BatchProcess; import com.constellio.model.entities.batchprocess.BatchProcessAction; import com.constellio.model.entities.records.Record; import com.constellio.model.entities.records.Transaction; import com.constellio.model.services.batch.actions.ChangeValueOfMetadataBatchProcessAction; import com.constellio.model.services.batch.manager.BatchProcessesManager; import com.constellio.model.services.factories.ModelLayerFactory; import com.constellio.model.services.records.AddToBatchProcessImpactHandler; import com.constellio.model.services.records.RecordServicesException; import com.constellio.model.services.records.RecordServicesImpl; import com.constellio.model.services.records.cache.RecordsCaches; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.search.query.logical.condition.LogicalSearchCondition; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.TestRecord; //@SlowTest public class BatchProcessControllerAcceptanceTest extends ConstellioTest { String anotherSchemaRecordText = "this is a text"; String anotherSchemaRecordNewText = "this is an other text"; RecordsCaches recordsCaches; RecordDao recordDao; RecordDao eventsDao; RecordDao notificationsDao; RecordServicesImpl recordServices; BatchProcessControllerAcceptanceTestSchemasSetup schemas = new BatchProcessControllerAcceptanceTestSchemasSetup(); BatchProcessControllerAcceptanceTestSchemasSetup.ZeSchemaMetadatas zeSchema = schemas.new ZeSchemaMetadatas(); BatchProcessControllerAcceptanceTestSchemasSetup.AnotherSchemaMetadatas anotherSchema = schemas.new AnotherSchemaMetadatas(); BatchProcessControllerAcceptanceTestSchemasSetup.ThirdSchemaMetadatas thirdSchema = schemas.new ThirdSchemaMetadatas(); // BatchProcessController controller; BatchProcessesManager batchProcessManager; ModelLayerFactory modelFactory; Record zeSchemaRecord; List<String> recordIds; @Before public void setUp() throws Exception { withSpiedServices(ModelLayerFactory.class); recordsCaches = getModelLayerFactory().getRecordsCaches(); eventsDao = spy(getDataLayerFactory().newEventsDao()); recordDao = spy(getDataLayerFactory().newRecordDao()); notificationsDao = spy(getDataLayerFactory().newNotificationsDao()); DataStoreTypesFactory typesFactory = getDataLayerFactory().newTypesFactory(); UniqueIdGenerator uniqueIdGenerator = getDataLayerFactory().getUniqueIdGenerator(); recordServices = spy(new RecordServicesImpl(recordDao, eventsDao, notificationsDao, getModelLayerFactory(), typesFactory, uniqueIdGenerator, recordsCaches)); modelFactory = getModelLayerFactory(); batchProcessManager = modelFactory.getBatchProcessesManager(); // controller = getModelLayerFactory().getBatchProcessesController(); // controller.start(); defineSchemasManager().using(schemas.withCopiedTextMetadataFromAnotherSchema().withATitle().withAStringMetadata()); zeSchemaRecord = createZeSchemaFirstRecord(); recordIds = addAnotherSchemaAndThirdSchemaRecords(zeSchemaRecord); SearchServices searchServices = getModelLayerFactory().newSearchServices(); doReturn(new AddToBatchProcessImpactHandler(batchProcessManager, searchServices)).when(recordServices) .addToBatchProcessModificationImpactHandler(); givenWaitForBatchProcessAfterTestIsDisabled(); } // @After // public void tearDown() // throws InterruptedException { // controller.clear(); // } @Test public void whenReindexingCopiedValueInOneTransactionThenReindexedForAllRecords() throws Exception { zeSchemaRecord.set(zeSchema.text(), anotherSchemaRecordNewText); recordServices.update(zeSchemaRecord); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordNewText); } @Test public void whenReindexingCopiedValueInBatchProcessThenReindexedForAllRecords() throws Exception { zeSchemaRecord.set(zeSchema.text(), anotherSchemaRecordNewText); List<BatchProcess> batchProcesses = recordServices.updateAsync(zeSchemaRecord); waitForBatchProcess(); assertThat(batchProcesses).hasSize(1); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordNewText); } @Test public void givenSystemErrorWhenAddingBatchProcessThenNoModifications() throws Exception { batchProcessManager = spy(batchProcessManager); doThrow(Error.class).when(batchProcessManager) .addBatchProcessInStandby(any(LogicalSearchCondition.class), any(BatchProcessAction.class), anyString()); when(modelFactory.getBatchProcessesManager()).thenReturn(batchProcessManager); recordServices = new RecordServicesImpl(recordDao, eventsDao, notificationsDao, modelFactory, getDataLayerFactory().newTypesFactory(), getDataLayerFactory().getUniqueIdGenerator(), recordsCaches); zeSchemaRecord.set(zeSchema.text(), anotherSchemaRecordNewText); try { recordServices.updateAsync(zeSchemaRecord); fail("Error expected"); } catch (Error e) { } assertThat(batchProcessManager.getCurrentBatchProcess()).isNull(); assertThat(batchProcessManager.getPendingBatchProcesses()).isEmpty(); assertThat(batchProcessManager.getFinishedBatchProcesses()).isEmpty(); assertThat(recordServices.getDocumentById(zeSchemaRecord.getId()).get(zeSchema.text())) .isEqualTo(anotherSchemaRecordText); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordText); } @Test public void givenSystemErrorWhenExecutingTransactionDTOThenNoModifications() throws Exception { doThrow(Error.class).when(recordDao).execute(any(TransactionDTO.class)); zeSchemaRecord.set(zeSchema.text(), anotherSchemaRecordNewText); try { recordServices.updateAsync(zeSchemaRecord); fail("Error expected"); } catch (Error e) { } assertThat(batchProcessManager.getCurrentBatchProcess()).isNull(); assertThat(batchProcessManager.getPendingBatchProcesses()).isEmpty(); assertThat(batchProcessManager.getFinishedBatchProcesses()).isEmpty(); assertThat(batchProcessManager.getStandbyBatchProcesses()).hasSize(1); assertThat(recordServices.getDocumentById(zeSchemaRecord.getId()).get(zeSchema.text())) .isEqualTo(anotherSchemaRecordText); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordText); } @Test public void givenMultipleOptimisticLockingThenSystemErrorWhenExecutingTransactionDTOThenNoModificationsAndOnlyOneStandbyBatchProcess() throws Exception { givenWaitForBatchProcessAfterTestIsDisabled(); // A really bad day... doThrow(RecordDaoException.OptimisticLocking.class).doThrow(RecordDaoException.OptimisticLocking.class) .doThrow(Error.class).when(recordDao).execute(any(TransactionDTO.class)); zeSchemaRecord.set(zeSchema.text(), anotherSchemaRecordNewText); try { recordServices.updateAsync(zeSchemaRecord); fail("Error expected"); } catch (Error e) { } assertThat(batchProcessManager.getCurrentBatchProcess()).isNull(); assertThat(batchProcessManager.getPendingBatchProcesses()).isEmpty(); assertThat(batchProcessManager.getFinishedBatchProcesses()).isEmpty(); assertThat(batchProcessManager.getStandbyBatchProcesses()).hasSize(1); assertThat(recordServices.getDocumentById(zeSchemaRecord.getId()).get(zeSchema.text())) .isEqualTo(anotherSchemaRecordText); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordText); } @Test public void givenMultipleOptimisticLockingWhenExecutingTransactionDTOThenOnlyOneStandbyBatchProcess() throws Exception { // A bad day... doThrow(RecordDaoException.OptimisticLocking.class).doThrow(RecordDaoException.OptimisticLocking.class) .doCallRealMethod().when(recordDao).execute(any(TransactionDTO.class)); zeSchemaRecord.set(zeSchema.text(), anotherSchemaRecordNewText); List<BatchProcess> batchProcesses = recordServices.updateAsync(zeSchemaRecord); assertThat(batchProcesses).hasSize(1); assertThat(batchProcessManager.getAllBatchProcessesCount()).isEqualTo(1); waitForBatchProcess(); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordNewText); } @Test public void whenChangingValueInOneTransactionThenChangedForAllRecords() throws Exception { Record zeSchemaRecord1 = createZeSchemaRecordWithTitleAndString(); Record zeSchemaRecord2 = createZeSchemaRecordWithTitleAndString(); List<String> recordIds = new ArrayList<>(); recordIds.add(zeSchemaRecord1.getId()); recordIds.add(zeSchemaRecord2.getId()); Map<String, Object> changedMetadataValues = new HashMap<>(); changedMetadataValues.put(zeSchema.title().getCode(), "changedTitle"); changedMetadataValues.put(zeSchema.stringMetadata().getCode(), "changedString"); BatchProcessAction action = new ChangeValueOfMetadataBatchProcessAction(changedMetadataValues); LogicalSearchCondition condition = fromAllSchemasIn(zeCollection).where(IDENTIFIER).isIn(recordIds); BatchProcess batchProcess = batchProcessManager.addBatchProcessInStandby(condition, action, "zeTitle"); batchProcessManager.markAsPending(batchProcess); waitForBatchProcess(); for (Record record : recordServices.getRecordsById(zeCollection, recordIds)) { assertThat(record.get(zeSchema.title())).isEqualTo("changedTitle"); assertThat(record.get(zeSchema.stringMetadata())).isEqualTo("changedString"); } } private Record createZeSchemaRecordWithTitleAndString() throws RecordServicesException { Record zeSchemaRecord = recordServices.newRecordWithSchema(zeSchema.instance()); zeSchemaRecord.set(zeSchema.title(), "initialTitle"); zeSchemaRecord.set(zeSchema.stringMetadata(), "initialString"); recordServices.add(zeSchemaRecord); return zeSchemaRecord; } private List<String> addAnotherSchemaAndThirdSchemaRecords(Record zeSchemaRecord) throws Exception { List<String> recordIds = new ArrayList<>(); Transaction transaction = new Transaction(); for (int i = 0; i < 30; i++) { Record anotherSchemaRecord = new TestRecord(anotherSchema, "_" + (i + 1)); anotherSchemaRecord.set(anotherSchema.referenceToZeSchema(), zeSchemaRecord.getId()); transaction.addUpdate(anotherSchemaRecord); for (int j = 0; j < 10; j++) { Record thirdSchemaRecord = new TestRecord(thirdSchema, anotherSchemaRecord.getId() + "_" + (i + 1)); thirdSchemaRecord.set(thirdSchema.referenceToAnotherSchema(), anotherSchemaRecord.getId()); transaction.addUpdate(thirdSchemaRecord); } } recordServices.execute(transaction); recordIds.addAll(transaction.getRecordIds()); assertThatAllRecordsHaveCorrectTextAndLength(recordIds, anotherSchemaRecordText); return recordIds; } private void assertThatAllRecordsHaveCorrectTextAndLength(List<String> recordIds, String text) { for (String recordId : recordIds) { Record record = recordServices.getDocumentById(recordId); if (record.getSchemaCode().equals(anotherSchema.code())) { assertThat(record.get(anotherSchema.copiedText())).isEqualTo(text); assertThat(record.get(anotherSchema.copiedTextLength())).isEqualTo(Double.valueOf(text.length())); } else { assertThat(record.get(thirdSchema.copiedText())).isEqualTo(text); assertThat(record.get(thirdSchema.copiedTextLength())).isEqualTo(Double.valueOf(text.length())); } } } private Record createZeSchemaFirstRecord() throws RecordServicesException { Record anotherSchemaFirstRecord = new TestRecord(zeSchema); anotherSchemaFirstRecord.set(zeSchema.text(), anotherSchemaRecordText); recordServices.add(anotherSchemaFirstRecord); return anotherSchemaFirstRecord; } }