package org.jabref.shared;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import org.jabref.model.entry.BibEntry;
import org.jabref.shared.exception.InvalidDBMSConnectionPropertiesException;
import org.jabref.shared.exception.OfflineLockException;
import org.jabref.testutils.category.DatabaseTests;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.junit.experimental.categories.Category;
import org.junit.runner.RunWith;
import org.junit.runners.Parameterized;
import org.junit.runners.Parameterized.Parameter;
import org.junit.runners.Parameterized.Parameters;
@RunWith(Parameterized.class)
@Category(DatabaseTests.class)
public class DBMSProcessorTest {
private DBMSConnection dbmsConnection;
private DBMSProcessor dbmsProcessor;
@Parameter
public DBMSType dbmsType;
@Before
public void setUp() throws SQLException, InvalidDBMSConnectionPropertiesException {
dbmsConnection = TestConnector.getTestDBMSConnection(dbmsType);
dbmsProcessor = DBMSProcessor.getProcessorInstance(dbmsConnection);
dbmsProcessor.setupSharedDatabase();
}
@Parameters(name = "Test with {0} database system")
public static Collection<DBMSType> getTestingDatabaseSystems() {
return TestManager.getDBMSTypeTestParameter();
}
@Test
public void testCheckBaseIntegrity() throws SQLException {
Assert.assertTrue(dbmsProcessor.checkBaseIntegrity());
clear();
Assert.assertFalse(dbmsProcessor.checkBaseIntegrity());
}
@Test
public void testSetUpSharedDatabase() throws SQLException {
clear();
dbmsProcessor.setupSharedDatabase();
Assert.assertTrue(dbmsProcessor.checkBaseIntegrity());
}
@Test
public void testInsertEntry() throws SQLException {
BibEntry expectedEntry = getBibEntryExample();
dbmsProcessor.insertEntry(expectedEntry);
BibEntry emptyEntry = new BibEntry();
emptyEntry.getSharedBibEntryData().setSharedID(1);
dbmsProcessor.insertEntry(emptyEntry); // does not insert, due to same sharedID.
Map<String, String> actualFieldMap = new HashMap<>();
try (ResultSet entryResultSet = selectFrom("ENTRY")) {
Assert.assertTrue(entryResultSet.next());
Assert.assertEquals(1, entryResultSet.getInt("SHARED_ID"));
Assert.assertEquals("inproceedings", entryResultSet.getString("TYPE"));
Assert.assertEquals(1, entryResultSet.getInt("VERSION"));
Assert.assertFalse(entryResultSet.next());
try (ResultSet fieldResultSet = selectFrom("FIELD")) {
while (fieldResultSet.next()) {
actualFieldMap.put(fieldResultSet.getString("NAME"), fieldResultSet.getString("VALUE"));
}
}
}
Map<String, String> expectedFieldMap = expectedEntry.getFieldMap();
Assert.assertEquals(expectedFieldMap, actualFieldMap);
}
@Test
public void testUpdateEntry() throws OfflineLockException, SQLException {
BibEntry expectedEntry = getBibEntryExample();
dbmsProcessor.insertEntry(expectedEntry);
expectedEntry.setType("book");
expectedEntry.setField("author", "Michael J and Hutchings");
expectedEntry.setField("customField", "custom value");
expectedEntry.clearField("booktitle");
dbmsProcessor.updateEntry(expectedEntry);
Optional<BibEntry> actualEntryOptional = dbmsProcessor
.getSharedEntry(expectedEntry.getSharedBibEntryData().getSharedID());
if (actualEntryOptional.isPresent()) {
Assert.assertEquals(expectedEntry, actualEntryOptional.get());
} else {
Assert.fail();
}
}
@Test(expected = OfflineLockException.class)
public void testUpdateNewerEntry() throws OfflineLockException, SQLException {
BibEntry bibEntry = getBibEntryExample();
dbmsProcessor.insertEntry(bibEntry);
bibEntry.getSharedBibEntryData().setVersion(0); // simulate older version
bibEntry.setField("year", "1993");
dbmsProcessor.updateEntry(bibEntry);
}
@Test
public void testUpdateEqualEntry() throws OfflineLockException, SQLException {
BibEntry expectedBibEntry = getBibEntryExample();
dbmsProcessor.insertEntry(expectedBibEntry);
expectedBibEntry.getSharedBibEntryData().setVersion(0); // simulate older version
dbmsProcessor.updateEntry(expectedBibEntry);
Optional<BibEntry> actualBibEntryOptional = dbmsProcessor
.getSharedEntry(expectedBibEntry.getSharedBibEntryData().getSharedID());
if (actualBibEntryOptional.isPresent()) {
Assert.assertEquals(expectedBibEntry, actualBibEntryOptional.get());
} else {
Assert.fail();
}
}
@Test
public void testRemoveEntry() throws SQLException {
BibEntry bibEntry = getBibEntryExample();
dbmsProcessor.insertEntry(bibEntry);
dbmsProcessor.removeEntry(bibEntry);
try (ResultSet resultSet = selectFrom("ENTRY")) {
Assert.assertFalse(resultSet.next());
}
}
@Test
public void testGetSharedEntries() {
BibEntry bibEntry = getBibEntryExampleWithEmptyFields();
dbmsProcessor.insertEntry(bibEntry);
List<BibEntry> expectedEntries = Arrays.asList(bibEntry);
List<BibEntry> actualEntries = dbmsProcessor.getSharedEntries();
Assert.assertEquals(expectedEntries, actualEntries);
}
@Test
public void testGetSharedEntry() {
BibEntry expectedBibEntry = getBibEntryExampleWithEmptyFields();
dbmsProcessor.insertEntry(expectedBibEntry);
Optional<BibEntry> actualBibEntryOptional = dbmsProcessor
.getSharedEntry(expectedBibEntry.getSharedBibEntryData().getSharedID());
if (actualBibEntryOptional.isPresent()) {
Assert.assertEquals(expectedBibEntry, actualBibEntryOptional.get());
} else {
Assert.fail();
}
}
@Test
public void testGetNotExistingSharedEntry() {
Optional<BibEntry> actualBibEntryOptional = dbmsProcessor.getSharedEntry(1);
Assert.assertFalse(actualBibEntryOptional.isPresent());
}
@Test
public void testGetSharedIDVersionMapping() throws OfflineLockException, SQLException {
BibEntry firstEntry = getBibEntryExample();
BibEntry secondEntry = getBibEntryExample();
dbmsProcessor.insertEntry(firstEntry);
dbmsProcessor.insertEntry(secondEntry);
dbmsProcessor.updateEntry(secondEntry);
Map<Integer, Integer> expectedIDVersionMap = new HashMap<>();
expectedIDVersionMap.put(firstEntry.getSharedBibEntryData().getSharedID(), 1);
expectedIDVersionMap.put(secondEntry.getSharedBibEntryData().getSharedID(), 2);
Map<Integer, Integer> actualIDVersionMap = dbmsProcessor.getSharedIDVersionMapping();
Assert.assertEquals(expectedIDVersionMap, actualIDVersionMap);
}
@Test
public void testGetSharedMetaData() {
insertMetaData("databaseType", "bibtex;");
insertMetaData("protectedFlag", "true;");
insertMetaData("saveActions", "enabled;\nauthor[capitalize,html_to_latex]\ntitle[title_case]\n;");
insertMetaData("saveOrderConfig", "specified;author;false;title;false;year;true;");
Map<String, String> expectedMetaData = getMetaDataExample();
Map<String, String> actualMetaData = dbmsProcessor.getSharedMetaData();
Assert.assertEquals(expectedMetaData, actualMetaData);
}
@Test
public void testSetSharedMetaData() throws SQLException {
Map<String, String> expectedMetaData = getMetaDataExample();
dbmsProcessor.setSharedMetaData(expectedMetaData);
Map<String, String> actualMetaData = dbmsProcessor.getSharedMetaData();
Assert.assertEquals(expectedMetaData, actualMetaData);
}
private Map<String, String> getMetaDataExample() {
Map<String, String> expectedMetaData = new HashMap<>();
expectedMetaData.put("databaseType", "bibtex;");
expectedMetaData.put("protectedFlag", "true;");
expectedMetaData.put("saveActions", "enabled;\nauthor[capitalize,html_to_latex]\ntitle[title_case]\n;");
expectedMetaData.put("saveOrderConfig", "specified;author;false;title;false;year;true;");
return expectedMetaData;
}
private BibEntry getBibEntryExampleWithEmptyFields() {
BibEntry bibEntry = new BibEntry();
bibEntry.setField("author", "Author");
bibEntry.setField("title", "");
bibEntry.setField("year", "");
bibEntry.getSharedBibEntryData().setSharedID(1);
return bibEntry;
}
private BibEntry getBibEntryExample() {
BibEntry bibEntry = new BibEntry();
bibEntry.setType("inproceedings");
bibEntry.setField("author", "Wirthlin, Michael J and Hutchings, Brad L and Gilson, Kent L");
bibEntry.setField("title", "The nano processor: a low resource reconfigurable processor");
bibEntry.setField("booktitle", "FPGAs for Custom Computing Machines, 1994. Proceedings. IEEE Workshop on");
bibEntry.setField("year", "1994");
bibEntry.setCiteKey("nanoproc1994");
return bibEntry;
}
private ResultSet selectFrom(String table) {
try {
return dbmsConnection.getConnection().createStatement().executeQuery("SELECT * FROM " + escape(table));
} catch (SQLException e) {
Assert.fail(e.getMessage());
return null;
}
}
// Oracle does not support multiple tuple insertion in one INSERT INTO command.
// Therefore this function was defined to improve the readability and to keep the code short.
private void insertMetaData(String key, String value) {
try {
dbmsConnection.getConnection().createStatement().executeUpdate("INSERT INTO " + escape("METADATA") + "("
+ escape("KEY") + ", " + escape("VALUE") + ") VALUES("
+ escapeValue(key) + ", " + escapeValue(value) + ")");
} catch (SQLException e) {
Assert.fail(e.getMessage());
}
}
private String escape(String expression) {
return dbmsProcessor.escape(expression);
}
private String escapeValue(String value) {
return "'" + value + "'";
}
@After
public void clear() throws SQLException {
TestManager.clearTables(dbmsConnection);
}
}