Completed
Push — master ( e05cd4...bb1ac9 )
by Luís
10s
created

testContainerIsNotEmptyWhenDataIsPassedToItAfterwards()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 8
Code Lines 4

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 8
rs 9.4285
c 0
b 0
f 0
cc 1
eloc 4
nc 1
nop 0
1
<?php
2
/*
3
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
4
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
5
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
6
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
7
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
8
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
9
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
10
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
11
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
12
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
13
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
14
 *
15
 * This software consists of voluntary contributions made by many individuals
16
 * and is licensed under the MIT license.
17
 */
18
19
declare(strict_types=1);
20
21
namespace PSR7SessionsTest\Storageless\Session;
22
23
use PHPUnit_Framework_TestCase;
24
use PSR7Sessions\Storageless\Session\DefaultSessionData;
25
26
/**
27
 * @covers \PSR7Sessions\Storageless\Session\DefaultSessionData
28
 */
29
final class DefaultSessionDataTest extends PHPUnit_Framework_TestCase
30
{
31
    public function testFromFromTokenDataBuildsADataContainer()
32
    {
33
        self::assertInstanceOf(DefaultSessionData::class, DefaultSessionData::fromTokenData([]));
34
    }
35
36
    public function testNewEmptySessionProducesAContainer()
37
    {
38
        self::assertInstanceOf(DefaultSessionData::class, DefaultSessionData::newEmptySession());
39
    }
40
41
    public function testContainerIsEmptyWhenCreatedExplicitlyAsEmpty()
42
    {
43
        self::assertTrue(DefaultSessionData::newEmptySession()->isEmpty());
44
    }
45
46
    public function testContainerIsEmptyWhenCreatedWithoutData()
47
    {
48
        self::assertTrue(DefaultSessionData::fromTokenData([])->isEmpty());
49
    }
50
51
    public function testContainerIsNotEmptyWhenDataIsProvided()
52
    {
53
        self::assertFalse(DefaultSessionData::fromTokenData(['foo' => 'bar'])->isEmpty());
54
    }
55
56
    public function testContainerIsNotEmptyWhenDataIsPassedToItAfterwards()
57
    {
58
        $session = DefaultSessionData::newEmptySession();
59
60
        $session->set('foo', 'bar');
61
62
        self::assertFalse($session->isEmpty());
63
    }
64
65
    public function testContainerIsEmptyWhenDataIsRemovedFromIt()
66
    {
67
        $session = DefaultSessionData::fromTokenData(['foo' => 'bar']);
68
69
        $session->remove('foo');
70
71
        self::assertTrue($session->isEmpty());
72
    }
73
74
    public function testClearWillRemoveEverythingFromTheSessionContainer()
75
    {
76
        $session = DefaultSessionData::fromTokenData([
77
            'foo' => 'bar',
78
            'baz' => 'tab',
79
        ]);
80
81
        $session->clear();
82
83
        self::assertTrue($session->isEmpty());
84
        self::assertTrue($session->hasChanged());
85
        self::assertFalse($session->has('foo'));
86
        self::assertFalse($session->has('baz'));
87
    }
88
89
    /**
90
     * @dataProvider storageScalarDataProvider
91
     */
92
    public function testContainerDataIsStoredAndRetrieved(string $key, $value)
93
    {
94
        $session = DefaultSessionData::newEmptySession();
95
96
        $session->set($key, $value);
97
        self::assertSame($value, $session->get($key));
98
    }
99
100
    /**
101
     * @dataProvider storageScalarDataProvider
102
     */
103
    public function testSettingDataInAContainerMarksTheContainerAsMutated(string $key, $value)
104
    {
105
        $session = DefaultSessionData::newEmptySession();
106
107
        $session->set($key, $value);
108
109
        self::assertTrue($session->hasChanged());
110
    }
111
112
    /**
113
     * @dataProvider storageScalarDataProvider
114
     */
115
    public function testContainerIsNotChangedWhenScalarDataIsSetAndOverwrittenInIt(string $key, $value)
116
    {
117
        $session = DefaultSessionData::fromTokenData([$key => $value]);
118
119
        self::assertFalse($session->hasChanged());
120
121
        $session->set($key, $value);
122
123
        self::assertFalse($session->hasChanged());
124
    }
125
126
    /**
127
     * @dataProvider storageNonScalarDataProvider
128
     */
129
    public function testContainerIsNotChangedWhenNonScalarDataIsSetAndOverwrittenInIt($nonScalarValue)
130
    {
131
        $session = DefaultSessionData::fromTokenData(['key' => $nonScalarValue]);
132
133
        self::assertFalse($session->hasChanged());
134
135
        $session->set('key', $nonScalarValue);
136
137
        self::assertFalse($session->hasChanged());
138
    }
139
140
    /**
141
     * @dataProvider storageScalarDataProvider
142
     */
143
    public function testContainerBuiltWithDataContainsData(string $key, $value)
144
    {
145
        $session = DefaultSessionData::fromTokenData([$key => $value]);
146
147
        self::assertTrue($session->has($key));
148
        self::assertSame($value, $session->get($key));
149
    }
150
151
    /**
152
     * @dataProvider storageScalarDataProvider
153
     */
154
    public function testContainerBuiltWithStdClassContainsData(string $key, $value)
155
    {
156
        if ("\0" === $key || "\0" === $value || '' === $key) {
157
            self::markTestSkipped('Null bytes or empty keys are not supported by PHP\'s stdClass');
158
        }
159
160
        $session = DefaultSessionData::fromDecodedTokenData((object) [$key => $value]);
161
162
        self::assertTrue($session->has($key));
163
        self::assertSame($value, $session->get($key));
164
    }
165
166
    /**
167
     * @dataProvider storageNonScalarDataProvider
168
     */
169
    public function testContainerStoresScalarValueFromNestedObjects($nonScalar, $expectedScalar)
170
    {
171
        $session = DefaultSessionData::fromTokenData(['key' => $nonScalar]);
172
173
        self::assertSame($expectedScalar, $session->get('key'));
174
175
        $session->set('otherKey', $nonScalar);
176
177
        self::assertSame($expectedScalar, $session->get('otherKey'));
178
    }
179
180
    /**
181
     * @dataProvider storageScalarDataProvider
182
     */
183
    public function testGetWillReturnDefaultValueOnNonExistingKey(string $key, $value)
184
    {
185
        $session = DefaultSessionData::newEmptySession();
186
187
        self::assertFalse($session->has($key));
188
        self::assertSame($value, $session->get($key, $value));
189
    }
190
191
    /**
192
     * @dataProvider storageNonScalarDataProvider
193
     */
194
    public function testGetWillReturnScalarCastDefaultValueOnNonExistingKey($nonScalar, $expectedScalar)
195
    {
196
        self::assertSame($expectedScalar, DefaultSessionData::newEmptySession()->get('key', $nonScalar));
197
    }
198
199
    public function testAllMethodsAreCoveredByAnInterfacedMethod()
200
    {
201
        $reflection = new \ReflectionClass(DefaultSessionData::class);
202
        $interfaces = $reflection->getInterfaces();
203
204
        foreach ($reflection->getMethods() as $method) {
205
            if ($method->isConstructor() || $method->isStatic() || ! $method->isPublic()) {
206
                continue;
207
            }
208
209
            self::assertNotEmpty(array_filter(
210
                $interfaces,
211
                function (\ReflectionClass $interface) use ($method) {
212
                    return $interface->hasMethod($method->getName());
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
213
                }
214
            ), $method->getName());
0 ignored issues
show
Bug introduced by
Consider using $method->name. There is an issue with getName() and APC-enabled PHP versions.
Loading history...
215
        }
216
    }
217
218
    public function storageNonScalarDataProvider() : array
219
    {
220
        return [
221
            'class' => [
222
                new class
223
                {
224
                    public $foo = 'bar';
225
                },
226
                ['foo' => 'bar'],
227
            ],
228
            'object' => [
229
                (object) ['baz' => [(object) ['tab' => 'taz']]],
230
                ['baz' => [['tab' => 'taz']]],
231
            ],
232
            'array'  => [
233
                [(object) ['tar' => 'tan']],
234
                [['tar' => 'tan']],
235
            ],
236
            'jsonSerializable' => [
237
                new class implements \JsonSerializable
238
                {
239
                    public function jsonSerialize()
240
                    {
241
                        return (object) ['war' => 'zip'];
242
                    }
243
                },
244
                ['war' => 'zip'],
245
            ],
246
            'emptyObject' => [
247
                new \stdClass(),
248
                [],
249
            ],
250
        ];
251
    }
252
253
    public function storageScalarDataProvider() : array
254
    {
255
        return [
256
            'string'             => ['foo', 'bar'],
257
            'empty string'       => ['foo', ''],
258
            'null-ish string'    => ['foo', 'null'],
259
            'null'               => ['foo', null],
260
            'empty string key'   => ['', 'bar'],
261
            '0-ish string'       => ['foo', '0'],
262
            'empty array string' => ['foo', '[]'],
263
            'null byte'          => ['foo', "\0"],
264
            'null byte key'      => ["\0", 'bar'],
265
            'zero'               => ['foo', 0],
266
            'integer'            => ['foo', 1],
267
            'negative integer'   => ['foo', -1],
268
            'large integer'      => ['foo', PHP_INT_MAX],
269
            'small integer'      => ['foo', PHP_INT_MIN],
270
            'float'              => ['foo', 0.1],
271
            'float zero'         => ['foo', 0.0],
272
            'empty array'        => ['foo', []],
273
            '0-indexed-array'    => ['foo', ['bar', 'baz']],
274
            'map'                => ['foo', ['bar' => 'baz']],
275
            'nested array'       => ['foo', ['bar' => []]],
276
        ];
277
    }
278
}
279