Skip to content

Commit b9d33c2

Browse files
committed
test(backend): add unit tests for SieveScriptCache and SieveConnectionManager
1 parent 9b6a033 commit b9d33c2

File tree

7 files changed

+639
-1
lines changed

7 files changed

+639
-1
lines changed

tests/phpunit/bootstrap.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525

2626
/* get the framework */
2727
require APP_PATH.'lib/framework.php';
28+
require APP_PATH.'modules/sievefilters/hm-sieve-script-cache.php';
2829

2930
/* get the stubs */
3031
require APP_PATH.'tests/phpunit/stubs.php';

tests/phpunit/mocks.php

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,98 @@ function getResultCode() {
114114
}
115115
}
116116

117+
/**
118+
* Mock cache class for testing
119+
*/
120+
class MockCache {
121+
private $data = [];
122+
123+
public function get($key, $default = false, $returnArray = false) {
124+
return isset($this->data[$key]) ? $this->data[$key] : $default;
125+
}
126+
127+
public function set($key, $value, $ttl = 0, $returnArray = false) {
128+
$this->data[$key] = $value;
129+
return true;
130+
}
131+
132+
public function del($key) {
133+
if (isset($this->data[$key])) {
134+
unset($this->data[$key]);
135+
return true;
136+
}
137+
return false;
138+
}
139+
140+
public function clear() {
141+
$this->data = [];
142+
}
143+
}
144+
145+
/**
146+
* Mock Sieve Client for testing
147+
*/
148+
class MockSieveClient {
149+
private $scripts = [];
150+
private $connected = false;
151+
152+
public function connect($username, $password, $secure = true, $authzid = "", $authType = "PLAIN") {
153+
$this->connected = true;
154+
return true;
155+
}
156+
157+
public function getScript($name) {
158+
return isset($this->scripts[$name]) ? $this->scripts[$name] : false;
159+
}
160+
161+
public function listScripts() {
162+
return array_keys($this->scripts);
163+
}
164+
165+
public function putScript($name, $content) {
166+
$this->scripts[$name] = $content;
167+
return true;
168+
}
169+
170+
public function activateScript($name) {
171+
return isset($this->scripts[$name]);
172+
}
173+
174+
public function removeScripts($name) {
175+
if (isset($this->scripts[$name])) {
176+
unset($this->scripts[$name]);
177+
return true;
178+
}
179+
return false;
180+
}
181+
182+
public function close() {
183+
$this->connected = false;
184+
return true;
185+
}
186+
187+
public function renameScript($oldName, $newName) {
188+
if (isset($this->scripts[$oldName])) {
189+
$this->scripts[$newName] = $this->scripts[$oldName];
190+
unset($this->scripts[$oldName]);
191+
return true;
192+
}
193+
return false;
194+
}
195+
196+
public function getCapabilities() {
197+
return ['SIEVE' => '1.0', 'STARTTLS'];
198+
}
199+
200+
public function isConnected() {
201+
return $this->connected;
202+
}
203+
204+
public function setScript($name, $content) {
205+
$this->scripts[$name] = $content;
206+
}
207+
}
208+
117209
class Hm_Mock_Redis extends Hm_Mock_Memcached {
118210
function connect($server, $port) {
119211
return true;

tests/phpunit/modules/core/output_modules.php

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1089,7 +1089,6 @@ public function test_message_list_heading() {
10891089
$this->assertEquals(array('<div class="message_list p-0 _list"><div class="content_title d-flex flex-wrap justify-content-between px-3 align-items-center"><div class="d-flex align-items-center gap-1 flex-wrap mb-2 mb-md-0"><a class="toggle_link" href="#"><i class="bi bi-check-square-fill"></i></a><div class="msg_controls fs-6 d-none gap-1 align-items-center"><div class="dropdown on_mobile"><button type="button" class="btn btn-outline-success btn-sm dropdown-toggle" id="coreMsgControlDropdown" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">Actions</button><ul class="dropdown-menu" aria-labelledby="coreMsgControlDropdown"><li><a class="dropdown-item msg_read core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="read">Read</a></li><li><a class="dropdown-item msg_unread core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="unread">Unread</a></li><li><a class="dropdown-item msg_flag core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="flag">Flag</a></li><li><a class="dropdown-item msg_unflag core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="unflag">Unflag</a></li><li><a class="dropdown-item msg_delete core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="delete">Delete</a></li><li><a class="dropdown-item msg_archive core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="archive">Archive</a></li><li><a class="dropdown-item msg_junk core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="junk">Junk</a></li></ul></div><a class="msg_read core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="read">Read</a><a class="msg_unread core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="unread">Unread</a><a class="msg_flag core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="flag">Flag</a><a class="msg_unflag core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="unflag">Unflag</a><a class="msg_delete core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="delete">Delete</a><a class="msg_archive core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="archive">Archive</a><a class="msg_junk core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="junk">Junk</a></div><div class="mailbox_list_title ms-2"></div><select name="sort" style="width: 150px" class="combined_sort form-select form-select-sm ms-2"><option value="arrival">Arrival Date &darr;</option><option value="-arrival">Arrival Date &uarr;</option><option value="date">Sent Date &darr;</option><option value="-date">Sent Date &uarr;</option><option value="from">From &darr;</option><option value="-from">From &uarr;</option><option value="to">To &darr;</option><option value="-to">To &uarr;</option><option value="subject">Subject &darr;</option><option value="-subject">Subject &uarr;</option></select></div><div class="list_controls no_mobile d-flex gap-3 align-items-center"></div><div class="list_controls on_mobile"><i class="bi bi-filter-circle" onclick="listControlsMenu()"></i><div id="list_controls_menu" class="list_controls_menu"></div></div><div class="list_sources w-100 mt-2"><div class="src_title fs-5 mb-2">Sources</div></div></div>'), $res->output_response);
10901090
$test->handler_response = array('list_path' => 'combined_inbox');
10911091
$res = $test->run();
1092-
echo "=== OUTPUT ===\n" . $res->output_response[0] . "\n=== END ===\n";
10931092
$this->assertEquals(array('<div class="message_list p-0 combined_inbox_list"><div class="content_title d-flex flex-wrap justify-content-between px-3 align-items-center"><div class="d-flex align-items-center gap-1 flex-wrap mb-2 mb-md-0"><a class="toggle_link" href="#"><i class="bi bi-check-square-fill"></i></a><div class="msg_controls fs-6 d-none gap-1 align-items-center"><div class="dropdown on_mobile"><button type="button" class="btn btn-outline-success btn-sm dropdown-toggle" id="coreMsgControlDropdown" data-bs-toggle="dropdown" aria-haspopup="true" aria-expanded="true">Actions</button><ul class="dropdown-menu" aria-labelledby="coreMsgControlDropdown"><li><a class="dropdown-item msg_read core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="read">Read</a></li><li><a class="dropdown-item msg_unread core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="unread">Unread</a></li><li><a class="dropdown-item msg_flag core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="flag">Flag</a></li><li><a class="dropdown-item msg_unflag core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="unflag">Unflag</a></li><li><a class="dropdown-item msg_delete core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="delete">Delete</a></li><li><a class="dropdown-item msg_archive core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="archive">Archive</a></li><li><a class="dropdown-item msg_junk core_msg_control btn btn-sm btn-light text-black-50" href="#" data-action="junk">Junk</a></li></ul></div><a class="msg_read core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="read">Read</a><a class="msg_unread core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="unread">Unread</a><a class="msg_flag core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="flag">Flag</a><a class="msg_unflag core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="unflag">Unflag</a><a class="msg_delete core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="delete">Delete</a><a class="msg_archive core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="archive">Archive</a><a class="msg_junk core_msg_control btn btn-sm btn-light no_mobile border text-black-50" href="#" data-action="junk">Junk</a></div><div class="mailbox_list_title ms-2"></div><select name="sort" style="width: 150px" class="combined_sort form-select form-select-sm ms-2"><option value="arrival">Arrival Date &darr;</option><option value="-arrival">Arrival Date &uarr;</option><option value="date">Sent Date &darr;</option><option value="-date">Sent Date &uarr;</option><option value="from">From &darr;</option><option value="-from">From &uarr;</option><option value="to">To &darr;</option><option value="-to">To &uarr;</option><option value="subject">Subject &darr;</option><option value="-subject">Subject &uarr;</option></select></div><div class="list_controls no_mobile d-flex gap-3 align-items-center"><a class="refresh_link" title="Refresh" href="#"><i class="bi bi-arrow-clockwise refresh_list"></i></a><a href="#" title="Sources" class="source_link"><i class="bi bi-folder-fill refresh_list"></i></a><a title="Configure" href="?page=settings#all_setting"><i class="bi bi-gear-wide refresh_list"></i></a></div><div class="list_controls on_mobile"><i class="bi bi-filter-circle" onclick="listControlsMenu()"></i><div id="list_controls_menu" class="list_controls_menu"><a class="refresh_link" title="Refresh" href="#"><i class="bi bi-arrow-clockwise refresh_list"></i></a><a href="#" title="Sources" class="source_link"><i class="bi bi-folder-fill refresh_list"></i></a><a title="Configure" href="?page=settings#all_setting"><i class="bi bi-gear-wide refresh_list"></i></a></div></div><div class="list_sources w-100 mt-2"><div class="src_title fs-5 mb-2">Sources</div></div></div>'), $res->output_response);
10941093
}
10951094
/**

tests/phpunit/phpunit.xml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,15 @@
5555
<testsuite name="environment">
5656
<file>./environment.php</file>
5757
</testsuite>
58+
<testsuite name="sieve_cache">
59+
<file>./sieve_cache.php</file>
60+
</testsuite>
61+
<testsuite name="sieve_connection_manager">
62+
<file>./sieve_connection_manager.php</file>
63+
</testsuite>
64+
<testsuite name="sieve_service">
65+
<file>./sieve_service.php</file>
66+
</testsuite>
5867
<testsuite name="db">
5968
<file>./db.php</file>
6069
</testsuite>

tests/phpunit/sieve_cache.php

Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
<?php
2+
3+
use PHPUnit\Framework\TestCase;
4+
/**
5+
* Tests for SieveCache
6+
*/
7+
class Hm_Test_sieve_cache extends TestCase {
8+
9+
private $mockCache;
10+
11+
public function setUp(): void {
12+
require 'bootstrap.php';
13+
14+
$this->mockCache = new MockCache();
15+
SieveScriptCache::setCache($this->mockCache);
16+
17+
SieveScriptCache::setCacheTTL(3600);
18+
}
19+
20+
public function tearDown(): void {
21+
$this->mockCache->clear();
22+
}
23+
24+
/**
25+
* @preserveGlobalState disabled
26+
* @runInSeparateProcess
27+
*/
28+
public function test_setCache() {
29+
$cache = new MockCache();
30+
SieveScriptCache::setCache($cache);
31+
32+
$this->assertTrue(SieveScriptCache::cacheScript('test_key', 'test_script', 'test_content'));
33+
}
34+
35+
/**
36+
* @preserveGlobalState disabled
37+
* @runInSeparateProcess
38+
*/
39+
public function test_setCache_invalid() {
40+
$originalCache = new MockCache();
41+
SieveScriptCache::setCache($originalCache);
42+
43+
SieveScriptCache::setCache("invalid_cache");
44+
45+
$this->assertTrue(SieveScriptCache::cacheScript('test_key', 'test_script', 'test_content'));
46+
}
47+
48+
/**
49+
* @preserveGlobalState disabled
50+
* @runInSeparateProcess
51+
*/
52+
public function test_setCacheTTL() {
53+
SieveScriptCache::setCacheTTL(1800);
54+
55+
SieveScriptCache::cacheScript('test_key', 'test_script', 'test_content');
56+
57+
$this->assertEquals('test_content', SieveScriptCache::getCachedScript('test_key', 'test_script'));
58+
}
59+
60+
/**
61+
* @preserveGlobalState disabled
62+
* @runInSeparateProcess
63+
*/
64+
public function test_cacheScript_and_getCachedScript() {
65+
$key = 'test_server';
66+
$scriptName = 'vacation.sieve';
67+
$scriptContent = 'require "vacation"; vacation :days 10 "I\'m on vacation";';
68+
69+
$result = SieveScriptCache::cacheScript($key, $scriptName, $scriptContent);
70+
$this->assertTrue($result);
71+
72+
$cached = SieveScriptCache::getCachedScript($key, $scriptName);
73+
$this->assertEquals($scriptContent, $cached);
74+
}
75+
76+
/**
77+
* @preserveGlobalState disabled
78+
* @runInSeparateProcess
79+
*/
80+
public function test_getCachedScript_not_found() {
81+
$result = SieveScriptCache::getCachedScript('nonexistent_key', 'nonexistent_script');
82+
$this->assertFalse($result);
83+
}
84+
85+
/**
86+
* @preserveGlobalState disabled
87+
* @runInSeparateProcess
88+
*/
89+
public function test_getCachedScript_expired() {
90+
// Set a very short TTL
91+
SieveScriptCache::setCacheTTL(1);
92+
93+
$key = 'test_server';
94+
$scriptName = 'test.sieve';
95+
$scriptContent = 'test content';
96+
97+
SieveScriptCache::cacheScript($key, $scriptName, $scriptContent);
98+
99+
// Wait for expiration
100+
sleep(2);
101+
102+
$result = SieveScriptCache::getCachedScript($key, $scriptName);
103+
$this->assertFalse($result);
104+
}
105+
106+
/**
107+
* @preserveGlobalState disabled
108+
* @runInSeparateProcess
109+
*/
110+
public function test_invalidateScript() {
111+
$key = 'test_server';
112+
$scriptName = 'test.sieve';
113+
$scriptContent = 'test content';
114+
115+
SieveScriptCache::cacheScript($key, $scriptName, $scriptContent);
116+
117+
$this->assertEquals($scriptContent, SieveScriptCache::getCachedScript($key, $scriptName));
118+
119+
$result = SieveScriptCache::invalidateScript($key, $scriptName);
120+
$this->assertTrue($result);
121+
122+
$this->assertFalse(SieveScriptCache::getCachedScript($key, $scriptName));
123+
}
124+
125+
/**
126+
* @preserveGlobalState disabled
127+
* @runInSeparateProcess
128+
*/
129+
public function test_cacheScriptsList_and_getCachedScriptsList() {
130+
$key = 'test_server';
131+
$scripts = ['vacation.sieve', 'spam.sieve', 'forward.sieve'];
132+
133+
$result = SieveScriptCache::cacheScriptsList($key, $scripts);
134+
$this->assertTrue($result);
135+
136+
$cached = SieveScriptCache::getCachedScriptsList($key);
137+
$this->assertEquals($scripts, $cached);
138+
}
139+
140+
/**
141+
* @preserveGlobalState disabled
142+
* @runInSeparateProcess
143+
*/
144+
public function test_invalidateScriptsList() {
145+
$key = 'test_server';
146+
$scripts = ['test1.sieve', 'test2.sieve'];
147+
148+
SieveScriptCache::cacheScriptsList($key, $scripts);
149+
150+
$this->assertEquals($scripts, SieveScriptCache::getCachedScriptsList($key));
151+
152+
$result = SieveScriptCache::invalidateScriptsList($key);
153+
$this->assertTrue($result);
154+
155+
$this->assertFalse(SieveScriptCache::getCachedScriptsList($key));
156+
}
157+
158+
/**
159+
* @preserveGlobalState disabled
160+
* @runInSeparateProcess
161+
*/
162+
public function test_isCached() {
163+
$key = 'test_server';
164+
$scriptName = 'test.sieve';
165+
$scriptContent = 'test content';
166+
167+
$this->assertFalse(SieveScriptCache::isCached($key, $scriptName));
168+
169+
SieveScriptCache::cacheScript($key, $scriptName, $scriptContent);
170+
171+
$this->assertTrue(SieveScriptCache::isCached($key, $scriptName));
172+
}
173+
174+
/**
175+
* @preserveGlobalState disabled
176+
* @runInSeparateProcess
177+
*/
178+
public function test_clearScriptCache() {
179+
$key = 'test_server';
180+
$scriptName = 'test.sieve';
181+
$scriptContent = 'test content';
182+
183+
SieveScriptCache::cacheScript($key, $scriptName, $scriptContent);
184+
185+
$result = SieveScriptCache::clearScriptCache($key, $scriptName);
186+
$this->assertTrue($result);
187+
188+
$this->assertFalse(SieveScriptCache::getCachedScript($key, $scriptName));
189+
}
190+
191+
/**
192+
* @preserveGlobalState disabled
193+
* @runInSeparateProcess
194+
*/
195+
public function test_clearAllCache() {
196+
$key = 'test_server';
197+
198+
SieveScriptCache::cacheScript($key, 'script1.sieve', 'content1');
199+
SieveScriptCache::cacheScript($key, 'script2.sieve', 'content2');
200+
SieveScriptCache::cacheScriptsList($key, ['script1.sieve', 'script2.sieve']);
201+
202+
$result = SieveScriptCache::clearAllCache($key);
203+
$this->assertTrue($result);
204+
205+
$this->assertFalse(SieveScriptCache::getCachedScriptsList($key));
206+
}
207+
208+
/**
209+
* @preserveGlobalState disabled
210+
* @runInSeparateProcess
211+
*/
212+
public function test_operations_without_cache() {
213+
// Force cache to null using reflection since setCache(null) doesn't work
214+
$reflection = new ReflectionClass('SieveScriptCache');
215+
$cacheProperty = $reflection->getProperty('cache');
216+
$cacheProperty->setAccessible(true);
217+
$cacheProperty->setValue(null);
218+
219+
// All operations should return false
220+
$this->assertFalse(SieveScriptCache::cacheScript('key', 'script', 'content'));
221+
$this->assertFalse(SieveScriptCache::getCachedScript('key', 'script'));
222+
$this->assertFalse(SieveScriptCache::invalidateScript('key', 'script'));
223+
$this->assertFalse(SieveScriptCache::cacheScriptsList('key', []));
224+
$this->assertFalse(SieveScriptCache::getCachedScriptsList('key'));
225+
$this->assertFalse(SieveScriptCache::invalidateScriptsList('key'));
226+
$this->assertFalse(SieveScriptCache::clearScriptCache('key', 'script'));
227+
$this->assertFalse(SieveScriptCache::clearAllCache('key'));
228+
229+
// Restore cache for other tests
230+
$this->mockCache = new MockCache();
231+
SieveScriptCache::setCache($this->mockCache);
232+
}
233+
}

0 commit comments

Comments
 (0)