/* * Copyright 2007 T-Rank AS * * 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 no.trank.openpipe.jdbc.store; import junit.framework.TestCase; import no.trank.openpipe.api.document.Document; import no.trank.openpipe.api.document.DocumentOperation; import no.trank.openpipe.api.document.DocumentProducer; import no.trank.openpipe.util.Iterators; import org.apache.ws.jaxme.sqls.hsqldb.HsqlDbSQLFactoryImpl; import org.hsqldb.jdbcDriver; import org.springframework.jdbc.core.simple.ParameterizedRowMapper; import org.springframework.jdbc.core.simple.SimpleJdbcTemplate; import org.springframework.jdbc.datasource.SingleConnectionDataSource; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Timestamp; import java.util.Arrays; import java.util.Iterator; import java.util.List; import static org.easymock.EasyMock.*; /** * @version $Revision$ */ public class StateDocumentProducerTest extends TestCase { private static final String FIELD_ID = "id"; private static final String TABLE_NAME = "doc_store"; private static final String COL_ID_NAME = "id"; private static final int ID_MAX_LENGTH = 256; private static final String COL_UPD_NAME = "updated"; private SingleConnectionDataSource dataSource; private SimpleJdbcTemplate jdbcTemplate; private static final String DOC_ID = "1"; public void testValidateSchemaFailes() throws Exception { jdbcTemplate.getJdbcOperations().execute("CREATE CACHED TABLE " + TABLE_NAME + " (" + COL_ID_NAME + " INTEGER NOT NULL, " + COL_UPD_NAME + " TIMESTAMP NOT NULL, PRIMARY KEY (" + COL_ID_NAME + "))"); { final DocumentProducer mockProd = setUpMockProducer(Iterators.<Document>emptyIterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); try { producer.init(); fail("Init sould fail"); } catch (Exception e) { // Success } } dropTable(); jdbcTemplate.getJdbcOperations().execute("CREATE CACHED TABLE " + TABLE_NAME + " (" + COL_ID_NAME + " VARCHAR(" + (ID_MAX_LENGTH - 1) + ") NOT NULL, " + COL_UPD_NAME + " TIMESTAMP NOT NULL, PRIMARY KEY (" + COL_ID_NAME + "))"); { final DocumentProducer mockProd = setUpMockProducer(Iterators.<Document>emptyIterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); try { producer.init(); fail("Init sould fail"); } catch (Exception e) { // Success } } dropTable(); jdbcTemplate.getJdbcOperations().execute("CREATE CACHED TABLE " + TABLE_NAME + " (" + COL_ID_NAME + " VARCHAR(" + ID_MAX_LENGTH + ") NOT NULL, " + COL_UPD_NAME + " DATE NOT NULL, PRIMARY KEY (" + COL_ID_NAME + "))"); { final DocumentProducer mockProd = setUpMockProducer(Iterators.<Document>emptyIterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); try { producer.init(); fail("Init sould fail"); } catch (Exception e) { // Success } } } public void testValidateSchemaSucceeds() throws Exception { createValidTable(); // Setting up mock final DocumentProducer mockProd = setUpMockProducer(Iterators.<Document>emptyIterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertFalse(it.hasNext()); producer.close(); verify(mockProd); } public void testStoreNewDocuments() throws Exception { // Setting up mock final Document doc = new Document(); doc.setFieldValue(FIELD_ID, DOC_ID); final DocumentProducer mockProd = setUpMockProducer(Arrays.asList(doc).iterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertTrue(it.hasNext()); assertEquals(DocumentOperation.ADD_VALUE, it.next().getOperation()); assertFalse(it.hasNext()); producer.close(); verify(mockProd); final String id = jdbcTemplate.queryForObject(getSelectId(), new StringRowMapper(), (Object[]) null); assertEquals(DOC_ID, id); } public void testStoreNewDocuments_RollbackOnFail() throws Exception { // Setting up mock final Document doc = new Document(); doc.setFieldValue(FIELD_ID, DOC_ID); final DocumentProducer mockProd = setUpMockProducer(Arrays.asList(doc).iterator(), true); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertTrue(it.hasNext()); assertEquals(DocumentOperation.ADD_VALUE, it.next().getOperation()); assertFalse(it.hasNext()); producer.fail(); verify(mockProd); final List<String> id = jdbcTemplate.query(getSelectId(), new StringRowMapper(), (Object[]) null); assertTrue(id.isEmpty()); } public void testUpdateModifiedDocuments() throws Exception { createValidTable(); final Timestamp ts = new Timestamp(System.currentTimeMillis()); Thread.sleep(15); // So test will not fail on Windows jdbcTemplate.update("INSERT INTO " + TABLE_NAME + " (" + COL_ID_NAME + ", " + COL_UPD_NAME + ") VALUES (?, ?)", DOC_ID, ts); // Setting up mock final Document doc = new Document(); doc.setFieldValue(FIELD_ID, DOC_ID); final DocumentProducer mockProd = setUpMockProducer(Arrays.asList(doc).iterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertTrue(it.hasNext()); assertEquals(DocumentOperation.MODIFY_VALUE, it.next().getOperation()); assertFalse(it.hasNext()); producer.close(); verify(mockProd); final String id = jdbcTemplate.queryForObject(getSelectId(), new StringRowMapper(), (Object[]) null); assertEquals(DOC_ID, id); final Timestamp upd = jdbcTemplate.queryForObject(getSelectUpd(), new TimestampRowMapper(), (Object[]) null); assertTrue(upd.after(ts)); } public void testUpdateModifiedDocuments_RollbackOnFail() throws Exception { createValidTable(); final Timestamp ts = new Timestamp(System.currentTimeMillis()); Thread.sleep(15); // So test will not fail on Windows jdbcTemplate.update("INSERT INTO " + TABLE_NAME + " (" + COL_ID_NAME + ", " + COL_UPD_NAME + ") VALUES (?, ?)", DOC_ID, ts); // Setting up mock final Document doc = new Document(); doc.setFieldValue(FIELD_ID, DOC_ID); final DocumentProducer mockProd = setUpMockProducer(Arrays.asList(doc).iterator(), true); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertTrue(it.hasNext()); assertEquals(DocumentOperation.MODIFY_VALUE, it.next().getOperation()); assertFalse(it.hasNext()); producer.fail(); verify(mockProd); final String id = jdbcTemplate.queryForObject(getSelectId(), new StringRowMapper(), (Object[]) null); assertEquals(DOC_ID, id); final Timestamp upd = jdbcTemplate.queryForObject(getSelectUpd(), new TimestampRowMapper(), (Object[]) null); assertTrue(upd.equals(ts)); } public void testDeletedDocuments() throws Exception { createValidTable(); final Timestamp ts = new Timestamp(System.currentTimeMillis()); jdbcTemplate.update("INSERT INTO " + TABLE_NAME + " (" + COL_ID_NAME + ", " + COL_UPD_NAME + ") VALUES (?, ?)", DOC_ID, ts); // Setting up mock final DocumentProducer mockProd = setUpMockProducer(Iterators.<Document>emptyIterator(), false); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertTrue(it.hasNext()); final Document doc = it.next(); assertEquals(DocumentOperation.DELETE_VALUE, doc.getOperation()); assertEquals(DOC_ID, doc.getFieldValue(FIELD_ID)); assertFalse(it.hasNext()); producer.close(); verify(mockProd); final List<String> id = jdbcTemplate.query(getSelectId(), new StringRowMapper(), (Object[]) null); assertTrue(id.isEmpty()); } public void testDeletedDocuments_RollbackOnFail() throws Exception { createValidTable(); final Timestamp ts = new Timestamp(System.currentTimeMillis()); jdbcTemplate.update("INSERT INTO " + TABLE_NAME + " (" + COL_ID_NAME + ", " + COL_UPD_NAME + ") VALUES (?, ?)", DOC_ID, ts); // Setting up mock final DocumentProducer mockProd = setUpMockProducer(Iterators.<Document>emptyIterator(), true); final StateDocumentProducer producer = setupProducer(mockProd); producer.init(); final Iterator<Document> it = producer.iterator(); assertTrue(it.hasNext()); final Document doc = it.next(); assertEquals(DocumentOperation.DELETE_VALUE, doc.getOperation()); assertEquals(DOC_ID, doc.getFieldValue(FIELD_ID)); assertFalse(it.hasNext()); producer.fail(); verify(mockProd); final List<String> id = jdbcTemplate.query(getSelectId(), new StringRowMapper(), (Object[]) null); assertFalse(id.isEmpty()); } private static DocumentProducer setUpMockProducer(Iterator<Document> it, boolean fail) { final DocumentProducer mockProd = createMock(DocumentProducer.class); mockProd.init(); expectLastCall().once(); expect(mockProd.iterator()).andReturn(it).once(); if (fail) { mockProd.fail(); } else { mockProd.close(); } expectLastCall().once(); replay(mockProd); return mockProd; } private void dropTable() { if (jdbcTemplate != null) jdbcTemplate.getJdbcOperations().execute("DROP TABLE " + TABLE_NAME); } private void createValidTable() { jdbcTemplate.getJdbcOperations().execute("CREATE CACHED TABLE " + TABLE_NAME + " (" + COL_ID_NAME + " VARCHAR(" + ID_MAX_LENGTH + ") NOT NULL, " + COL_UPD_NAME + " TIMESTAMP NOT NULL, PRIMARY KEY (" + COL_ID_NAME + "))"); } private static String getSelectId() { return "SELECT " + COL_ID_NAME + " FROM " + TABLE_NAME; } private static String getSelectUpd() { return "SELECT " + COL_UPD_NAME + " FROM " + TABLE_NAME; } private StateDocumentProducer setupProducer(DocumentProducer mockProd) { final StateDocumentProducer producer = new StateDocumentProducer(mockProd); producer.setDataSource(dataSource); producer.setSqlFactory(new HsqlDbSQLFactoryImpl()); producer.setIdFieldName(FIELD_ID); producer.setTableName(TABLE_NAME); producer.setIdColumnName(COL_ID_NAME); producer.setIdMaxLength(ID_MAX_LENGTH); producer.setUpdColumnName(COL_UPD_NAME); return producer; } @Override protected void setUp() throws Exception { dataSource = new SingleConnectionDataSource(); dataSource.setDriverClassName(jdbcDriver.class.getName()); dataSource.setUrl("jdbc:hsqldb:mem:test"); dataSource.setSuppressClose(true); jdbcTemplate = new SimpleJdbcTemplate(dataSource); } @Override protected void tearDown() throws Exception { if (dataSource != null) { dropTable(); dataSource.destroy(); } } private static class TimestampRowMapper implements ParameterizedRowMapper<Timestamp> { @Override public Timestamp mapRow(ResultSet rs, int rowNum) throws SQLException { return rs.getTimestamp(1); } } }