1<?php
2
3/*
4 * This file is part of the Stash package.
5 *
6 * (c) Robert Hafner <tedivm@tedivm.com>
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12namespace Stash\Test;
13
14use Stash\Exception\InvalidArgumentException;
15use Stash\Pool;
16use Stash\Driver\Ephemeral;
17use Stash\Test\Stubs\LoggerStub;
18use Stash\Test\Stubs\DriverExceptionStub;
19
20/**
21 * @package Stash
22 * @author  Robert Hafner <tedivm@tedivm.com>
23 */
24class AbstractPoolTest extends \PHPUnit\Framework\TestCase
25{
26    protected $data = array(array('test', 'test'));
27    protected $multiData = array('key' => 'value',
28        'key1' => 'value1',
29        'key2' => 'value2',
30        'key3' => 'value3');
31
32    protected $poolClass = '\Stash\Pool';
33
34    public function testSetDriver()
35    {
36        $driver = new Ephemeral();
37        $pool = new $this->poolClass($driver);
38        $this->assertAttributeEquals($driver, 'driver', $pool);
39    }
40
41    public function testSetItemDriver()
42    {
43        $pool = $this->getTestPool();
44        $stash = $pool->getItem('test');
45        $this->assertAttributeInstanceOf('Stash\Driver\Ephemeral', 'driver', $stash, 'set driver is pushed to new stash objects');
46    }
47
48    public function testSetItemClass()
49    {
50        $mockItem = $this->createMock('Stash\Interfaces\ItemInterface');
51        $mockClassName = get_class($mockItem);
52        $pool = $this->getTestPool();
53
54        $this->assertTrue($pool->setItemClass($mockClassName));
55        $this->assertAttributeEquals($mockClassName, 'itemClass', $pool);
56    }
57
58    public function testSetItemClassFakeClassException()
59    {
60        try {
61            $pool = $this->getTestPool();
62            $pool->setItemClass('FakeClassName');
63        } catch (\Exception $expected) {
64            return;
65        }
66        $this->fail('An expected exception has not been raised.');
67    }
68
69    public function testSetItemClassImproperClassException()
70    {
71        try {
72            $pool = $this->getTestPool();
73            $pool->setItemClass('\stdClass');
74        } catch (\Exception $expected) {
75            return;
76        }
77        $this->fail('An expected exception has not been raised.');
78    }
79
80    public function testGetItem()
81    {
82        $pool = $this->getTestPool();
83
84        $stash = $pool->getItem('base/one');
85        $this->assertInstanceOf('Stash\Item', $stash, 'getItem returns a Stash\Item object');
86
87        $stash->set($this->data)->save();
88        $storedData = $stash->get();
89        $this->assertEquals($this->data, $storedData, 'getItem returns working Stash\Item object');
90
91        $key = $stash->getKey();
92        $this->assertEquals('base/one', $key, 'Pool sets proper Item key.');
93
94        $pool->setNamespace('TestNamespace');
95        $item = $pool->getItem('test/item');
96
97        $this->assertAttributeEquals('TestNamespace', 'namespace', $item, 'Pool sets Item namespace.');
98    }
99
100    public function testSaveItem()
101    {
102        $pool = $this->getTestPool();
103
104        $this->assertFalse($pool->hasItem('base/one'), 'Pool->hasItem() returns false for item without stored data.');
105        $item = $pool->getItem('base/one');
106        $this->assertInstanceOf('Stash\Item', $item, 'getItem returns a Stash\Item object');
107
108        $key = $item->getKey();
109        $this->assertEquals('base/one', $key, 'Pool sets proper Item key.');
110
111        $item->set($this->data);
112        $this->assertTrue($pool->save($item), 'Pool->save() returns true.');
113        $storedData = $item->get();
114        $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on passed Item.');
115
116        $item = $pool->getItem('base/one');
117        $storedData = $item->get();
118        $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on new Item instance.');
119
120        $this->assertTrue($pool->hasItem('base/one'), 'Pool->hasItem() returns true for item with stored data.');
121
122        $pool->setNamespace('TestNamespace');
123        $item = $pool->getItem('test/item');
124
125        $this->assertAttributeEquals('TestNamespace', 'namespace', $item, 'Pool sets Item namespace.');
126    }
127
128
129    public function testSaveDeferredItem()
130    {
131        $pool = $this->getTestPool();
132
133        $this->assertFalse($pool->hasItem('base/one'), 'Pool->hasItem() returns false for item without stored data.');
134        $item = $pool->getItem('base/one');
135        $this->assertInstanceOf('Stash\Item', $item, 'getItem returns a Stash\Item object');
136
137        $key = $item->getKey();
138        $this->assertEquals('base/one', $key, 'Pool sets proper Item key.');
139
140        $item->set($this->data);
141        $this->assertTrue($pool->saveDeferred($item), 'Pool->save() returns true.');
142        $storedData = $item->get();
143        $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on passed Item.');
144
145        $item = $pool->getItem('base/one');
146        $storedData = $item->get();
147        $this->assertEquals($this->data, $storedData, 'Pool->save() returns proper data on new Item instance.');
148
149        $this->assertTrue($pool->hasItem('base/one'), 'Pool->hasItem() returns true for item with stored data.');
150
151        $pool->setNamespace('TestNamespace');
152        $item = $pool->getItem('test/item');
153
154        $this->assertAttributeEquals('TestNamespace', 'namespace', $item, 'Pool sets Item namespace.');
155    }
156
157    public function testHasItem()
158    {
159        $pool = $this->getTestPool();
160        $this->assertFalse($pool->hasItem('base/one'), 'Pool->hasItem() returns false for item without stored data.');
161        $item = $pool->getItem('base/one');
162        $item->set($this->data);
163        $pool->save($item);
164        $this->assertTrue($pool->hasItem('base/one'), 'Pool->hasItem() returns true for item with stored data.');
165    }
166
167    public function testCommit()
168    {
169        $pool = $this->getTestPool();
170        $this->assertTrue($pool->commit());
171    }
172
173
174    /**
175     * @expectedException InvalidArgumentException
176     * @expectedExceptionMessage Invalid or Empty Node passed to getItem constructor.
177     */
178    public function testGetItemInvalidKeyMissingNode()
179    {
180        $pool = $this->getTestPool();
181        $item = $pool->getItem('This/Test//Fail');
182    }
183
184    public function testGetItems()
185    {
186        $pool = $this->getTestPool();
187
188        $keys = array_keys($this->multiData);
189
190        $cacheIterator = $pool->getItems($keys);
191        $keyData = $this->multiData;
192        foreach ($cacheIterator as $key => $stash) {
193            $this->assertTrue($stash->isMiss(), 'new Cache in iterator is empty');
194            $stash->set($keyData[$key])->save();
195            unset($keyData[$key]);
196        }
197        $this->assertCount(0, $keyData, 'all keys are accounted for the in cache iterator');
198
199        $cacheIterator = $pool->getItems($keys);
200        foreach ($cacheIterator as $key => $stash) {
201            $this->assertEquals($key, $stash->getKey(), 'Item key is not equals key in iterator');
202            $data = $stash->get($key);
203            $this->assertEquals($this->multiData[$key], $data, 'data put into the pool comes back the same through iterators.');
204        }
205    }
206
207    public function testDeleteItems()
208    {
209        $pool = $this->getTestPool();
210
211        $keys = array_keys($this->multiData);
212
213        $cacheIterator = $pool->getItems($keys);
214        $keyData = $this->multiData;
215        foreach ($cacheIterator as $stash) {
216            $key = $stash->getKey();
217            $this->assertTrue($stash->isMiss(), 'new Cache in iterator is empty');
218            $stash->set($keyData[$key])->save();
219            unset($keyData[$key]);
220        }
221        $this->assertCount(0, $keyData, 'all keys are accounted for the in cache iterator');
222
223        $cacheIterator = $pool->getItems($keys);
224        foreach ($cacheIterator as $item) {
225            $key = $item->getKey();
226            $data = $item->get($key);
227            $this->assertEquals($this->multiData[$key], $data, 'data put into the pool comes back the same through iterators.');
228        }
229
230        $this->assertTrue($pool->deleteItems($keys), 'deleteItems returns true.');
231        $cacheIterator = $pool->getItems($keys);
232        foreach ($cacheIterator as $item) {
233            $this->assertTrue($item->isMiss(), 'data cleared using deleteItems is removed from the cache.');
234        }
235    }
236
237
238
239    public function testClearCache()
240    {
241        $pool = $this->getTestPool();
242
243        $stash = $pool->getItem('base/one');
244        $stash->set($this->data)->save();
245        $this->assertTrue($pool->clear(), 'clear returns true');
246
247        $stash = $pool->getItem('base/one');
248        $this->assertNull($stash->get(), 'clear removes item');
249        $this->assertTrue($stash->isMiss(), 'clear causes cache miss');
250    }
251
252    public function testPurgeCache()
253    {
254        $pool = $this->getTestPool();
255
256        $stash = $pool->getItem('base/one');
257        $stash->set($this->data)->expiresAfter(-600)->save();
258        $this->assertTrue($pool->purge(), 'purge returns true');
259
260        $stash = $pool->getItem('base/one');
261        $this->assertNull($stash->get(), 'purge removes item');
262        $this->assertTrue($stash->isMiss(), 'purge causes cache miss');
263    }
264
265    public function testNamespacing()
266    {
267        $pool = $this->getTestPool();
268
269        $this->assertAttributeEquals(null, 'namespace', $pool, 'Namespace starts empty.');
270        $this->assertTrue($pool->setNamespace('TestSpace'), 'setNamespace returns true.');
271        $this->assertAttributeEquals('TestSpace', 'namespace', $pool, 'setNamespace sets the namespace.');
272        $this->assertEquals('TestSpace', $pool->getNamespace(), 'getNamespace returns current namespace.');
273
274        $this->assertTrue($pool->setNamespace(), 'setNamespace returns true when setting null.');
275        $this->assertAttributeEquals(null, 'namespace', $pool, 'setNamespace() empties namespace.');
276        $this->assertFalse($pool->getNamespace(), 'getNamespace returns false when no namespace is set.');
277    }
278
279    /**
280     * @expectedException InvalidArgumentException
281     * @expectedExceptionMessage Namespace must be alphanumeric.
282     */
283    public function testInvalidNamespace()
284    {
285        $pool = $this->getTestPool();
286        $pool->setNamespace('!@#$%^&*(');
287    }
288
289
290    public function testSetLogger()
291    {
292        $pool = $this->getTestPool();
293
294        $driver = new DriverExceptionStub();
295        $pool->setDriver($driver);
296
297        $logger = new LoggerStub();
298        $pool->setLogger($logger);
299
300        $this->assertAttributeInstanceOf('Stash\Test\Stubs\LoggerStub', 'logger', $pool, 'setLogger injects logger into Pool.');
301
302        $item = $pool->getItem('testItem');
303        $this->assertAttributeInstanceOf('Stash\Test\Stubs\LoggerStub', 'logger', $item, 'setLogger injects logger into Pool.');
304    }
305
306    public function testLoggerClear()
307    {
308        $pool = $this->getTestPool();
309
310        $driver = new DriverExceptionStub();
311        $pool->setDriver($driver);
312
313        $logger = new LoggerStub();
314        $pool->setLogger($logger);
315
316        // triggerlogging
317        $pool->clear();
318
319        $this->assertInstanceOf(
320            'Stash\Test\Exception\TestException',
321            $logger->lastContext['exception'],
322            'Logger was passed exception in event context.'
323        );
324
325        $this->assertTrue(strlen($logger->lastMessage) > 0, 'Logger message set after "get" exception.');
326        $this->assertEquals('critical', $logger->lastLevel, 'Exceptions logged as critical.');
327    }
328
329    public function testLoggerPurge()
330    {
331        $pool = $this->getTestPool();
332
333        $driver = new DriverExceptionStub();
334        $pool->setDriver($driver);
335
336        $logger = new LoggerStub();
337        $pool->setLogger($logger);
338
339        // triggerlogging
340        $pool->purge();
341
342        $this->assertInstanceOf(
343            'Stash\Test\Exception\TestException',
344            $logger->lastContext['exception'],
345            'Logger was passed exception in event context.'
346        );
347        $this->assertTrue(strlen($logger->lastMessage) > 0, 'Logger message set after "set" exception.');
348        $this->assertEquals('critical', $logger->lastLevel, 'Exceptions logged as critical.');
349    }
350
351    /**
352     * @return \Stash\Pool
353     */
354    protected function getTestPool()
355    {
356        return new $this->poolClass();
357    }
358}
359