From 9610d1b785cb05e06b94eb2c749e7a7f80dcc90b Mon Sep 17 00:00:00 2001 From: mattab <matthieu.aubry@gmail.com> Date: Sat, 6 Dec 2014 01:09:12 +1300 Subject: [PATCH] Added integration test for the model, to test the SQL query generated --- plugins/Live/Model.php | 220 ++++++++++-------- .../tests/{Integration => System}/APITest.php | 0 plugins/Live/tests/System/ModelTest.php | 123 ++++++++++ tests/PHPUnit/Integration/SegmentTest.php | 30 +-- 4 files changed, 261 insertions(+), 112 deletions(-) rename plugins/Live/tests/{Integration => System}/APITest.php (100%) create mode 100644 plugins/Live/tests/System/ModelTest.php diff --git a/plugins/Live/Model.php b/plugins/Live/Model.php index 2353450572..2d438bff9b 100644 --- a/plugins/Live/Model.php +++ b/plugins/Live/Model.php @@ -36,105 +36,10 @@ class Model */ public function queryLogVisits($idSite, $period, $date, $segment, $countVisitorsToFetch, $visitorId, $minTimestamp, $filterSortOrder) { - $where = $whereBind = array(); - - list($whereClause, $idSites) = $this->getIdSitesWhereClause($idSite); - - $where[] = $whereClause; - $whereBind = $idSites; - - if (strtolower($filterSortOrder) !== 'asc') { - $filterSortOrder = 'DESC'; - } - - $orderBy = "idsite, visit_last_action_time " . $filterSortOrder; - $orderByParent = "sub.visit_last_action_time " . $filterSortOrder; - - if (!empty($visitorId)) { - $where[] = "log_visit.idvisitor = ? "; - $whereBind[] = @Common::hex2bin($visitorId); - } - - if (!empty($minTimestamp)) { - $where[] = "log_visit.visit_last_action_time > ? "; - $whereBind[] = date("Y-m-d H:i:s", $minTimestamp); - } - - // If no other filter, only look at the last 24 hours of stats - if (empty($visitorId) - && empty($countVisitorsToFetch) - && empty($period) - && empty($date) - ) { - $period = 'day'; - $date = 'yesterdaySameTime'; - } - - // SQL Filter with provided period - if (!empty($period) && !empty($date)) { - $currentSite = new Site($idSite); - $currentTimezone = $currentSite->getTimezone(); - - $dateString = $date; - if ($period == 'range') { - $processedPeriod = new Range('range', $date); - if ($parsedDate = Range::parseDateRange($date)) { - $dateString = $parsedDate[2]; - } - } else { - $processedDate = Date::factory($date); - if ($date == 'today' - || $date == 'now' - || $processedDate->toString() == Date::factory('now', $currentTimezone)->toString() - ) { - $processedDate = $processedDate->subDay(1); - } - $processedPeriod = Period\Factory::build($period, $processedDate); - } - $dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone); - $where[] = "log_visit.visit_last_action_time >= ?"; - $whereBind[] = $dateStart->toString('Y-m-d H:i:s'); - - if (!in_array($date, array('now', 'today', 'yesterdaySameTime')) - && strpos($date, 'last') === false - && strpos($date, 'previous') === false - && Date::factory($dateString)->toString('Y-m-d') != Date::factory('now', $currentTimezone)->toString() - ) { - $dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone); - $where[] = " log_visit.visit_last_action_time <= ?"; - $dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s'); - $whereBind[] = $dateEndString; - } - } + list($sql, $bind) = $this->makeLogVisitsQueryString($idSite, $period, $date, $segment, $countVisitorsToFetch, $visitorId, $minTimestamp, $filterSortOrder); - if (count($where) > 0) { - $where = join(" - AND ", $where); - } else { - $where = false; - } - - $segment = new Segment($segment, $idSite); - - // Subquery to use the indexes for ORDER BY - $select = "log_visit.*"; - $from = "log_visit"; - $groupBy = false; - $subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy, $groupBy); - - $sqlLimit = $countVisitorsToFetch >= 1 ? " LIMIT 0, " . (int)$countVisitorsToFetch : ""; - - // Group by idvisit so that a visitor converting 2 goals only appears once - $sql = " - SELECT sub.* FROM ( - " . $subQuery['sql'] . " - $sqlLimit - ) AS sub - GROUP BY sub.idvisit - ORDER BY $orderByParent - "; try { - $data = Db::fetchAll($sql, $subQuery['bind']); + $data = Db::fetchAll($sql, $bind); return $data; } catch (Exception $e) { echo $e->getMessage(); @@ -265,4 +170,125 @@ class Model } return $visitorId; } + + /** + * @param $idSite + * @param $period + * @param $date + * @param $segment + * @param $countVisitorsToFetch + * @param $visitorId + * @param $minTimestamp + * @param $filterSortOrder + * @return array + * @throws Exception + */ + public function makeLogVisitsQueryString($idSite, $period, $date, $segment, $countVisitorsToFetch, $visitorId, $minTimestamp, $filterSortOrder) + { + $where = $whereBind = array(); + + list($whereClause, $idSites) = $this->getIdSitesWhereClause($idSite); + + $where[] = $whereClause; + $whereBind = $idSites; + + if (!empty($visitorId)) { + $where[] = "log_visit.idvisitor = ? "; + $whereBind[] = @Common::hex2bin($visitorId); + } + + if (!empty($minTimestamp)) { + $where[] = "log_visit.visit_last_action_time > ? "; + $whereBind[] = date("Y-m-d H:i:s", $minTimestamp); + } + + // If no other filter, only look at the last 24 hours of stats + if (empty($visitorId) + && empty($countVisitorsToFetch) + && empty($period) + && empty($date) + ) { + $period = 'day'; + $date = 'yesterdaySameTime'; + } + + // SQL Filter with provided period + if (!empty($period) && !empty($date)) { + $currentSite = $this->makeSite($idSite); + $currentTimezone = $currentSite->getTimezone(); + + $dateString = $date; + if ($period == 'range') { + $processedPeriod = new Range('range', $date); + if ($parsedDate = Range::parseDateRange($date)) { + $dateString = $parsedDate[2]; + } + } else { + $processedDate = Date::factory($date); + if ($date == 'today' + || $date == 'now' + || $processedDate->toString() == Date::factory('now', $currentTimezone)->toString() + ) { + $processedDate = $processedDate->subDay(1); + } + $processedPeriod = Period\Factory::build($period, $processedDate); + } + $dateStart = $processedPeriod->getDateStart()->setTimezone($currentTimezone); + $where[] = "log_visit.visit_last_action_time >= ?"; + $whereBind[] = $dateStart->toString('Y-m-d H:i:s'); + + if (!in_array($date, array('now', 'today', 'yesterdaySameTime')) + && strpos($date, 'last') === false + && strpos($date, 'previous') === false + && Date::factory($dateString)->toString('Y-m-d') != Date::factory('now', $currentTimezone)->toString() + ) { + $dateEnd = $processedPeriod->getDateEnd()->setTimezone($currentTimezone); + $where[] = " log_visit.visit_last_action_time <= ?"; + $dateEndString = $dateEnd->addDay(1)->toString('Y-m-d H:i:s'); + $whereBind[] = $dateEndString; + } + } + + if (count($where) > 0) { + $where = join(" + AND ", $where); + } else { + $where = false; + } + + if (strtolower($filterSortOrder) !== 'asc') { + $filterSortOrder = 'DESC'; + } + $segment = new Segment($segment, $idSite); + + // Subquery to use the indexes for ORDER BY + $select = "log_visit.*"; + $from = "log_visit"; + $groupBy = false; + $limit = $countVisitorsToFetch >= 1 ? (int)$countVisitorsToFetch : 0; + $orderBy = "idsite, visit_last_action_time " . $filterSortOrder; + $orderByParent = "sub.visit_last_action_time " . $filterSortOrder; + + $subQuery = $segment->getSelectQuery($select, $from, $where, $whereBind, $orderBy, $groupBy, $limit); + + $bind = $subQuery['bind']; + // Group by idvisit so that a visitor converting 2 goals only appears once + $sql = " + SELECT sub.* FROM ( + " . $subQuery['sql'] . " + ) AS sub + GROUP BY sub.idvisit + ORDER BY $orderByParent + "; + return array($sql, $bind); + } + + /** + * @param $idSite + * @return Site + */ + protected function makeSite($idSite) + { + return new Site($idSite); + } } \ No newline at end of file diff --git a/plugins/Live/tests/Integration/APITest.php b/plugins/Live/tests/System/APITest.php similarity index 100% rename from plugins/Live/tests/Integration/APITest.php rename to plugins/Live/tests/System/APITest.php diff --git a/plugins/Live/tests/System/ModelTest.php b/plugins/Live/tests/System/ModelTest.php new file mode 100644 index 0000000000..d5dea34767 --- /dev/null +++ b/plugins/Live/tests/System/ModelTest.php @@ -0,0 +1,123 @@ +<?php +/** + * Piwik - free/libre analytics platform + * + * @link http://piwik.org + * @license http://www.gnu.org/licenses/gpl-3.0.html GPL v3 or later + */ + +namespace Piwik\Plugins\Live\tests\Integration; + +use Piwik\Access; +use Piwik\Common; +use Piwik\Plugins\Live\Model; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\FakeAccess; +use Piwik\Tests\Framework\TestCase\SystemTestCase; +use Piwik\Tests\Integration\SegmentTest; + +/** + * @group LiveModelTest + * @group Plugins + */ +class ModelTest extends SystemTestCase +{ + function setUp() + { + $this->setSuperUser(); + Fixture::createWebsite('2010-01-01'); + } + + public function test_makeLogVisitsQueryString() + { + $model = new Model(); + list($sql, $bind) = $model->makeLogVisitsQueryString( + $idSite = 1, + $period = 'month', + $date = '2010-01-01', + $segment = false, + $countVisitorsToFetch = 100, + $visitorId = false, + $minTimestamp = false, + $filterSortOrder = false + ); + $expectedSql = ' SELECT sub.* FROM + ( + SELECT log_visit.* + FROM piwiktests_log_visit AS log_visit + WHERE log_visit.idsite in (?) + AND log_visit.visit_last_action_time >= ? + AND log_visit.visit_last_action_time <= ? + ORDER BY idsite, visit_last_action_time DESC + LIMIT 100 + ) AS sub + GROUP BY sub.idvisit + ORDER BY sub.visit_last_action_time DESC + '; + $expectedBind = array( + '1', + '2010-01-01 00:00:00', + '2010-02-01 00:00:00', + ); + $this->assertEquals(SegmentTest::removeExtraWhiteSpaces($expectedSql), SegmentTest::removeExtraWhiteSpaces($sql)); + $this->assertEquals(SegmentTest::removeExtraWhiteSpaces($expectedBind), SegmentTest::removeExtraWhiteSpaces($bind)); + } + + + public function test_makeLogVisitsQueryString_whenSegment() + { + $model = new Model(); + list($sql, $bind) = $model->makeLogVisitsQueryString( + $idSite = 1, + $period = 'month', + $date = '2010-01-01', + $segment = 'customVariablePageName1==Test', + $countVisitorsToFetch = 100, + $visitorId = 'abc', + $minTimestamp = false, + $filterSortOrder = false + ); + $expectedSql = ' SELECT sub.* FROM + ( + + SELECT log_inner.* + FROM ( + SELECT log_visit.* + FROM piwiktests_log_visit AS log_visit + LEFT JOIN piwiktests_log_link_visit_action AS log_link_visit_action + ON log_link_visit_action.idvisit = log_visit.idvisit + WHERE ( log_visit.idsite in (?) + AND log_visit.idvisitor = ? + AND log_visit.visit_last_action_time >= ? + AND log_visit.visit_last_action_time <= ? ) + AND ( log_link_visit_action.custom_var_k1 = ? ) + GROUP BY log_visit.idvisit + ORDER BY NULL + LIMIT 100 + ) AS log_inner + ORDER BY idsite, visit_last_action_time DESC + LIMIT 100 + ) AS sub + GROUP BY sub.idvisit + ORDER BY sub.visit_last_action_time DESC + '; + $expectedBind = array( + '1', + Common::hex2bin('abc'), + '2010-01-01 00:00:00', + '2010-02-01 00:00:00', + 'Test', + ); + $this->assertEquals(SegmentTest::removeExtraWhiteSpaces($expectedSql), SegmentTest::removeExtraWhiteSpaces($sql)); + $this->assertEquals(SegmentTest::removeExtraWhiteSpaces($expectedBind), SegmentTest::removeExtraWhiteSpaces($bind)); + } + + + protected function setSuperUser() + { + $pseudoMockAccess = new FakeAccess(); + FakeAccess::$superUser = true; + Access::setSingletonInstance($pseudoMockAccess); + } + +} \ No newline at end of file diff --git a/tests/PHPUnit/Integration/SegmentTest.php b/tests/PHPUnit/Integration/SegmentTest.php index e0fd9cd3c4..864420a161 100644 --- a/tests/PHPUnit/Integration/SegmentTest.php +++ b/tests/PHPUnit/Integration/SegmentTest.php @@ -36,11 +36,11 @@ class SegmentTest extends IntegrationTestCase parent::tearDown(); } - protected function _filterWhiteSpaces($valueToFilter) + static public function removeExtraWhiteSpaces($valueToFilter) { if (is_array($valueToFilter)) { foreach ($valueToFilter as $key => $value) { - $valueToFilter[$key] = $this->_filterWhiteSpaces($value); + $valueToFilter[$key] = self::removeExtraWhiteSpaces($value); } return $valueToFilter; } else { @@ -117,11 +117,11 @@ class SegmentTest extends IntegrationTestCase $segment = new Segment($segment, $idSites = array()); $sql = $segment->getSelectQuery($select, $from, false); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($sql)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($sql)); // calling twice should give same results $sql = $segment->getSelectQuery($select, array($from)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($sql)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($sql)); $this->assertEquals(32, strlen($segment->getHash())); } @@ -150,7 +150,7 @@ class SegmentTest extends IntegrationTestCase ( log_visit.custom_var_k1 = ? AND log_visit.visitor_returning = ? )", "bind" => array(1, 'Test', 0)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinVisitOnAction() @@ -178,7 +178,7 @@ class SegmentTest extends IntegrationTestCase ( log_link_visit_action.custom_var_k1 = ? AND log_visit.visitor_returning = ? )", "bind" => array(1, 'Test', 0)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinActionOnVisit() @@ -214,7 +214,7 @@ class SegmentTest extends IntegrationTestCase ) AS log_inner", "bind" => array(1, 'Test', 0)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinConversionOnAction() @@ -242,7 +242,7 @@ class SegmentTest extends IntegrationTestCase ( log_link_visit_action.custom_var_k1 = ? AND log_conversion.idgoal = ? AND log_link_visit_action.custom_var_k2 = ? )", "bind" => array(1, 'Test', 1, 'Test2')); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinActionOnConversion() @@ -270,7 +270,7 @@ class SegmentTest extends IntegrationTestCase ( ( log_conversion.idgoal IS NULL OR log_conversion.idgoal <> ? ) AND log_link_visit_action.custom_var_k1 = ? AND log_conversion.idgoal = ? )", "bind" => array(1, 2, 'Test', 1)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinConversionOnVisit() @@ -305,7 +305,7 @@ class SegmentTest extends IntegrationTestCase ) AS log_inner", "bind" => array(1, 1)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinConversionOnly() @@ -332,7 +332,7 @@ class SegmentTest extends IntegrationTestCase ( log_conversion.idgoal = ? )", "bind" => array(1, 1)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } public function test_getSelectQuery_whenJoinVisitOnConversion() @@ -360,7 +360,7 @@ class SegmentTest extends IntegrationTestCase ( (log_conversion.idgoal = ? OR HOUR(log_visit.visit_last_action_time) = ? ))", "bind" => array(1, 1, 12)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } /** @@ -391,7 +391,7 @@ class SegmentTest extends IntegrationTestCase HOUR(log_visit.visit_last_action_time) = ? AND log_conversion.idgoal = ? ", "bind" => array(12, 1)); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } /** @@ -429,7 +429,7 @@ class SegmentTest extends IntegrationTestCase ) AS log_inner", "bind" => array(1, 12, 'Test')); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } /** @@ -496,6 +496,6 @@ class SegmentTest extends IntegrationTestCase LIMIT 33", "bind" => array(1, 'Test')); - $this->assertEquals($this->_filterWhiteSpaces($expected), $this->_filterWhiteSpaces($query)); + $this->assertEquals($this->removeExtraWhiteSpaces($expected), $this->removeExtraWhiteSpaces($query)); } } -- GitLab