15cc1319aSAndreas Gohr<?php 25cc1319aSAndreas Gohr 35cc1319aSAndreas Gohrnamespace dokuwiki\plugin\statistics\test; 45cc1319aSAndreas Gohr 5696a1b1bSAndreas Gohruse dokuwiki\plugin\statistics\Logger; 641d1fffcSAndreas Gohruse DokuWikiTest; 7696a1b1bSAndreas Gohruse helper_plugin_statistics; 85cc1319aSAndreas Gohr 95cc1319aSAndreas Gohr/** 10696a1b1bSAndreas Gohr * Tests for the statistics plugin Logger class 115cc1319aSAndreas Gohr * 125cc1319aSAndreas Gohr * @group plugin_statistics 135cc1319aSAndreas Gohr * @group plugins 145cc1319aSAndreas Gohr */ 155cc1319aSAndreas Gohrclass LoggerTest extends DokuWikiTest 165cc1319aSAndreas Gohr{ 17de1daf8cSAndreas Gohr protected $pluginsEnabled = ['statistics', 'sqlite']; 18de1daf8cSAndreas Gohr 19696a1b1bSAndreas Gohr /** @var helper_plugin_statistics */ 20696a1b1bSAndreas Gohr protected $helper; 215cc1319aSAndreas Gohr 2241d1fffcSAndreas Gohr const SESSION_ID = 'test-session-12345'; 2341d1fffcSAndreas Gohr const USER_ID = 'test-uid-12345'; 2441d1fffcSAndreas Gohr const USER_AGENT = 'Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/91.0.4472.124 Safari/537.36'; 25696a1b1bSAndreas Gohr 26696a1b1bSAndreas Gohr public function setUp(): void 27696a1b1bSAndreas Gohr { 28696a1b1bSAndreas Gohr parent::setUp(); 29696a1b1bSAndreas Gohr 30696a1b1bSAndreas Gohr // Load the helper plugin 31696a1b1bSAndreas Gohr $this->helper = plugin_load('helper', 'statistics'); 32696a1b1bSAndreas Gohr 3341d1fffcSAndreas Gohr // set default user agent 3441d1fffcSAndreas Gohr $_SERVER['HTTP_USER_AGENT'] = self::USER_AGENT; 35696a1b1bSAndreas Gohr 3641d1fffcSAndreas Gohr // Set up session data that Logger expects 3741d1fffcSAndreas Gohr $_SESSION[DOKU_COOKIE]['statistics']['uid'] = self::USER_ID; 3841d1fffcSAndreas Gohr $_SESSION[DOKU_COOKIE]['statistics']['id'] = self::SESSION_ID; 39696a1b1bSAndreas Gohr } 40696a1b1bSAndreas Gohr 41696a1b1bSAndreas Gohr public function tearDown(): void 42696a1b1bSAndreas Gohr { 43696a1b1bSAndreas Gohr unset($_SERVER['HTTP_USER_AGENT']); 4441d1fffcSAndreas Gohr unset($_SESSION[DOKU_COOKIE]['statistics']); 45696a1b1bSAndreas Gohr parent::tearDown(); 46696a1b1bSAndreas Gohr } 47696a1b1bSAndreas Gohr 48696a1b1bSAndreas Gohr /** 49696a1b1bSAndreas Gohr * Test constructor initializes properties correctly 50696a1b1bSAndreas Gohr */ 51696a1b1bSAndreas Gohr public function testConstructor() 52696a1b1bSAndreas Gohr { 5341d1fffcSAndreas Gohr $this->assertInstanceOf(Logger::class, $this->helper->getLogger()); 54696a1b1bSAndreas Gohr 55696a1b1bSAndreas Gohr // Test that bot user agents throw exception 56696a1b1bSAndreas Gohr $_SERVER['HTTP_USER_AGENT'] = 'Googlebot/2.1 (+http://www.google.com/bot.html)'; 57696a1b1bSAndreas Gohr 5841d1fffcSAndreas Gohr $this->expectException(\dokuwiki\plugin\statistics\IgnoreException::class); 59696a1b1bSAndreas Gohr $this->expectExceptionMessage('Bot detected, not logging'); 60696a1b1bSAndreas Gohr new Logger($this->helper); 61696a1b1bSAndreas Gohr } 62696a1b1bSAndreas Gohr 63696a1b1bSAndreas Gohr /** 64696a1b1bSAndreas Gohr * Test begin and end transaction methods 65696a1b1bSAndreas Gohr */ 66696a1b1bSAndreas Gohr public function testBeginEnd() 67696a1b1bSAndreas Gohr { 6841d1fffcSAndreas Gohr $this->helper->getLogger()->begin(); 69696a1b1bSAndreas Gohr 70696a1b1bSAndreas Gohr // Verify transaction is active by checking PDO 71696a1b1bSAndreas Gohr $pdo = $this->helper->getDB()->getPdo(); 72696a1b1bSAndreas Gohr $this->assertTrue($pdo->inTransaction()); 73696a1b1bSAndreas Gohr 7441d1fffcSAndreas Gohr $this->helper->getLogger()->end(); 75696a1b1bSAndreas Gohr 76696a1b1bSAndreas Gohr // Verify transaction is committed 77696a1b1bSAndreas Gohr $this->assertFalse($pdo->inTransaction()); 78696a1b1bSAndreas Gohr } 79696a1b1bSAndreas Gohr 80696a1b1bSAndreas Gohr /** 8141d1fffcSAndreas Gohr * Test user logging 82696a1b1bSAndreas Gohr */ 8341d1fffcSAndreas Gohr public function testLogUser() 84696a1b1bSAndreas Gohr { 85696a1b1bSAndreas Gohr // Test with no user (should not log) 8641d1fffcSAndreas Gohr $_SERVER['REMOTE_USER'] = ''; 8741d1fffcSAndreas Gohr $this->helper->getLogger()->begin(); 8841d1fffcSAndreas Gohr $this->helper->getLogger()->end(); 89696a1b1bSAndreas Gohr 9041d1fffcSAndreas Gohr $count = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM users'); 91696a1b1bSAndreas Gohr $this->assertEquals(0, $count); 92696a1b1bSAndreas Gohr 93696a1b1bSAndreas Gohr // Test with user 9441d1fffcSAndreas Gohr $_SERVER['REMOTE_USER'] = 'testuser'; 9541d1fffcSAndreas Gohr $this->helper->getLogger()->begin(); 9641d1fffcSAndreas Gohr $this->helper->getLogger()->end(); 97696a1b1bSAndreas Gohr 9841d1fffcSAndreas Gohr $count = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM users'); 99696a1b1bSAndreas Gohr $this->assertEquals(1, $count); 100696a1b1bSAndreas Gohr 10141d1fffcSAndreas Gohr $user = $this->helper->getDB()->queryValue('SELECT user FROM users WHERE user = ?', ['testuser']); 102696a1b1bSAndreas Gohr $this->assertEquals('testuser', $user); 103696a1b1bSAndreas Gohr } 104696a1b1bSAndreas Gohr 105696a1b1bSAndreas Gohr /** 106696a1b1bSAndreas Gohr * Data provider for logGroups test 107696a1b1bSAndreas Gohr */ 108696a1b1bSAndreas Gohr public function logGroupsProvider() 109696a1b1bSAndreas Gohr { 110696a1b1bSAndreas Gohr return [ 11141d1fffcSAndreas Gohr 'empty groups' => [[], 0], 11241d1fffcSAndreas Gohr 'single group' => [['admin'], 1], 11341d1fffcSAndreas Gohr 'multiple groups' => [['admin', 'user'], 2], 11441d1fffcSAndreas Gohr 'filtered groups' => [['admin', 'nonexistent'], 2], // all groups are logged 115696a1b1bSAndreas Gohr ]; 116696a1b1bSAndreas Gohr } 117696a1b1bSAndreas Gohr 118696a1b1bSAndreas Gohr /** 119696a1b1bSAndreas Gohr * Test logGroups method 120696a1b1bSAndreas Gohr * @dataProvider logGroupsProvider 121696a1b1bSAndreas Gohr */ 12241d1fffcSAndreas Gohr public function testLogGroups($groups, $expectedCount) 123696a1b1bSAndreas Gohr { 12441d1fffcSAndreas Gohr global $USERINFO; 125696a1b1bSAndreas Gohr 12641d1fffcSAndreas Gohr // Set up a test user and groups 12741d1fffcSAndreas Gohr $_SERVER['REMOTE_USER'] = 'testuser'; 12841d1fffcSAndreas Gohr $USERINFO = ['grps' => $groups]; 12923e0cc03SAndreas Gohr 130696a1b1bSAndreas Gohr 13141d1fffcSAndreas Gohr $this->helper->getLogger()->begin(); 13241d1fffcSAndreas Gohr $this->helper->getLogger()->end(); 13341d1fffcSAndreas Gohr 13441d1fffcSAndreas Gohr $count = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM groups WHERE user = ?', ['testuser']); 135696a1b1bSAndreas Gohr $this->assertEquals($expectedCount, $count); 136696a1b1bSAndreas Gohr 137696a1b1bSAndreas Gohr if ($expectedCount > 0) { 13841d1fffcSAndreas Gohr $loggedGroups = $this->helper->getDB()->queryAll('SELECT `group` FROM groups WHERE user = ?', ['testuser']); 139696a1b1bSAndreas Gohr $this->assertCount($expectedCount, $loggedGroups); 140696a1b1bSAndreas Gohr } 141696a1b1bSAndreas Gohr } 142696a1b1bSAndreas Gohr 143696a1b1bSAndreas Gohr /** 14441d1fffcSAndreas Gohr * Data provider for testLogReferer test 145696a1b1bSAndreas Gohr */ 14641d1fffcSAndreas Gohr public function logRefererProvider() 147696a1b1bSAndreas Gohr { 148696a1b1bSAndreas Gohr return [ 149696a1b1bSAndreas Gohr 'google search' => [ 150696a1b1bSAndreas Gohr 'https://www.google.com/search?q=dokuwiki+test', 151569a5066SAndreas Gohr 'google', 152569a5066SAndreas Gohr true // should be logged 153696a1b1bSAndreas Gohr ], 154569a5066SAndreas Gohr 'bing search' => [ 155569a5066SAndreas Gohr 'https://www.bing.com/search?q=test+query', 156569a5066SAndreas Gohr 'bing', 157569a5066SAndreas Gohr true // should be logged 158569a5066SAndreas Gohr ], 159569a5066SAndreas Gohr 'external referer' => [ 160696a1b1bSAndreas Gohr 'https://example.com/page', 161696a1b1bSAndreas Gohr null, 162569a5066SAndreas Gohr true // should be logged 163569a5066SAndreas Gohr ], 164569a5066SAndreas Gohr 'direct access (empty referer)' => [ 165569a5066SAndreas Gohr '', 166569a5066SAndreas Gohr null, 167569a5066SAndreas Gohr true // should be logged 168569a5066SAndreas Gohr ], 169569a5066SAndreas Gohr 'ws' => [ 170569a5066SAndreas Gohr ' ', 171569a5066SAndreas Gohr null, 172569a5066SAndreas Gohr true // should be logged (trimmed to empty) 173696a1b1bSAndreas Gohr ], 174696a1b1bSAndreas Gohr ]; 175696a1b1bSAndreas Gohr } 176696a1b1bSAndreas Gohr 177696a1b1bSAndreas Gohr /** 17841d1fffcSAndreas Gohr * Test logReferer method 17941d1fffcSAndreas Gohr * @dataProvider logRefererProvider 180696a1b1bSAndreas Gohr */ 181569a5066SAndreas Gohr public function testLogReferer($referer, $expectedEngine, $shouldBeLogged) 182696a1b1bSAndreas Gohr { 183569a5066SAndreas Gohr $logger = $this->helper->getLogger(); 184569a5066SAndreas Gohr $logger->begin(); 185569a5066SAndreas Gohr $refId = $logger->logReferer($referer); 186569a5066SAndreas Gohr $logger->end(); 187569a5066SAndreas Gohr 188569a5066SAndreas Gohr if ($shouldBeLogged) { 18941d1fffcSAndreas Gohr $this->assertNotNull($refId); 19041d1fffcSAndreas Gohr $refererRecord = $this->helper->getDB()->queryRecord('SELECT * FROM referers WHERE id = ?', [$refId]); 191569a5066SAndreas Gohr $this->assertNotNull($refererRecord); 19241d1fffcSAndreas Gohr $this->assertEquals($expectedEngine, $refererRecord['engine']); 193569a5066SAndreas Gohr $this->assertEquals(trim($referer), $refererRecord['url']); 194569a5066SAndreas Gohr } else { 195569a5066SAndreas Gohr $this->assertNull($refId); 196569a5066SAndreas Gohr } 197569a5066SAndreas Gohr } 198569a5066SAndreas Gohr 199569a5066SAndreas Gohr /** 200569a5066SAndreas Gohr * Test that internal referers (our own pages) are not logged 201569a5066SAndreas Gohr */ 202569a5066SAndreas Gohr public function testLogRefererInternal() 203569a5066SAndreas Gohr { 204569a5066SAndreas Gohr // Test internal referer (should return null and not be logged) 205569a5066SAndreas Gohr $internalReferer = DOKU_URL; 206569a5066SAndreas Gohr $refId = $this->helper->getLogger()->logReferer($internalReferer); 207569a5066SAndreas Gohr $this->assertNull($refId, 'Internal referers should not be logged'); 208569a5066SAndreas Gohr 209569a5066SAndreas Gohr // Verify no referer was actually stored 210569a5066SAndreas Gohr $count = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM referers WHERE url = ?', [$internalReferer]); 211569a5066SAndreas Gohr $this->assertEquals(0, $count, 'Internal referer should not be stored in database'); 212569a5066SAndreas Gohr 213569a5066SAndreas Gohr // Test another internal referer pattern 214569a5066SAndreas Gohr $internalReferer2 = rtrim(DOKU_URL, '/') . '/doku.php?id=start'; 215569a5066SAndreas Gohr $refId2 = $this->helper->getLogger()->logReferer($internalReferer2); 216569a5066SAndreas Gohr $this->assertNull($refId2, 'Internal wiki pages should not be logged as referers'); 217696a1b1bSAndreas Gohr } 218696a1b1bSAndreas Gohr 219696a1b1bSAndreas Gohr /** 220696a1b1bSAndreas Gohr * Test logSearch method 221696a1b1bSAndreas Gohr */ 222696a1b1bSAndreas Gohr public function testLogSearch() 223696a1b1bSAndreas Gohr { 224696a1b1bSAndreas Gohr $query = 'test search query'; 225696a1b1bSAndreas Gohr $words = ['test', 'search', 'query']; 226696a1b1bSAndreas Gohr 22741d1fffcSAndreas Gohr $this->helper->getLogger()->logSearch($query, $words); 228696a1b1bSAndreas Gohr 229696a1b1bSAndreas Gohr // Check search table 230696a1b1bSAndreas Gohr $search = $this->helper->getDB()->queryRecord('SELECT * FROM search ORDER BY dt DESC LIMIT 1'); 231696a1b1bSAndreas Gohr $this->assertEquals($query, $search['query']); 232696a1b1bSAndreas Gohr 233696a1b1bSAndreas Gohr // Check searchwords table 234de1daf8cSAndreas Gohr $wordCount = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM searchwords WHERE sid = ?', [$search['id']]); 235696a1b1bSAndreas Gohr $this->assertEquals(3, $wordCount); 236696a1b1bSAndreas Gohr 237de1daf8cSAndreas Gohr $loggedWords = $this->helper->getDB()->queryAll('SELECT word FROM searchwords WHERE sid = ? ORDER BY word', [$search['id']]); 238696a1b1bSAndreas Gohr $this->assertEquals(['query', 'search', 'test'], array_column($loggedWords, 'word')); 239696a1b1bSAndreas Gohr } 240696a1b1bSAndreas Gohr 241696a1b1bSAndreas Gohr /** 242696a1b1bSAndreas Gohr * Test logSession method 243696a1b1bSAndreas Gohr */ 244696a1b1bSAndreas Gohr public function testLogSession() 245696a1b1bSAndreas Gohr { 24641d1fffcSAndreas Gohr $_SERVER['REMOTE_USER'] = 'testuser'; 247696a1b1bSAndreas Gohr 24841d1fffcSAndreas Gohr // Test session creation 24941d1fffcSAndreas Gohr $logger = $this->helper->getLogger(); 25041d1fffcSAndreas Gohr 25141d1fffcSAndreas Gohr $logger->begin(); 25241d1fffcSAndreas Gohr $logger->end(); 25341d1fffcSAndreas Gohr 25441d1fffcSAndreas Gohr $sessionCount = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM sessions'); 255696a1b1bSAndreas Gohr $this->assertEquals(1, $sessionCount); 256696a1b1bSAndreas Gohr 25741d1fffcSAndreas Gohr $session = $this->helper->getDB()->queryRecord('SELECT * FROM sessions LIMIT 1'); 25841d1fffcSAndreas Gohr $this->assertIsArray($session); 25941d1fffcSAndreas Gohr $this->assertEquals('testuser', $session['user']); 26041d1fffcSAndreas Gohr $this->assertEquals(self::SESSION_ID, $session['session']); 26141d1fffcSAndreas Gohr $this->assertEquals(self::USER_ID, $session['uid']); 26241d1fffcSAndreas Gohr $this->assertEquals(self::USER_AGENT, $session['ua']); 26341d1fffcSAndreas Gohr $this->assertEquals('Chrome', $session['ua_info']); 26441d1fffcSAndreas Gohr $this->assertEquals('browser', $session['ua_type']); 26541d1fffcSAndreas Gohr $this->assertEquals('91', $session['ua_ver']); 26641d1fffcSAndreas Gohr $this->assertEquals('Windows', $session['os']); 267696a1b1bSAndreas Gohr 268696a1b1bSAndreas Gohr } 269696a1b1bSAndreas Gohr 270696a1b1bSAndreas Gohr /** 271696a1b1bSAndreas Gohr * Test logIp method 272696a1b1bSAndreas Gohr */ 273696a1b1bSAndreas Gohr public function testLogIp() 274696a1b1bSAndreas Gohr { 275696a1b1bSAndreas Gohr $ip = '8.8.8.8'; 27641d1fffcSAndreas Gohr $_SERVER['REMOTE_ADDR'] = $ip; 277696a1b1bSAndreas Gohr 278c7cad24dSAndreas Gohr // Create a mock HTTP client 279c7cad24dSAndreas Gohr $mockHttpClient = $this->createMock(\dokuwiki\HTTP\DokuHTTPClient::class); 2806c24c4b8SAndreas Gohr 281c7cad24dSAndreas Gohr // Mock the API response 282c7cad24dSAndreas Gohr $mockResponse = json_encode([ 283c7cad24dSAndreas Gohr 'status' => 'success', 284c7cad24dSAndreas Gohr 'country' => 'United States', 285c7cad24dSAndreas Gohr 'countryCode' => 'US', 286c7cad24dSAndreas Gohr 'city' => 'Ashburn', 287c7cad24dSAndreas Gohr 'query' => $ip 288c7cad24dSAndreas Gohr ]); 2896c24c4b8SAndreas Gohr 290c7cad24dSAndreas Gohr $mockHttpClient->expects($this->once()) 291c7cad24dSAndreas Gohr ->method('get') 292c7cad24dSAndreas Gohr ->with('http://ip-api.com/json/' . $ip) 293c7cad24dSAndreas Gohr ->willReturn($mockResponse); 294696a1b1bSAndreas Gohr 295c7cad24dSAndreas Gohr // Set timeout property 296c7cad24dSAndreas Gohr $mockHttpClient->timeout = 10; 297c7cad24dSAndreas Gohr 298c7cad24dSAndreas Gohr // Create logger with mock HTTP client 299ba6b3b10SAndreas Gohr $this->helper->httpClient = $mockHttpClient; 300ba6b3b10SAndreas Gohr $logger = new Logger($this->helper); 301c7cad24dSAndreas Gohr 302c7cad24dSAndreas Gohr // Test with IP that doesn't exist in database 30341d1fffcSAndreas Gohr $logger->logIp(); 304c7cad24dSAndreas Gohr 305c7cad24dSAndreas Gohr // Verify the IP was logged 306c7cad24dSAndreas Gohr $ipRecord = $this->helper->getDB()->queryRecord('SELECT * FROM iplocation WHERE ip = ?', [$ip]); 307c7cad24dSAndreas Gohr $this->assertNotNull($ipRecord); 308c7cad24dSAndreas Gohr $this->assertEquals($ip, $ipRecord['ip']); 309c7cad24dSAndreas Gohr $this->assertEquals('United States', $ipRecord['country']); 310c7cad24dSAndreas Gohr $this->assertEquals('US', $ipRecord['code']); 311c7cad24dSAndreas Gohr $this->assertEquals('Ashburn', $ipRecord['city']); 312c7cad24dSAndreas Gohr $this->assertNotEmpty($ipRecord['host']); // gethostbyaddr result 313c7cad24dSAndreas Gohr 314c7cad24dSAndreas Gohr // Test with IP that already exists and is recent (should not make HTTP call) 315c7cad24dSAndreas Gohr $mockHttpClient2 = $this->createMock(\dokuwiki\HTTP\DokuHTTPClient::class); 316c7cad24dSAndreas Gohr $mockHttpClient2->expects($this->never())->method('get'); 317c7cad24dSAndreas Gohr 318ba6b3b10SAndreas Gohr $this->helper->httpClient = $mockHttpClient2; 319ba6b3b10SAndreas Gohr $logger2 = new Logger($this->helper); 32041d1fffcSAndreas Gohr $logger2->logIp(); // Should not trigger HTTP call 321ba6b3b10SAndreas Gohr 322ba6b3b10SAndreas Gohr $this->helper->httpClient = null; // Reset HTTP client for other tests 323696a1b1bSAndreas Gohr } 324696a1b1bSAndreas Gohr 325696a1b1bSAndreas Gohr /** 326696a1b1bSAndreas Gohr * Test logOutgoing method 327696a1b1bSAndreas Gohr */ 328696a1b1bSAndreas Gohr public function testLogOutgoing() 329696a1b1bSAndreas Gohr { 330696a1b1bSAndreas Gohr global $INPUT; 331696a1b1bSAndreas Gohr 332696a1b1bSAndreas Gohr // Test without outgoing link 333696a1b1bSAndreas Gohr $INPUT->set('ol', ''); 33441d1fffcSAndreas Gohr $this->helper->getLogger()->logOutgoing(); 335696a1b1bSAndreas Gohr 336696a1b1bSAndreas Gohr $count = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM outlinks'); 337696a1b1bSAndreas Gohr $this->assertEquals(0, $count); 338696a1b1bSAndreas Gohr 339696a1b1bSAndreas Gohr // Test with outgoing link 340696a1b1bSAndreas Gohr $link = 'https://example.com'; 341696a1b1bSAndreas Gohr $page = 'test:page'; 342696a1b1bSAndreas Gohr $INPUT->set('ol', $link); 343696a1b1bSAndreas Gohr $INPUT->set('p', $page); 344696a1b1bSAndreas Gohr 34541d1fffcSAndreas Gohr $this->helper->getLogger()->logOutgoing(); 346696a1b1bSAndreas Gohr 347696a1b1bSAndreas Gohr $count = $this->helper->getDB()->queryValue('SELECT COUNT(*) FROM outlinks'); 348696a1b1bSAndreas Gohr $this->assertEquals(1, $count); 349696a1b1bSAndreas Gohr 350696a1b1bSAndreas Gohr $outlink = $this->helper->getDB()->queryRecord('SELECT * FROM outlinks ORDER BY dt DESC LIMIT 1'); 351696a1b1bSAndreas Gohr $this->assertEquals($link, $outlink['link']); 352696a1b1bSAndreas Gohr $this->assertEquals($page, $outlink['page']); 353696a1b1bSAndreas Gohr } 354696a1b1bSAndreas Gohr 355696a1b1bSAndreas Gohr /** 35641d1fffcSAndreas Gohr * Test logPageView method 357696a1b1bSAndreas Gohr */ 35841d1fffcSAndreas Gohr public function testLogPageView() 359696a1b1bSAndreas Gohr { 360de1daf8cSAndreas Gohr global $INPUT, $USERINFO, $conf; 361de1daf8cSAndreas Gohr 362de1daf8cSAndreas Gohr $conf['plugin']['statistics']['loggroups'] = ['admin', 'user']; 363696a1b1bSAndreas Gohr 364696a1b1bSAndreas Gohr $page = 'test:page'; 365696a1b1bSAndreas Gohr $referer = 'https://example.com'; 366696a1b1bSAndreas Gohr $user = 'testuser'; 367696a1b1bSAndreas Gohr 368696a1b1bSAndreas Gohr $INPUT->set('p', $page); 369696a1b1bSAndreas Gohr $INPUT->set('r', $referer); 370696a1b1bSAndreas Gohr $INPUT->set('sx', 1920); 371696a1b1bSAndreas Gohr $INPUT->set('sy', 1080); 372696a1b1bSAndreas Gohr $INPUT->set('vx', 1200); 373696a1b1bSAndreas Gohr $INPUT->set('vy', 800); 374696a1b1bSAndreas Gohr $INPUT->server->set('REMOTE_USER', $user); 375696a1b1bSAndreas Gohr 376696a1b1bSAndreas Gohr $USERINFO = ['grps' => ['admin', 'user']]; 377696a1b1bSAndreas Gohr 37841d1fffcSAndreas Gohr $logger = $this->helper->getLogger(); 37941d1fffcSAndreas Gohr $logger->begin(); 38041d1fffcSAndreas Gohr $logger->logPageView(); 38141d1fffcSAndreas Gohr $logger->end(); 382696a1b1bSAndreas Gohr 38341d1fffcSAndreas Gohr // Check pageviews table 38441d1fffcSAndreas Gohr $pageview = $this->helper->getDB()->queryRecord('SELECT * FROM pageviews ORDER BY dt DESC LIMIT 1'); 38541d1fffcSAndreas Gohr $this->assertEquals($page, $pageview['page']); 38641d1fffcSAndreas Gohr $this->assertEquals(1920, $pageview['screen_x']); 38741d1fffcSAndreas Gohr $this->assertEquals(1080, $pageview['screen_y']); 38841d1fffcSAndreas Gohr $this->assertEquals(1200, $pageview['view_x']); 38941d1fffcSAndreas Gohr $this->assertEquals(800, $pageview['view_y']); 39041d1fffcSAndreas Gohr $this->assertEquals(self::SESSION_ID, $pageview['session']); 391696a1b1bSAndreas Gohr } 392696a1b1bSAndreas Gohr 393696a1b1bSAndreas Gohr /** 394696a1b1bSAndreas Gohr * Data provider for logMedia test 395696a1b1bSAndreas Gohr */ 396696a1b1bSAndreas Gohr public function logMediaProvider() 397696a1b1bSAndreas Gohr { 398696a1b1bSAndreas Gohr return [ 399696a1b1bSAndreas Gohr 'image inline' => ['test.jpg', 'image/jpeg', true, 1024], 400696a1b1bSAndreas Gohr 'video not inline' => ['test.mp4', 'video/mp4', false, 2048], 401696a1b1bSAndreas Gohr 'document' => ['test.pdf', 'application/pdf', false, 512], 402696a1b1bSAndreas Gohr ]; 403696a1b1bSAndreas Gohr } 404696a1b1bSAndreas Gohr 405696a1b1bSAndreas Gohr /** 406696a1b1bSAndreas Gohr * Test logMedia method 407696a1b1bSAndreas Gohr * @dataProvider logMediaProvider 408696a1b1bSAndreas Gohr */ 409696a1b1bSAndreas Gohr public function testLogMedia($media, $mime, $inline, $size) 410696a1b1bSAndreas Gohr { 411696a1b1bSAndreas Gohr global $INPUT; 412696a1b1bSAndreas Gohr 413696a1b1bSAndreas Gohr $user = 'testuser'; 414696a1b1bSAndreas Gohr $INPUT->server->set('REMOTE_USER', $user); 415696a1b1bSAndreas Gohr 41641d1fffcSAndreas Gohr $this->helper->getLogger()->logMedia($media, $mime, $inline, $size); 417696a1b1bSAndreas Gohr 418696a1b1bSAndreas Gohr $mediaLog = $this->helper->getDB()->queryRecord('SELECT * FROM media ORDER BY dt DESC LIMIT 1'); 419696a1b1bSAndreas Gohr $this->assertEquals($media, $mediaLog['media']); 420696a1b1bSAndreas Gohr $this->assertEquals($size, $mediaLog['size']); 421696a1b1bSAndreas Gohr $this->assertEquals($inline ? 1 : 0, $mediaLog['inline']); 422696a1b1bSAndreas Gohr 423696a1b1bSAndreas Gohr [$mime1, $mime2] = explode('/', strtolower($mime)); 424696a1b1bSAndreas Gohr $this->assertEquals($mime1, $mediaLog['mime1']); 425696a1b1bSAndreas Gohr $this->assertEquals($mime2, $mediaLog['mime2']); 426696a1b1bSAndreas Gohr } 427696a1b1bSAndreas Gohr 428696a1b1bSAndreas Gohr /** 429696a1b1bSAndreas Gohr * Data provider for logEdit test 430696a1b1bSAndreas Gohr */ 431696a1b1bSAndreas Gohr public function logEditProvider() 432696a1b1bSAndreas Gohr { 433696a1b1bSAndreas Gohr return [ 434696a1b1bSAndreas Gohr 'create page' => ['new:page', 'create'], 435696a1b1bSAndreas Gohr 'edit page' => ['existing:page', 'edit'], 436696a1b1bSAndreas Gohr 'delete page' => ['old:page', 'delete'], 437696a1b1bSAndreas Gohr ]; 438696a1b1bSAndreas Gohr } 439696a1b1bSAndreas Gohr 440696a1b1bSAndreas Gohr /** 441696a1b1bSAndreas Gohr * Test logEdit method 442696a1b1bSAndreas Gohr * @dataProvider logEditProvider 443696a1b1bSAndreas Gohr */ 444696a1b1bSAndreas Gohr public function testLogEdit($page, $type) 445696a1b1bSAndreas Gohr { 44641d1fffcSAndreas Gohr global $INPUT, $USERINFO; 447de1daf8cSAndreas Gohr 44823e0cc03SAndreas Gohr 449696a1b1bSAndreas Gohr $user = 'testuser'; 450696a1b1bSAndreas Gohr $INPUT->server->set('REMOTE_USER', $user); 451696a1b1bSAndreas Gohr $USERINFO = ['grps' => ['admin']]; 452696a1b1bSAndreas Gohr 45341d1fffcSAndreas Gohr $this->helper->getLogger()->logEdit($page, $type); 454696a1b1bSAndreas Gohr 455696a1b1bSAndreas Gohr // Check edits table 456696a1b1bSAndreas Gohr $edit = $this->helper->getDB()->queryRecord('SELECT * FROM edits ORDER BY dt DESC LIMIT 1'); 457696a1b1bSAndreas Gohr $this->assertEquals($page, $edit['page']); 458696a1b1bSAndreas Gohr $this->assertEquals($type, $edit['type']); 459696a1b1bSAndreas Gohr } 460696a1b1bSAndreas Gohr 461696a1b1bSAndreas Gohr /** 462696a1b1bSAndreas Gohr * Data provider for logLogin test 463696a1b1bSAndreas Gohr */ 464696a1b1bSAndreas Gohr public function logLoginProvider() 465696a1b1bSAndreas Gohr { 466696a1b1bSAndreas Gohr return [ 467696a1b1bSAndreas Gohr 'login' => ['login', 'testuser'], 468696a1b1bSAndreas Gohr 'logout' => ['logout', 'testuser'], 469696a1b1bSAndreas Gohr 'create' => ['create', 'newuser'], 470696a1b1bSAndreas Gohr ]; 471696a1b1bSAndreas Gohr } 472696a1b1bSAndreas Gohr 473696a1b1bSAndreas Gohr /** 474696a1b1bSAndreas Gohr * Test logLogin method 475696a1b1bSAndreas Gohr * @dataProvider logLoginProvider 476696a1b1bSAndreas Gohr */ 477696a1b1bSAndreas Gohr public function testLogLogin($type, $user) 478696a1b1bSAndreas Gohr { 47941d1fffcSAndreas Gohr $this->helper->getLogger()->logLogin($type, $user); 480696a1b1bSAndreas Gohr $login = $this->helper->getDB()->queryRecord('SELECT * FROM logins ORDER BY dt DESC LIMIT 1'); 481696a1b1bSAndreas Gohr $this->assertEquals($type, $login['type']); 482696a1b1bSAndreas Gohr $this->assertEquals($user, $login['user']); 483696a1b1bSAndreas Gohr } 484696a1b1bSAndreas Gohr 485696a1b1bSAndreas Gohr /** 486696a1b1bSAndreas Gohr * Test logHistoryPages method 487696a1b1bSAndreas Gohr */ 488696a1b1bSAndreas Gohr public function testLogHistoryPages() 489696a1b1bSAndreas Gohr { 49041d1fffcSAndreas Gohr $this->helper->getLogger()->logHistoryPages(); 491696a1b1bSAndreas Gohr 492696a1b1bSAndreas Gohr // Check that both page_count and page_size entries were created 493696a1b1bSAndreas Gohr $pageCount = $this->helper->getDB()->queryValue('SELECT value FROM history WHERE info = ?', ['page_count']); 494696a1b1bSAndreas Gohr $pageSize = $this->helper->getDB()->queryValue('SELECT value FROM history WHERE info = ?', ['page_size']); 495696a1b1bSAndreas Gohr 496696a1b1bSAndreas Gohr $this->assertIsNumeric($pageCount); 497696a1b1bSAndreas Gohr $this->assertIsNumeric($pageSize); 498696a1b1bSAndreas Gohr $this->assertGreaterThanOrEqual(0, $pageCount); 499696a1b1bSAndreas Gohr $this->assertGreaterThanOrEqual(0, $pageSize); 500696a1b1bSAndreas Gohr } 501696a1b1bSAndreas Gohr 502696a1b1bSAndreas Gohr /** 503696a1b1bSAndreas Gohr * Test logHistoryMedia method 504696a1b1bSAndreas Gohr */ 505696a1b1bSAndreas Gohr public function testLogHistoryMedia() 506696a1b1bSAndreas Gohr { 50741d1fffcSAndreas Gohr $this->helper->getLogger()->logHistoryMedia(); 508696a1b1bSAndreas Gohr 509696a1b1bSAndreas Gohr // Check that both media_count and media_size entries were created 510696a1b1bSAndreas Gohr $mediaCount = $this->helper->getDB()->queryValue('SELECT value FROM history WHERE info = ?', ['media_count']); 511696a1b1bSAndreas Gohr $mediaSize = $this->helper->getDB()->queryValue('SELECT value FROM history WHERE info = ?', ['media_size']); 512696a1b1bSAndreas Gohr 513696a1b1bSAndreas Gohr $this->assertIsNumeric($mediaCount); 514696a1b1bSAndreas Gohr $this->assertIsNumeric($mediaSize); 515696a1b1bSAndreas Gohr $this->assertGreaterThanOrEqual(0, $mediaCount); 516696a1b1bSAndreas Gohr $this->assertGreaterThanOrEqual(0, $mediaSize); 517696a1b1bSAndreas Gohr } 518696a1b1bSAndreas Gohr 519696a1b1bSAndreas Gohr /** 520696a1b1bSAndreas Gohr * Test that feedreader user agents are handled correctly 521696a1b1bSAndreas Gohr */ 522696a1b1bSAndreas Gohr public function testFeedReaderUserAgent() 523696a1b1bSAndreas Gohr { 52400f786d8SAndreas Gohr // Use a user agent that DeviceDetector recognizes as a feedreader 52500f786d8SAndreas Gohr $_SERVER['HTTP_USER_AGENT'] = 'BashPodder/1.0 (http://bashpodder.sourceforge.net/)'; 526696a1b1bSAndreas Gohr 527696a1b1bSAndreas Gohr $logger = new Logger($this->helper); 528696a1b1bSAndreas Gohr 529696a1b1bSAndreas Gohr // Use reflection to access protected property 530696a1b1bSAndreas Gohr $reflection = new \ReflectionClass($logger); 531696a1b1bSAndreas Gohr $uaTypeProperty = $reflection->getProperty('uaType'); 532696a1b1bSAndreas Gohr $uaTypeProperty->setAccessible(true); 533696a1b1bSAndreas Gohr 534696a1b1bSAndreas Gohr $this->assertEquals('feedreader', $uaTypeProperty->getValue($logger)); 535696a1b1bSAndreas Gohr } 536*086792c5SAndreas Gohr 537*086792c5SAndreas Gohr /** 538*086792c5SAndreas Gohr * Data provider for logCampaign test 539*086792c5SAndreas Gohr */ 540*086792c5SAndreas Gohr public function logCampaignProvider() 541*086792c5SAndreas Gohr { 542*086792c5SAndreas Gohr return [ 543*086792c5SAndreas Gohr 'all utm parameters' => [ 544*086792c5SAndreas Gohr ['utm_campaign' => 'summer_sale', 'utm_source' => 'google', 'utm_medium' => 'cpc'], 545*086792c5SAndreas Gohr ['summer_sale', 'google', 'cpc'], 546*086792c5SAndreas Gohr true 547*086792c5SAndreas Gohr ], 548*086792c5SAndreas Gohr 'only campaign' => [ 549*086792c5SAndreas Gohr ['utm_campaign' => 'newsletter'], 550*086792c5SAndreas Gohr ['newsletter', null, null], 551*086792c5SAndreas Gohr true 552*086792c5SAndreas Gohr ], 553*086792c5SAndreas Gohr 'only source' => [ 554*086792c5SAndreas Gohr ['utm_source' => 'facebook'], 555*086792c5SAndreas Gohr [null, 'facebook', null], 556*086792c5SAndreas Gohr true 557*086792c5SAndreas Gohr ], 558*086792c5SAndreas Gohr 'only medium' => [ 559*086792c5SAndreas Gohr ['utm_medium' => 'email'], 560*086792c5SAndreas Gohr [null, null, 'email'], 561*086792c5SAndreas Gohr true 562*086792c5SAndreas Gohr ], 563*086792c5SAndreas Gohr 'campaign and source' => [ 564*086792c5SAndreas Gohr ['utm_campaign' => 'holiday', 'utm_source' => 'twitter'], 565*086792c5SAndreas Gohr ['holiday', 'twitter', null], 566*086792c5SAndreas Gohr true 567*086792c5SAndreas Gohr ], 568*086792c5SAndreas Gohr 'no utm parameters' => [ 569*086792c5SAndreas Gohr [], 570*086792c5SAndreas Gohr [null, null, null], 571*086792c5SAndreas Gohr false 572*086792c5SAndreas Gohr ], 573*086792c5SAndreas Gohr 'empty utm parameters' => [ 574*086792c5SAndreas Gohr ['utm_campaign' => '', 'utm_source' => '', 'utm_medium' => ''], 575*086792c5SAndreas Gohr [null, null, null], 576*086792c5SAndreas Gohr false 577*086792c5SAndreas Gohr ], 578*086792c5SAndreas Gohr 'whitespace utm parameters' => [ 579*086792c5SAndreas Gohr ['utm_campaign' => ' ', 'utm_source' => ' ', 'utm_medium' => ' '], 580*086792c5SAndreas Gohr [null, null, null], 581*086792c5SAndreas Gohr false 582*086792c5SAndreas Gohr ], 583*086792c5SAndreas Gohr 'mixed empty and valid' => [ 584*086792c5SAndreas Gohr ['utm_campaign' => '', 'utm_source' => 'instagram', 'utm_medium' => ''], 585*086792c5SAndreas Gohr [null, 'instagram', null], 586*086792c5SAndreas Gohr true 587*086792c5SAndreas Gohr ], 588*086792c5SAndreas Gohr ]; 589*086792c5SAndreas Gohr } 590*086792c5SAndreas Gohr 591*086792c5SAndreas Gohr /** 592*086792c5SAndreas Gohr * Test logCampaign method 593*086792c5SAndreas Gohr * @dataProvider logCampaignProvider 594*086792c5SAndreas Gohr */ 595*086792c5SAndreas Gohr public function testLogCampaign($inputParams, $expectedValues, $shouldBeLogged) 596*086792c5SAndreas Gohr { 597*086792c5SAndreas Gohr global $INPUT; 598*086792c5SAndreas Gohr 599*086792c5SAndreas Gohr // Clean up any existing campaign data first 600*086792c5SAndreas Gohr $this->helper->getDB()->exec('DELETE FROM campaigns WHERE session = ?', [self::SESSION_ID]); 601*086792c5SAndreas Gohr 602*086792c5SAndreas Gohr // Set up INPUT parameters 603*086792c5SAndreas Gohr foreach ($inputParams as $key => $value) { 604*086792c5SAndreas Gohr $INPUT->set($key, $value); 605*086792c5SAndreas Gohr } 606*086792c5SAndreas Gohr 607*086792c5SAndreas Gohr $logger = $this->helper->getLogger(); 608*086792c5SAndreas Gohr $logger->begin(); 609*086792c5SAndreas Gohr $logger->end(); 610*086792c5SAndreas Gohr 611*086792c5SAndreas Gohr if ($shouldBeLogged) { 612*086792c5SAndreas Gohr $campaign = $this->helper->getDB()->queryRecord( 613*086792c5SAndreas Gohr 'SELECT * FROM campaigns WHERE session = ? ORDER BY rowid DESC LIMIT 1', 614*086792c5SAndreas Gohr [self::SESSION_ID] 615*086792c5SAndreas Gohr ); 616*086792c5SAndreas Gohr 617*086792c5SAndreas Gohr $this->assertNotNull($campaign, 'Campaign should be logged'); 618*086792c5SAndreas Gohr $this->assertEquals(self::SESSION_ID, $campaign['session']); 619*086792c5SAndreas Gohr $this->assertEquals($expectedValues[0], $campaign['campaign']); 620*086792c5SAndreas Gohr $this->assertEquals($expectedValues[1], $campaign['source']); 621*086792c5SAndreas Gohr $this->assertEquals($expectedValues[2], $campaign['medium']); 622*086792c5SAndreas Gohr } else { 623*086792c5SAndreas Gohr $count = $this->helper->getDB()->queryValue( 624*086792c5SAndreas Gohr 'SELECT COUNT(*) FROM campaigns WHERE session = ?', 625*086792c5SAndreas Gohr [self::SESSION_ID] 626*086792c5SAndreas Gohr ); 627*086792c5SAndreas Gohr $this->assertEquals(0, $count, 'No campaign should be logged'); 628*086792c5SAndreas Gohr } 629*086792c5SAndreas Gohr 630*086792c5SAndreas Gohr // Clean up INPUT for next test 631*086792c5SAndreas Gohr foreach ($inputParams as $key => $value) { 632*086792c5SAndreas Gohr $INPUT->set($key, null); 633*086792c5SAndreas Gohr } 634*086792c5SAndreas Gohr } 635*086792c5SAndreas Gohr 636*086792c5SAndreas Gohr /** 637*086792c5SAndreas Gohr * Test that logCampaign uses INSERT OR IGNORE to prevent duplicates 638*086792c5SAndreas Gohr */ 639*086792c5SAndreas Gohr public function testLogCampaignDuplicatePrevention() 640*086792c5SAndreas Gohr { 641*086792c5SAndreas Gohr global $INPUT; 642*086792c5SAndreas Gohr 643*086792c5SAndreas Gohr // Clean up any existing campaign data first 644*086792c5SAndreas Gohr $this->helper->getDB()->exec('DELETE FROM campaigns WHERE session = ?', [self::SESSION_ID]); 645*086792c5SAndreas Gohr 646*086792c5SAndreas Gohr $INPUT->set('utm_campaign', 'test_campaign'); 647*086792c5SAndreas Gohr $INPUT->set('utm_source', 'test_source'); 648*086792c5SAndreas Gohr $INPUT->set('utm_medium', 'test_medium'); 649*086792c5SAndreas Gohr 650*086792c5SAndreas Gohr // Log the same campaign twice 651*086792c5SAndreas Gohr $logger1 = $this->helper->getLogger(); 652*086792c5SAndreas Gohr $logger1->begin(); 653*086792c5SAndreas Gohr $logger1->end(); 654*086792c5SAndreas Gohr 655*086792c5SAndreas Gohr $logger2 = $this->helper->getLogger(); 656*086792c5SAndreas Gohr $logger2->begin(); 657*086792c5SAndreas Gohr $logger2->end(); 658*086792c5SAndreas Gohr 659*086792c5SAndreas Gohr // Should only have one record due to INSERT OR IGNORE 660*086792c5SAndreas Gohr $count = $this->helper->getDB()->queryValue( 661*086792c5SAndreas Gohr 'SELECT COUNT(*) FROM campaigns WHERE session = ?', 662*086792c5SAndreas Gohr [self::SESSION_ID] 663*086792c5SAndreas Gohr ); 664*086792c5SAndreas Gohr $this->assertEquals(1, $count, 'Should only have one campaign record due to INSERT OR IGNORE'); 665*086792c5SAndreas Gohr 666*086792c5SAndreas Gohr // Clean up 667*086792c5SAndreas Gohr $INPUT->set('utm_campaign', null); 668*086792c5SAndreas Gohr $INPUT->set('utm_source', null); 669*086792c5SAndreas Gohr $INPUT->set('utm_medium', null); 670*086792c5SAndreas Gohr } 6715cc1319aSAndreas Gohr} 672