/* * Copyright (c) 2017 OBiBa. All rights reserved. * * This program and the accompanying materials * are made available under the terms of the GNU Public License v3.0. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package org.obiba.magma.datasource.mongodb; import java.io.IOException; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.TreeSet; import javax.annotation.Nullable; import org.junit.After; import org.junit.Assume; import org.junit.Before; import org.junit.Ignore; import org.junit.Test; import org.obiba.core.util.FileUtil; import org.obiba.magma.Datasource; import org.obiba.magma.DatasourceFactory; import org.obiba.magma.MagmaEngine; import org.obiba.magma.NoSuchVariableException; import org.obiba.magma.Value; import org.obiba.magma.ValueSequence; import org.obiba.magma.ValueSet; import org.obiba.magma.ValueTable; import org.obiba.magma.ValueTableWriter; import org.obiba.magma.ValueType; import org.obiba.magma.Variable; import org.obiba.magma.VariableEntity; import org.obiba.magma.VariableValueSource; import org.obiba.magma.datasource.fs.FsDatasource; import org.obiba.magma.datasource.generated.GeneratedValueTable; import org.obiba.magma.support.DatasourceCopier; import org.obiba.magma.support.Initialisables; import org.obiba.magma.support.VariableEntityBean; import org.obiba.magma.test.EmbeddedMongoProcessWrapper; import org.obiba.magma.type.BinaryType; import org.obiba.magma.type.BooleanType; import org.obiba.magma.type.DateTimeType; import org.obiba.magma.type.DateType; import org.obiba.magma.type.DecimalType; import org.obiba.magma.type.IntegerType; import org.obiba.magma.type.LineStringType; import org.obiba.magma.type.LocaleType; import org.obiba.magma.type.PointType; import org.obiba.magma.type.PolygonType; import org.obiba.magma.type.TextType; import org.obiba.magma.xstream.MagmaXStreamExtension; import com.google.common.base.Charsets; import com.google.common.base.Function; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; import com.google.common.collect.Lists; import com.google.common.collect.Sets; import static org.fest.assertions.api.Assertions.assertThat; import static org.junit.Assert.fail; @SuppressWarnings({ "UnusedAssignment", "OverlyCoupledClass" }) public class MongoDBDatasourceTest { private static final String DB_TEST = "magma-test"; private static final String TABLE_TEST = "TABLE"; private static final String PARTICIPANT = "Participant"; private static final String ONYX_DATA_5_ZIP = "5-onyx-data.zip"; private String dbUrl; private EmbeddedMongoProcessWrapper mongo; @Before public void before() { // run test only if MongoDB is running Assume.assumeTrue(setupMongoDB()); } private boolean setupMongoDB() { try { mongo = new EmbeddedMongoProcessWrapper(); mongo.start(); dbUrl = "mongodb://" + mongo.getServerSocketAddress() + '/' + DB_TEST; new MagmaEngine().extend(new MagmaXStreamExtension()); return true; } catch(Exception e) { return false; } } @After public void after() { MagmaEngine.get().shutdown(); mongo.stop(); } @Test public void test_writers() throws IOException { FsDatasource onyx = new FsDatasource("onyx", FileUtil.getFileFromResource(ONYX_DATA_5_ZIP)); DatasourceFactory factory = new MongoDBDatasourceFactory("ds-" + DB_TEST, dbUrl); Datasource ds = factory.create(); Initialisables.initialise(ds, onyx); DatasourceCopier.Builder.newCopier().build().copy(onyx, ds); assertThat(ds.getValueTable("AnkleBrachial").getVariableEntities()).hasSize(5); assertThat(ds.getValueTable("AnkleBrachial").getVariables()).hasSize(21); } @Test public void test_restart_magma() throws IOException { FsDatasource onyx = new FsDatasource("onyx", FileUtil.getFileFromResource(ONYX_DATA_5_ZIP)); Datasource datasource1 = new MongoDBDatasourceFactory("ds-" + DB_TEST, dbUrl).create(); Initialisables.initialise(datasource1, onyx); DatasourceCopier.Builder.newCopier().build().copy(onyx, datasource1); Datasource datasource2 = new MongoDBDatasourceFactory("ds-" + DB_TEST, dbUrl).create(); Initialisables.initialise(datasource2); ValueTable valueTable = datasource2.getValueTable("AnkleBrachial"); assertThat(valueTable.getVariableEntities()).hasSize(5); assertThat(valueTable.getVariables()).hasSize(21); assertThat(valueTable.getVariableCount()).isEqualTo(21); } @Test public void test_integer_sequence_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, IntegerType.get().sequenceOf("1,2,3,4")); testWriteReadValue(ds, id++, IntegerType.get().nullSequence()); } @Test public void test_text_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, TextType.get().valueOf("Il était déjà mort...")); testWriteReadValue(ds, id++, TextType.get().valueOf("!@#$%^&*()_+{}\":\\`~")); testWriteReadValue(ds, id++, TextType.get().nullValue()); } @Test public void test_integer_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, IntegerType.get().valueOf("1")); testWriteReadValue(ds, id++, IntegerType.get().valueOf("-1")); testWriteReadValue(ds, id++, IntegerType.get().nullValue()); testWriteReadValue(ds, id++, IntegerType.get().valueOf(Long.MAX_VALUE)); testWriteReadValue(ds, id++, IntegerType.get().valueOf(Long.MIN_VALUE)); } @Test public void test_decimal_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, DecimalType.get().valueOf("1.2")); testWriteReadValue(ds, id++, DecimalType.get().valueOf("-1.2")); testWriteReadValue(ds, id++, DecimalType.get().nullValue()); testWriteReadValue(ds, id++, DecimalType.get().valueOf(Double.MAX_VALUE)); testWriteReadValue(ds, id++, DecimalType.get().valueOf(Double.MIN_VALUE)); } @Test public void test_date_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, DateType.get().valueOf("1973-01-15")); testWriteReadValue(ds, id++, DateType.get().nullValue()); } @Test public void test_dateTime_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, DateTimeType.get().valueOf("1973-01-15 11:03:14")); testWriteReadValue(ds, id++, DateTimeType.get().nullValue()); } @Test public void test_boolean_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, BooleanType.get().valueOf(true)); testWriteReadValue(ds, id++, BooleanType.get().valueOf(false)); testWriteReadValue(ds, id++, BooleanType.get().nullValue()); } @Test public void test_locale_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, LocaleType.get().valueOf("ca_FR")); testWriteReadValue(ds, id++, LocaleType.get().valueOf("fr")); testWriteReadValue(ds, id++, LocaleType.get().nullValue()); } @Test public void test_binary_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, BinaryType.get().valueOf("coucou".getBytes(Charsets.UTF_8))); testWriteReadValue(ds, id++, BinaryType.get().valueOf(new byte[2])); testWriteReadValue(ds, id++, BinaryType.get().nullValue()); } @Test public void test_binary_sequence_writer() throws Exception { Datasource ds = createDatasource(); Collection<Value> sequence = Lists.newArrayList(); sequence.add(BinaryType.get().valueOf("coucou".getBytes(Charsets.UTF_8))); sequence.add(BinaryType.get().nullValue()); sequence.add(BinaryType.get().valueOf(new byte[2])); testWriteReadValue(ds, 1, BinaryType.get().sequenceOf(sequence)); } @Test public void test_point_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, PointType.get().valueOf("[30.1,40.2]")); testWriteReadValue(ds, id++, PointType.get().nullValue()); } @Test public void test_line_string_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, LineStringType.get().valueOf("[[30.1,40.2],[21.3,44.55]]")); testWriteReadValue(ds, id++, LineStringType.get().nullValue()); } @Test public void test_polygon_writer() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, PolygonType.get().valueOf("[[[30.1,40.2],[21.3,44.55],[30.1,40.2]]]")); testWriteReadValue(ds, id++, PolygonType.get().valueOf("[[[30.1,40.2],[21.3,44.55],[30.1,40.2]],[[1.1,2.2],[3.3,4.4],[1.1,2.2]]]")); testWriteReadValue(ds, id++, PolygonType.get().nullValue()); } @Test public void test_batch_writer() throws Exception { Datasource ds = createDatasource(); ((MongoDBDatasource)ds).setBatchSize(1000); Variable variable = Variable.Builder.newVariable("BATCHTEST", TextType.get(), "Participant").repeatable(false) .build(); try(ValueTableWriter tableWriter = ds.createWriter(TABLE_TEST, variable.getEntityType())) { try(ValueTableWriter.VariableWriter variableWriter = tableWriter.writeVariables()) { variableWriter.writeVariable(variable); } } try(ValueTableWriter tableWriter = ds.createWriter(TABLE_TEST, variable.getEntityType())) { for(int i = 0; i < 999; i++) { VariableEntity entity = new VariableEntityBean("Participant", Integer.toString(i)); try(ValueTableWriter.ValueSetWriter valueSetWriter = tableWriter.writeValueSet(entity)) { valueSetWriter.writeValue(variable, TextType.get().valueOf("test value " + i)); } } } } @Test public void test_batch_writer_replace_existing() throws Exception { Datasource ds = createDatasource(); ((MongoDBDatasource) ds).setBatchSize(1000); Variable variable = Variable.Builder.newVariable("BATCHTEST", TextType.get(), PARTICIPANT).repeatable(false) .build(); try(ValueTableWriter tableWriter = ds.createWriter(TABLE_TEST, variable.getEntityType())) { try(ValueTableWriter.VariableWriter variableWriter = tableWriter.writeVariables()) { variableWriter.writeVariable(variable); } } try(ValueTableWriter tableWriter = ds.createWriter(TABLE_TEST, variable.getEntityType())) { for(int i = 0; i < 999; i++) { VariableEntity entity = new VariableEntityBean(PARTICIPANT, Integer.toString(i)); try(ValueTableWriter.ValueSetWriter valueSetWriter = tableWriter.writeValueSet(entity)) { valueSetWriter.writeValue(variable, TextType.get().valueOf("test value " + i)); } } for(int i = 0; i < 999; i++) { //replace existing documents VariableEntity entity = new VariableEntityBean(PARTICIPANT, Integer.toString(i)); try(ValueTableWriter.ValueSetWriter valueSetWriter = tableWriter.writeValueSet(entity)) { valueSetWriter.writeValue(variable, TextType.get().valueOf("new test value " + i)); } } } ValueTable table = ds.getValueTable(TABLE_TEST); ValueSet valueSet = table.getValueSet(new VariableEntityBean(PARTICIPANT, "1")); Value value = table.getValue(variable, valueSet); assertThat(value.getValue()).isEqualTo("new test value 1"); } @SuppressWarnings({ "OverlyLongMethod", "PMD.NcssMethodCount" }) @Test public void test_remove_variable() throws Exception { Datasource ds = prepareDatasource(); ValueTable table = ds.getValueTable(TABLE_TEST); ValueSet valueSet = table.getValueSet(new VariableEntityBean(PARTICIPANT, "1")); Variable textVariable = table.getVariable(generateVariableName(BinaryType.get())); assertThat(table.getVariables()).hasSize(2); assertThat(textVariable).isNotNull(); assertThat(table.getVariable(generateVariableName(IntegerType.get()))).isNotNull(); Value tableLastUpdate = table.getTimestamps().getLastUpdate(); Value valueSetLastUpdate = valueSet.getTimestamps().getLastUpdate(); ValueTableWriter.VariableWriter variableWriter = ds.createWriter(TABLE_TEST, PARTICIPANT).writeVariables(); variableWriter.removeVariable(textVariable); int tableLastUpdateCompare = ds.getValueTable(TABLE_TEST).getTimestamps().getLastUpdate() .compareTo(tableLastUpdate); assertThat(tableLastUpdateCompare).isGreaterThan(0); int valueSetLastUpdateCompare = table.getValueSet(new VariableEntityBean(PARTICIPANT, "1")).getTimestamps() .getLastUpdate().compareTo(valueSetLastUpdate); assertThat(valueSetLastUpdateCompare).isGreaterThan(0); try { table.getVariable(textVariable.getName()); fail("Should throw NoSuchVariableException"); } catch(NoSuchVariableException e) { } //TODO check in mongo that values were removed } @Test public void test_remove_valueset() throws Exception { Datasource ds = prepareDatasource(); VariableEntity oneEntity = new VariableEntityBean(PARTICIPANT, "1"); ValueTable table = ds.getValueTable(TABLE_TEST); assertThat(table.hasValueSet(oneEntity)).isTrue(); Value tableLastUpdate = table.getTimestamps().getLastUpdate(); ValueTableWriter.ValueSetWriter valueSetWriter = ds.createWriter(TABLE_TEST, PARTICIPANT).writeValueSet(oneEntity); valueSetWriter.remove(); valueSetWriter.close(); int tableLastUpdateCompare = ds.getValueTable(TABLE_TEST).getTimestamps().getLastUpdate() .compareTo(tableLastUpdate); assertThat(tableLastUpdateCompare).isGreaterThan(0); assertThat(table.hasValueSet(oneEntity)).isFalse(); //TODO check in mongo that values were removed } @Test public void test_remove_all_valuesets() throws Exception { Datasource ds = prepareDatasource(); VariableEntity oneEntity = new VariableEntityBean(PARTICIPANT, "1"); ValueTable table = ds.getValueTable(TABLE_TEST); assertThat(table.hasValueSet(oneEntity)).isTrue(); assertThat(table.canDropValueSets()).isTrue(); Value tableLastUpdate = table.getTimestamps().getLastUpdate(); table.dropValueSets(); int tableLastUpdateCompare = ds.getValueTable(TABLE_TEST).getTimestamps().getLastUpdate() .compareTo(tableLastUpdate); assertThat(tableLastUpdateCompare).isGreaterThan(0); assertThat(table.hasValueSet(oneEntity)).isFalse(); //TODO check in mongo that values were removed } private Datasource prepareDatasource() throws Exception { Datasource ds = createDatasource(); int id = 1; testWriteReadValue(ds, id++, BinaryType.get().valueOf("tutu".getBytes(Charsets.UTF_8))); testWriteReadValue(ds, id++, BinaryType.get().valueOf(new byte[2])); testWriteReadValue(ds, id++, BinaryType.get().nullValue()); testWriteReadValue(ds, id++, BinaryType.get().valueOf("toto".getBytes(Charsets.UTF_8))); id = 1; testWriteReadValue(ds, id++, IntegerType.get().valueOf("1")); testWriteReadValue(ds, id++, IntegerType.get().nullValue()); testWriteReadValue(ds, id++, IntegerType.get().valueOf(Long.MAX_VALUE)); testWriteReadValue(ds, id++, IntegerType.get().valueOf(Long.MIN_VALUE)); return ds; } @Test @SuppressWarnings({ "OverlyLongMethod", "PMD.NcssMethodCount" }) public void test_update_variable() throws IOException { Variable variable1 = Variable.Builder.newVariable("Variable to update", IntegerType.get(), PARTICIPANT) // .unit("kg").addCategory("1", "One", false) // .build(); Variable variable2 = Variable.Builder.newVariable("Variable 2", IntegerType.get(), PARTICIPANT) // .addCategory("2", "Two", false) // .build(); Variable variable3 = Variable.Builder.newVariable("Variable 3", IntegerType.get(), PARTICIPANT) // .build(); ImmutableSet<Variable> variables = ImmutableSet.of(variable1, variable2, variable3); Datasource datasource1 = createDatasource(); ValueTable generatedValueTable = new GeneratedValueTable(datasource1, variables, 10); MagmaEngine.get().addDatasource(datasource1); DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable, TABLE_TEST, datasource1); Variable newVariable = Variable.Builder.newVariable("Variable to update", IntegerType.get(), PARTICIPANT) // .unit("g").addCategory("1", "One", false) // .addCategory("2", "Two", false) // .build(); try(ValueTableWriter tableWriter = datasource1.createWriter(TABLE_TEST, PARTICIPANT); ValueTableWriter.VariableWriter variableWriter = tableWriter.writeVariables()) { variableWriter.writeVariable(newVariable); } Datasource datasource2 = createDatasource(); ValueTable table = datasource2.getValueTable(TABLE_TEST); assertThat(table.getVariables()).hasSize(3); Variable variable = table.getVariable("Variable to update"); assertThat(variable.getUnit()).isEqualTo(newVariable.getUnit()); assertThat(variable.getCategories()).hasSize(newVariable.getCategories().size()); List<Variable> foundVariables = Lists.newArrayList(table.getVariables()); assertThat(foundVariables.get(0).getName()).isEqualTo(newVariable.getName()); assertThat(foundVariables.get(1).getName()).isEqualTo(variable2.getName()); assertThat(foundVariables.get(2).getName()).isEqualTo(variable3.getName()); } @Test public void test_count_variables() throws IOException { List<Variable> variables = new ArrayList<>(); for(int i = 0; i < 100; i++) { variables.add(Variable.Builder.newVariable("Variable " + i, IntegerType.get(), PARTICIPANT).build()); } Datasource ds1 = createDatasource(); ValueTable generatedValueTable = new GeneratedValueTable(ds1, variables, 50); MagmaEngine.get().addDatasource(ds1); DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable, "table1", ds1); assertThat(createDatasource().getValueTable("table1").getVariableCount()).isEqualTo(100); } @Test public void test_count_valueSets() throws InterruptedException, IOException { ImmutableSet<Variable> variables = ImmutableSet.of(// Variable.Builder.newVariable("Test Variable", IntegerType.get(), PARTICIPANT).build(), // Variable.Builder.newVariable("Other Variable", DecimalType.get(), PARTICIPANT).build()); Datasource ds = createDatasource(); ValueTable generatedValueTable = new GeneratedValueTable(ds, variables, 100); MagmaEngine.get().addDatasource(ds); DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable, "table1", ds); assertThat(createDatasource().getValueTable("table1").getValueSetCount()).isEqualTo(100); } @Test @Ignore // See http://jira.obiba.org/jira/browse/OPAL-2423 public void test_get_binary_values_as_vector() throws IOException { Variable variable1 = Variable.Builder.newVariable("V1", BinaryType.get(), PARTICIPANT) // .build(); ImmutableSet<Variable> variables = ImmutableSet.of(variable1); Datasource datasource1 = createDatasource(); ValueTable generatedValueTable = new GeneratedValueTable(datasource1, variables, 1); MagmaEngine.get().addDatasource(datasource1); DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable, TABLE_TEST, datasource1); VariableValueSource variableValueSource = MagmaEngine.get().getDatasource(datasource1.getName()) .getValueTable(TABLE_TEST).getVariableValueSource("V1"); assertThat(variableValueSource.getValueType().getName().equals(BinaryType.get().getName())); TreeSet<VariableEntity> entities = Sets.newTreeSet(generatedValueTable.getVariableEntities()); Iterable<Value> values = variableValueSource.asVectorSource().getValues(entities); for(Value value : values) { assertThat(value.getValue()).isNotNull(); assertThat(value.getValueType().getName()).isEqualTo(BinaryType.get().getName()); } } @Test public void test_get_values_null_non_null() throws IOException { Variable variable1 = Variable.Builder.newVariable("V1", BinaryType.get(), PARTICIPANT) // .build(); ImmutableSet<Variable> variables = ImmutableSet.of(variable1); Datasource datasource1 = createDatasource(); ValueTable generatedValueTable = new GeneratedValueTable(datasource1, variables, 1); MagmaEngine.get().addDatasource(datasource1); DatasourceCopier.Builder.newCopier().build().copy(generatedValueTable, TABLE_TEST, datasource1); final ValueTable valueTable = MagmaEngine.get().getDatasource(datasource1.getName()).getValueTable(TABLE_TEST); TreeSet<VariableEntity> entities = Sets.newTreeSet(valueTable.getVariableEntities()); final Variable variable = MagmaEngine.get().getDatasource(datasource1.getName()).getValueTable(TABLE_TEST) .getVariable("V1"); Iterable<Value> values = Iterables.transform(entities, new Function<VariableEntity, Value>() { @Nullable @Override public Value apply(@Nullable VariableEntity input) { ValueSet valueSet = valueTable.getValueSet(input); Value value = valueTable.getValue(variable, valueSet); return value; } }); for(Value value : values) { assertThat(value).isNotNull(); } } private Datasource createDatasource() { DatasourceFactory factory = new MongoDBDatasourceFactory("ds-" + DB_TEST, dbUrl); Datasource ds = factory.create(); Initialisables.initialise(ds); return ds; } private String generateVariableName(ValueType type) { return type.getName().toUpperCase(); } private void testWriteReadValue(Datasource ds, int identifier, Value value) throws Exception { VariableEntity entity = new VariableEntityBean("Participant", Integer.toString(identifier)); Variable variable = Variable.Builder .newVariable(generateVariableName(value.getValueType()), value.getValueType(), entity.getType()) .repeatable(value.isSequence()).build(); writeValue(ds, entity, variable, value); Thread.sleep(10); readValue(ds, entity, variable, value); } private void writeValue(Datasource ds, VariableEntity entity, Variable variable, Value value) { try(ValueTableWriter tableWriter = ds.createWriter(TABLE_TEST, variable.getEntityType())) { try(ValueTableWriter.VariableWriter variableWriter = tableWriter.writeVariables()) { variableWriter.writeVariable(variable); } try(ValueTableWriter.ValueSetWriter valueSetWriter = tableWriter.writeValueSet(entity)) { valueSetWriter.writeValue(variable, value); } } } private void readValue(Datasource ds, VariableEntity entity, Variable variable, Value expected) { ValueTable table = ds.getValueTable(TABLE_TEST); ValueSet valueSet = table.getValueSet(entity); Value value = table.getValue(variable, valueSet); assertThat(table.getEntityType()).isEqualTo(variable.getEntityType()); assertThat(table.getVariable(variable.getName()).getValueType()).isEqualTo(variable.getValueType()); if(expected.isSequence()) { ValueSequence valueSequence = value.asSequence(); ValueSequence expectedSequence = expected.asSequence(); int expectedSize = expectedSequence.getSize(); assertThat(valueSequence.getSize()).isEqualTo(expectedSize); for(int i = 0; i < expectedSize; i++) { Value valueItem = valueSequence.get(i); Value expectedItem = expectedSequence.get(i); assertThat(valueItem.isNull()).isEqualTo(expectedItem.isNull()); assertThat(valueItem.toString()).isEqualTo(expectedItem.toString()); } } else { assertThat(value.isNull()).isEqualTo(expected.isNull()); assertThat(value.toString()).isEqualTo(expected.toString()); } } }