From f3c36ee5c983a93ffb99dd1201b9e9831aa0eb11 Mon Sep 17 00:00:00 2001 From: cweiske Date: Sat, 24 Oct 2009 08:07:55 +0000 Subject: [PATCH] Implement voting service and unit tests for it git-svn-id: https://semanticscuttle.svn.sourceforge.net/svnroot/semanticscuttle/trunk@401 b3834d28-1941-0410-a4f8-b48e95affb8f --- src/SemanticScuttle/Service/Vote.php | 137 +++++++++-- tests/VoteTest.php | 353 +++++++++++++++++++++++++++ 2 files changed, 474 insertions(+), 16 deletions(-) create mode 100644 tests/VoteTest.php diff --git a/src/SemanticScuttle/Service/Vote.php b/src/SemanticScuttle/Service/Vote.php index 3c4a144..4a5baab 100644 --- a/src/SemanticScuttle/Service/Vote.php +++ b/src/SemanticScuttle/Service/Vote.php @@ -1,4 +1,15 @@ + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ /** * SemanticScuttle voting system. @@ -15,16 +26,14 @@ * sure lookups are really fast, since every bookmarks * in a list shows its voting. * + * @category Bookmarking + * @package SemanticScuttle * @author Christian Weiske + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle */ -class SemanticScuttle_Service_Vote extends SemanticScuttle_Service +class SemanticScuttle_Service_Vote extends SemanticScuttle_DbService { - /** - * Database object - * - * @var sql_db - */ - protected $db; @@ -62,14 +71,39 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service /** * Returns the sum of votes for the given bookmark. * + * @internal + * Uses the "votes" table to retrieve the votes, which + * has high costs. It is more efficient to get the sum of + * all votes for a bookmark from the bookmarks table, + * field bVoting. + * * @param integer $bookmark Bookmark ID * * @return integer Vote (can be positive, 0 or negative) */ public function getVoting($bookmark) { - //FIXME - } + $query = 'SELECT SUM(vote) as sum FROM ' . $this->getTableName() + . ' WHERE bid = "' . $this->db->sql_escape($bookmark) . '"'; + + if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) { + message_die( + GENERAL_ERROR, 'Could not get voting', + '', __LINE__, __FILE__, $query, $this->db + ); + //FIXME: throw exception + return false; + } + + $row = $this->db->sql_fetchrow($dbres); + $this->db->sql_freeresult($dbres); + + if (!$row) { + return false; + } + + return (int)$row['sum']; + }//public function getVoting(..) @@ -83,7 +117,26 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service */ public function getVotes($bookmark) { - //FIXME + $query = 'SELECT COUNT(vote) as count FROM ' . $this->getTableName() + . ' WHERE bid = "' . $this->db->sql_escape($bookmark) . '"'; + + if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) { + message_die( + GENERAL_ERROR, 'Could not get vote count', + '', __LINE__, __FILE__, $query, $this->db + ); + //FIXME: throw exception + return false; + } + + $row = $this->db->sql_fetchrow($dbres); + $this->db->sql_freeresult($dbres); + + if (!$row) { + return false; + } + + return (int)$row['count']; } @@ -99,7 +152,28 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service */ public function hasVoted($bookmark, $user) { - //FIXME + $query = 'SELECT COUNT(vote) as count FROM ' . $this->getTableName() + . ' WHERE' + . ' bid = "' . $this->db->sql_escape($bookmark) . '"' + . ' AND uid = "' . $this->db->sql_escape($user) . '"'; + + if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) { + message_die( + GENERAL_ERROR, 'Could not get vote count', + '', __LINE__, __FILE__, $query, $this->db + ); + //FIXME: throw exception + return false; + } + + $row = $this->db->sql_fetchrow($dbres); + $this->db->sql_freeresult($dbres); + + if (!$row) { + return false; + } + + return (int)$row['count'] == 1; } @@ -111,11 +185,32 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service * @param integer $bookmark Bookmark ID * @param integer $user User ID * - * @return integer Either 1 or -1. + * @return integer Either 1 or -1, null when not voted. */ public function getVote($bookmark, $user) { - //FIXME + $query = 'SELECT vote FROM ' . $this->getTableName() + . ' WHERE' + . ' bid = "' . $this->db->sql_escape($bookmark) . '"' + . ' AND uid = "' . $this->db->sql_escape($user) . '"'; + + if (!($dbres = $this->db->sql_query_limit($query, 1, 0))) { + message_die( + GENERAL_ERROR, 'Could not get vote count', + '', __LINE__, __FILE__, $query, $this->db + ); + //FIXME: throw exception + return false; + } + + $row = $this->db->sql_fetchrow($dbres); + $this->db->sql_freeresult($dbres); + + if (!$row) { + return null; + } + + return $row['vote']; } @@ -149,7 +244,7 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service return false; } - $dbresult = $this->db->sql_query( + $res = $this->db->sql_query( 'INSERT INTO ' . $this->getTableName() . ' SET' . ' bid = ' . (int)$bookmark @@ -157,8 +252,17 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service . ',vote = ' . (int)$vote ); //FIXME: check for sql error - $this->db->sql_freeresult(); - //FIXME: update bookmarks table + $this->db->sql_freeresult($res); + + //update bookmark table + $bm = SemanticScuttle_Service_Factory::get('Bookmark'); + $res = $this->db->sql_query( + 'UPDATE ' . $bm->getTableName() + . ' SET bVoting = bVoting + ' . (int)$vote + ); + $this->db->sql_freeresult($res); + + return true; } @@ -172,6 +276,7 @@ class SemanticScuttle_Service_Vote extends SemanticScuttle_Service */ public function rewriteVotings() { + throw new Exception('Not implemented yet'); //FIXME //SELECT bid, SUM( vote ) FROM sc_votes GROUP BY bid } diff --git a/tests/VoteTest.php b/tests/VoteTest.php new file mode 100644 index 0000000..90fae48 --- /dev/null +++ b/tests/VoteTest.php @@ -0,0 +1,353 @@ + + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ + +require_once 'prepare.php'; +require_once 'PHPUnit/Framework.php'; + +if (!defined('PHPUnit_MAIN_METHOD')) { + define('PHPUnit_MAIN_METHOD', 'VoteTest::main'); +} + +/** + * Unit tests for the SemanticScuttle voting system. + * + * @category Bookmarking + * @package SemanticScuttle + * @author Christian Weiske + * @license GPL http://www.gnu.org/licenses/gpl.html + * @link http://sourceforge.net/projects/semanticscuttle + */ +class VoteTest extends PHPUnit_Framework_TestCase +{ + /** + * Vote service instance to test. + * + * @var SemanticScuttle_Service_Vote + */ + protected $vs = null; + + + + /** + * Used to run this test class standalone + * + * @return void + */ + public static function main() + { + require_once 'PHPUnit/TextUI/TestRunner.php'; + PHPUnit_TextUI_TestRunner::run( + new PHPUnit_Framework_TestSuite('VoteTest') + ); + } + + + + public function setUp() + { + //FIXME: create true new instance + $this->vs = SemanticScuttle_Service_Factory::get('Vote'); + } + + + + /** + * Create a new bookmark. + * + * @return integer ID of bookmark + */ + protected function addBookmark() + { + $bs = SemanticScuttle_Service_Factory::get('Bookmark'); + $rand = rand(); + $bid = $bs->addBookmark( + 'http://example.org/' . $rand, + 'unittest bookmark #' . $rand, + 'description', + null, + 0, + array('unittest') + ); + return $bid; + } + + + + /** + * Test getVoting() when no votes have been cast. + * + * @return void + */ + public function testGetVotingZero() + { + $bid = $this->addBookmark(); + $this->assertEquals(0, $this->vs->getVoting($bid)); + } + + + + /** + * Test getVoting() when one positive vote has been cast. + * + * @return void + */ + public function testGetVotingOne() + { + $bid = $this->addBookmark(); + $this->vs->vote($bid, 1, 1); + $this->assertEquals(1, $this->vs->getVoting($bid)); + } + + + + /** + * Test getVoting() when one nevative vote has been cast. + * + * @return void + */ + public function testGetVotingMinusOne() + { + $bid = $this->addBookmark(); + $this->vs->vote($bid, 1, -1); + $this->assertEquals(-1, $this->vs->getVoting($bid)); + } + + + + /** + * Test getVoting() when several votes have been cast. + * + * @return void + */ + public function testGetVotingSum() + { + $bid = $this->addBookmark(); + $this->vs->vote($bid, 1, 1); + $this->vs->vote($bid, 2, -1); + $this->vs->vote($bid, 3, 1); + $this->vs->vote($bid, 4, 1); + $this->assertEquals(2, $this->vs->getVoting($bid)); + } + + + + /** + * Test getVotes() when no vote has been cast. + * + * @return void + */ + public function testGetVotesZero() + { + $bid = $this->addBookmark(); + $this->assertEquals(0, $this->vs->getVotes($bid)); + } + + + + /** + * Test getVotes() when one vote has been cast. + * + * @return void + */ + public function testGetVotesOne() + { + $bid = $this->addBookmark(); + $this->vs->vote($bid, 1, 1); + $this->assertEquals(1, $this->vs->getVotes($bid)); + } + + + + /** + * Test getVoting() when several votes have been cast. + * + * @return void + */ + public function testGetVotesMultiple() + { + $bid = $this->addBookmark(); + $this->vs->vote($bid, 1, 1); + $this->vs->vote($bid, 2, -1); + $this->vs->vote($bid, 3, 1); + $this->vs->vote($bid, 4, 1); + $this->assertEquals(4, $this->vs->getVotes($bid)); + } + + + + /** + * Test hasVoted() when a no vote has been cast + * + * @return void + */ + public function testHasVotedFalse() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->assertFalse($this->vs->hasVoted($bid, $uid)); + } + + + + /** + * Test hasVoted() when a vote has been cast + * + * @return void + */ + public function testHasVotedTrue() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->vs->vote($bid, $uid, 1); + $this->assertTrue($this->vs->hasVoted($bid, $uid)); + } + + + + /** + * Test hasVoted() when a vote has been cast for other bookmarks + * + * @return void + */ + public function testHasVotedFalseOthers() + { + $uid = 1; + $bid = $this->addBookmark(); + $bid2 = $this->addBookmark(); + $bid3 = $this->addBookmark(); + + $this->vs->vote($bid, $uid, 1); + $this->vs->vote($bid3, $uid, 1); + + $this->assertFalse($this->vs->hasVoted($bid2, $uid)); + } + + + + /** + * Test getVote() when no vote has been cast. + * + * @return void + */ + public function testGetVoteNone() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->assertNull($this->vs->getVote($bid, $uid)); + } + + + + /** + * Test getVote() when a positive vote has been cast. + * + * @return void + */ + public function testGetVoteOne() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->vs->vote($bid, $uid, 1); + $this->assertEquals(1, $this->vs->getVote($bid, $uid)); + } + + + + /** + * Test getVote() when a negavitve vote has been cast. + * + * @return void + */ + public function testGetVoteMinusOne() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->vs->vote($bid, $uid, -1); + $this->assertEquals(-1, $this->vs->getVote($bid, $uid)); + } + + + + /** + * Test vote() with wrong vote parameter + * + * @return void + */ + public function testVoteWrongVoteParam() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->assertFalse($this->vs->vote($bid, $uid, 2)); + $this->assertFalse($this->vs->vote($bid, $uid, 0)); + $this->assertFalse($this->vs->vote($bid, $uid, 1.5)); + $this->assertFalse($this->vs->vote($bid, $uid, -1.1)); + $this->assertFalse($this->vs->vote($bid, $uid, 'yes')); + $this->assertFalse($this->vs->vote($bid, $uid, 'no')); + } + + + + /** + * Test vote() when the user already has voted + * + * @return void + */ + public function testVoteHasVoted() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->assertTrue($this->vs->vote($bid, $uid, 1)); + $this->assertFalse($this->vs->vote($bid, $uid, 1)); + + $bid = $this->addBookmark(); + $this->assertTrue($this->vs->vote($bid, $uid, -1)); + $this->assertFalse($this->vs->vote($bid, $uid, 1)); + } + + + + /** + * Test vote() with positive vote + * + * @return void + */ + public function testVotePositive() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->assertTrue($this->vs->vote($bid, $uid, 1)); + $this->assertEquals(1, $this->vs->getVote($bid, $uid)); + } + + + + /** + * Test vote() with negative vote + * + * @return void + */ + public function testVoteNegative() + { + $uid = 1; + $bid = $this->addBookmark(); + $this->assertTrue($this->vs->vote($bid, $uid, -1)); + $this->assertEquals(-1, $this->vs->getVote($bid, $uid)); + } + +}//class VoteTest extends PHPUnit_Framework_TestCase + + +if (PHPUnit_MAIN_METHOD == 'VoteTest::main') { + VoteTest::main(); +} +?> \ No newline at end of file