diff options
author | Thomas Steur <thomas.steur@googlemail.com> | 2014-12-04 05:04:02 +0300 |
---|---|---|
committer | Thomas Steur <thomas.steur@googlemail.com> | 2014-12-04 05:04:02 +0300 |
commit | 9d71fc8e92e5b434fd69c7ab4b83a69169064cf3 (patch) | |
tree | 8cb2798746e4defb1a978cd758df72006ec419ae /plugins/BulkTracking | |
parent | 784b738f9c4a92c42a9d8e6b85c28a82a3af0a2f (diff) |
Tracker refactoring
Diffstat (limited to 'plugins/BulkTracking')
18 files changed, 1495 insertions, 0 deletions
diff --git a/plugins/BulkTracking/.gitignore b/plugins/BulkTracking/.gitignore new file mode 100644 index 0000000000..c8c9480010 --- /dev/null +++ b/plugins/BulkTracking/.gitignore @@ -0,0 +1 @@ +tests/System/processed/*xml
\ No newline at end of file diff --git a/plugins/BulkTracking/BulkTracking.php b/plugins/BulkTracking/BulkTracking.php new file mode 100644 index 0000000000..b55a681030 --- /dev/null +++ b/plugins/BulkTracking/BulkTracking.php @@ -0,0 +1,84 @@ +<?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\BulkTracking; +use Piwik\Plugins\BulkTracking\Tracker\Handler; +use Piwik\Plugins\BulkTracking\Tracker\Requests; +use Piwik\Tracker\RequestSet; + +class BulkTracking extends \Piwik\Plugin +{ + /** + * @var Requests + */ + private $requests; + + /** + * @see Piwik\Plugin::getListHooksRegistered + */ + public function getListHooksRegistered() + { + return array( + 'Tracker.newHandler' => 'setHandlerIfBulkRequest', + 'Tracker.initRequestSet' => 'initRequestSet', + ); + } + + public function setRequests(Requests $requests) + { + $this->requests = $requests; + } + + public function initRequestSet(RequestSet $requestSet) + { + if ($this->isUsingBulkRequest()) { + + $bulk = $this->buildBulkRequests(); + + list($requests, $token) = $bulk->initRequestsAndTokenAuth($bulk->getRawBulkRequest()); + + if ($bulk->requiresAuthentication()) { + $bulk->authenticateRequests($requests); + } + + if (!$requestSet->getTokenAuth()) { + $requestSet->setTokenAuth($token); + } + + $requestSet->setRequests($requests); + } + } + + public function setHandlerIfBulkRequest(&$handler) + { + if (!is_null($handler)) { + return; + } + + if ($this->isUsingBulkRequest()) { + $handler = new Handler(); + } + } + + private function isUsingBulkRequest() + { + $requests = $this->buildBulkRequests(); + $rawData = $requests->getRawBulkRequest(); + + return $requests->isUsingBulkRequest($rawData); + } + + private function buildBulkRequests() + { + if (!is_null($this->requests)) { + return $this->requests; + } + + return new Requests(); + } +} diff --git a/plugins/BulkTracking/README.md b/plugins/BulkTracking/README.md new file mode 100644 index 0000000000..ecb28c2d7b --- /dev/null +++ b/plugins/BulkTracking/README.md @@ -0,0 +1,18 @@ +# Piwik BulkTracking Plugin + +## Description + +Add your plugin description here. + +## FAQ + +__My question?__ +My answer + +## Changelog + +Here goes the changelog text. + +## Support + +Please direct any feedback to ...
\ No newline at end of file diff --git a/plugins/BulkTracking/Tracker/Handler.php b/plugins/BulkTracking/Tracker/Handler.php new file mode 100644 index 0000000000..e260237093 --- /dev/null +++ b/plugins/BulkTracking/Tracker/Handler.php @@ -0,0 +1,82 @@ +<?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\BulkTracking\Tracker; + +use Piwik\Tracker; +use Piwik\Tracker\RequestSet; +use Piwik\Tracker\TrackerConfig; +use Exception; + +class Handler extends Tracker\Handler +{ + private $transactionId = null; + + public function __construct() + { + $this->setResponse(new Response()); + } + + public function onStartTrackRequests(Tracker $tracker, RequestSet $requestSet) + { + if ($this->isTransactionSupported()) { + $this->beginTransaction(); + } + } + + public function onAllRequestsTracked(Tracker $tracker, RequestSet $requestSet) + { + $this->commitTransaction(); + + // Do not run schedule task if we are importing logs or doing custom tracking (as it could slow down) + } + + public function onException(Tracker $tracker, RequestSet $requestSet, Exception $e) + { + $this->rollbackTransaction(); + parent::onException($tracker, $requestSet, $e); + } + + private function beginTransaction() + { + if (empty($this->transactionId)) { + $this->transactionId = $this->getDb()->beginTransaction(); + } + } + + private function commitTransaction() + { + if (!empty($this->transactionId)) { + $this->getDb()->commit($this->transactionId); + $this->transactionId = null; + } + } + + private function rollbackTransaction() + { + if (!empty($this->transactionId)) { + $this->getDb()->rollback($this->transactionId); + $this->transactionId = null; + } + } + + private function getDb() + { + return Tracker::getDatabase(); + } + + /** + * @return bool + */ + private function isTransactionSupported() + { + return (bool) TrackerConfig::getConfigValue('bulk_requests_use_transaction'); + } + +} diff --git a/plugins/BulkTracking/Tracker/Requests.php b/plugins/BulkTracking/Tracker/Requests.php new file mode 100644 index 0000000000..af4eb23a15 --- /dev/null +++ b/plugins/BulkTracking/Tracker/Requests.php @@ -0,0 +1,111 @@ +<?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\BulkTracking\Tracker; + +use Exception; +use Piwik\Common; +use Piwik\Tracker\Request; +use Piwik\Tracker\TrackerConfig; + +class Requests +{ + + public function requiresAuthentication() + { + $requiresAuth = TrackerConfig::getConfigValue('bulk_requests_require_authentication'); + + return !empty($requiresAuth); + } + + /** + * @param Request[] $requests + * @throws Exception + */ + public function authenticateRequests($requests) + { + foreach ($requests as $request) { + $this->checkTokenAuthNotEmpty($request->getTokenAuth()); + + if (!$request->isAuthenticated()) { + $msg = sprintf("token_auth specified does not have Admin permission for idsite=%s", $request->getIdSite()); + throw new Exception($msg); + } + } + } + + private function checkTokenAuthNotEmpty($token) + { + if (empty($token)) { + throw new Exception("token_auth must be specified when using Bulk Tracking Import. " + . " See <a href='http://developer.piwik.org/api-reference/tracking-api'>Tracking Doc</a>"); + } + } + + /** + * @return string + */ + public function getRawBulkRequest() + { + return file_get_contents("php://input"); + } + + public function isUsingBulkRequest($rawData) + { + if (!empty($rawData)) { + return strpos($rawData, '"requests"') || strpos($rawData, "'requests'"); + } + + return false; + } + + public function getRequestsArrayFromBulkRequest($rawData) + { + $rawData = trim($rawData); + $rawData = Common::sanitizeLineBreaks($rawData); + + // POST data can be array of string URLs or array of arrays w/ visit info + $jsonData = json_decode($rawData, $assoc = true); + + $tokenAuth = Common::getRequestVar('token_auth', false, 'string', $jsonData); + + $requests = array(); + if (isset($jsonData['requests'])) { + $requests = $jsonData['requests']; + } + + return array($requests, $tokenAuth); + } + + public function initRequestsAndTokenAuth($rawData) + { + list($requests, $tokenAuth) = $this->getRequestsArrayFromBulkRequest($rawData); + + $validRequests = array(); + + if (!empty($requests)) { + + foreach ($requests as $index => $request) { + // if a string is sent, we assume its a URL and try to parse it + if (is_string($request)) { + $params = array(); + + $url = @parse_url($request); + if (!empty($url['query'])) { + @parse_str($url['query'], $params); + $validRequests[] = new Request($params, $tokenAuth); + } + } else { + $validRequests[] = new Request($request, $tokenAuth); + } + } + } + + return array($validRequests, $tokenAuth); + } +} diff --git a/plugins/BulkTracking/Tracker/Response.php b/plugins/BulkTracking/Tracker/Response.php new file mode 100644 index 0000000000..fd3e304a5e --- /dev/null +++ b/plugins/BulkTracking/Tracker/Response.php @@ -0,0 +1,77 @@ +<?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\BulkTracking\Tracker; + +use Exception; +use Piwik\Common; +use Piwik\Tracker; + +class Response extends Tracker\Response +{ + /** + * Echos an error message & other information, then exits. + * + * @param Tracker $tracker + * @param Exception $e + * @param int $statusCode eg 500 + */ + public function outputException(Tracker $tracker, Exception $e, $statusCode) + { + Common::sendResponseCode($statusCode); + + $this->logExceptionToErrorLog($e); + + $result = $this->formatException($tracker, $e); + + echo json_encode($result); + } + + public function outputResponse(Tracker $tracker) + { + if ($this->hasAlreadyPrintedOutput()) { + return; + } + + $result = $this->formatResponse($tracker); + + echo json_encode($result); + } + + public function getOutput() + { + Common::sendHeader('Content-Type: application/json'); + + return parent::getOutput(); + } + + private function formatException(Tracker $tracker, Exception $e) + { + // when doing bulk tracking we return JSON so the caller will know how many succeeded + $result = array( + 'status' => 'error', + 'tracked' => $tracker->getCountOfLoggedRequests() + ); + + // send error when in debug mode + if ($tracker->isDebugModeEnabled()) { + $result['message'] = $this->getMessageFromException($e); + } + + return $result; + } + + private function formatResponse(Tracker $tracker) + { + return array( + 'status' => 'success', + 'tracked' => $tracker->getCountOfLoggedRequests() + ); + } + +} diff --git a/plugins/BulkTracking/screenshots/.gitkeep b/plugins/BulkTracking/screenshots/.gitkeep new file mode 100644 index 0000000000..e69de29bb2 --- /dev/null +++ b/plugins/BulkTracking/screenshots/.gitkeep diff --git a/plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php b/plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php new file mode 100644 index 0000000000..aac9c14a2a --- /dev/null +++ b/plugins/BulkTracking/tests/Fixtures/SimpleFixtureTrackFewVisits.php @@ -0,0 +1,77 @@ +<?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\BulkTracking\tests\Fixtures; + +use Piwik\Date; +use Piwik\Tests\Framework\Fixture; + +/** + * Generates tracker testing data for our TrackerTest + * + * This Simple fixture adds one website and tracks one visit with couple pageviews and an ecommerce conversion + */ +class SimpleFixtureTrackFewVisits extends Fixture +{ + public $dateTime = '2013-01-23 01:23:45'; + public $idSite = 1; + + public function setUp() + { + $this->setUpWebsite(); + $this->trackFirstVisit(); + $this->trackSecondVisit(); + } + + public function tearDown() + { + // empty + } + + private function setUpWebsite() + { + if (!self::siteCreated($this->idSite)) { + $idSite = self::createWebsite($this->dateTime, $ecommerce = 1); + $this->assertSame($this->idSite, $idSite); + } + } + + protected function trackFirstVisit() + { + $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true); + + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime()); + $t->setUrl('http://example.com/'); + self::checkResponse($t->doTrackPageView('Viewing homepage')); + + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime()); + $t->setUrl('http://example.com/sub/page'); + self::checkResponse($t->doTrackPageView('Second page view')); + + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.25)->getDatetime()); + $t->addEcommerceItem($sku = 'SKU_ID', $name = 'Test item!', $category = 'Test & Category', $price = 777, $quantity = 33); + self::checkResponse($t->doTrackEcommerceOrder('TestingOrder', $grandTotal = 33 * 77)); + } + + protected function trackSecondVisit() + { + $t = self::getTracker($this->idSite, $this->dateTime, $defaultInit = true); + $t->setIp('56.11.55.73'); + + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.1)->getDatetime()); + $t->setUrl('http://example.com/sub/page'); + self::checkResponse($t->doTrackPageView('Viewing homepage')); + + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.2)->getDatetime()); + $t->setUrl('http://example.com/?search=this is a site search query'); + self::checkResponse($t->doTrackPageView('Site search query')); + + $t->setForceVisitDateTime(Date::factory($this->dateTime)->addHour(0.3)->getDatetime()); + $t->addEcommerceItem($sku = 'SKU_ID2', $name = 'A durable item', $category = 'Best seller', $price = 321); + self::checkResponse($t->doTrackEcommerceCartUpdate($grandTotal = 33 * 77)); + } +}
\ No newline at end of file diff --git a/plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php new file mode 100644 index 0000000000..c6c39c4ee9 --- /dev/null +++ b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Requests.php @@ -0,0 +1,42 @@ +<?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\BulkTracking\tests\Framework\Mock\Tracker; + +use Piwik\Tracker; + +class Requests extends \Piwik\Plugins\BulkTracking\Tracker\Requests +{ + private $rawData; + private $requiresAuth = false; + + public function setRawData($rawData) + { + $this->rawData = $rawData; + } + + public function getRawBulkRequest() + { + if (!is_null($this->rawData)) { + return $this->rawData; + } + + return parent::getRawBulkRequest(); + } + + public function enableRequiresAuth() + { + $this->requiresAuth = true; + } + + public function requiresAuthentication() + { + return $this->requiresAuth; + } + +} diff --git a/plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php new file mode 100644 index 0000000000..e514745ddb --- /dev/null +++ b/plugins/BulkTracking/tests/Framework/Mock/Tracker/Response.php @@ -0,0 +1,21 @@ +<?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\BulkTracking\tests\Framework\Mock\Tracker; + +use Piwik\Tracker; +use Exception; + +class Response extends \Piwik\Plugins\BulkTracking\Tracker\Response +{ + protected function logExceptionToErrorLog(Exception $e) + { + // prevent from writing to console in tests + } + +} diff --git a/plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php b/plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php new file mode 100644 index 0000000000..23b96776ae --- /dev/null +++ b/plugins/BulkTracking/tests/Framework/TestCase/BulkTrackingTestCase.php @@ -0,0 +1,96 @@ +<?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\BulkTracking\tests\Framework\TestCase; + +use Piwik\Plugin; +use Piwik\Plugins\BulkTracking\BulkTracking; +use Piwik\Tests\Framework\Fixture; +use Piwik\Plugins\BulkTracking\tests\Framework\Mock\Tracker\Requests; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Handler as DefaultHandler; +use Piwik\Tracker\RequestSet; + +/** + * @group BulkTracking + * @group BulkTrackingTest + * @group Plugins + * @group Tracker + */ +class BulkTrackingTestCase extends IntegrationTestCase +{ + /** + * @var BulkTracking + */ + protected $bulk; + + private $pluginBackup; + + public function setUp() + { + parent::setUp(); + + $this->bulk = new BulkTracking(); + + $this->pluginBackup = Plugin\Manager::getInstance()->getLoadedPlugin('BulkTracking'); + Plugin\Manager::getInstance()->addLoadedPlugin('BulkTracking', $this->bulk); + } + + public function tearDown() + { + Plugin\Manager::getInstance()->addLoadedPlugin('BulkTracking', $this->pluginBackup); + parent::tearDown(); + } + + protected function getSuperUserToken() + { + Fixture::createSuperUser(false); + return Fixture::getTokenAuth(); + } + + protected function injectRawDataToBulk($rawData, $requiresAuth = false) + { + $requests = new Requests(); + $requests->setRawData($rawData); + + if ($requiresAuth) { + $requests->enableRequiresAuth(); + } + + $this->bulk->setRequests($requests); + } + + protected function initRequestSet($rawData, $requiresAuth = false, $initToken = null) + { + $requestSet = new RequestSet(); + + if (!is_null($initToken)) { + $requestSet->setTokenAuth($initToken); + } + + $this->injectRawDataToBulk($rawData, $requiresAuth); + + $this->bulk->initRequestSet($requestSet); + + return $requestSet; + } + + protected function getDummyRequest($token = null) + { + $params = array(array('idsite' => '1', 'rec' => '1'), array('idsite' => '2', 'rec' => '1')); + $params = array('requests' => $params); + + if (!is_null($token)) { + $params['token_auth'] = $token; + } + + $request = json_encode($params); + + return $request; + } +} diff --git a/plugins/BulkTracking/tests/Integration/BulkTrackingTest.php b/plugins/BulkTracking/tests/Integration/BulkTrackingTest.php new file mode 100644 index 0000000000..c495afcee1 --- /dev/null +++ b/plugins/BulkTracking/tests/Integration/BulkTrackingTest.php @@ -0,0 +1,147 @@ +<?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\BulkTracking\tests\Integration; + +use Piwik\Plugin; +use Piwik\Plugins\BulkTracking\tests\Framework\TestCase\BulkTrackingTestCase; +use Piwik\Plugins\BulkTracking\Tracker\Handler; +use Piwik\Tracker\Handler as DefaultHandler; +use Piwik\Tracker\RequestSet; + +/** + * @group BulkTracking + * @group BulkTrackingTest + * @group Plugins + * @group Tracker + */ +class BulkTrackingTest extends BulkTrackingTestCase +{ + public function test_initRequestSet_shouldNotSetAnything_IfItIsActuallyNotUsingBulkRequest() + { + $requestSet = new RequestSet(); + $this->bulk->initRequestSet($requestSet); + + $this->assertEquals(array(), $requestSet->getRequests()); + $this->assertEquals(false, $requestSet->getTokenAuth()); + } + + public function test_initRequestSet_shouldNotSetAnything_IfNotBulkRequestRawDataIsGiven() + { + $requestSet = $this->initRequestSet('invalid:requests'); + + $this->assertEquals(array(), $requestSet->getRequests()); + $this->assertEquals(false, $requestSet->getTokenAuth()); + } + + public function test_initRequestSet_shouldInitialize_AsItIsABulkRequest() + { + $token = $this->getSuperUserToken(); + $request = $this->getDummyRequest($token); + + $requestSet = $this->initRequestSet($request); + + $requests = $requestSet->getRequests(); + $this->assertCount(2, $requests); + $this->assertEquals(array('idsite' => '1', 'rec' => '1'), $requests[0]->getParams()); + $this->assertEquals(array('idsite' => '2', 'rec' => '1'), $requests[1]->getParams()); + $this->assertEquals($token, $requestSet->getTokenAuth()); + } + + public function test_initRequestSet_shouldNotOverwriteAToken_IfOneIsAlreadySet() + { + $token = $this->getSuperUserToken(); + $request = $this->getDummyRequest($token); + + $requestSet = $this->initRequestSet($request, false, 'initialtoken'); + + $this->assertEquals('initialtoken', $requestSet->getTokenAuth()); + $this->assertCount(2, $requestSet->getRequests()); + } + + public function test_initRequestSet_shouldNotFail_IfNoTokenProvided_AsAuthenticationIsDisabledByDefault() + { + $request = $this->getDummyRequest(); + + $requestSet = $this->initRequestSet($request); + + $requests = $requestSet->getRequests(); + $this->assertCount(2, $requests); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage token_auth must be specified when using Bulk Tracking Import + */ + public function test_initRequestSet_shouldTriggerException_InCaseNoValidTokenProvidedAndAuthenticationIsRequired() + { + $request = $this->getDummyRequest(false); + + $this->initRequestSet($request, true); + } + + public function test_setHandlerIfBulkRequest_shouldSetBulkHandler_InCaseNoHandlerIsSetAndItIsABulkRequest() + { + $this->injectRawDataToBulk($this->getDummyRequest()); + + $handler = null; + $this->bulk->setHandlerIfBulkRequest($handler); + + $this->assertTrue($handler instanceof Handler); + } + + public function test_setHandlerIfBulkRequest_shouldNotSetAHandler_IfOneIsAlreadySetEvenIfItIsABulkRequest() + { + $this->injectRawDataToBulk($this->getDummyRequest()); + + $default = new DefaultHandler(); + $handler = $default; + + $this->bulk->setHandlerIfBulkRequest($default); + + $this->assertSame($default, $handler); + } + + public function test_setHandlerIfBulkRequest_shouldNotSetAHandler_IfItIsNotABulkRequest() + { + $this->injectRawDataToBulk('{"test":"not a bulk request"}'); + + $handler = null; + + $this->bulk->setHandlerIfBulkRequest($handler); + + $this->assertNull($handler); + } + + public function test_getListHooksRegistered_shouldListenToNewTrackerEventAndCreateBulkHandler_IfBulkRequest() + { + $this->injectRawDataToBulk($this->getDummyRequest()); + + $handler = DefaultHandler\Factory::make(); + + $this->assertTrue($handler instanceof Handler); + } + + public function test_getListHooksRegistered_shouldListenToNewTrackerEventAndNotCreateBulkHandler_IfNotBulkRequest() + { + $handler = DefaultHandler\Factory::make(); + + $this->assertTrue($handler instanceof DefaultHandler); + } + + public function test_getListHooksRegistered_shouldListenToInitRequestSetEventAndInit_IfBulkRequest() + { + $this->injectRawDataToBulk($this->getDummyRequest()); + + $requestSet = new RequestSet(); + $requestSet->initRequestsAndTokenAuth(); + + $this->assertCount(2, $requestSet->getRequests()); + } + +} diff --git a/plugins/BulkTracking/tests/Integration/HandlerTest.php b/plugins/BulkTracking/tests/Integration/HandlerTest.php new file mode 100644 index 0000000000..1b2b46c8cb --- /dev/null +++ b/plugins/BulkTracking/tests/Integration/HandlerTest.php @@ -0,0 +1,161 @@ +<?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\BulkTracking\tests\Integration; + +use Piwik\Exception\InvalidRequestParameterException; +use Piwik\Exception\UnexpectedWebsiteFoundException; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\Mock\Tracker\Response; +use Piwik\Tests\Framework\Mock\Tracker\ScheduledTasksRunner; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker; +use Piwik\Plugins\BulkTracking\Tracker\Handler; +use Piwik\Tests\Framework\Mock\Tracker\RequestSet; +use Exception; + +/** + * @group HandlerTest + * @group Handler + * @group Tracker + */ +class HandlerTest extends IntegrationTestCase +{ + /** + * @var Handler + */ + private $handler; + + /** + * @var Response + */ + private $response; + + /** + * @var Tracker + */ + private $tracker; + + /** + * @var RequestSet + */ + private $requestSet; + + public function setUp() + { + parent::setUp(); + + Fixture::createWebsite('2014-01-01 00:00:00'); + Tracker\Cache::deleteTrackerCache(); + + $this->response = new Response(); + $this->handler = new Handler(); + $this->handler->setResponse($this->response); + $this->tracker = new Tracker(); + $this->requestSet = new RequestSet(); + } + + public function test_init_ShouldInitiateResponseInstance() + { + $this->handler->init($this->tracker, $this->requestSet); + + $this->assertTrue($this->response->isInit); + $this->assertFalse($this->response->isResponseOutput); + $this->assertFalse($this->response->isSend); + } + + public function test_finish_ShouldOutputAndSendResponse() + { + $response = $this->handler->finish($this->tracker, $this->requestSet); + + $this->assertEquals('My Dummy Content', $response); + + $this->assertFalse($this->response->isInit); + $this->assertFalse($this->response->isExceptionOutput); + $this->assertTrue($this->response->isResponseOutput); + $this->assertTrue($this->response->isSend); + } + + public function test_onException_ShouldOutputAndSendResponse() + { + $this->executeOnException($this->buildException()); + + $this->assertFalse($this->response->isInit); + $this->assertFalse($this->response->isResponseOutput); + $this->assertTrue($this->response->isExceptionOutput); + $this->assertFalse($this->response->isSend); + } + + public function test_onException_ShouldPassExceptionToResponse() + { + $exception = $this->buildException(); + + $this->executeOnException($exception); + + $this->assertSame($exception, $this->response->exception); + $this->assertSame(500, $this->response->statusCode); + } + + public function test_onException_ShouldSendStatusCode400IfUnexpectedWebsite() + { + $this->executeOnException(new UnexpectedWebsiteFoundException('test')); + $this->assertSame(400, $this->response->statusCode); + } + + public function test_onException_ShouldSendStatusCode400IfInvalidRequestParameterException() + { + $this->executeOnException(new InvalidRequestParameterException('test')); + $this->assertSame(400, $this->response->statusCode); + } + + public function test_onException_ShouldNotRethrowAnException() + { + $exception = $this->buildException(); + + $this->handler->onException($this->tracker, $this->requestSet, $exception); + $this->assertTrue(true); + } + + public function test_onAllRequestsTracked_ShouldNeverTriggerScheduledTasksEvenIfEnabled() + { + $runner = new ScheduledTasksRunner(); + $runner->shouldRun = true; + + $this->handler->setScheduledTasksRunner($runner); + $this->handler->onAllRequestsTracked($this->tracker, $this->requestSet); + + $this->assertFalse($runner->ranScheduledTasks); + } + + public function test_process_ShouldTrackAllSetRequests() + { + $this->assertSame(0, $this->tracker->getCountOfLoggedRequests()); + + $this->requestSet->setRequests(array( + array('idsite' => 1, 'url' => 'http://localhost/foo?bar'), + array('idsite' => 1, 'url' => 'http://localhost'), + )); + + $this->handler->process($this->tracker, $this->requestSet); + + $this->assertSame(2, $this->tracker->getCountOfLoggedRequests()); + } + + private function buildException() + { + return new \Exception('MyMessage', 292); + } + + private function executeOnException(Exception $exception) + { + try { + $this->handler->onException($this->tracker, $this->requestSet, $exception); + } catch (Exception $e) { + } + } +} diff --git a/plugins/BulkTracking/tests/Integration/RequestsTest.php b/plugins/BulkTracking/tests/Integration/RequestsTest.php new file mode 100644 index 0000000000..ab45204303 --- /dev/null +++ b/plugins/BulkTracking/tests/Integration/RequestsTest.php @@ -0,0 +1,127 @@ +<?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\BulkTracking\tests\Integration; + +use Piwik\Plugins\BulkTracking\Tracker\Requests; +use Piwik\Plugins\UsersManager\API; +use Piwik\Plugins\UsersManager\UsersManager; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\IntegrationTestCase; +use Piwik\Tracker\Request; +use Piwik\Tracker\TrackerConfig; + +/** + * @group BulkTracking + * @group RequestsTest + * @group Plugins + * @group Tracker + */ +class RequestsTest extends IntegrationTestCase +{ + /** + * @var Requests + */ + private $requests; + + public function setUp() + { + parent::setUp(); + + $this->requests = new Requests(); + } + + public function tearDown() + { + // clean up your test here if needed + + parent::tearDown(); + } + + public function test_requiresAuthentication_shouldReturnTrue_IfEnabled() + { + $oldConfig = TrackerConfig::getConfigValue('bulk_requests_require_authentication'); + TrackerConfig::setConfigValue('bulk_requests_require_authentication', 1); + + $this->assertTrue($this->requests->requiresAuthentication()); + + TrackerConfig::setConfigValue('bulk_requests_require_authentication', $oldConfig); + } + + public function test_requiresAuthentication_shouldReturnFalse_IfDisabled() + { + $oldConfig = TrackerConfig::getConfigValue('bulk_requests_require_authentication'); + TrackerConfig::setConfigValue('bulk_requests_require_authentication', 0); + + $this->assertFalse($this->requests->requiresAuthentication()); + + TrackerConfig::setConfigValue('bulk_requests_require_authentication', $oldConfig); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage token_auth must be specified when using Bulk Tracking Import + */ + public function test_authenticateRequests_shouldThrowAnException_IfTokenAuthIsEmpty() + { + $requests = array($this->buildDummyRequest()); + $this->requests->authenticateRequests($requests); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage token_auth must be specified when using Bulk Tracking Import + */ + public function test_authenticateRequests_shouldThrowAnException_IfAnyTokenAuthIsEmpty() + { + $requests = array($this->buildDummyRequest($this->getSuperUserToken()), $this->buildDummyRequest()); + $this->requests->authenticateRequests($requests); + } + + /** + * @expectedException \Exception + * @expectedExceptionMessage token_auth specified does not have Admin permission for idsite=1 + */ + public function test_authenticateRequests_shouldThrowAnException_IfTokenIsNotValid() + { + $dummyToken = API::getInstance()->getTokenAuth('test', UsersManager::getPasswordHash('2')); + $superUserToken = $this->getSuperUserToken(); + + $requests = array($this->buildDummyRequest($superUserToken), $this->buildDummyRequest($dummyToken)); + $this->requests->authenticateRequests($requests); + } + + public function test_authenticateRequests_shouldNotFail_IfAllTokensAreValid() + { + $superUserToken = $this->getSuperUserToken(); + + $requests = array($this->buildDummyRequest($superUserToken), $this->buildDummyRequest($superUserToken)); + $this->requests->authenticateRequests($requests); + + $this->assertTrue(true); + } + + public function test_authenticateRequests_shouldNotFail_IfEmptyRequestSetGiven() + { + $this->requests->authenticateRequests(array()); + + $this->assertTrue(true); + } + + private function getSuperUserToken() + { + Fixture::createSuperUser(false); + return Fixture::getTokenAuth(); + } + + private function buildDummyRequest($token = false) + { + return new Request(array('idsite' => '1'), $token); + } + +} diff --git a/plugins/BulkTracking/tests/Integration/TrackerTest.php b/plugins/BulkTracking/tests/Integration/TrackerTest.php new file mode 100644 index 0000000000..dd1e37ea70 --- /dev/null +++ b/plugins/BulkTracking/tests/Integration/TrackerTest.php @@ -0,0 +1,133 @@ +<?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\BulkTracking\tests\Integration; + +use Piwik\Common; +use Piwik\Plugin; +use Piwik\Plugins\BulkTracking\tests\Framework\Mock\Tracker\Response; +use Piwik\Plugins\BulkTracking\tests\Framework\TestCase\BulkTrackingTestCase; +use Piwik\Plugins\BulkTracking\Tracker\Handler; +use Piwik\Tests\Framework\Fixture; +use Piwik\Tracker; +use Piwik\Translate; +use Piwik\Tests\Framework\Mock\Tracker\RequestSet; + +class TestIntegrationTracker extends Tracker { + + protected function loadTrackerPlugins() + { + // if we reload the plugins we would lose the injected data :( + } +} + +/** + * @group TrackerTest + * @group Tracker + */ +class TrackerTest extends BulkTrackingTestCase +{ + /** + * @var TestIntegrationTracker + */ + private $tracker; + + public function setUp() + { + parent::setUp(); + + $this->tracker = new TestIntegrationTracker(); + + Fixture::createWebsite('2014-01-01 00:00:00'); + Fixture::createWebsite('2014-01-01 00:00:00'); + + $this->injectRawDataToBulk($this->getDummyRequest()); + } + + public function test_main_shouldIncreaseLoggedRequestsCounter() + { + $this->tracker->main($this->getHandler(), $this->getEmptyRequestSet()); + + $this->assertSame(2, $this->tracker->getCountOfLoggedRequests()); + } + + public function test_main_shouldUseBulkHandler() + { + $handler = $this->getHandler(); + $this->assertTrue($handler instanceof Handler); + } + + public function test_main_shouldReturnBulkTrackingResponse() + { + $response = $this->tracker->main($this->getHandler(), $this->getEmptyRequestSet()); + + $this->assertSame('{"status":"success","tracked":2}', $response); + } + + public function test_main_shouldReturnErrorResponse_InCaseOfAnyError() + { + $requestSet = new RequestSet(); + $requestSet->enableThrowExceptionOnInit(); + + $handler = $this->getHandler(); + $handler->setResponse(new Response()); + + $response = $this->tracker->main($handler, $requestSet); + + $this->assertSame('{"status":"error","tracked":0}', $response); + } + + public function test_main_shouldReturnErrorResponse_IfNotAuthorized() + { + $this->injectRawDataToBulk($this->getDummyRequest(), true); + + $handler = $this->getHandler(); + $handler->setResponse(new Response()); + + $response = $this->tracker->main($handler, $this->getEmptyRequestSet()); + + $this->assertSame('{"status":"error","tracked":0}', $response); + } + + public function test_main_shouldActuallyTrack() + { + $this->assertEmpty($this->getIdVisit(1)); + $this->assertEmpty($this->getIdVisit(2)); + + $requestSet = $this->getEmptyRequestSet(); + $this->tracker->main($this->getHandler(), $requestSet); + + $this->assertCount(2, $requestSet->getRequests(), 'Nothing tracked because it could not find 2 requests'); + + $visit1 = $this->getIdVisit(1); + $visit2 = $this->getIdVisit(2); + + $this->assertNotEmpty($visit1); + $this->assertEquals(1, $visit1['idsite']); + $this->assertNotEmpty($visit2); + $this->assertEquals(2, $visit2['idsite']); + + $this->assertEmpty($this->getIdVisit(3)); + } + + private function getHandler() + { + return Tracker\Handler\Factory::make(); + } + + private function getEmptyRequestSet() + { + return new Tracker\RequestSet(); + } + + private function getIdVisit($idVisit) + { + return Tracker::getDatabase()->fetchRow("SELECT * FROM " . Common::prefixTable('log_visit') . " WHERE idvisit = ?", array($idVisit)); + } + +}
\ No newline at end of file diff --git a/plugins/BulkTracking/tests/System/TrackerTest.php b/plugins/BulkTracking/tests/System/TrackerTest.php new file mode 100644 index 0000000000..5974a1acd5 --- /dev/null +++ b/plugins/BulkTracking/tests/System/TrackerTest.php @@ -0,0 +1,53 @@ +<?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\BulkTracking\tests\System; + +use Piwik\Tests\Framework\Fixture; +use Piwik\Tests\Framework\TestCase\SystemTestCase; + +/** + * @group BulkTracking + * @group TrackerTest + * @group Tracker + * @group Plugins + */ +class TrackerTest extends SystemTestCase +{ + public static $fixture = null; + + /** + * @var \PiwikTracker + */ + private $tracker; + + public function setUp() + { + parent::setUp(); + + $idSite = 1; + $dateTime = '2014-01-01 00:00:01'; + + if (!Fixture::siteCreated($idSite)) { + Fixture::createWebsite($dateTime); + } + + $this->tracker = Fixture::getTracker($idSite, $dateTime, $defaultInit = true); + $this->tracker->enableBulkTracking(); + } + + public function test_response_ShouldContainBulkTrackingApiResponse() + { + $this->tracker->doTrackPageView('Test'); + $this->tracker->doTrackPageView('Test'); + + $response = $this->tracker->doBulkTrack(); + + $this->assertEquals('{"status":"success","tracked":2}', $response); + } +}
\ No newline at end of file diff --git a/plugins/BulkTracking/tests/Unit/RequestsTest.php b/plugins/BulkTracking/tests/Unit/RequestsTest.php new file mode 100644 index 0000000000..8a89e5c220 --- /dev/null +++ b/plugins/BulkTracking/tests/Unit/RequestsTest.php @@ -0,0 +1,172 @@ +<?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\BulkTracking\tests\Unit; + +use Piwik\Plugins\BulkTracking\Tracker\Requests; +use Piwik\Tests\Framework\TestCase\UnitTestCase; +use Piwik\Tracker\Request; + +/** + * @group BulkTracking + * @group RequestsTest + * @group Plugins + */ +class RequestsTest extends UnitTestCase +{ + /** + * @var Requests + */ + private $requests; + + public function setUp() + { + parent::setUp(); + $this->requests = new Requests(); + } + + public function test_isUsingBulkRequest_shouldReturnFalse_IfRequestIsEmpty() + { + $this->assertFalse($this->requests->isUsingBulkRequest(false)); + $this->assertFalse($this->requests->isUsingBulkRequest(null)); + $this->assertFalse($this->requests->isUsingBulkRequest('')); + $this->assertFalse($this->requests->isUsingBulkRequest(0)); + } + + public function test_isUsingBulkRequest_shouldReturnFalse_IfRequestIsNotABulkRequest() + { + $this->assertFalse($this->requests->isUsingBulkRequest(5)); + $this->assertFalse($this->requests->isUsingBulkRequest('test')); + $this->assertFalse($this->requests->isUsingBulkRequest('requests')); + $this->assertFalse($this->requests->isUsingBulkRequest('{"test": "val", "request:" []}')); + $this->assertFalse($this->requests->isUsingBulkRequest('[5, 10, "request"]')); + } + + public function test_isUsingBulkRequest_shouldReturnTrue_IfRequestIsABulkRequest() + { + $request = $this->buildRequestRawData(array(array('idsite' => '1'))); + $this->assertTrue($this->requests->isUsingBulkRequest($request)); + + // don't know why this one is supposed to count as bulk request! + $this->assertTrue($this->requests->isUsingBulkRequest("{'requests'")); + } + + public function test_getRequestsArrayFromBulkRequest_ShouldFindRequestsAndEmptyTokenAndItShouldTrimWhitespaceFromRawData() + { + $requests = array(array('idsite' => '1'), array('idsite' => '2')); + $request = $this->buildRequestRawData($requests); + + $result = $this->requests->getRequestsArrayFromBulkRequest(' ' . $request . ' '); + + $expected = array(array(array('idsite' => '1'), array('idsite' => '2')), ''); + $this->assertEquals($expected, $result); + } + + public function test_getRequestsArrayFromBulkRequest_shouldRecognize() + { + $token = md5('2'); + + $request = $this->buildRequestRawData(array(), $token); + $result = $this->requests->getRequestsArrayFromBulkRequest($request); + + $expected = array(array(), $token); + $this->assertEquals($expected, $result); + } + + public function test_initRequestsAndTokenAuth_NoRequestsSetShouldStillFindToken() + { + $token = md5('2'); + + $request = json_encode(array('requests' => array(), 'token_auth' => $token)); + $result = $this->requests->initRequestsAndTokenAuth($request); + + $expected = array(array(), $token); + $this->assertEquals($expected, $result); + } + + public function test_initRequestsAndTokenAuth_ShouldFindRequestsAndEmptyToken() + { + $params = array(array('idsite' => '1'), array('idsite' => '2')); + $request = $this->buildRequestRawData($params); + + $result = $this->requests->initRequestsAndTokenAuth($request); + + /** @var Request[] $requests */ + $requests = $result[0]; + $tokenAuth = $result[1]; + + $this->assertEquals('', $tokenAuth); // none was set + + $this->assertEquals(array('idsite' => '1'), $requests[0]->getParams()); + $this->assertEquals('', $requests[0]->getTokenAuth()); + $this->assertEquals(array('idsite' => '2'), $requests[1]->getParams()); + $this->assertEquals('', $requests[1]->getTokenAuth()); + $this->assertCount(2, $requests); + } + + public function test_initRequestsAndTokenAuth_ShouldFindRequestsAndASetTokenAndPassItToRequestInstances() + { + $token = md5(2); + $params = array(array('idsite' => '1'), array('idsite' => '2')); + $request = $this->buildRequestRawData($params, $token); + + $result = $this->requests->initRequestsAndTokenAuth($request); + + /** @var Request[] $requests */ + $requests = $result[0]; + + $this->assertEquals($token, $result[1]); + $this->assertEquals($token, $requests[0]->getTokenAuth()); + $this->assertEquals($token, $requests[1]->getTokenAuth()); + } + + public function test_initRequestsAndTokenAuth_ShouldIgnoreEmptyUrls() + { + $token = md5(2); + $params = array(array('idsite' => '1'), '', array('idsite' => '2')); + $request = $this->buildRequestRawData($params, $token); + + $result = $this->requests->initRequestsAndTokenAuth($request); + + /** @var Request[] $requests */ + $requests = $result[0]; + + $this->assertEquals(array('idsite' => '1'), $requests[0]->getParams()); + $this->assertEquals(array('idsite' => '2'), $requests[1]->getParams()); + $this->assertCount(2, $requests); + } + + public function test_initRequestsAndTokenAuth_ShouldResolveUrls() + { + $token = md5(2); + $params = array('piwik.php?idsite=1', '', 'piwik.php?idsite=3&rec=0', array('idsite' => '2')); + $request = $this->buildRequestRawData($params, $token); + + $result = $this->requests->initRequestsAndTokenAuth($request); + + /** @var Request[] $requests */ + $requests = $result[0]; + + $this->assertEquals(array('idsite' => '1'), $requests[0]->getParams()); + $this->assertEquals(array('idsite' => '3', 'rec' => '0'), $requests[1]->getParams()); + $this->assertEquals(array('idsite' => '2'), $requests[2]->getParams()); + $this->assertCount(3, $requests); + } + + private function buildRequestRawData($requests, $token = null) + { + $params = array('requests' => $requests); + + if (!empty($token)) { + $params['token_auth'] = $token; + } + + return json_encode($params); + } + +} diff --git a/plugins/BulkTracking/tests/Unit/ResponseTest.php b/plugins/BulkTracking/tests/Unit/ResponseTest.php new file mode 100644 index 0000000000..9da1e9a6b3 --- /dev/null +++ b/plugins/BulkTracking/tests/Unit/ResponseTest.php @@ -0,0 +1,93 @@ +<?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\BulkTracking\tests\Unit; + +use Piwik\Plugins\BulkTracking\Tracker\Response; +use Piwik\Tests\Framework\Mock\Tracker; +use Piwik\Tests\Framework\TestCase\UnitTestCase; +use Exception; + +class TestResponse extends Response { + + protected function logExceptionToErrorLog(Exception $e) + { + // prevent console from outputting the error_log message + } +} + +/** + * @group BulkTracking + * @group ResponseTest + * @group Plugins + */ +class ResponseTest extends UnitTestCase +{ + /** + * @var TestResponse + */ + private $response; + + public function setUp() + { + parent::setUp(); + + $this->response = new TestResponse(); + $this->response->init(new Tracker()); + } + + public function test_outputException_shouldOutputBulkResponse() + { + $tracker = $this->getTrackerWithCountedRequests(); + + $this->response->outputException($tracker, new Exception('My Custom Message'), 400); + $content = $this->response->getOutput(); + + $this->assertEquals('{"status":"error","tracked":5}', $content); + } + + public function test_outputException_shouldOutputDebugMessageIfEnabled() + { + $tracker = $this->getTrackerWithCountedRequests(); + $tracker->enableDebugMode(); + + $this->response->outputException($tracker, new Exception('My Custom Message'), 400); + $content = $this->response->getOutput(); + + $this->assertStringStartsWith('{"status":"error","tracked":5,"message":"My Custom Message\n', $content); + } + + public function test_outputResponse_shouldOutputBulkResponse() + { + $tracker = $this->getTrackerWithCountedRequests(); + + $this->response->outputResponse($tracker); + $content = $this->response->getOutput(); + + $this->assertEquals('{"status":"success","tracked":5}', $content); + } + + public function test_outputResponse_shouldNotOutputAnything_IfExceptionResponseAlreadySent() + { + $tracker = $this->getTrackerWithCountedRequests(); + + $this->response->outputException($tracker, new Exception('My Custom Message'), 400); + $this->response->outputResponse($tracker); + $content = $this->response->getOutput(); + + $this->assertEquals('{"status":"error","tracked":5}', $content); + } + + private function getTrackerWithCountedRequests() + { + $tracker = new Tracker(); + $tracker->setCountOfLoggedRequests(5); + return $tracker; + } + +} |