/*
* Copyright 2013 Gordon Burgett and individual contributors
*
* 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.xflatdb.xflat.db;
import java.io.File;
import java.io.IOException;
import java.util.List;
import org.apache.commons.logging.LogFactory;
import org.hamcrest.Description;
import org.hamcrest.Matcher;
import org.hamcrest.Matchers;
import org.hamcrest.TypeSafeMatcher;
import org.jdom2.Document;
import org.jdom2.Element;
import org.jdom2.JDOMException;
import org.jdom2.xpath.XPathExpression;
import org.jdom2.xpath.XPathFactory;
import static org.junit.Assert.*;
import org.junit.BeforeClass;
import org.junit.Test;
import org.xflatdb.xflat.Cursor;
import org.xflatdb.xflat.KeyValueTable;
import org.xflatdb.xflat.Table;
import org.xflatdb.xflat.TableConfig;
import org.xflatdb.xflat.XFlatException;
import org.xflatdb.xflat.query.XPathQuery;
import org.xflatdb.xflat.util.DocumentFileWrapper;
import test.Baz;
import test.Foo;
import test.Utils;
/**
*
* @author gordon
*/
public class DatabaseIntegrationTest {
static File workspace = new File("integrationtests");
@BeforeClass
public static void setUpClass(){
if(workspace.exists()){
Utils.deleteDir(workspace);
}
}
private Document loadTableDoc(String testName, String tableName) throws IOException, JDOMException {
LogFactory.getLog(getClass())
.trace(String.format("getting table doc %s", tableName));
File doc = new File(new File(workspace, testName), tableName + ".xml");
return new DocumentFileWrapper(doc).readFile();
}
private XFlatDatabase getDatabase(String testName){
File dbDir = new File(workspace, testName);
XFlatDatabase ret = new XFlatDatabase(dbDir);
return ret;
}
@Test
public void testInsertAndRetrieve_Foo() throws Exception {
System.out.println("testInsertAndRetrieve_Foo");
XFlatDatabase db = getDatabase("InsertAndRetrieveFoo");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.initialize();
try{
Table<Foo> fooTable = db.getTable(Foo.class);
Foo f = new Foo();
f.fooInt = 26;
fooTable.insert(f);
Foo f2 = fooTable.find(f.getId());
assertEquals("Should retrieve equal foo", f, f2);
assertNotSame("Should not be the same foo", f, f2);
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("InsertAndRetrieveFoo", "Foo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 1 row on disk", 1, rows.size());
assertEquals("Should have right data", "26",
rows.get(0).getChild("foo").getChild("fooInt").getText());
}//end testInsertAndRetrieve_Foo
@Test
public void testInsertAndDelete_Foo() throws Exception {
System.out.println("testInsertAndDelete_Foo");
XFlatDatabase db = getDatabase("InsertAndDeleteFoo");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.initialize();
try{
Table<Foo> fooTable = db.getTable(Foo.class);
Foo f = new Foo();
f.fooInt = 26;
fooTable.insert(f);
Foo f2 = fooTable.find(f.getId());
assertEquals("Should retrieve equal foo", f, f2);
fooTable.delete(f.getId());
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("InsertAndDeleteFoo", "Foo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have no rows on disk", 0, rows.size());
}//end testInsertAndDelete_Foo
@Test
public void testInsertMany_QueriesAll() throws Exception {
System.out.println("testInsertMany_QueriesAll");
XFlatDatabase db = getDatabase("InsertMany_QueriesAll");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.configureTable("Foo", new TableConfig()
.withIdGenerator(BigIntIdGenerator.class));
db.initialize();
try{
Table<Foo> fooTable = db.getTable(Foo.class);
for(int i = 0; i < 500; i++){
Foo f = new Foo();
f.fooInt = i;
fooTable.insert(f);
}
XPathExpression<Object> expression = XPathFactory.instance().compile("foo/fooInt");
try(Cursor<Foo> fooCursor = fooTable.find(XPathQuery.gte(expression, 100))){
int i = 100;
for(Foo f2 : fooCursor){
assertThat("Expected items 100 to 499", f2.fooInt,
Matchers.allOf(Matchers.greaterThanOrEqualTo(100), Matchers.lessThan(500)));
assertThat("Expected to use integer ID generator", Integer.parseInt(f2.getId()),
Matchers.allOf(Matchers.greaterThan(0), Matchers.lessThanOrEqualTo(500)));
i++;
}
assertEquals("Expected to retrieve 400 items", 500, i);
}
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("InsertMany_QueriesAll", "Foo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 500 rows on disk", 500, rows.size());
}//end testInsertMany_QueriesAll
@Test
public void testInsertMany_DeleteMatching() throws Exception {
System.out.println("testInsertMany_DeleteMatching");
XFlatDatabase db = getDatabase("InsertMany_DeleteMatching");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.configureTable("Foo", new TableConfig()
.withIdGenerator(BigIntIdGenerator.class));
db.initialize();
XPathQuery query;
try{
Table<Foo> fooTable = db.getTable(Foo.class);
for(int i = 0; i < 500; i++){
Foo f = new Foo();
f.fooInt = i;
fooTable.insert(f);
}
XPathExpression<Object> expression = XPathFactory.instance().compile("foo/fooInt");
query = XPathQuery.or(
XPathQuery.lt(expression, 100),
XPathQuery.eq(expression, 175),
XPathQuery.and(
XPathQuery.gt(expression, 400),
XPathQuery.matches(expression, isEven(), Integer.class)
)
);
int rowsDeleted = fooTable.deleteAll(query);
//100 for lt(100)
//of [401-499], only the even ones. That is, 49
//1 for eq(175)
assertEquals("Expected 150 rows deleted", 150, rowsDeleted);
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("InsertMany_DeleteMatching", "Foo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 350 rows on disk", 350, rows.size());
Matcher<Element> rowMatcher = query.getRowMatcher();
for(Element row : rows){
assertThat("Should not have any matching rows",
row, Matchers.not(rowMatcher));
}
}//end testInsertMany_DeleteMatching
@Test
public void testInsert_Resume_Read() throws Exception {
System.out.println("testInsert_Resume_Read");
XFlatDatabase db = getDatabase("Insert_Resume_Read");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.initialize();
Foo f = new Foo();
f.fooInt = 84;
try{
Table<Foo> fooTable = db.getTable(Foo.class);
fooTable.insert(f);
}
finally{
db.shutdown();
}
db = getDatabase("Insert_Resume_Read");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.initialize();
try{
Table<Foo> fooTable = db.getTable(Foo.class);
Foo f2 = fooTable.find(f.getId());
assertEquals("Should be able to read data", f.fooInt, f2.fooInt);
assertNotSame("Should be different instance", f, f2);
}
finally{
db.shutdown();
}
}//end testInsert_Resume_Read
@Test
public void testInsert_Resume_ValidatesConfig() throws Exception {
System.out.println("testInsert_Resume_ValidatesConfig");
XFlatDatabase db = getDatabase("Insert_Resume_Validate");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
db.configureTable("Foo", new TableConfig()
.withIdGenerator(TimestampIdGenerator.class));
db.initialize();
Foo f = new Foo();
f.fooInt = 84;
try{
Table<Foo> fooTable = db.getTable(Foo.class);
fooTable.insert(f);
}
finally{
db.shutdown();
}
db = getDatabase("Insert_Resume_Validate");
db.getConversionService().addConverter(Foo.class, Element.class, new Foo.ToElementConverter());
db.getConversionService().addConverter(Element.class, Foo.class, new Foo.FromElementConverter());
//use a different ID generator
db.configureTable("Foo", new TableConfig()
.withIdGenerator(BigIntIdGenerator.class));
boolean didThrow = false;
try {
//ACT
db.initialize();
} catch (XFlatException expected) {
didThrow = true;
}
assertTrue("Should have thrown XflatException", didThrow);
}//end testInsert_Resume_ValidatesConfig
@Test
public void testInsert_Baz_UsesJaxbConversionService() throws Exception {
System.out.println("testInsert_Baz_UsesJaxbConversionService");
XFlatDatabase db = getDatabase("Insert_Baz_UsesJaxb");
db.initialize();
try{
Table<Baz> bazTable = db.getTable(Baz.class);
Baz b = new Baz();
b.setAttrInt(81);
b.getTestData().add("test data 1");
b.getTestData().add("test data 2");
b.getTestData().add("test data 3");
bazTable.insert(b);
Baz b2 = bazTable.find(b.getId());
assertNotSame("Should not be the same baz", b, b2);
assertEquals("should be same data", b.getAttrInt(), b2.getAttrInt());
assertThat("should be same data", b2.getTestData(),
Matchers.contains("test data 1", "test data 2", "test data 3"));
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("Insert_Baz_UsesJaxb", "Baz");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 1 row on disk", 1, rows.size());
assertEquals("Should have right data", "81",
rows.get(0).getChild("baz").getAttributeValue("attrInt"));
}//end testInsert_Baz_UsesJaxbConversionService
//<editor-fold desc="key value pair table">
@Test
public void testAdd_Baz_NewBazAdded() throws Exception {
System.out.println("testAdd_Baz_NewBazAdded");
XFlatDatabase db = getDatabase("Add_Baz_NewBazAdded");
db.initialize();
try{
KeyValueTable table = db.getKeyValueTable("kvFoo");
Baz b = new Baz();
b.setAttrInt(81);
b.getTestData().add("test data 1");
b.getTestData().add("test data 2");
b.getTestData().add("test data 3");
table.add("test", b);
Baz b2 = table.get("test", Baz.class);
assertNotSame("Should not be the same baz", b, b2);
assertEquals("should be same data", b.getAttrInt(), b2.getAttrInt());
assertThat("should be same data", b2.getTestData(),
Matchers.contains("test data 1", "test data 2", "test data 3"));
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("Add_Baz_NewBazAdded", "kvFoo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 1 row on disk", 1, rows.size());
assertEquals("Should have right data", "81",
rows.get(0).getChild("baz").getAttributeValue("attrInt"));
}
@Test
public void testSet_Baz_ReplacesOld() throws Exception {
System.out.println("testSet_Baz_ReplacesOld");
XFlatDatabase db = getDatabase("Set_Baz_ReplacesOld");
db.initialize();
try{
KeyValueTable table = db.getKeyValueTable("kvFoo");
Baz b = new Baz();
b.setAttrInt(81);
b.getTestData().add("test data 1");
b.getTestData().add("test data 2");
b.getTestData().add("test data 3");
table.add("test", b);
Baz b2 = new Baz();
b2.setAttrInt(82);
b2.getTestData().add("test data 4");
table.set("test", b2);
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("Set_Baz_ReplacesOld", "kvFoo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 1 row on disk", 1, rows.size());
assertEquals("Should have new data on disk", "82",
rows.get(0).getChild("baz").getAttributeValue("attrInt"));
}
@Test
public void testPut_Baz_ReplacesOld() throws Exception {
System.out.println("testPut_Baz_ReplacesOld");
XFlatDatabase db = getDatabase("Put_Baz_ReplacesOld");
db.initialize();
try{
KeyValueTable table = db.getKeyValueTable("kvFoo");
Baz b = new Baz();
b.setAttrInt(81);
b.getTestData().add("test data 1");
b.getTestData().add("test data 2");
b.getTestData().add("test data 3");
table.add("test", b);
Baz b2 = new Baz();
b2.setAttrInt(82);
b2.getTestData().add("test data 4");
Baz bOld = table.put("test", b2);
assertEquals("should return old data", b.getAttrInt(), bOld.getAttrInt());
assertThat("should return old data", bOld.getTestData(),
Matchers.contains("test data 1", "test data 2", "test data 3"));
}
finally{
db.shutdown();
}
Document tableDoc = this.loadTableDoc("Put_Baz_ReplacesOld", "kvFoo");
List<Element> rows = Utils.getRows(tableDoc);
assertEquals("Should have 1 row on disk", 1, rows.size());
assertEquals("Should have new data on disk", "82",
rows.get(0).getChild("baz").getAttributeValue("attrInt"));
}
//</editor-fold>
private Matcher<Integer> isEven(){
return new TypeSafeMatcher<Integer>(){
@Override
protected boolean matchesSafely(Integer item) {
return item % 2 == 0;
}
@Override
public void describeTo(Description description) {
description.appendText("an even integer");
}
};
}
}