/* * 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; import junit.framework.TestCase; import no.trank.openpipe.api.document.Document; import no.trank.openpipe.api.document.DocumentOperation; import org.hsqldb.jdbcDriver; import org.springframework.jdbc.core.JdbcTemplate; import org.springframework.jdbc.datasource.SingleConnectionDataSource; import java.sql.Timestamp; import java.util.*; /** * @version $Revision$ */ public class SimpleJdbcDocumentProducerTest extends TestCase { private static final String SQL_QUERY = "SELECT text, id FROM documents WHERE status = "; private static final String SQL_PRE = "INSERT INTO status_count (status, s_count) VALUES ("; private static final String SQL_POST = "INSERT INTO status_ts (status, done_ts, processed) VALUES ("; private static final String SQL_FAIL = "INSERT INTO status_ts (status, done_ts, processed) VALUES ("; private static final Map<String, Short> opStatusMap = new HashMap<String, Short>(); private static final short STATUS_ADD = (short) 0; private static final short STATUS_MODIFY = (short) 1; private static final short STATUS_DELETE = (short) 2; private static final int COUNT = 50; private final int[] statusCounts = new int[3]; private JdbcTemplate jdbcTemplate; private SingleConnectionDataSource dataSource; private int nextId; public void testEmptyIterator() throws Exception { final SimpleJdbcDocumentProducer producer = new SimpleJdbcDocumentProducer(); producer.init(); final Iterator<Document> iterator = producer.iterator(); assertNotNull(iterator); assertFalse(iterator.hasNext()); } public void testErrorHandling() throws Exception { setUpDb(); final SimpleJdbcDocumentProducer producer = setUpProducer(); final int[] counts = new int[3]; for (Document doc : producer) { counts[getStatus(doc)]++; } producer.fail(); for (int i = 0; i < counts.length; i++) { assertEquals(statusCounts[i], counts[i]); assertEquals(statusCounts[i], jdbcTemplate.queryForInt("SELECT s_count FROM status_count WHERE status = ?", new Object[]{i})); List<?> resultList = jdbcTemplate.queryForList("SELECT done_ts FROM status_ts WHERE status = ? AND processed = true", new Object[]{i}, Timestamp.class); assertEquals("Fail was called on the producer, postSql should not have been run.", 0, resultList.size()); assertNotNull("Close was called on the producer, postSql should have been run.", jdbcTemplate.queryForObject("SELECT done_ts FROM status_ts WHERE status = ? AND processed = false", new Object[]{i}, Timestamp.class)); } } public void testIterable() throws Exception { setUpDb(); final SimpleJdbcDocumentProducer producer = setUpProducer(); producer.iterator(); try { producer.iterator(); fail("Producer should not allow that the iterator is fetched more than once."); } catch (IllegalStateException e) { // Working as intended } } public void testIterator() throws Exception { setUpDb(); final SimpleJdbcDocumentProducer producer = setUpProducer(); final int[] counts = new int[3]; for (Document doc : producer) { counts[getStatus(doc)]++; } producer.close(); for (int i = 0; i < counts.length; i++) { assertEquals(statusCounts[i], counts[i]); assertEquals(statusCounts[i], jdbcTemplate.queryForInt("SELECT s_count FROM status_count WHERE status = ?", new Object[]{i})); assertNotNull("Close was called on the producer, postSql should have been run.", jdbcTemplate.queryForObject("SELECT done_ts FROM status_ts WHERE status = ? AND processed = true", new Object[]{i}, Timestamp.class)); } } protected SimpleJdbcDocumentProducer setUpProducer() { final SimpleJdbcDocumentProducer producer = new SimpleJdbcDocumentProducer(); producer.setJdbcTemplate(jdbcTemplate); producer.setAddPreSql(Arrays.asList(SQL_PRE + STATUS_ADD + ", " + statusCounts[STATUS_ADD] + ")")); producer.setAddSql(Arrays.asList(SQL_QUERY + STATUS_ADD)); producer.setAddPostSql(Arrays.asList(SQL_POST + STATUS_ADD + ", NOW(), true)")); producer.setAddFailSql(Arrays.asList(SQL_FAIL + STATUS_ADD + ", NOW(), false)")); producer.setModifyPreSql(Arrays.asList(SQL_PRE + STATUS_MODIFY + ", " + statusCounts[STATUS_MODIFY] + ")")); producer.setModifySql(Arrays.asList(SQL_QUERY + STATUS_MODIFY)); producer.setModifyPostSql(Arrays.asList(SQL_POST + STATUS_MODIFY + ", NOW(), true)")); producer.setModifyFailSql(Arrays.asList(SQL_FAIL + STATUS_MODIFY + ", NOW(), false)")); producer.setDeletePreSql(Arrays.asList(SQL_PRE + STATUS_DELETE + ", " + statusCounts[STATUS_DELETE] + ")")); producer.setDeleteSql(Arrays.asList(SQL_QUERY + STATUS_DELETE)); producer.setDeletePostSql(Arrays.asList(SQL_POST + STATUS_DELETE + ", NOW(), true)")); producer.setDeleteFailSql(Arrays.asList(SQL_FAIL + STATUS_DELETE + ", NOW(), false)")); producer.init(); return producer; } private static short getStatus(Document doc) { final Short status = opStatusMap.get(doc.getOperation()); if (status == null) { fail("Unknown opertation: " + doc.getOperation()); } return status; } protected void setUpDb() throws Exception { dataSource = new SingleConnectionDataSource(); dataSource.setDriverClassName(jdbcDriver.class.getName()); dataSource.setUrl("jdbc:hsqldb:mem:test"); dataSource.setSuppressClose(true); jdbcTemplate = new JdbcTemplate(dataSource); jdbcTemplate.execute("CREATE TABLE documents (text VARCHAR(128), id INTEGER PRIMARY KEY, status SMALLINT)"); jdbcTemplate.execute("CREATE TABLE status_count (status SMALLINT PRIMARY KEY, s_count INTEGER)"); jdbcTemplate.execute("CREATE TABLE status_ts (status SMALLINT PRIMARY KEY, done_ts TIMESTAMP, processed BOOLEAN)"); final Random rnd = new Random(); for (int i = 0; i < COUNT; i++) { final int r = rnd.nextInt(100); final short status = r >= 50 ? STATUS_ADD : r < 25 ? STATUS_DELETE : STATUS_MODIFY; statusCounts[status]++; insert(status); } } @Override protected void tearDown() throws Exception { if (dataSource != null) { if (jdbcTemplate != null) { jdbcTemplate.execute("DROP TABLE documents"); jdbcTemplate.execute("DROP TABLE status_count"); jdbcTemplate.execute("DROP TABLE status_ts"); } dataSource.destroy(); } } private void insert(short status) { final int id = nextId++; jdbcTemplate.update("INSERT INTO documents (id, text, status) VALUES (?, ?, ?)", new Object[]{id, "Some text [" + id + "]", status}); } static { opStatusMap.put(DocumentOperation.ADD_VALUE, STATUS_ADD); opStatusMap.put(DocumentOperation.MODIFY_VALUE, STATUS_MODIFY); opStatusMap.put(DocumentOperation.DELETE_VALUE, STATUS_DELETE); } }