Passed
Push — main ( 08d8b4...a03e47 )
by Chema
57s queued 14s
created

php$0 ➔ test_extend_non_existing_service()   A

Complexity

Conditions 1

Size

Total Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

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