Skip to content

Commit da34123

Browse files
committed
test: improved code coverage
1 parent 73c0264 commit da34123

File tree

1 file changed

+377
-0
lines changed

1 file changed

+377
-0
lines changed
Lines changed: 377 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,377 @@
1+
<?php
2+
3+
namespace phpMyFAQ\Faq;
4+
5+
use phpMyFAQ\Configuration;
6+
use phpMyFAQ\Database\Sqlite3;
7+
use phpMyFAQ\Language\Plurals;
8+
use phpMyFAQ\Translation;
9+
use PHPUnit\Framework\TestCase;
10+
use PHPUnit\Framework\MockObject\MockObject;
11+
use ReflectionClass;
12+
13+
/**
14+
* Class StatisticsTest
15+
*
16+
* @package phpMyFAQ\Faq
17+
*/
18+
class StatisticsTest extends TestCase
19+
{
20+
private Statistics $statistics;
21+
private Configuration|MockObject $configurationMock;
22+
private Sqlite3|MockObject $dbMock;
23+
24+
protected function setUp(): void
25+
{
26+
parent::setUp();
27+
28+
Translation::create()
29+
->setLanguagesDir(PMF_TRANSLATION_DIR)
30+
->setDefaultLanguage('en')
31+
->setCurrentLanguage('en')
32+
->setMultiByteLanguage();
33+
34+
// Create database mock
35+
$this->dbMock = $this->createMock(Sqlite3::class);
36+
37+
// Create configuration mock
38+
$this->configurationMock = $this->createMock(Configuration::class);
39+
$this->configurationMock->method('getDb')->willReturn($this->dbMock);
40+
41+
// Default to basic security level
42+
$this->configurationMock->method('get')
43+
->with('security.permLevel')
44+
->willReturn('basic');
45+
}
46+
47+
public function testConstructorWithBasicSecurityLevel(): void
48+
{
49+
$this->configurationMock->expects($this->once())
50+
->method('get')
51+
->with('security.permLevel')
52+
->willReturn('basic');
53+
54+
$statistics = new Statistics($this->configurationMock);
55+
56+
$this->assertInstanceOf(Statistics::class, $statistics);
57+
58+
$reflection = new ReflectionClass($statistics);
59+
60+
$groupSupportProperty = $reflection->getProperty('groupSupport');
61+
$groupSupportProperty->setAccessible(true);
62+
$this->assertFalse($groupSupportProperty->getValue($statistics));
63+
64+
$pluralsProperty = $reflection->getProperty('plurals');
65+
$pluralsProperty->setAccessible(true);
66+
$this->assertInstanceOf(Plurals::class, $pluralsProperty->getValue($statistics));
67+
}
68+
69+
public function testConstructorWithAdvancedSecurityLevel(): void
70+
{
71+
$configMock = $this->createMock(Configuration::class);
72+
$configMock->method('getDb')->willReturn($this->dbMock);
73+
$configMock->expects($this->once())
74+
->method('get')
75+
->with('security.permLevel')
76+
->willReturn('medium');
77+
78+
$statistics = new Statistics($configMock);
79+
80+
$this->assertInstanceOf(Statistics::class, $statistics);
81+
82+
$reflection = new ReflectionClass($statistics);
83+
$groupSupportProperty = $reflection->getProperty('groupSupport');
84+
$groupSupportProperty->setAccessible(true);
85+
$this->assertTrue($groupSupportProperty->getValue($statistics));
86+
}
87+
88+
public function testConstructorInitializesPlurals(): void
89+
{
90+
$statistics = new Statistics($this->configurationMock);
91+
92+
$reflection = new ReflectionClass($statistics);
93+
$pluralsProperty = $reflection->getProperty('plurals');
94+
$pluralsProperty->setAccessible(true);
95+
$plurals = $pluralsProperty->getValue($statistics);
96+
97+
$this->assertInstanceOf(Plurals::class, $plurals);
98+
}
99+
100+
public function testSetUserWithValidUserId(): void
101+
{
102+
$this->configurationMock->method('get')->willReturn('basic');
103+
$statistics = new Statistics($this->configurationMock);
104+
105+
$result = $statistics->setUser(123);
106+
107+
$this->assertInstanceOf(Statistics::class, $result);
108+
$this->assertSame($statistics, $result);
109+
110+
$reflection = new ReflectionClass($statistics);
111+
$userProperty = $reflection->getProperty('user');
112+
$userProperty->setAccessible(true);
113+
$this->assertEquals(123, $userProperty->getValue($statistics));
114+
}
115+
116+
public function testSetUserWithNegativeOne(): void
117+
{
118+
$this->configurationMock->method('get')->willReturn('basic');
119+
$statistics = new Statistics($this->configurationMock);
120+
121+
$statistics->setUser(-1);
122+
123+
$reflection = new ReflectionClass($statistics);
124+
$userProperty = $reflection->getProperty('user');
125+
$userProperty->setAccessible(true);
126+
$this->assertEquals(-1, $userProperty->getValue($statistics));
127+
}
128+
129+
public function testSetUserFluentInterface(): void
130+
{
131+
$this->configurationMock->method('get')->willReturn('basic');
132+
$statistics = new Statistics($this->configurationMock);
133+
134+
$result = $statistics->setUser(456)->setUser(789);
135+
136+
$this->assertInstanceOf(Statistics::class, $result);
137+
138+
$reflection = new ReflectionClass($statistics);
139+
$userProperty = $reflection->getProperty('user');
140+
$userProperty->setAccessible(true);
141+
$this->assertEquals(789, $userProperty->getValue($statistics));
142+
}
143+
144+
public function testSetGroupsWithValidArray(): void
145+
{
146+
$this->configurationMock->method('get')->willReturn('basic');
147+
$statistics = new Statistics($this->configurationMock);
148+
149+
$groups = [1, 2, 3, 5, 8];
150+
$result = $statistics->setGroups($groups);
151+
152+
$this->assertInstanceOf(Statistics::class, $result);
153+
$this->assertSame($statistics, $result);
154+
155+
$reflection = new ReflectionClass($statistics);
156+
$groupsProperty = $reflection->getProperty('groups');
157+
$groupsProperty->setAccessible(true);
158+
$this->assertEquals($groups, $groupsProperty->getValue($statistics));
159+
}
160+
161+
public function testSetGroupsWithEmptyArray(): void
162+
{
163+
$this->configurationMock->method('get')->willReturn('basic');
164+
$statistics = new Statistics($this->configurationMock);
165+
166+
$statistics->setGroups([]);
167+
168+
$reflection = new ReflectionClass($statistics);
169+
$groupsProperty = $reflection->getProperty('groups');
170+
$groupsProperty->setAccessible(true);
171+
$this->assertEquals([], $groupsProperty->getValue($statistics));
172+
}
173+
174+
public function testSetGroupsWithDefaultMinusOne(): void
175+
{
176+
$this->configurationMock->method('get')->willReturn('basic');
177+
$statistics = new Statistics($this->configurationMock);
178+
179+
$statistics->setGroups([-1]);
180+
181+
$reflection = new ReflectionClass($statistics);
182+
$groupsProperty = $reflection->getProperty('groups');
183+
$groupsProperty->setAccessible(true);
184+
$this->assertEquals([-1], $groupsProperty->getValue($statistics));
185+
}
186+
187+
public function testSetGroupsFluentInterface(): void
188+
{
189+
$this->configurationMock->method('get')->willReturn('basic');
190+
$statistics = new Statistics($this->configurationMock);
191+
192+
$result = $statistics->setGroups([1, 2])->setGroups([3, 4, 5]);
193+
194+
$this->assertInstanceOf(Statistics::class, $result);
195+
196+
$reflection = new ReflectionClass($statistics);
197+
$groupsProperty = $reflection->getProperty('groups');
198+
$groupsProperty->setAccessible(true);
199+
$this->assertEquals([3, 4, 5], $groupsProperty->getValue($statistics));
200+
}
201+
202+
public function testDefaultUserAndGroupsValues(): void
203+
{
204+
$this->configurationMock->method('get')->willReturn('basic');
205+
$statistics = new Statistics($this->configurationMock);
206+
207+
$reflection = new ReflectionClass($statistics);
208+
209+
$userProperty = $reflection->getProperty('user');
210+
$userProperty->setAccessible(true);
211+
$this->assertEquals(-1, $userProperty->getValue($statistics));
212+
213+
$groupsProperty = $reflection->getProperty('groups');
214+
$groupsProperty->setAccessible(true);
215+
$this->assertEquals([-1], $groupsProperty->getValue($statistics));
216+
}
217+
218+
public function testFluentInterfaceChaining(): void
219+
{
220+
$this->configurationMock->method('get')->willReturn('basic');
221+
$statistics = new Statistics($this->configurationMock);
222+
223+
$result = $statistics
224+
->setUser(999)
225+
->setGroups([10, 20, 30])
226+
->setUser(888);
227+
228+
$this->assertInstanceOf(Statistics::class, $result);
229+
230+
$reflection = new ReflectionClass($statistics);
231+
232+
$userProperty = $reflection->getProperty('user');
233+
$userProperty->setAccessible(true);
234+
$this->assertEquals(888, $userProperty->getValue($result));
235+
236+
$groupsProperty = $reflection->getProperty('groups');
237+
$groupsProperty->setAccessible(true);
238+
$this->assertEquals([10, 20, 30], $groupsProperty->getValue($result));
239+
}
240+
241+
public function testTotalFaqsWithoutLanguage(): void
242+
{
243+
$this->configurationMock->method('get')->willReturn('basic');
244+
$statistics = new Statistics($this->configurationMock);
245+
246+
$expectedQuery = "SELECT id FROM faqdata WHERE active = 'yes' AND date_start <= '" . date('YmdHis') . "' AND date_end >= '" . date('YmdHis') . "'";
247+
248+
$resultMock = $this->createMock(\SQLite3Result::class);
249+
250+
$this->dbMock->expects($this->once())
251+
->method('query')
252+
->with($this->stringContains("SELECT id FROM faqdata WHERE active = 'yes'"))
253+
->willReturn($resultMock);
254+
255+
$this->dbMock->expects($this->once())
256+
->method('numRows')
257+
->with($resultMock)
258+
->willReturn(5);
259+
260+
$result = $statistics->totalFaqs();
261+
262+
$this->assertEquals(5, $result);
263+
}
264+
265+
public function testTotalFaqsWithSpecificLanguage(): void
266+
{
267+
$this->configurationMock->method('get')->willReturn('basic');
268+
$statistics = new Statistics($this->configurationMock);
269+
270+
$resultMock = $this->createMock(\SQLite3Result::class);
271+
272+
$this->dbMock->expects($this->once())
273+
->method('escape')
274+
->with('en')
275+
->willReturn('en');
276+
277+
$this->dbMock->expects($this->once())
278+
->method('query')
279+
->with($this->stringContains("AND lang = 'en'"))
280+
->willReturn($resultMock);
281+
282+
$this->dbMock->expects($this->once())
283+
->method('numRows')
284+
->with($resultMock)
285+
->willReturn(3);
286+
287+
$result = $statistics->totalFaqs('en');
288+
289+
$this->assertEquals(3, $result);
290+
}
291+
292+
public function testTotalFaqsWithNoResults(): void
293+
{
294+
$this->configurationMock->method('get')->willReturn('basic');
295+
$statistics = new Statistics($this->configurationMock);
296+
297+
$resultMock = $this->createMock(\SQLite3Result::class);
298+
299+
$this->dbMock->expects($this->once())
300+
->method('query')
301+
->willReturn($resultMock);
302+
303+
$this->dbMock->expects($this->once())
304+
->method('numRows')
305+
->with($resultMock)
306+
->willReturn(0);
307+
308+
$result = $statistics->totalFaqs();
309+
310+
$this->assertEquals(0, $result);
311+
}
312+
313+
public function testTotalFaqsWithNegativeNumRows(): void
314+
{
315+
$this->configurationMock->method('get')->willReturn('basic');
316+
$statistics = new Statistics($this->configurationMock);
317+
318+
$resultMock = $this->createMock(\SQLite3Result::class);
319+
320+
$this->dbMock->expects($this->once())
321+
->method('query')
322+
->willReturn($resultMock);
323+
324+
$this->dbMock->expects($this->once())
325+
->method('numRows')
326+
->with($resultMock)
327+
->willReturn(-1);
328+
329+
$result = $statistics->totalFaqs();
330+
331+
$this->assertEquals(0, $result);
332+
}
333+
334+
public function testTotalFaqsQueryContainsDateFilter(): void
335+
{
336+
$this->configurationMock->method('get')->willReturn('basic');
337+
$statistics = new Statistics($this->configurationMock);
338+
339+
$now = date('YmdHis');
340+
$resultMock = $this->createMock(\SQLite3Result::class);
341+
342+
$this->dbMock->expects($this->once())
343+
->method('query')
344+
->with($this->logicalAnd(
345+
$this->stringContains("date_start <= '$now'"),
346+
$this->stringContains("date_end >= '$now'")
347+
))
348+
->willReturn($resultMock);
349+
350+
$this->dbMock->method('numRows')->willReturn(0);
351+
352+
$statistics->totalFaqs();
353+
}
354+
355+
public function testTotalFaqsEscapesLanguageParameter(): void
356+
{
357+
$this->configurationMock->method('get')->willReturn('basic');
358+
$statistics = new Statistics($this->configurationMock);
359+
360+
$maliciousLanguage = "en'; DROP TABLE faqdata; --";
361+
$escapedLanguage = "en\\'; DROP TABLE faqdata; --";
362+
363+
$this->dbMock->expects($this->once())
364+
->method('escape')
365+
->with($maliciousLanguage)
366+
->willReturn($escapedLanguage);
367+
368+
$this->dbMock->expects($this->once())
369+
->method('query')
370+
->with($this->stringContains($escapedLanguage))
371+
->willReturn($this->createMock(\SQLite3Result::class));
372+
373+
$this->dbMock->method('numRows')->willReturn(0);
374+
375+
$statistics->totalFaqs($maliciousLanguage);
376+
}
377+
}

0 commit comments

Comments
 (0)