Passed
Push — main ( edbce0...46740f )
by Chema
01:03 queued 14s
created

anonymous//tests/Unit/ContainerTest.php$0   A

Complexity

Total Complexity 1

Size/Duplication

Total Lines 4
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
wmc 1
c 1
b 0
f 0
dl 0
loc 4
rs 10
1
<?php
2
3
declare(strict_types=1);
4
5
namespace GacelaTest\Unit;
6
7
use ArrayObject;
8
use Gacela\Container\Container;
9
use Gacela\Container\Exception\ContainerException;
10
use GacelaTest\Fake\ClassWithInterfaceDependencies;
11
use GacelaTest\Fake\ClassWithObjectDependencies;
12
use GacelaTest\Fake\ClassWithoutDependencies;
13
use GacelaTest\Fake\Person;
14
use GacelaTest\Fake\PersonInterface;
15
use PHPUnit\Framework\TestCase;
16
17
final class ContainerTest extends TestCase
18
{
19
    public function test_static_create_without_dependencies(): void
20
    {
21
        $actual = Container::create(ClassWithoutDependencies::class);
22
23
        self::assertEquals(new ClassWithoutDependencies(), $actual);
24
    }
25
26
    public function test_static_create_with_dependencies(): void
27
    {
28
        $actual = Container::create(ClassWithObjectDependencies::class);
29
30
        self::assertEquals(new ClassWithObjectDependencies(new Person()), $actual);
31
    }
32
33
    public function test_without_dependencies(): void
34
    {
35
        $container = new Container();
36
        $actual = $container->get(ClassWithoutDependencies::class);
37
38
        self::assertEquals(new ClassWithoutDependencies(), $actual);
39
    }
40
41
    public function test_object_with_resolvable_dependencies(): void
42
    {
43
        $container = new Container();
44
        $actual = $container->get(ClassWithObjectDependencies::class);
45
46
        self::assertEquals(new ClassWithObjectDependencies(new Person()), $actual);
47
    }
48
49
    public function test_interface_dependency(): void
50
    {
51
        $container = new Container([
52
            PersonInterface::class => Person::class,
53
        ]);
54
        $actual = $container->get(ClassWithObjectDependencies::class);
55
56
        self::assertEquals(new ClassWithObjectDependencies(new Person()), $actual);
57
    }
58
59
    public function test_use_mapped_interface_dependency(): void
60
    {
61
        $person = new Person();
62
        $person->name = 'anything';
63
64
        $container = new Container([
65
            PersonInterface::class => $person,
66
        ]);
67
        $actual = $container->get(ClassWithInterfaceDependencies::class);
68
69
        self::assertEquals(new ClassWithInterfaceDependencies($person), $actual);
70
    }
71
72
    public function test_has_not_existing_class(): void
73
    {
74
        $container = new Container();
75
        $actual = $container->has(InexistentClass::class);
76
77
        self::assertFalse($actual);
78
    }
79
80
    public function test_resolve_object_from_interface(): void
81
    {
82
        $person = new Person();
83
        $person->name = 'person-name';
84
85
        $container = new Container([
86
            PersonInterface::class => $person,
87
        ]);
88
        $resolvedPerson = $container->get(PersonInterface::class);
89
90
        self::assertSame($resolvedPerson, $person);
91
    }
92
93
    public function test_resolve_new_object(): void
94
    {
95
        // We are registering 'PersonInterface::class', but 'Person::class' was not.
96
        // As result, a 'new Person()' will be resolved.
97
        $person = new Person();
98
        $person->name = 'person-name';
99
100
        $container = new Container([
101
            PersonInterface::class => $person,
102
        ]);
103
        $resolvedPerson = $container->get(Person::class);
104
105
        self::assertEquals($resolvedPerson, new Person()); // different objects!
106
    }
107
108
    public function test_interface_not_registered_returns_null(): void
109
    {
110
        $container = new Container([
111
            Person::class => new Person(),
112
        ]);
113
        $resolvedPerson = $container->get(PersonInterface::class);
114
115
        self::assertNull($resolvedPerson);
116
    }
117
118
    public function test_resolve_object_from_classname(): void
119
    {
120
        $container = new Container([
121
            PersonInterface::class => Person::class,
122
        ]);
123
        $resolvedPerson = $container->get(PersonInterface::class);
124
125
        self::assertEquals($resolvedPerson, new Person());
126
    }
127
128
    public function test_resolve_object_from_instance_in_a_callable(): void
129
    {
130
        $person = new Person();
131
        $person->name = 'person-name';
132
133
        $container = new Container([
134
            PersonInterface::class => static fn () => $person,
135
        ]);
136
        $resolvedPerson = $container->get(PersonInterface::class);
137
138
        self::assertEquals($resolvedPerson, $person);
139
    }
140
141
    public function test_resolve_object_from_callable_classname_in_a_callable(): void
142
    {
143
        $container = new Container([
144
            PersonInterface::class => static fn () => Person::class,
145
        ]);
146
        $resolvedPerson = $container->get(PersonInterface::class);
147
148
        self::assertEquals($resolvedPerson, new Person());
149
    }
150
151
    public function test_get_non_existing_service(): void
152
    {
153
        $container = new Container();
154
155
        self::assertNull($container->get('unknown-service_name'));
156
    }
157
158
    public function test_has_service(): void
159
    {
160
        $container = new Container();
161
        $container->set('service_name', 'value');
162
163
        self::assertTrue($container->has('service_name'));
164
        self::assertFalse($container->has('unknown-service_name'));
165
    }
166
167
    public function test_remove_existing_service(): void
168
    {
169
        $container = new Container();
170
        $container->set('service_name', 'value');
171
        $container->remove('service_name');
172
173
        self::assertNull($container->get('service_name'));
174
    }
175
176
    public function test_resolve_service_as_raw_string(): void
177
    {
178
        $container = new Container();
179
        $container->set('service_name', 'value');
180
181
        $resolvedService = $container->get('service_name');
182
        self::assertSame('value', $resolvedService);
183
184
        $cachedResolvedService = $container->get('service_name');
185
        self::assertSame('value', $cachedResolvedService);
186
    }
187
188
    public function test_resolve_service_as_function(): void
189
    {
190
        $container = new Container();
191
        $container->set('service_name', static fn () => 'value');
192
193
        $resolvedService = $container->get('service_name');
194
        self::assertSame('value', $resolvedService);
195
196
        $cachedResolvedService = $container->get('service_name');
197
        self::assertSame('value', $cachedResolvedService);
198
    }
199
200
    public function test_resolve_service_as_callable_class(): void
201
    {
202
        $container = new Container();
203
        $container->set(
204
            'service_name',
205
            new class() {
206
                public function __invoke(): string
207
                {
208
                    return 'value';
209
                }
210
            },
211
        );
212
213
        $resolvedService = $container->get('service_name');
214
        self::assertSame('value', $resolvedService);
215
216
        $cachedResolvedService = $container->get('service_name');
217
        self::assertSame('value', $cachedResolvedService);
218
    }
219
220
    public function test_resolve_non_factory_service_with_random(): void
221
    {
222
        $container = new Container();
223
        $container->set(
224
            'service_name',
225
            static fn () => 'value_' . random_int(0, PHP_INT_MAX),
226
        );
227
228
        self::assertSame(
229
            $container->get('service_name'),
230
            $container->get('service_name'),
231
        );
232
    }
233
234
    public function test_resolve_factory_service_with_random(): void
235
    {
236
        $container = new Container();
237
        $container->set(
238
            'service_name',
239
            $container->factory(
240
                static fn () => 'value_' . random_int(0, PHP_INT_MAX),
241
            ),
242
        );
243
244
        self::assertNotSame(
245
            $container->get('service_name'),
246
            $container->get('service_name'),
247
        );
248
    }
249
250
    public function test_extend_existing_callable_service(): void
251
    {
252
        $container = new Container();
253
        $container->set('n3', 3);
254
        $container->set('service_name', static fn () => new ArrayObject([1, 2]));
255
256
        $container->extend(
257
            'service_name',
258
            static function (ArrayObject $arrayObject, Container $container) {
259
                $arrayObject->append($container->get('n3'));
260
                return $arrayObject;
261
            },
262
        );
263
264
        $container->extend(
265
            'service_name',
266
            static fn (ArrayObject $arrayObject) => $arrayObject->append(4),
267
        );
268
269
        /** @var ArrayObject $actual */
270
        $actual = $container->get('service_name');
271
272
        self::assertEquals(new ArrayObject([1, 2, 3, 4]), $actual);
273
    }
274
275
    public function test_extend_existing_object_service(): void
276
    {
277
        $container = new Container();
278
        $container->set('n3', 3);
279
        $container->set('service_name', new ArrayObject([1, 2]));
280
281
        $container->extend(
282
            'service_name',
283
            static function (ArrayObject $arrayObject, Container $container) {
284
                $arrayObject->append($container->get('n3'));
285
                return $arrayObject;
286
            },
287
        );
288
289
        $container->extend(
290
            'service_name',
291
            static function (ArrayObject $arrayObject): void {
292
                $arrayObject->append(4);
293
            },
294
        );
295
296
        /** @var ArrayObject $actual */
297
        $actual = $container->get('service_name');
298
299
        self::assertEquals(new ArrayObject([1, 2, 3, 4]), $actual);
300
    }
301
302
    public function test_extend_existing_array_service(): void
303
    {
304
        $container = new Container();
305
        $container->set('service_name', [1, 2]);
306
307
        $container->extend(
308
            'service_name',
309
            static function (array $arrayObject): array {
310
                $arrayObject[] = 3;
311
                return $arrayObject;
312
            },
313
        );
314
315
        $container->extend(
316
            'service_name',
317
            static function (array &$arrayObject): void {
318
                $arrayObject[] = 4;
319
            },
320
        );
321
322
        /** @var ArrayObject $actual */
323
        $actual = $container->get('service_name');
324
325
        self::assertEquals([1, 2, 3, 4], $actual);
326
    }
327
328
    public function test_extend_non_existing_service(): void
329
    {
330
        $container = new Container();
331
        $container->extend('service_name', static fn () => '');
332
333
        self::assertNull($container->get('service_name'));
334
    }
335
336
    public function test_service_not_extendable(): void
337
    {
338
        $container = new Container();
339
        $container->set('service_name', 'raw string');
340
341
        $this->expectExceptionObject(ContainerException::instanceNotExtendable());
342
        $container->extend('service_name', static fn (string $str) => $str);
343
    }
344
345
    public function test_extend_existing_used_object_service_is_allowed(): void
346
    {
347
        $container = new Container();
348
        $container->set('service_name', new ArrayObject([1, 2]));
349
        $container->get('service_name'); // and get frozen
350
351
        $this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
352
353
        $container->extend(
354
            'service_name',
355
            static fn (ArrayObject $arrayObject) => $arrayObject->append(3),
356
        );
357
    }
358
359
    public function test_extend_existing_used_callable_service_then_error(): void
360
    {
361
        $container = new Container();
362
        $container->set('service_name', static fn () => new ArrayObject([1, 2]));
363
        $container->get('service_name'); // and get frozen
364
365
        $this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
366
367
        $container->extend(
368
            'service_name',
369
            static fn (ArrayObject $arrayObject) => $arrayObject->append(3),
370
        );
371
    }
372
373
    public function test_extend_later_existing_frozen_object_service_then_error(): void
374
    {
375
        $container = new Container();
376
        $container->extend(
377
            'service_name',
378
            static fn (ArrayObject $arrayObject) => $arrayObject->append(3),
379
        );
380
381
        $container->set('service_name', new ArrayObject([1, 2]));
382
        $container->get('service_name'); // and get frozen
383
384
        $this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
385
386
        $container->extend(
387
            'service_name',
388
            static fn (ArrayObject $arrayObject) => $arrayObject->append(4),
389
        );
390
    }
391
392
    public function test_extend_later_existing_frozen_callable_service_then_error(): void
393
    {
394
        $container = new Container();
395
        $container->extend(
396
            'service_name',
397
            static fn (ArrayObject $arrayObject) => $arrayObject->append(3),
398
        );
399
400
        $container->set('service_name', static fn () => new ArrayObject([1, 2]));
401
        $container->get('service_name'); // and get frozen
402
403
        $this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
404
405
        $container->extend(
406
            'service_name',
407
            static fn (ArrayObject $arrayObject) => $arrayObject->append(4),
408
        );
409
    }
410
411
    public function test_set_existing_frozen_service(): void
412
    {
413
        $container = new Container();
414
        $container->set('service_name', static fn () => new ArrayObject([1, 2]));
415
        $container->get('service_name'); // and get frozen
416
417
        $this->expectExceptionObject(ContainerException::instanceFrozen('service_name'));
418
        $container->set('service_name', static fn () => new ArrayObject([3]));
419
    }
420
421
    public function test_protect_service_is_not_resolved(): void
422
    {
423
        $container = new Container();
424
        $service = static fn () => 'value';
425
        $container->set('service_name', $container->protect($service));
426
427
        self::assertSame($service, $container->get('service_name'));
428
    }
429
430
    public function test_protect_service_cannot_be_extended(): void
431
    {
432
        $container = new Container();
433
        $container->set(
434
            'service_name',
435
            $container->protect(static fn () => new ArrayObject([1, 2])),
436
        );
437
438
        $this->expectExceptionObject(ContainerException::instanceProtected('service_name'));
439
440
        $container->extend(
441
            'service_name',
442
            static fn (ArrayObject $arrayObject) => $arrayObject,
443
        );
444
    }
445
}
446