# HG changeset patch # User Severin Gehwolf # Date 1374159365 -7200 # Node ID 7690396a1f729e63172e370e078fb92848c61d1d # Parent 03ed49a50413893fb6054204cde3eb163781e6d8 Implement PreparedStatement in WebStorage (Part 2). Reviewed-by: ebaron Review-thread: http://icedtea.classpath.org/pipermail/thermostat/2013-July/007536.html diff -r 03ed49a50413 -r 7690396a1f72 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImplTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementImplTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,483 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.storage.internal.statement; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.util.ArrayList; +import java.util.List; + +import org.junit.After; +import org.junit.Before; +import org.junit.Test; + +import com.redhat.thermostat.common.Pair; +import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.IllegalPatchException; +import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.Query; +import com.redhat.thermostat.storage.core.Query.SortDirection; +import com.redhat.thermostat.storage.model.Pojo; +import com.redhat.thermostat.storage.query.BinaryComparisonExpression; +import com.redhat.thermostat.storage.query.BinaryComparisonOperator; +import com.redhat.thermostat.storage.query.BinaryLogicalExpression; +import com.redhat.thermostat.storage.query.BinaryLogicalOperator; +import com.redhat.thermostat.storage.query.Expression; +import com.redhat.thermostat.storage.query.LiteralExpression; + +public class ParsedStatementImplTest { + + private Query statement; + + @Before + public void setup() { + statement = new TestQuery(); + } + + @After + public void tearDown() { + statement = null; + } + + @Test + public void canPatchWhereAndExpr() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE a = ? AND c = ? + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode leftEqual = new BinaryExpressionNode(and); + BinaryExpressionNode rightEqual = new BinaryExpressionNode(and); + leftEqual.setOperator(BinaryComparisonOperator.EQUALS); + rightEqual.setOperator(BinaryComparisonOperator.EQUALS); + and.setLeftChild(leftEqual); + and.setRightChild(rightEqual); + TerminalNode a = new TerminalNode(leftEqual); + Key aKey = new Key<>("a", false); + a.setValue(aKey); + TerminalNode b = new TerminalNode(leftEqual); + UnfinishedValueNode patchB = new UnfinishedValueNode(); + patchB.setParameterIndex(0); + patchB.setType(String.class); + b.setValue(patchB); + leftEqual.setLeftChild(a); + leftEqual.setRightChild(b); + TerminalNode c = new TerminalNode(rightEqual); + c.setValue("c"); + rightEqual.setLeftChild(c); + TerminalNode d = new TerminalNode(rightEqual); + UnfinishedValueNode dPatch = new UnfinishedValueNode(); + dPatch.setParameterIndex(1); + dPatch.setType(Integer.class); + d.setValue(dPatch); + rightEqual.setRightChild(d); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(2); + preparedStatement.setString(0, "test1"); + preparedStatement.setInt(1, 2); + PreparedParameter[] params = preparedStatement.getParams(); + // finally test the patching + Query query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + Expression expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryLogicalExpression); + BinaryLogicalExpression andFinal = (BinaryLogicalExpression) expectedExpression; + assertEquals(BinaryLogicalOperator.AND, andFinal.getOperator()); + assertTrue(andFinal.getLeftOperand() instanceof BinaryComparisonExpression); + assertTrue(andFinal.getRightOperand() instanceof BinaryComparisonExpression); + BinaryComparisonExpression left = (BinaryComparisonExpression)andFinal.getLeftOperand(); + BinaryComparisonExpression right = (BinaryComparisonExpression)andFinal.getRightOperand(); + assertEquals(BinaryComparisonOperator.EQUALS, left.getOperator()); + assertEquals(BinaryComparisonOperator.EQUALS, right.getOperator()); + assertTrue(left.getLeftOperand() instanceof LiteralExpression); + assertTrue(left.getRightOperand() instanceof LiteralExpression); + LiteralExpression leftLiteral1 = (LiteralExpression)left.getLeftOperand(); + LiteralExpression rightLiteral1 = (LiteralExpression)left.getRightOperand(); + assertEquals(aKey, leftLiteral1.getValue()); + assertEquals("test1", rightLiteral1.getValue()); + LiteralExpression leftLiteral2 = (LiteralExpression)right.getLeftOperand(); + LiteralExpression rightLiteral2 = (LiteralExpression)right.getRightOperand(); + assertEquals("c", leftLiteral2.getValue()); + // right literal value should have been patched to a "d" + assertEquals(2, rightLiteral2.getValue()); + } + + @Test + public void canPatchBasicWhereEquals() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE a = ? + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(and); + a.setValue(new Key<>("a", false)); + TerminalNode b = new TerminalNode(and); + UnfinishedValueNode bPatch = new UnfinishedValueNode(); + bPatch.setParameterIndex(0); + bPatch.setType(Boolean.class); + b.setValue(bPatch); + and.setLeftChild(a); + and.setRightChild(b); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); + preparedStatement.setBoolean(0, true); + PreparedParameter[] params = preparedStatement.getParams(); + // finally test the patching + Query query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + Expression expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + BinaryComparisonExpression root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + LiteralExpression leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + LiteralExpression rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key<>("a", false), leftLiteral1.getValue()); + // this should have gotten patched to a "b" + assertEquals(true, rightLiteral1.getValue()); + // now do it again with a different value + preparedStatement = new PreparedStatementImpl<>(1); + preparedStatement.setBoolean(0, false); + params = preparedStatement.getParams(); + query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + q = (TestQuery)query; + expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key<>("a", false), leftLiteral1.getValue()); + assertEquals(false, rightLiteral1.getValue()); + } + + @Test + public void canPatchBasicWhereEqualsLHSKeyAndRHSValue() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE ?s = ?b + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(and); + UnfinishedValueNode aPatch = new UnfinishedValueNode(); + aPatch.setLHS(true); + aPatch.setType(String.class); + aPatch.setParameterIndex(0); + a.setValue(aPatch); + TerminalNode b = new TerminalNode(and); + UnfinishedValueNode bPatch = new UnfinishedValueNode(); + bPatch.setParameterIndex(1); + bPatch.setType(Boolean.class); + b.setValue(bPatch); + and.setLeftChild(a); + and.setRightChild(b); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(2); + preparedStatement.setString(0, "a"); + preparedStatement.setBoolean(1, true); + PreparedParameter[] params = preparedStatement.getParams(); + // finally test the patching + Query query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + Expression expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + BinaryComparisonExpression root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + LiteralExpression leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + LiteralExpression rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key<>("a", false), leftLiteral1.getValue()); + // this should have gotten patched to a "b" + assertEquals(true, rightLiteral1.getValue()); + // now do it again with a different value + preparedStatement = new PreparedStatementImpl<>(2); + preparedStatement.setString(0, "a"); + preparedStatement.setBoolean(1, false); + params = preparedStatement.getParams(); + query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + q = (TestQuery)query; + expectedExpression = q.expr; + assertTrue(expectedExpression instanceof BinaryComparisonExpression); + root = (BinaryComparisonExpression)expectedExpression; + assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); + assertTrue(root.getLeftOperand() instanceof LiteralExpression); + assertTrue(root.getRightOperand() instanceof LiteralExpression); + leftLiteral1 = (LiteralExpression)root.getLeftOperand(); + rightLiteral1 = (LiteralExpression)root.getRightOperand(); + assertEquals(new Key<>("a", false), leftLiteral1.getValue()); + assertEquals(false, rightLiteral1.getValue()); + } + + @Test + public void canPatchBasicLimit() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + LimitExpression limitExpnToPatch = new LimitExpression(); + UnfinishedLimitValue unfinished = new UnfinishedLimitValue(); + unfinished.setParameterIndex(0); + limitExpnToPatch.setValue(unfinished); + suffixExpn.setLimitExpn(limitExpnToPatch); + suffixExpn.setSortExpn(null); + suffixExpn.setWhereExpn(null); + parsedStmt.setSuffixExpression(suffixExpn); + // set the value for the one unfinished param + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); + preparedStatement.setInt(0, 3); + PreparedParameter[] params = preparedStatement.getParams(); + // finally test the patching + Query query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + assertEquals(3, q.limitVal); + } + + @Test + public void canPatchBasicSort() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + // SORT ? ASC, b DSC + SortExpression sortExpn = new SortExpression(); + SortMember member = new SortMember(); + member.setDirection(SortDirection.ASCENDING); + UnfinishedSortKey unfinished = new UnfinishedSortKey(); + unfinished.setParameterIndex(0); + member.setSortKey(unfinished); + SortMember member2 = new SortMember(); + member2.setDirection(SortDirection.DESCENDING); + member2.setSortKey("b"); + sortExpn.addMember(member); + sortExpn.addMember(member2); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(sortExpn); + suffixExpn.setWhereExpn(null); + parsedStmt.setSuffixExpression(suffixExpn); + // set the value for the one unfinished param + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); + preparedStatement.setString(0, "a"); + PreparedParameter[] params = preparedStatement.getParams(); + // finally test the patching + Query query = (Query)parsedStmt.patchStatement(params); + assertTrue(query instanceof TestQuery); + TestQuery q = (TestQuery)query; + List, SortDirection>> actualSorts = q.sorts; + assertEquals(2, actualSorts.size()); + Pair, SortDirection> first = actualSorts.get(0); + Key firstKeyActual = (Key)first.getFirst(); + Key expectedFirst = new Key<>("a", false); + assertEquals(expectedFirst, firstKeyActual); + assertEquals(SortDirection.ASCENDING, first.getSecond()); + Pair, SortDirection> second = actualSorts.get(1); + Key secondKeyActual = (Key)second.getFirst(); + Key expectedSecond = new Key<>("b", false); + assertEquals(expectedSecond, secondKeyActual); + assertEquals(SortDirection.DESCENDING, second.getSecond()); + } + + @Test + public void failPatchWithWrongType() throws IllegalPatchException { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE 'a' = ?s AND 'c' = ?i + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryLogicalOperator.AND); + BinaryExpressionNode leftEqual = new BinaryExpressionNode(and); + BinaryExpressionNode rightEqual = new BinaryExpressionNode(and); + leftEqual.setOperator(BinaryComparisonOperator.EQUALS); + rightEqual.setOperator(BinaryComparisonOperator.EQUALS); + and.setLeftChild(leftEqual); + and.setRightChild(rightEqual); + TerminalNode a = new TerminalNode(leftEqual); + Key aKey = new Key<>("a", false); + a.setValue(aKey); + TerminalNode b = new TerminalNode(leftEqual); + UnfinishedValueNode patchB = new UnfinishedValueNode(); + patchB.setType(String.class); + patchB.setParameterIndex(0); + b.setValue(patchB); + leftEqual.setLeftChild(a); + leftEqual.setRightChild(b); + TerminalNode c = new TerminalNode(rightEqual); + Key cKey = new Key<>("c", false); + c.setValue(cKey); + rightEqual.setLeftChild(c); + TerminalNode d = new TerminalNode(rightEqual); + UnfinishedValueNode dPatch = new UnfinishedValueNode(); + dPatch.setParameterIndex(1); + dPatch.setType(Integer.class); + d.setValue(dPatch); + rightEqual.setRightChild(d); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + // next, create the PreparedStatement we are going to use for + // patching. + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(2); + preparedStatement.setString(0, "test1"); + preparedStatement.setString(1, "foo"); + // finally test the patching + try { + PreparedParameter[] params = preparedStatement.getParams(); + parsedStmt.patchStatement(params); + fail("should have failed to patch param 1 with a string value (expected int)"); + } catch (IllegalPatchException e) { + // pass + assertTrue(e.getMessage().contains("invalid type when attempting to patch")); + } + } + + @Test + public void failPatchBasicEqualsIfIndexOutofBounds() { + // create the parsedStatementImpl we are going to use + ParsedStatementImpl parsedStmt = new ParsedStatementImpl<>(statement); + SuffixExpression suffixExpn = new SuffixExpression(); + suffixExpn.setLimitExpn(null); + suffixExpn.setSortExpn(null); + // WHERE a = ? + WhereExpression expn = new WhereExpression(); + BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); + expn.getRoot().setValue(and); + and.setOperator(BinaryComparisonOperator.EQUALS); + TerminalNode a = new TerminalNode(and); + a.setValue("a"); + TerminalNode b = new TerminalNode(and); + UnfinishedValueNode bPatch = new UnfinishedValueNode(); + bPatch.setParameterIndex(1); // out of bounds + b.setValue(bPatch); + and.setLeftChild(a); + and.setRightChild(b); + suffixExpn.setWhereExpn(expn); + parsedStmt.setNumFreeParams(1); + parsedStmt.setSuffixExpression(suffixExpn); + PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); + preparedStatement.setString(0, "b"); + try { + PreparedParameter[] params = preparedStatement.getParams(); + // this should fail + parsedStmt.patchStatement(params); + fail("should not reach here"); + } catch (IllegalPatchException e) { + // pass + assertTrue(e.getCause() instanceof ArrayIndexOutOfBoundsException); + } + } + + private static class TestQuery implements Query { + + private Expression expr; + private List, SortDirection>> sorts; + private int limitVal = -1; + + private TestQuery() { + sorts = new ArrayList<>(); + } + + @Override + public void where(Expression expr) { + this.expr = expr; + } + + @Override + public void sort(Key key, SortDirection direction) { + Pair, SortDirection> sortPair = new Pair, SortDirection>(key, direction); + sorts.add(sortPair); + } + + @Override + public void limit(int n) { + this.limitVal = n; + } + + @Override + public Cursor execute() { + // Not implemented + return null; + } + + @Override + public Expression getWhereExpression() { + // Not implemented + return null; + } + + } +} diff -r 03ed49a50413 -r 7690396a1f72 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementTest.java --- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/ParsedStatementTest.java Thu Jul 18 16:52:29 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,463 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.storage.internal.statement; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; - -import java.util.ArrayList; -import java.util.List; - -import org.junit.After; -import org.junit.Before; -import org.junit.Test; - -import com.redhat.thermostat.common.Pair; -import com.redhat.thermostat.storage.core.Cursor; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Query.SortDirection; -import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.storage.query.BinaryComparisonExpression; -import com.redhat.thermostat.storage.query.BinaryComparisonOperator; -import com.redhat.thermostat.storage.query.BinaryLogicalExpression; -import com.redhat.thermostat.storage.query.BinaryLogicalOperator; -import com.redhat.thermostat.storage.query.Expression; -import com.redhat.thermostat.storage.query.LiteralExpression; - -public class ParsedStatementTest { - - private Query statement; - - @Before - public void setup() { - statement = new TestQuery(); - } - - @After - public void tearDown() { - statement = null; - } - - @Test - public void canPatchWhereAndExpr() throws IllegalPatchException { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - suffixExpn.setLimitExpn(null); - suffixExpn.setSortExpn(null); - // WHERE a = ? AND c = ? - WhereExpression expn = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); - expn.getRoot().setValue(and); - and.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode leftEqual = new BinaryExpressionNode(and); - BinaryExpressionNode rightEqual = new BinaryExpressionNode(and); - leftEqual.setOperator(BinaryComparisonOperator.EQUALS); - rightEqual.setOperator(BinaryComparisonOperator.EQUALS); - and.setLeftChild(leftEqual); - and.setRightChild(rightEqual); - TerminalNode a = new TerminalNode(leftEqual); - Key aKey = new Key<>("a", false); - a.setValue(aKey); - TerminalNode b = new TerminalNode(leftEqual); - UnfinishedValueNode patchB = new UnfinishedValueNode(); - patchB.setParameterIndex(0); - patchB.setType(String.class); - b.setValue(patchB); - leftEqual.setLeftChild(a); - leftEqual.setRightChild(b); - TerminalNode c = new TerminalNode(rightEqual); - c.setValue("c"); - rightEqual.setLeftChild(c); - TerminalNode d = new TerminalNode(rightEqual); - UnfinishedValueNode dPatch = new UnfinishedValueNode(); - dPatch.setParameterIndex(1); - dPatch.setType(Integer.class); - d.setValue(dPatch); - rightEqual.setRightChild(d); - suffixExpn.setWhereExpn(expn); - parsedStmt.setNumFreeParams(1); - parsedStmt.setSuffixExpression(suffixExpn); - // next, create the PreparedStatement we are going to use for - // patching. - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(2); - preparedStatement.setString(0, "test1"); - preparedStatement.setInt(1, 2); - // finally test the patching - Query query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - TestQuery q = (TestQuery)query; - Expression expectedExpression = q.expr; - assertTrue(expectedExpression instanceof BinaryLogicalExpression); - BinaryLogicalExpression andFinal = (BinaryLogicalExpression) expectedExpression; - assertEquals(BinaryLogicalOperator.AND, andFinal.getOperator()); - assertTrue(andFinal.getLeftOperand() instanceof BinaryComparisonExpression); - assertTrue(andFinal.getRightOperand() instanceof BinaryComparisonExpression); - BinaryComparisonExpression left = (BinaryComparisonExpression)andFinal.getLeftOperand(); - BinaryComparisonExpression right = (BinaryComparisonExpression)andFinal.getRightOperand(); - assertEquals(BinaryComparisonOperator.EQUALS, left.getOperator()); - assertEquals(BinaryComparisonOperator.EQUALS, right.getOperator()); - assertTrue(left.getLeftOperand() instanceof LiteralExpression); - assertTrue(left.getRightOperand() instanceof LiteralExpression); - LiteralExpression leftLiteral1 = (LiteralExpression)left.getLeftOperand(); - LiteralExpression rightLiteral1 = (LiteralExpression)left.getRightOperand(); - assertEquals(aKey, leftLiteral1.getValue()); - assertEquals("test1", rightLiteral1.getValue()); - LiteralExpression leftLiteral2 = (LiteralExpression)right.getLeftOperand(); - LiteralExpression rightLiteral2 = (LiteralExpression)right.getRightOperand(); - assertEquals("c", leftLiteral2.getValue()); - // right literal value should have been patched to a "d" - assertEquals(2, rightLiteral2.getValue()); - } - - @Test - public void canPatchBasicWhereEquals() throws IllegalPatchException { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - suffixExpn.setLimitExpn(null); - suffixExpn.setSortExpn(null); - // WHERE a = ? - WhereExpression expn = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); - expn.getRoot().setValue(and); - and.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(and); - a.setValue(new Key<>("a", false)); - TerminalNode b = new TerminalNode(and); - UnfinishedValueNode bPatch = new UnfinishedValueNode(); - bPatch.setParameterIndex(0); - bPatch.setType(boolean.class); - b.setValue(bPatch); - and.setLeftChild(a); - and.setRightChild(b); - suffixExpn.setWhereExpn(expn); - parsedStmt.setNumFreeParams(1); - parsedStmt.setSuffixExpression(suffixExpn); - // next, create the PreparedStatement we are going to use for - // patching. - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); - preparedStatement.setBoolean(0, true); - // finally test the patching - Query query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - TestQuery q = (TestQuery)query; - Expression expectedExpression = q.expr; - assertTrue(expectedExpression instanceof BinaryComparisonExpression); - BinaryComparisonExpression root = (BinaryComparisonExpression)expectedExpression; - assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); - assertTrue(root.getLeftOperand() instanceof LiteralExpression); - assertTrue(root.getRightOperand() instanceof LiteralExpression); - LiteralExpression leftLiteral1 = (LiteralExpression)root.getLeftOperand(); - LiteralExpression rightLiteral1 = (LiteralExpression)root.getRightOperand(); - assertEquals(new Key<>("a", false), leftLiteral1.getValue()); - // this should have gotten patched to a "b" - assertEquals(true, rightLiteral1.getValue()); - // now do it again with a different value - preparedStatement = new PreparedStatementImpl<>(1); - preparedStatement.setBoolean(0, false); - query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - q = (TestQuery)query; - expectedExpression = q.expr; - assertTrue(expectedExpression instanceof BinaryComparisonExpression); - root = (BinaryComparisonExpression)expectedExpression; - assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); - assertTrue(root.getLeftOperand() instanceof LiteralExpression); - assertTrue(root.getRightOperand() instanceof LiteralExpression); - leftLiteral1 = (LiteralExpression)root.getLeftOperand(); - rightLiteral1 = (LiteralExpression)root.getRightOperand(); - assertEquals(new Key<>("a", false), leftLiteral1.getValue()); - assertEquals(false, rightLiteral1.getValue()); - } - - @Test - public void canPatchBasicWhereEqualsLHSKeyAndRHSValue() throws IllegalPatchException { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - suffixExpn.setLimitExpn(null); - suffixExpn.setSortExpn(null); - // WHERE ?s = ?b - WhereExpression expn = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); - expn.getRoot().setValue(and); - and.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(and); - UnfinishedValueNode aPatch = new UnfinishedValueNode(); - aPatch.setLHS(true); - aPatch.setType(String.class); - aPatch.setParameterIndex(0); - a.setValue(aPatch); - TerminalNode b = new TerminalNode(and); - UnfinishedValueNode bPatch = new UnfinishedValueNode(); - bPatch.setParameterIndex(1); - bPatch.setType(boolean.class); - b.setValue(bPatch); - and.setLeftChild(a); - and.setRightChild(b); - suffixExpn.setWhereExpn(expn); - parsedStmt.setNumFreeParams(1); - parsedStmt.setSuffixExpression(suffixExpn); - // next, create the PreparedStatement we are going to use for - // patching. - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(2); - preparedStatement.setString(0, "a"); - preparedStatement.setBoolean(1, true); - // finally test the patching - Query query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - TestQuery q = (TestQuery)query; - Expression expectedExpression = q.expr; - assertTrue(expectedExpression instanceof BinaryComparisonExpression); - BinaryComparisonExpression root = (BinaryComparisonExpression)expectedExpression; - assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); - assertTrue(root.getLeftOperand() instanceof LiteralExpression); - assertTrue(root.getRightOperand() instanceof LiteralExpression); - LiteralExpression leftLiteral1 = (LiteralExpression)root.getLeftOperand(); - LiteralExpression rightLiteral1 = (LiteralExpression)root.getRightOperand(); - assertEquals(new Key<>("a", false), leftLiteral1.getValue()); - // this should have gotten patched to a "b" - assertEquals(true, rightLiteral1.getValue()); - // now do it again with a different value - preparedStatement = new PreparedStatementImpl<>(2); - preparedStatement.setString(0, "a"); - preparedStatement.setBoolean(1, false); - query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - q = (TestQuery)query; - expectedExpression = q.expr; - assertTrue(expectedExpression instanceof BinaryComparisonExpression); - root = (BinaryComparisonExpression)expectedExpression; - assertEquals(BinaryComparisonOperator.EQUALS, root.getOperator()); - assertTrue(root.getLeftOperand() instanceof LiteralExpression); - assertTrue(root.getRightOperand() instanceof LiteralExpression); - leftLiteral1 = (LiteralExpression)root.getLeftOperand(); - rightLiteral1 = (LiteralExpression)root.getRightOperand(); - assertEquals(new Key<>("a", false), leftLiteral1.getValue()); - assertEquals(false, rightLiteral1.getValue()); - } - - @Test - public void canPatchBasicLimit() throws IllegalPatchException { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - LimitExpression limitExpnToPatch = new LimitExpression(); - UnfinishedLimitValue unfinished = new UnfinishedLimitValue(); - unfinished.setParameterIndex(0); - limitExpnToPatch.setValue(unfinished); - suffixExpn.setLimitExpn(limitExpnToPatch); - suffixExpn.setSortExpn(null); - suffixExpn.setWhereExpn(null); - parsedStmt.setSuffixExpression(suffixExpn); - // set the value for the one unfinished param - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); - preparedStatement.setInt(0, 3); - Query query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - TestQuery q = (TestQuery)query; - assertEquals(3, q.limitVal); - } - - @Test - public void canPatchBasicSort() throws IllegalPatchException { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - // SORT ? ASC, b DSC - SortExpression sortExpn = new SortExpression(); - SortMember member = new SortMember(); - member.setDirection(SortDirection.ASCENDING); - UnfinishedSortKey unfinished = new UnfinishedSortKey(); - unfinished.setParameterIndex(0); - member.setSortKey(unfinished); - SortMember member2 = new SortMember(); - member2.setDirection(SortDirection.DESCENDING); - member2.setSortKey("b"); - sortExpn.addMember(member); - sortExpn.addMember(member2); - suffixExpn.setLimitExpn(null); - suffixExpn.setSortExpn(sortExpn); - suffixExpn.setWhereExpn(null); - parsedStmt.setSuffixExpression(suffixExpn); - // set the value for the one unfinished param - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); - preparedStatement.setString(0, "a"); - Query query = (Query)parsedStmt.patchQuery(preparedStatement); - assertTrue(query instanceof TestQuery); - TestQuery q = (TestQuery)query; - List, SortDirection>> actualSorts = q.sorts; - assertEquals(2, actualSorts.size()); - Pair, SortDirection> first = actualSorts.get(0); - Key firstKeyActual = (Key)first.getFirst(); - Key expectedFirst = new Key<>("a", false); - assertEquals(expectedFirst, firstKeyActual); - assertEquals(SortDirection.ASCENDING, first.getSecond()); - Pair, SortDirection> second = actualSorts.get(1); - Key secondKeyActual = (Key)second.getFirst(); - Key expectedSecond = new Key<>("b", false); - assertEquals(expectedSecond, secondKeyActual); - assertEquals(SortDirection.DESCENDING, second.getSecond()); - } - - @Test - public void failPatchWithWrongType() throws IllegalPatchException { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - suffixExpn.setLimitExpn(null); - suffixExpn.setSortExpn(null); - // WHERE 'a' = ?s AND 'c' = ?i - WhereExpression expn = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); - expn.getRoot().setValue(and); - and.setOperator(BinaryLogicalOperator.AND); - BinaryExpressionNode leftEqual = new BinaryExpressionNode(and); - BinaryExpressionNode rightEqual = new BinaryExpressionNode(and); - leftEqual.setOperator(BinaryComparisonOperator.EQUALS); - rightEqual.setOperator(BinaryComparisonOperator.EQUALS); - and.setLeftChild(leftEqual); - and.setRightChild(rightEqual); - TerminalNode a = new TerminalNode(leftEqual); - Key aKey = new Key<>("a", false); - a.setValue(aKey); - TerminalNode b = new TerminalNode(leftEqual); - UnfinishedValueNode patchB = new UnfinishedValueNode(); - patchB.setType(String.class); - patchB.setParameterIndex(0); - b.setValue(patchB); - leftEqual.setLeftChild(a); - leftEqual.setRightChild(b); - TerminalNode c = new TerminalNode(rightEqual); - Key cKey = new Key<>("c", false); - c.setValue(cKey); - rightEqual.setLeftChild(c); - TerminalNode d = new TerminalNode(rightEqual); - UnfinishedValueNode dPatch = new UnfinishedValueNode(); - dPatch.setParameterIndex(1); - dPatch.setType(Integer.class); - d.setValue(dPatch); - rightEqual.setRightChild(d); - suffixExpn.setWhereExpn(expn); - parsedStmt.setNumFreeParams(1); - parsedStmt.setSuffixExpression(suffixExpn); - // next, create the PreparedStatement we are going to use for - // patching. - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(2); - preparedStatement.setString(0, "test1"); - preparedStatement.setString(1, "foo"); - // finally test the patching - try { - parsedStmt.patchQuery(preparedStatement); - fail("should have failed to patch param 1 with a string value (expected int)"); - } catch (IllegalPatchException e) { - // pass - assertTrue(e.getMessage().contains("invalid type when attempting to patch")); - } - } - - @Test - public void failPatchBasicEqualsIfIndexOutofBounds() { - // create the parsedStatementImpl we are going to use - ParsedStatement parsedStmt = new ParsedStatement<>(statement); - SuffixExpression suffixExpn = new SuffixExpression(); - suffixExpn.setLimitExpn(null); - suffixExpn.setSortExpn(null); - // WHERE a = ? - WhereExpression expn = new WhereExpression(); - BinaryExpressionNode and = new BinaryExpressionNode(expn.getRoot()); - expn.getRoot().setValue(and); - and.setOperator(BinaryComparisonOperator.EQUALS); - TerminalNode a = new TerminalNode(and); - a.setValue("a"); - TerminalNode b = new TerminalNode(and); - UnfinishedValueNode bPatch = new UnfinishedValueNode(); - bPatch.setParameterIndex(1); // out of bounds - b.setValue(bPatch); - and.setLeftChild(a); - and.setRightChild(b); - suffixExpn.setWhereExpn(expn); - parsedStmt.setNumFreeParams(1); - parsedStmt.setSuffixExpression(suffixExpn); - PreparedStatementImpl preparedStatement = new PreparedStatementImpl<>(1); - preparedStatement.setString(0, "b"); - // this should fail - try { - parsedStmt.patchQuery(preparedStatement); - } catch (IllegalPatchException e) { - // pass - assertTrue(e.getCause() instanceof ArrayIndexOutOfBoundsException); - } - } - - private static class TestQuery implements Query { - - private Expression expr; - private List, SortDirection>> sorts; - private int limitVal = -1; - - private TestQuery() { - sorts = new ArrayList<>(); - } - - @Override - public void where(Expression expr) { - this.expr = expr; - } - - @Override - public void sort(Key key, SortDirection direction) { - Pair, SortDirection> sortPair = new Pair, SortDirection>(key, direction); - sorts.add(sortPair); - } - - @Override - public void limit(int n) { - this.limitVal = n; - } - - @Override - public Cursor execute() { - // Not implemented - return null; - } - - } -} diff -r 03ed49a50413 -r 7690396a1f72 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java --- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java Thu Jul 18 16:52:29 2013 +0200 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/PreparedStatementImplTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -163,6 +163,12 @@ called = true; return null; } + + @Override + public Expression getWhereExpression() { + // not implemented + return null; + } } } diff -r 03ed49a50413 -r 7690396a1f72 storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java --- a/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java Thu Jul 18 16:52:29 2013 +0200 +++ b/storage/core/src/test/java/com/redhat/thermostat/storage/internal/statement/StatementDescriptorParserTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -59,6 +59,19 @@ import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.storage.dao.AgentInfoDAO; import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.internal.statement.BinaryExpressionNode; +import com.redhat.thermostat.storage.internal.statement.LimitExpression; +import com.redhat.thermostat.storage.internal.statement.NotBooleanExpressionNode; +import com.redhat.thermostat.storage.internal.statement.ParsedStatementImpl; +import com.redhat.thermostat.storage.internal.statement.SortExpression; +import com.redhat.thermostat.storage.internal.statement.SortMember; +import com.redhat.thermostat.storage.internal.statement.StatementDescriptorParser; +import com.redhat.thermostat.storage.internal.statement.SuffixExpression; +import com.redhat.thermostat.storage.internal.statement.TerminalNode; +import com.redhat.thermostat.storage.internal.statement.UnfinishedLimitValue; +import com.redhat.thermostat.storage.internal.statement.UnfinishedSortKey; +import com.redhat.thermostat.storage.internal.statement.UnfinishedValueNode; +import com.redhat.thermostat.storage.internal.statement.WhereExpression; import com.redhat.thermostat.storage.query.BinaryComparisonOperator; import com.redhat.thermostat.storage.query.BinaryLogicalOperator; @@ -87,7 +100,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName(); StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = (ParsedStatement)parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression tree = statement.getSuffixExpression(); @@ -101,7 +114,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' != 'b'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression tree = statement.getSuffixExpression(); @@ -128,7 +141,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT ?i"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = (ParsedStatement)parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(1, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -146,7 +159,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " SORT 'a' ASC , 'b' DSC , 'c' ASC"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = (ParsedStatement)parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression suffixExpn = statement.getSuffixExpression(); @@ -170,7 +183,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' AND 'c' = 'd' AND 'e' < ?i"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(1, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -229,7 +242,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' AND 'c' = 'd' AND 'e' < 'f' AND 'g' >= 'h'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -297,7 +310,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < ?i"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(1, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -357,7 +370,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -426,7 +439,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h' AND 'x' = 'y' AND 'u' = 'w' AND 's' = 't'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl) parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -533,7 +546,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = 'b' OR NOT 'c' = 'd' OR 'e' < 'f' OR 'g' >= 'h' AND NOT 'x' = 'y' AND 'u' = 'w' AND 's' = 't'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl) parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression expn = statement.getSuffixExpression(); @@ -644,7 +657,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' AND 'c' = ?s OR NOT 'x' >= ?i SORT 'a' ASC , 'b' DSC , 'c' ASC"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = (ParsedStatement) parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl) parser.parse(); assertEquals(2, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression suffixExpn = statement.getSuffixExpression(); @@ -717,7 +730,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' OR 'c' = ?s SORT 'a' ASC , ?s DSC , 'c' ASC"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = (ParsedStatement) parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl) parser.parse(); assertEquals(2, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression suffixExpn = statement.getSuffixExpression(); @@ -773,7 +786,7 @@ String descrString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' < 'b' SORT 'a' DSC"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descrString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = (ParsedStatement)parser.parse(); + ParsedStatementImpl statement = (ParsedStatementImpl)parser.parse(); assertEquals(0, statement.getNumParams()); assertEquals(mockQuery.getClass().getName(), statement.getRawStatement().getClass().getName()); SuffixExpression suffixExpn = statement.getSuffixExpression(); @@ -807,9 +820,9 @@ String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = null; + ParsedStatementImpl statement = null; try { - statement = (ParsedStatement)parser.parse(); + statement = (ParsedStatementImpl)parser.parse(); } catch (DescriptorParsingException e) { fail(e.getMessage()); } @@ -839,16 +852,15 @@ assertTrue(WhereExpressions.equals(where, suffixExpn.getWhereExpn())); } - @SuppressWarnings("rawtypes") @Test public void testParseSimpleWithAndOr() { String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE '" + Key.AGENT_ID.getName() + "' = ?s" + " AND ?s < ?b OR 'a' = 'b'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = null; + ParsedStatementImpl statement = null; try { - statement = (ParsedStatement)parser.parse(); + statement = (ParsedStatementImpl)parser.parse(); } catch (DescriptorParsingException e) { e.printStackTrace(); fail(e.getMessage()); @@ -870,7 +882,7 @@ BinaryExpressionNode equality = new BinaryExpressionNode(and); equality.setOperator(BinaryComparisonOperator.EQUALS); TerminalNode a = new TerminalNode(equality); - Key aKey = new Key("a", false); + Key aKey = new Key<>("a", false); a.setValue(aKey); equality.setLeftChild(a); TerminalNode b = new TerminalNode(equality); @@ -880,7 +892,7 @@ BinaryExpressionNode equality2 = new BinaryExpressionNode(and); equality2.setOperator(BinaryComparisonOperator.EQUALS); TerminalNode c = new TerminalNode(equality2); - Key cKey = new Key(Key.AGENT_ID.getName(), false); + Key cKey = new Key<>(Key.AGENT_ID.getName(), false); c.setValue(cKey); equality2.setLeftChild(c); UnfinishedValueNode patch1 = new UnfinishedValueNode(); @@ -903,7 +915,7 @@ UnfinishedValueNode patch2 = new UnfinishedValueNode(); patch2.setLHS(false); patch2.setParameterIndex(2); - patch2.setType(boolean.class); + patch2.setType(Boolean.class); TerminalNode y = new TerminalNode(lessThan); y.setValue(patch2); lessThan.setRightChild(y); @@ -916,9 +928,9 @@ String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?s AND ?s = 'd'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = null; + ParsedStatementImpl statement = null; try { - statement = (ParsedStatement)parser.parse(); + statement = (ParsedStatementImpl)parser.parse(); } catch (DescriptorParsingException e) { e.printStackTrace(); fail(e.getMessage()); @@ -970,9 +982,9 @@ String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE NOT 'a' = ?s OR ?s = 'd'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = null; + ParsedStatementImpl statement = null; try { - statement = (ParsedStatement)parser.parse(); + statement = (ParsedStatementImpl)parser.parse(); } catch (DescriptorParsingException e) { e.printStackTrace(); fail(e.getMessage()); @@ -1026,9 +1038,9 @@ String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " WHERE 'a' = ?s OR ?s = 'd'"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = null; + ParsedStatementImpl statement = null; try { - statement = (ParsedStatement)parser.parse(); + statement = (ParsedStatementImpl)parser.parse(); } catch (DescriptorParsingException e) { e.printStackTrace(); fail(e.getMessage()); @@ -1080,9 +1092,9 @@ String descString = "QUERY " + AgentInfoDAO.CATEGORY.getName() + " LIMIT 1"; StatementDescriptor desc = new StatementDescriptor<>(AgentInfoDAO.CATEGORY, descString); parser = new StatementDescriptorParser<>(storage, desc); - ParsedStatement statement = null; + ParsedStatementImpl statement = null; try { - statement = (ParsedStatement)parser.parse(); + statement = (ParsedStatementImpl)parser.parse(); } catch (DescriptorParsingException e) { e.printStackTrace(); fail(e.getMessage()); diff -r 03ed49a50413 -r 7690396a1f72 web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java --- a/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java Thu Jul 18 16:52:29 2013 +0200 +++ b/web/client/src/test/java/com/redhat/thermostat/web/client/internal/WebStorageTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -96,11 +96,18 @@ import com.redhat.thermostat.storage.core.Connection.ConnectionListener; import com.redhat.thermostat.storage.core.Connection.ConnectionStatus; import com.redhat.thermostat.storage.core.Cursor; +import com.redhat.thermostat.storage.core.DescriptorParsingException; +import com.redhat.thermostat.storage.core.IllegalDescriptorException; import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedParameters; +import com.redhat.thermostat.storage.core.PreparedStatement; import com.redhat.thermostat.storage.core.Put; -import com.redhat.thermostat.storage.core.Query; import com.redhat.thermostat.storage.core.Remove; +import com.redhat.thermostat.storage.core.StatementDescriptor; +import com.redhat.thermostat.storage.core.StatementExecutionException; import com.redhat.thermostat.storage.core.Update; +import com.redhat.thermostat.storage.model.Pojo; import com.redhat.thermostat.storage.query.Expression; import com.redhat.thermostat.storage.query.ExpressionFactory; import com.redhat.thermostat.storage.query.Operator; @@ -108,8 +115,14 @@ import com.redhat.thermostat.test.FreePortFinder.TryPort; import com.redhat.thermostat.web.common.ExpressionSerializer; import com.redhat.thermostat.web.common.OperatorSerializer; +import com.redhat.thermostat.web.common.PreparedParameterSerializer; +import com.redhat.thermostat.web.common.ThermostatGSONConverter; import com.redhat.thermostat.web.common.WebInsert; -import com.redhat.thermostat.web.common.WebQuery; +import com.redhat.thermostat.web.common.WebPreparedStatement; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; +import com.redhat.thermostat.web.common.WebPreparedStatementSerializer; +import com.redhat.thermostat.web.common.WebQueryResponse; +import com.redhat.thermostat.web.common.WebQueryResponseSerializer; import com.redhat.thermostat.web.common.WebRemove; import com.redhat.thermostat.web.common.WebUpdate; @@ -262,37 +275,108 @@ storage.registerCategory(category); } + + @Test + public void preparingFaultyDescriptorThrowsException() throws UnsupportedEncodingException, IOException { + Gson gson = new GsonBuilder() + .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .create(); + // missing quotes for LHS key + String strDesc = "QUERY test WHERE a = ?s"; + StatementDescriptor desc = new StatementDescriptor<>(category, strDesc); + + WebPreparedStatementResponse fakeResponse = new WebPreparedStatementResponse(); + fakeResponse.setStatementId(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED); + prepareServer(gson.toJson(fakeResponse)); + try { + storage.prepareStatement(desc); + fail("Should have refused to prepare the statement"); + } catch (IllegalDescriptorException e) { + // should have thrown superclass DescriptorParsingException + fail(e.getMessage()); + } catch (DescriptorParsingException e) { + // pass + } + } + @Test - public void testFindAllPojos() throws UnsupportedEncodingException, IOException { + public void preparingUnknownDescriptorThrowsException() throws UnsupportedEncodingException, IOException { + Gson gson = new GsonBuilder() + .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .create(); + String strDesc = "QUERY test WHERE 'property1' = ?s"; + StatementDescriptor desc = new StatementDescriptor<>(category, strDesc); + + WebPreparedStatementResponse fakeResponse = new WebPreparedStatementResponse(); + fakeResponse.setStatementId(WebPreparedStatementResponse.ILLEGAL_STATEMENT); + prepareServer(gson.toJson(fakeResponse)); + try { + storage.prepareStatement(desc); + fail("Should have refused to prepare the statement"); + } catch (IllegalDescriptorException e) { + // pass + assertEquals(strDesc, e.getFailedDescriptor()); + } catch (DescriptorParsingException e) { + // should have thrown IllegalDescriptorException + fail(e.getMessage()); + } + } + + @Test + public void canPrepareAndExecuteQuery() throws UnsupportedEncodingException, IOException { TestObj obj1 = new TestObj(); obj1.setProperty1("fluffor1"); TestObj obj2 = new TestObj(); obj2.setProperty1("fluffor2"); - Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(Expression.class, new ExpressionSerializer()) - .registerTypeHierarchyAdapter(Operator.class, new OperatorSerializer()).create(); - - Key key1 = new Key<>("property1", true); - Query query = storage.createQuery(category); - ExpressionFactory factory = new ExpressionFactory(); - Expression expr = factory.equalTo(key1, "fluff"); - query.where(expr); - - prepareServer(gson.toJson(Arrays.asList(obj1, obj2))); - Cursor results = query.execute(); + Gson gson = new GsonBuilder().registerTypeHierarchyAdapter(PreparedParameter.class, new PreparedParameterSerializer()) + .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .registerTypeAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) + .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) + .create(); - StringReader reader = new StringReader(requestBody); - BufferedReader bufRead = new BufferedReader(reader); - String line = URLDecoder.decode(bufRead.readLine(), "UTF-8"); - String[] parts = line.split("="); - assertEquals("query", parts[0]); - WebQuery restQuery = gson.fromJson(parts[1], WebQuery.class); - - assertEquals(42, restQuery.getCategoryId()); - Expression restExpr = restQuery.getExpression(); - assertEquals(expr, restExpr); - + String strDesc = "QUERY test WHERE 'property1' = ?s"; + StatementDescriptor desc = new StatementDescriptor<>(category, strDesc); + PreparedStatement stmt = null; + + int fakePrepStmtId = 5; + WebPreparedStatementResponse fakeResponse = new WebPreparedStatementResponse(); + fakeResponse.setNumFreeVariables(1); + fakeResponse.setStatementId(fakePrepStmtId); + prepareServer(gson.toJson(fakeResponse)); + try { + stmt = storage.prepareStatement(desc); + } catch (DescriptorParsingException e) { + // descriptor should parse fine and is trusted + fail(e.getMessage()); + } + assertTrue(stmt instanceof WebPreparedStatement); + WebPreparedStatement webStmt = (WebPreparedStatement)stmt; + assertEquals(fakePrepStmtId, webStmt.getStatementId()); + PreparedParameters params = webStmt.getParams(); + assertEquals(1, params.getParams().length); + assertNull(params.getParams()[0]); + + // now set a parameter + stmt.setString(0, "fluff"); + assertEquals("fluff", params.getParams()[0].getValue()); + assertEquals(String.class, params.getParams()[0].getType()); + + WebQueryResponse fakeQueryResponse = new WebQueryResponse<>(); + fakeQueryResponse.setResponseCode(WebQueryResponse.SUCCESS); + fakeQueryResponse.setResultList(new TestObj[] { obj1, obj2 }); + prepareServer(gson.toJson(fakeQueryResponse)); + Cursor results = null; + try { + results = stmt.executeQuery(); + } catch (StatementExecutionException e) { + // should execute fine + e.printStackTrace(); + fail(e.getMessage()); + } + assertNotNull(results); + assertTrue(results instanceof WebCursor); assertTrue(results.hasNext()); assertEquals("fluffor1", results.next().getProperty1()); assertTrue(results.hasNext()); @@ -307,6 +391,17 @@ } @Test + public void createQueryFailsCorrectly() throws UnsupportedEncodingException, IOException { + try { + storage.createQuery(category); + fail("createQuery() should fail for WebStorage."); + } catch (IllegalStateException e) { + // pass + assertTrue(e.getMessage().contains("createQuery() not supported")); + } + } + + @Test public void testPut() throws IOException, JsonSyntaxException, ClassNotFoundException { TestObj obj = new TestObj(); diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParameterSerializerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParameterSerializerTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,222 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.PreparedParameter; + +public class PreparedParameterSerializerTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder().registerTypeAdapter( + PreparedParameter.class, + new PreparedParameterSerializer()).create(); + } + + @Test + public void canDeserializeBasic() { + // String + String jsonStr = "{ \"type\": \"java.lang.String\" , \"value\": \"testing\"}"; + PreparedParameter param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(String.class, param.getType()); + assertEquals("testing", param.getValue()); + // Integer + jsonStr = "{ \"type\": \"java.lang.Integer\" , \"value\": -1}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(Integer.class, param.getType()); + assertTrue(param.getValue() instanceof Integer); + assertEquals(-1, param.getValue()); + // Long + jsonStr = "{ \"type\": \"java.lang.Long\" , \"value\": -10}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(Long.class, param.getType()); + assertTrue(param.getValue() instanceof Long); + assertEquals(-10L, param.getValue()); + jsonStr = "{ \"type\": \"java.lang.Long\" , \"value\": 30000000003}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(Long.class, param.getType()); + assertTrue(param.getValue() instanceof Long); + assertEquals(30000000003L, param.getValue()); + // Boolean + jsonStr = "{ \"type\": \"java.lang.Boolean\" , \"value\": true}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(Boolean.class, param.getType()); + assertTrue(param.getValue() instanceof Boolean); + assertEquals(true, param.getValue()); + // String[] + String strArrayVal = "[ \"testing1\", \"testing2\", \"3\" ]"; + jsonStr = "{ \"type\": \"java.lang.String[]\" , \"value\": " + strArrayVal + "}"; + param = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(String[].class, param.getType()); + assertTrue(param.getValue() instanceof String[]); + String[] vals = (String[])param.getValue(); + assertEquals(3, vals.length); + assertEquals("testing1", vals[0]); + assertEquals("testing2", vals[1]); + assertEquals("3", vals[2]); + } + + @Test + public void failsDeserializationWrongTypeClass() { + String jsonStr = "{ \"type\": \"java.io.File\" , \"value\": true}"; + try { + gson.fromJson(jsonStr, PreparedParameter.class); + fail("should have failed to serialize"); + } catch (Exception e) { + // pass + Throwable cause = e.getCause(); + assertTrue(cause.getMessage().contains("Illegal type of parameter")); + } + } + + @Test + public void canSerializeBasic() { + // String + String expected = "{\"value\":\"testing\",\"type\":\"java.lang.String\"}"; + PreparedParameter param = new PreparedParameter(); + param.setType(String.class); + param.setValue("testing"); + String actual = gson.toJson(param); + assertEquals(expected, actual); + // Integer + expected = "{\"value\":-1,\"type\":\"java.lang.Integer\"}"; + param.setType(Integer.class); + param.setValue(-1); + actual = gson.toJson(param); + assertEquals(expected, actual); + // Long + expected = "{\"value\":30000000003,\"type\":\"java.lang.Long\"}"; + param.setType(Long.class); + param.setValue(30000000003L); + actual = gson.toJson(param); + assertEquals(expected, actual); + // boolean + expected = "{\"value\":true,\"type\":\"java.lang.Boolean\"}"; + param.setType(Boolean.class); + param.setValue(true); + actual = gson.toJson(param); + assertEquals(expected, actual); + // String[] + String strArrayVal = "[\"testing1\",\"testing2\",\"3\"]"; + expected = "{\"value\":" + strArrayVal + ",\"type\":\"java.lang.String[]\"}"; + param.setType(String[].class); + String[] array = new String[] { + "testing1", "testing2", "3" + }; + param.setValue(array); + actual = gson.toJson(param); + assertEquals(expected, actual); + } + + @Test + public void canSerializeDeserializeInteger() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(Integer.class); + expected.setValue(3); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeLong() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(Long.class); + expected.setValue(30000000003L); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeString() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(String.class); + expected.setValue("testing"); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + @Test + public void canSerializeDeserializeBoolean() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(Boolean.class); + expected.setValue(false); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + + expected = new PreparedParameter(); + expected.setType(Boolean.class); + expected.setValue(true); + jsonStr = gson.toJson(expected, PreparedParameter.class); + assertParameterEquals(expected, jsonStr); + } + + private void assertParameterEquals(PreparedParameter expected, + String jsonStr) { + PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(expected.getType(), actual.getType()); + assertEquals(expected.getValue(), actual.getValue()); + } + + @Test + public void canSerializeDeserializeStringArray() { + PreparedParameter expected = new PreparedParameter(); + expected.setType(String[].class); + String[] expectedArray = new String[] { + "one", "two", "three" + }; + expected.setValue(expectedArray); + String jsonStr = gson.toJson(expected, PreparedParameter.class); + PreparedParameter actual = gson.fromJson(jsonStr, PreparedParameter.class); + assertEquals(expected.getType(), actual.getType()); + String[] actualArray = (String[])actual.getValue(); + for (int i = 0; i < expectedArray.length; i++) { + assertEquals(expectedArray[i], actualArray[i]); + } + } + +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParametersSerializerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/PreparedParametersSerializerTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,88 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedParameters; + +public class PreparedParametersSerializerTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder().registerTypeAdapter( + PreparedParameter.class, + new PreparedParameterSerializer()).create(); + } + + @Test + public void canSerializeDeserialize() { + PreparedParameters params = new PreparedParameters(5); + params.setBoolean(0, true); + params.setInt(1, 2300); + params.setLong(2, 2200000000L); + params.setString(3, "testing"); + String[] list = new String[] { + "a", "b", "c" + }; + params.setStringList(4, list); + + String jsonStr = gson.toJson(params, PreparedParameters.class); + PreparedParameters actualParams = gson.fromJson(jsonStr, PreparedParameters.class); + + PreparedParameter[] expected = params.getParams(); + PreparedParameter[] actual = actualParams.getParams(); + + // last element is the string array, which we check manually + for (int i = 0; i < expected.length - 1; i++) { + assertEquals(expected[i].getType(), actual[i].getType()); + assertEquals(expected[i].getValue(), actual[i].getValue()); + } + String actualList[] = (String[])actual[4].getValue(); + for (int i = 0; i < list.length; i++) { + assertEquals(list[i], actualList[i]); + } + } +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/ThermostatGSONConverterTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/ThermostatGSONConverterTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,92 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.Pojo; + +public class ThermostatGSONConverterTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) + .create(); + } + + @Test + public void canSerializeDeserializeBasic() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation(); + agentInfo.setAgentId("testing"); + agentInfo.setAlive(true); + + String jsonStr = gson.toJson(agentInfo); + + AgentInformation actual = gson.fromJson(jsonStr, AgentInformation.class); + + assertEquals("testing", actual.getAgentId()); + assertEquals(true, actual.isAlive()); + } + + @Test + public void canSerializeDeserializeArray() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation(); + agentInfo.setAgentId("testing"); + agentInfo.setAlive(true); + AgentInformation[] agentInfos = new AgentInformation[] { + agentInfo + }; + + String jsonStr = gson.toJson(agentInfos); + + AgentInformation[] actual = gson.fromJson(jsonStr, AgentInformation[].class); + + assertEquals("testing", actual[0].getAgentId()); + assertEquals(true, actual[0].isAlive()); + } +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementResponseSerializerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementResponseSerializerTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,71 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +public class WebPreparedStatementResponseSerializerTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder().create(); + } + + @Test + public void testSerializationDeserializationBasic() { + WebPreparedStatementResponse response = new WebPreparedStatementResponse(); + response.setNumFreeVariables(6); + response.setStatementId(WebPreparedStatementResponse.ILLEGAL_STATEMENT); + + String jsonStr = gson.toJson(response, WebPreparedStatementResponse.class); + String expectedString = "{\"numFreeVariables\":6,\"statementId\":-1}"; + assertEquals(expectedString, jsonStr); + + WebPreparedStatementResponse actual = gson.fromJson(jsonStr, WebPreparedStatementResponse.class); + + assertEquals(6, actual.getNumFreeVariables()); + assertEquals(WebPreparedStatementResponse.ILLEGAL_STATEMENT, actual.getStatementId()); + } +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/WebPreparedStatementSerializerTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,95 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNotNull; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedParameters; + +public class WebPreparedStatementSerializerTest { + + private Gson gson; + + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapter(WebPreparedStatement.class, + new WebPreparedStatementSerializer()) + .registerTypeAdapter(PreparedParameter.class, + new PreparedParameterSerializer()).create(); + } + + @Test + public void canSerializeAndDeserialize() { + PreparedParameters params = new PreparedParameters(5); + params.setInt(0, 2); + params.setString(1, "testing"); + params.setLong(2, 222L); + params.setStringList(3, new String[] { "one", "two" }); + params.setBoolean(4, true); + WebPreparedStatement stmt = new WebPreparedStatement<>(); + stmt.setParams(params); + stmt.setStatementId(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED); + String jsonString = gson.toJson(stmt, WebPreparedStatement.class); + WebPreparedStatement newStmt = gson.fromJson(jsonString, WebPreparedStatement.class); + assertNotNull(newStmt); + PreparedParameters newParams = newStmt.getParams(); + PreparedParameter[] parameters = newParams.getParams(); + assertEquals(5, parameters.length); + assertEquals(2, parameters[0].getValue()); + assertEquals(Integer.class, parameters[0].getType()); + assertEquals("testing", parameters[1].getValue()); + assertEquals(String.class, parameters[1].getType()); + assertEquals(222L, parameters[2].getValue()); + assertEquals(Long.class, parameters[2].getType()); + String[] list = (String[])parameters[3].getValue(); + assertEquals(2, list.length); + assertEquals("one", list[0]); + assertEquals("two", list[1]); + assertEquals(String[].class, parameters[3].getType()); + assertEquals(true, parameters[4].getValue()); + assertEquals(Boolean.class, parameters[4].getType()); + assertEquals(WebPreparedStatementResponse.DESCRIPTOR_PARSE_FAILED, newStmt.getStatementId()); + } +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryResponseSerializerTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryResponseSerializerTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,189 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; + +import java.lang.reflect.Type; + +import org.junit.Before; +import org.junit.Test; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; +import com.redhat.thermostat.storage.model.AgentInformation; +import com.redhat.thermostat.storage.model.HostInfo; +import com.redhat.thermostat.storage.model.Pojo; + +public class WebQueryResponseSerializerTest { + + private Gson gson; + + @SuppressWarnings("rawtypes") + @Before + public void setup() { + gson = new GsonBuilder() + .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) + .registerTypeAdapter(WebQueryResponse.class, + new WebQueryResponseSerializer()).create(); + } + + @Test + public void canSerializeBasic() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation(); + agentInfo.setAgentId("testing"); + agentInfo.setAlive(false); + AgentInformation[] resultList = new AgentInformation[] { + agentInfo + }; + + // create the query response + WebQueryResponse response = new WebQueryResponse<>(); + response.setResultList(resultList); + response.setResponseCode(WebQueryResponse.ILLEGAL_PATCH); + + String jsonStr = gson.toJson(response); + String expectedJson = "{\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":false,\"backends\":[],\"agentId\":\"testing\"}],\"errno\":-1}"; + assertEquals(expectedJson, jsonStr); + } + + @Test + public void canDeserializeBasic() { + String rawJson = "{\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":true,\"backends\":[],\"agentId\":\"testing\"}],\"errno\":-1}"; + Type queryResponseType = new TypeToken>() {}.getType(); + WebQueryResponse actual = gson.fromJson(rawJson, queryResponseType); + + AgentInformation[] actualList = actual.getResultList(); + + assertEquals(WebQueryResponse.ILLEGAL_PATCH, actual.getResponseCode()); + assertEquals(1, actualList.length); + AgentInformation actualInfo = actualList[0]; + assertEquals(true, actualInfo.isAlive()); + assertEquals("testing", actualInfo.getAgentId()); + } + + @Test + public void canSerializeAndDeserializeBasic() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation(); + agentInfo.setAgentId("testing"); + agentInfo.setAlive(false); + AgentInformation[] resultList = new AgentInformation[] { + agentInfo + }; + + // create the query response + WebQueryResponse response = new WebQueryResponse<>(); + response.setResultList(resultList); + response.setResponseCode(WebQueryResponse.ILLEGAL_PATCH); + + String jsonStr = gson.toJson(response); + + // We need to tell GSON which parametrized type we want it to deserialize + // it to. + Type queryResponseType = new TypeToken>() {}.getType(); + WebQueryResponse actual = gson.fromJson(jsonStr, queryResponseType); + + AgentInformation[] actualList = actual.getResultList(); + + assertEquals(WebQueryResponse.ILLEGAL_PATCH, actual.getResponseCode()); + assertEquals(1, actualList.length); + AgentInformation actualInfo = actualList[0]; + assertEquals(false, actualInfo.isAlive()); + assertEquals("testing", actualInfo.getAgentId()); + } + + @Test + public void canSerializeAndDeserializeVariousPojos() { + // Our test pojo + AgentInformation agentInfo = new AgentInformation(); + agentInfo.setAgentId("testing"); + agentInfo.setAlive(false); + AgentInformation[] resultList = new AgentInformation[] { + agentInfo + }; + + // create the query response + WebQueryResponse response = new WebQueryResponse<>(); + response.setResultList(resultList); + response.setResponseCode(WebQueryResponse.ILLEGAL_PATCH); + + String jsonStr = gson.toJson(response); + String expectedJson = "{\"payload\":[{\"startTime\":0,\"stopTime\":0,\"alive\":false,\"backends\":[],\"agentId\":\"testing\"}],\"errno\":-1}"; + assertEquals(expectedJson, jsonStr); + + // We need to tell GSON which parametrized type we want it to deserialize + // it to. + Type queryResponseType = new TypeToken>() {}.getType(); + WebQueryResponse actual = gson.fromJson(jsonStr, queryResponseType); + + AgentInformation[] actualList = actual.getResultList(); + + assertEquals(WebQueryResponse.ILLEGAL_PATCH, actual.getResponseCode()); + assertEquals(1, actualList.length); + AgentInformation actualInfo = actualList[0]; + assertEquals(false, actualInfo.isAlive()); + assertEquals("testing", actualInfo.getAgentId()); + + // Do it again using HostInfo as model + HostInfo hostInfo = new HostInfo(); + hostInfo.setAgentId("something"); + hostInfo.setCpuCount(56); + hostInfo.setHostname("flukebox"); + + HostInfo[] hostInfoResults = new HostInfo[] { + hostInfo + }; + + WebQueryResponse expected = new WebQueryResponse<>(); + expected.setResultList(hostInfoResults); + expected.setResponseCode(WebQueryResponse.SUCCESS); + + jsonStr = gson.toJson(expected); + Type hostinfoQueryResponseType = new TypeToken>() {}.getType(); + WebQueryResponse actualResp = gson.fromJson(jsonStr, hostinfoQueryResponseType); + + assertEquals(WebQueryResponse.SUCCESS, actualResp.getResponseCode()); + HostInfo[] hostInfoList = actualResp.getResultList(); + assertEquals(1, hostInfoList.length); + assertEquals("something", hostInfoList[0].getAgentId()); + assertEquals(56, hostInfoList[0].getCpuCount()); + assertEquals("flukebox", hostInfoList[0].getHostname()); + } +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryResponseTest.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryResponseTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -0,0 +1,68 @@ +/* + * Copyright 2012, 2013 Red Hat, Inc. + * + * This file is part of Thermostat. + * + * Thermostat is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published + * by the Free Software Foundation; either version 2, or (at your + * option) any later version. + * + * Thermostat is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Thermostat; see the file COPYING. If not see + * . + * + * Linking this code with other modules is making a combined work + * based on this code. Thus, the terms and conditions of the GNU + * General Public License cover the whole combination. + * + * As a special exception, the copyright holders of this code give + * you permission to link this code with independent modules to + * produce an executable, regardless of the license terms of these + * independent modules, and to copy and distribute the resulting + * executable under terms of your choice, provided that you also + * meet, for each linked independent module, the terms and conditions + * of the license of that module. An independent module is a module + * which is not derived from or based on this code. If you modify + * this code, you may extend this exception to your version of the + * library, but you are not obligated to do so. If you do not wish + * to do so, delete this exception statement from your version. + */ + +package com.redhat.thermostat.web.common; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; + +import java.lang.reflect.ParameterizedType; + +import org.junit.Test; + +import com.redhat.thermostat.storage.model.Pojo; + +public class WebQueryResponseTest { + + /** + * Having this test is important since we need the type info for deserializing + * {@link WebQueryResponse}s. + */ + @Test + public void canGetCorrectParameterizedType() { + WebQueryResponse response = new WebQueryResponse<>(); + ParameterizedType type = response + .getRuntimeParametrizedType(TestPojo.class); + assertNull(type.getOwnerType()); + assertEquals(1, type.getActualTypeArguments().length); + assertEquals(TestPojo.class, type.getActualTypeArguments()[0]); + assertEquals(WebQueryResponse.class, type.getRawType()); + } + + private static class TestPojo implements Pojo { + // nothing + } +} diff -r 03ed49a50413 -r 7690396a1f72 web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryTest.java --- a/web/common/src/test/java/com/redhat/thermostat/web/common/WebQueryTest.java Thu Jul 18 16:52:29 2013 +0200 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,77 +0,0 @@ -/* - * Copyright 2012, 2013 Red Hat, Inc. - * - * This file is part of Thermostat. - * - * Thermostat is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published - * by the Free Software Foundation; either version 2, or (at your - * option) any later version. - * - * Thermostat is distributed in the hope that it will be useful, but - * WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with Thermostat; see the file COPYING. If not see - * . - * - * Linking this code with other modules is making a combined work - * based on this code. Thus, the terms and conditions of the GNU - * General Public License cover the whole combination. - * - * As a special exception, the copyright holders of this code give - * you permission to link this code with independent modules to - * produce an executable, regardless of the license terms of these - * independent modules, and to copy and distribute the resulting - * executable under terms of your choice, provided that you also - * meet, for each linked independent module, the terms and conditions - * of the license of that module. An independent module is a module - * which is not derived from or based on this code. If you modify - * this code, you may extend this exception to your version of the - * library, but you are not obligated to do so. If you do not wish - * to do so, delete this exception statement from your version. - */ - -package com.redhat.thermostat.web.common; - -import static org.junit.Assert.assertEquals; -import static org.junit.Assert.assertNotNull; - -import java.util.HashMap; -import java.util.Map; - -import org.junit.Test; - -import com.redhat.thermostat.storage.core.Category; -import com.redhat.thermostat.storage.core.Key; -import com.redhat.thermostat.storage.model.Pojo; -import com.redhat.thermostat.storage.query.Expression; -import com.redhat.thermostat.storage.query.ExpressionFactory; - -public class WebQueryTest { - - private static class TestObj implements Pojo { - - } - - @Test - public void test() { - Key key1 = new Key<>("testkey", true); - Category category = new Category<>("test", TestObj.class, key1); - Map categoryIdMap = new HashMap<>(); - categoryIdMap.put(category, 42); - WebQuery query = new WebQuery(42); - ExpressionFactory factory = new ExpressionFactory(); - Expression expr = factory.equalTo(key1, "fluff"); - query.where(expr); - - Expression retExpr = query.getExpression(); - assertNotNull(retExpr); - assertEquals(expr, retExpr); - - assertEquals(42, query.getCategoryId()); - } -} - diff -r 03ed49a50413 -r 7690396a1f72 web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java --- a/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Thu Jul 18 16:52:29 2013 +0200 +++ b/web/server/src/test/java/com/redhat/thermostat/web/server/WebStorageEndpointTest.java Thu Jul 18 16:56:05 2013 +0200 @@ -56,6 +56,7 @@ import java.io.OutputStreamWriter; import java.io.Reader; import java.lang.reflect.Method; +import java.lang.reflect.Type; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.ProtocolException; @@ -85,20 +86,25 @@ import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.reflect.TypeToken; import com.redhat.thermostat.storage.core.Add; import com.redhat.thermostat.storage.core.Categories; import com.redhat.thermostat.storage.core.Category; import com.redhat.thermostat.storage.core.Cursor; import com.redhat.thermostat.storage.core.Entity; import com.redhat.thermostat.storage.core.Key; +import com.redhat.thermostat.storage.core.ParsedStatement; import com.redhat.thermostat.storage.core.Persist; +import com.redhat.thermostat.storage.core.PreparedParameter; +import com.redhat.thermostat.storage.core.PreparedStatement; import com.redhat.thermostat.storage.core.Query; -import com.redhat.thermostat.storage.core.Query.SortDirection; import com.redhat.thermostat.storage.core.Remove; import com.redhat.thermostat.storage.core.Replace; +import com.redhat.thermostat.storage.core.StatementDescriptor; import com.redhat.thermostat.storage.core.Storage; import com.redhat.thermostat.storage.core.Update; import com.redhat.thermostat.storage.model.BasePojo; +import com.redhat.thermostat.storage.model.Pojo; import com.redhat.thermostat.storage.query.Expression; import com.redhat.thermostat.storage.query.ExpressionFactory; import com.redhat.thermostat.storage.query.Operator; @@ -106,9 +112,15 @@ import com.redhat.thermostat.test.FreePortFinder.TryPort; import com.redhat.thermostat.web.common.ExpressionSerializer; import com.redhat.thermostat.web.common.OperatorSerializer; +import com.redhat.thermostat.web.common.PreparedParameterSerializer; import com.redhat.thermostat.web.common.StorageWrapper; +import com.redhat.thermostat.web.common.ThermostatGSONConverter; import com.redhat.thermostat.web.common.WebInsert; -import com.redhat.thermostat.web.common.WebQuery; +import com.redhat.thermostat.web.common.WebPreparedStatement; +import com.redhat.thermostat.web.common.WebPreparedStatementResponse; +import com.redhat.thermostat.web.common.WebPreparedStatementSerializer; +import com.redhat.thermostat.web.common.WebQueryResponse; +import com.redhat.thermostat.web.common.WebQueryResponseSerializer; import com.redhat.thermostat.web.common.WebRemove; import com.redhat.thermostat.web.common.WebUpdate; import com.redhat.thermostat.web.server.auth.Roles; @@ -217,7 +229,7 @@ // manually maintained list of path handlers which should include // authorization checks final String[] authPaths = new String[] { - "find-all", "put-pojo", "register-category", "remove-pojo", + "prepare-statement", "query-execute", "put-pojo", "register-category", "remove-pojo", "update-pojo", "get-count", "save-file", "load-file", "purge", "ping", "generate-token", "verify-token" }; @@ -263,9 +275,10 @@ @SuppressWarnings({ "unchecked", "rawtypes" }) @Test - public void authorizedFindAllPojos() throws Exception { + public void authorizedPrepareQuery() throws Exception { String[] roleNames = new String[] { Roles.REGISTER_CATEGORY, + Roles.PREPARE_STATEMENT, Roles.READ }; String testuser = "testuser"; @@ -286,60 +299,96 @@ TestClass expected2 = new TestClass(); expected2.setKey1("fluff2"); expected2.setKey2(43); + // prepare-statement does this under the hood + Query mockMongoQuery = mock(Query.class); + when(mockStorage.createQuery(eq(category))).thenReturn(mockMongoQuery); + Cursor cursor = mock(Cursor.class); when(cursor.hasNext()).thenReturn(true).thenReturn(true).thenReturn(false); when(cursor.next()).thenReturn(expected1).thenReturn(expected2); + + PreparedStatement mockPreparedQuery = mock(PreparedStatement.class); + when(mockStorage.prepareStatement(any(StatementDescriptor.class))).thenReturn(mockPreparedQuery); + + ParsedStatement mockParsedStatement = mock(ParsedStatement.class); + when(mockParsedStatement.getNumParams()).thenReturn(1); + when(mockParsedStatement.patchStatement(any(PreparedParameter[].class))).thenReturn(mockMongoQuery); + when(mockPreparedQuery.getParsedStatement()).thenReturn(mockParsedStatement); + + // The web layer + when(mockPreparedQuery.executeQuery()).thenReturn(cursor); + // And the mongo layer + when(mockMongoQuery.execute()).thenReturn(cursor); - Query mockQuery = mock(Query.class); - when(mockStorage.createQuery(any(Category.class))).thenReturn(mockQuery); - when(mockQuery.execute()).thenReturn(cursor); - + String strDescriptor = "QUERY " + category.getName() + " WHERE '" + key1.getName() + "' = ?s SORT '" + key1.getName() + "' DSC LIMIT 42"; String endpoint = getEndpoint(); - URL url = new URL(endpoint + "/find-all"); + URL url = new URL(endpoint + "/prepare-statement"); HttpURLConnection conn = (HttpURLConnection) url.openConnection(); conn.setRequestMethod("POST"); sendAuthentication(conn, testuser, password); conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); conn.setDoInput(true); conn.setDoOutput(true); - Map categoryIdMap = new HashMap<>(); - categoryIdMap.put(category, categoryId); - WebQuery query = new WebQuery(categoryId); - Expression expr = factory.equalTo(key1, "fluff"); - query.where(expr); - query.sort(key1, SortDirection.DESCENDING); - query.limit(42); Gson gson = new GsonBuilder() - .registerTypeHierarchyAdapter(Expression.class, - new ExpressionSerializer()) - .registerTypeHierarchyAdapter(Operator.class, - new OperatorSerializer()).create(); + .registerTypeHierarchyAdapter(WebQueryResponse.class, new WebQueryResponseSerializer<>()) + .registerTypeAdapter(Pojo.class, new ThermostatGSONConverter()) + .registerTypeAdapter(WebPreparedStatement.class, new WebPreparedStatementSerializer()) + .registerTypeAdapter(PreparedParameter.class, new PreparedParameterSerializer()).create(); OutputStreamWriter out = new OutputStreamWriter(conn.getOutputStream()); - String body = "query=" + URLEncoder.encode(gson.toJson(query), "UTF-8"); + String body = "query-descriptor=" + URLEncoder.encode(strDescriptor, "UTF-8") + "&category-id=" + categoryId; out.write(body + "\n"); out.flush(); Reader in = new InputStreamReader(conn.getInputStream()); - TestClass[] results = gson.fromJson(in, TestClass[].class); + WebPreparedStatementResponse response = gson.fromJson(in, WebPreparedStatementResponse.class); + assertEquals(1, response.getNumFreeVariables()); + assertEquals(0, response.getStatementId()); + assertEquals("application/json; charset=UTF-8", conn.getContentType()); + + + + // now execute the query we've just prepared + WebPreparedStatement stmt = new WebPreparedStatement<>(1, 0); + stmt.setString(0, "fluff"); + + url = new URL(endpoint + "/query-execute"); + HttpURLConnection conn2 = (HttpURLConnection) url.openConnection(); + conn2.setRequestMethod("POST"); + sendAuthentication(conn2, testuser, password); + conn2.setRequestProperty("Content-Type", "application/x-www-form-urlencoded"); + conn2.setDoInput(true); + conn2.setDoOutput(true); + + out = new OutputStreamWriter(conn2.getOutputStream()); + body = "prepared-stmt=" + gson.toJson(stmt, WebPreparedStatement.class); + out.write(body + "\n"); + out.flush(); + + in = new InputStreamReader(conn2.getInputStream()); + Type typeToken = new TypeToken>(){}.getType(); + WebQueryResponse result = gson.fromJson(in, typeToken); + TestClass[] results = result.getResultList(); assertEquals(2, results.length); assertEquals("fluff1", results[0].getKey1()); assertEquals(42, results[0].getKey2()); assertEquals("fluff2", results[1].getKey1()); assertEquals(43, results[1].getKey2()); - assertEquals("application/json; charset=UTF-8", conn.getContentType()); - - verify(mockQuery).where(eq(expr)); - verify(mockQuery).sort(key1, SortDirection.DESCENDING); - verify(mockQuery).limit(42); - verify(mockQuery).execute(); - verifyNoMoreInteractions(mockQuery); + assertEquals("application/json; charset=UTF-8", conn2.getContentType()); + verify(mockMongoQuery).execute(); + verifyNoMoreInteractions(mockMongoQuery); } @Test - public void unauthorizedFindAllPojos() throws Exception { + public void unauthorizedPrepareStmt() throws Exception { + String failMsg = "thermostat-prepare-statement role missing, expected Forbidden!"; + doUnauthorizedTest("prepare-statement", failMsg); + } + + @Test + public void unauthorizedExecutePreparedQuery() throws Exception { String failMsg = "thermostat-read role missing, expected Forbidden!"; - doUnauthorizedTest("find-all", failMsg); + doUnauthorizedTest("query-execute", failMsg); } private void doUnauthorizedTest(String pathForEndPoint, String failMessage) throws Exception {