package com.constellio.model.services.records; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.Matchers.any; import static org.mockito.Matchers.anyObject; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.doReturn; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.never; import static org.mockito.Mockito.reset; import static org.mockito.Mockito.spy; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; import org.joda.time.LocalDate; import org.junit.Before; import org.junit.Test; import org.mockito.Mock; import com.constellio.model.entities.calculators.CalculatorParameters; import com.constellio.model.entities.calculators.MetadataValueCalculator; import com.constellio.model.entities.calculators.dependencies.Dependency; import com.constellio.model.entities.calculators.dependencies.LocalDependency; import com.constellio.model.entities.calculators.dependencies.ReferenceDependency; import com.constellio.model.entities.calculators.dependencies.SpecialDependencies; import com.constellio.model.entities.records.RecordUpdateOptions; import com.constellio.model.entities.records.TransactionRecordsReindexation; import com.constellio.model.entities.schemas.Metadata; import com.constellio.model.entities.schemas.MetadataSchemaTypes; import com.constellio.model.entities.schemas.entries.CalculatedDataEntry; import com.constellio.model.services.configs.SystemConfigurationsManager; import com.constellio.model.services.factories.ModelLayerLogger; import com.constellio.model.services.schemas.MetadataList; import com.constellio.model.services.schemas.MetadataSchemasManager; import com.constellio.model.services.search.SearchServices; import com.constellio.model.services.taxonomies.TaxonomiesManager; import com.constellio.sdk.tests.ConstellioTest; import com.constellio.sdk.tests.TestRecord; import com.constellio.sdk.tests.schemas.DaysBetweenMultivalueLocalDateAndAnotherSchemaRequiredDateCalculator; import com.constellio.sdk.tests.schemas.TestsSchemasSetup; import com.constellio.sdk.tests.schemas.TestsSchemasSetup.AnotherSchemaMetadatas; import com.constellio.sdk.tests.schemas.TestsSchemasSetup.ZeSchemaMetadatas; public class RecordAutomaticMetadataServicesCalculationTest extends ConstellioTest { RecordUpdateOptions options = new RecordUpdateOptions(); RecordAutomaticMetadataServices services; @Mock RecordProvider recordProvider; @Mock MetadataSchemasManager schemasManager; @Mock ModelLayerLogger modelLayerLogger; @Mock DaysBetweenMultivalueLocalDateAndAnotherSchemaRequiredDateCalculator calculator; @SuppressWarnings("rawtypes") @Mock LocalDependency aLocalDependency; @SuppressWarnings("rawtypes") @Mock LocalDependency anotherLocalDependency; @SuppressWarnings("rawtypes") @Mock ReferenceDependency aReferenceDependency; String idReferencedRecordWithJan1DateValue, idReferencedRecordWithJan2DateValue, idReferencedRecordWithoutDateValue; LocalDate jan1 = new LocalDate(2014, 1, 1); LocalDate jan2 = new LocalDate(2014, 1, 2); LocalDate jan3 = new LocalDate(2014, 1, 3); TestsSchemasSetup schemas; ZeSchemaMetadatas zeSchema; AnotherSchemaMetadatas anotherSchema; RecordImpl record; RecordImpl otherRecord; @SuppressWarnings("rawtypes") List dependencies; @Mock Metadata firstReindexedMetadata, secondReindexedMetadata; TransactionRecordsReindexation reindexedMetadata; @Mock TaxonomiesManager taxonomiesManager; @Mock SystemConfigurationsManager systemConfigurationsManager; @Mock SearchServices searchServices; @SuppressWarnings("unchecked") @Before public void setUp() throws Exception { schemas = new TestsSchemasSetup(); zeSchema = schemas.new ZeSchemaMetadatas(); anotherSchema = schemas.new AnotherSchemaMetadatas(); define(schemasManager).using(schemas.withCalculatedDaysBetweenLocalDateAndAnotherSchemaRequiredDate(false)); services = spy(new RecordAutomaticMetadataServices(schemasManager, taxonomiesManager, systemConfigurationsManager, modelLayerLogger, searchServices)); record = spy(new TestRecord(zeSchema)); otherRecord = spy(new TestRecord(anotherSchema)); configureCalculatorDependencies(); when(calculator.getDependencies()).thenReturn(dependencies); reset(schemasManager.getSchemaTypes(zeCollection)); reindexedMetadata = new TransactionRecordsReindexation(new MetadataList(firstReindexedMetadata, secondReindexedMetadata)); } @Test public void givenIdentifierSpecialDependencyWhenAddValuesThenAddId() { Map<Dependency, Object> valuesMap = new HashMap<>(); RecordImpl zeRecord = new TestRecord("zeSchema", "zeCollection", "zeId"); services.addValuesFromSpecialDependencies(zeRecord, recordProvider, valuesMap, SpecialDependencies.IDENTIFIER); assertThat(valuesMap).containsEntry(SpecialDependencies.IDENTIFIER, "zeId").hasSize(1); } @Test public void givenDependencyModifiedWhenSettingCalculatedValuesInRecordThenValueCalculated() { doNothing().when(services).calculateValueInRecord(any(RecordImpl.class), any(Metadata.class), any(RecordProvider.class), any(MetadataSchemaTypes.class), any(RecordUpdateOptions.class)); doReturn(true).when(services).calculatorDependencyModified(any(RecordImpl.class), any(MetadataValueCalculator.class), any(MetadataSchemaTypes.class), any(Metadata.class)); services.setCalculatedValuesInRecords(record, zeSchema.calculatedDaysBetween(), recordProvider, reindexedMetadata, schemas.getTypes(), options); verify(services) .calculateValueInRecord(record, zeSchema.calculatedDaysBetween(), recordProvider, schemas.getTypes(), options); } @Test public void givenDependencyNotModifiedWhenSettingCalculatedValuesInRecordThenValueCalculated() { doNothing().when(services).calculateValueInRecord(any(RecordImpl.class), any(Metadata.class), any(RecordProvider.class), any(MetadataSchemaTypes.class), any(RecordUpdateOptions.class)); doReturn(false).when(services).calculatorDependencyModified(any(RecordImpl.class), any(MetadataValueCalculator.class), any(MetadataSchemaTypes.class), any(Metadata.class)); services.setCalculatedValuesInRecords(record, zeSchema.calculatedDaysBetween(), recordProvider, reindexedMetadata, schemas.getTypes(), options); verify(services, never()) .calculateValueInRecord(record, zeSchema.calculatedDaysBetween(), recordProvider, schemas.getTypes(), options); } @Test public void givenDependencyValueModifiedInRecordWhenVerifyingIsModifiedThenReturnTrue() { doReturn(true).when(record).isModified(any(Metadata.class)); assertThat(services.calculatorDependencyModified(record, ((CalculatedDataEntry) zeSchema.calculatedDaysBetween() .getDataEntry()).getCalculator(), schemas.getTypes(), mock(Metadata.class))); } @Test public void givenDependencyValueNotModifiedInRecordWhenVerifyingIsModifiedThenReturnTrue() { doReturn(false).when(record).isModified(any(Metadata.class)); assertThat(services.calculatorDependencyModified(record, ((CalculatedDataEntry) zeSchema.calculatedDaysBetween() .getDataEntry()).getCalculator(), schemas.getTypes(), mock(Metadata.class))); } @Test public void whenUpdatingAutomaticValuesInRecordsThenSetValueForAllMetadatas() throws Exception { doNothing().when(services).setCalculatedValuesInRecords(any(RecordImpl.class), any(Metadata.class), any(RecordProvider.class), eq(reindexedMetadata), any(MetadataSchemaTypes.class), any(RecordUpdateOptions.class)); services.updateAutomaticMetadatas(record, recordProvider, reindexedMetadata, options); verify(services).setCalculatedValuesInRecords(record, zeSchema.calculatedDaysBetween(), recordProvider, reindexedMetadata, schemas.getTypes(), options); } @SuppressWarnings("unchecked") @Test public void givenRequiredDependencyUndefinedWhenCalculateValuesInRecordThenUseDefaultValue() { doReturn(false).when(services) .addValuesFromDependencies(any(RecordImpl.class), any(Metadata.class), any(RecordProvider.class), any(MetadataValueCalculator.class), any(Map.class), any(MetadataSchemaTypes.class), any(RecordUpdateOptions.class)); services.calculateValueInRecord(record, zeSchema.calculatedDaysBetween(), recordProvider, schemas.getTypes(), options); verify(record).updateAutomaticValue(zeSchema.calculatedDaysBetween(), -1.0); } @SuppressWarnings("unchecked") @Test public void givenRequiredDependencyDefinedWhenCalculateValuesInRecordThenCalculateValue() { doReturn(true).when(services) .addValuesFromDependencies(any(RecordImpl.class), any(Metadata.class), any(RecordProvider.class), any(MetadataValueCalculator.class), any(Map.class), any(MetadataSchemaTypes.class), any(RecordUpdateOptions.class)); doReturn(calculator).when(services).getCalculatorFrom(any(Metadata.class)); services.calculateValueInRecord(record, zeSchema.calculatedDaysBetween(), recordProvider, schemas.getTypes(), options); verify(record).updateAutomaticValue(eq(zeSchema.calculatedDaysBetween()), anyObject()); verify(calculator).calculate(any(CalculatorParameters.class)); } @SuppressWarnings({ "unchecked", "rawtypes" }) @Test public void givenTwoLocalAndOneReferenceDependenciesWhenGetValuesFromDependenciesThenAllValueRetrieved() { doReturn(true).when(services).addValueForLocalDependency(any(RecordImpl.class), any(Map.class), any(Dependency.class)); doReturn(true).when(services).addValueForReferenceDependency(any(RecordImpl.class), any(RecordProvider.class), any(Map.class), any(ReferenceDependency.class), any(RecordUpdateOptions.class)); Map aMap = mock(Map.class); services.addValuesFromDependencies(record, mock(Metadata.class), recordProvider, calculator, aMap, schemas.getTypes(), options); verify(services).addValueForLocalDependency(eq(record), eq(aMap), eq(aLocalDependency)); verify(services).addValueForLocalDependency(eq(record), eq(aMap), eq(anotherLocalDependency)); verify(services) .addValueForReferenceDependency(eq(record), eq(recordProvider), eq(aMap), eq(aReferenceDependency), eq(options)); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void givenLocalDependencyRequiredButNullWhenGettingValueThenReturnTrueAndDontAddValue() { Map aMap = mock(Map.class); Metadata aMetadata = mock(Metadata.class); when(aLocalDependency.isRequired()).thenReturn(true); doReturn(aMetadata).when(services).getMetadataFromDependency(any(RecordImpl.class), any(Dependency.class)); doReturn(null).when(record).get(any(Metadata.class)); assertThat(services.addValueForLocalDependency(record, aMap, aLocalDependency)).isFalse(); verify(aMap, never()).put(any(Dependency.class), anyObject()); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void givenLocalDependencyRequiredAndNotNullWhenGettingValueThenReturnTrueAndAddValue() { Map aMap = mock(Map.class); Metadata aMetadata = mock(Metadata.class); when(aLocalDependency.isRequired()).thenReturn(true); doReturn(aMetadata).when(services).getMetadataFromDependency(any(RecordImpl.class), any(Dependency.class)); doReturn("aValue").when(record).get(any(Metadata.class)); assertThat(services.addValueForLocalDependency(record, aMap, aLocalDependency)).isTrue(); verify(aMap).put(aLocalDependency, "aValue"); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void givenReferenceDependencyRequiredButNullWhenGettingValueThenReturnTrueAndDontAddValue() { Map aMap = mock(Map.class); Metadata aReferenceMetadata = mock(Metadata.class); Metadata theReferencedMetadata = mock(Metadata.class); doReturn(theReferencedMetadata).when(services).getDependentMetadataFromDependency(any(ReferenceDependency.class), eq(otherRecord)); when(aReferenceDependency.isRequired()).thenReturn(true); doReturn(aReferenceMetadata).when(services).getMetadataFromDependency(record, aReferenceDependency); doReturn("otherRecordId").when(record).get(aReferenceMetadata); when(recordProvider.getRecord("otherRecordId")).thenReturn(otherRecord); assertThat(services.addValueForReferenceDependency(record, recordProvider, aMap, aReferenceDependency, options)) .isFalse(); verify(aMap, never()).put(any(Dependency.class), anyObject()); } @SuppressWarnings({ "rawtypes", "unchecked" }) @Test public void givenReferenceDependencyRequiredAndNotNullWhenGettingValueThenReturnTrueAndAddValue() { Map aMap = mock(Map.class); Metadata aReferenceMetadata = mock(Metadata.class); Metadata theReferencedMetadata = mock(Metadata.class); when(aReferenceDependency.isRequired()).thenReturn(true); doReturn(aReferenceMetadata).when(services).getMetadataFromDependency(record, aReferenceDependency); doReturn(theReferencedMetadata).when(services).getDependentMetadataFromDependency(any(ReferenceDependency.class), eq(otherRecord)); doReturn("otherRecordId").when(record).get(aReferenceMetadata); when(recordProvider.getRecord("otherRecordId")).thenReturn(otherRecord); doReturn("aValue").when(otherRecord).get(theReferencedMetadata); assertThat(services.addValueForReferenceDependency(record, recordProvider, aMap, aReferenceDependency, options)).isTrue(); verify(aMap).put(aReferenceDependency, "aValue"); } @SuppressWarnings({ "unchecked", "rawtypes" }) private void configureCalculatorDependencies() { dependencies = new ArrayList(); dependencies.add(aLocalDependency); dependencies.add(anotherLocalDependency); dependencies.add(aReferenceDependency); } }