xref: /plugin/statistics/_test/LoggerTest.php (revision ba6b3b10263c8439a170c848fb8e0ce174e4f468)
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
299*ba6b3b10SAndreas Gohr        $this->helper->httpClient = $mockHttpClient;
300*ba6b3b10SAndreas 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
318*ba6b3b10SAndreas Gohr        $this->helper->httpClient = $mockHttpClient2;
319*ba6b3b10SAndreas Gohr        $logger2 = new Logger($this->helper);
32041d1fffcSAndreas Gohr        $logger2->logIp(); // Should not trigger HTTP call
321*ba6b3b10SAndreas Gohr
322*ba6b3b10SAndreas 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    }
5365cc1319aSAndreas Gohr}
537