1<?php declare(strict_types=1);
2
3namespace App\Tests\Unit\Container;
4
5use App\Container\Container;
6use App\Container\Exception\ContainerException;
7use App\Container\Exception\EntryNotFoundException;
8use PHPUnit\Framework\TestCase;
9
10class ContainerTest extends TestCase
11{
12    public function testContainer(): void
13    {
14        // Create container
15        $container = new Container();
16
17        // Service definitions
18        $container->set(MockService::class, function (Container $container) {
19            $service = new MockService($container->get(MockDependency::class));
20            $service->setProperty('group.param');
21
22            return $service;
23        });
24
25        $container->set(MockDependency::class, function (Container $container) {
26            return new MockDependency('group.param');
27        });
28
29        // Check retrieval of service
30        $service = $container->get(MockService::class);
31        $this->assertInstanceOf(MockService::class, $service);
32
33        // Check retrieval of dependency
34        $dependency = $container->get(MockDependency::class);
35        $this->assertInstanceOf(MockDependency::class, $dependency);
36
37        // Check that the dependency has been reused
38        $this->assertSame($dependency, $service->getDependency());
39
40        // Check the service calls have initialized
41        $this->assertEquals('group.param', $service->getProperty());
42    }
43
44    public function testHas(): void
45    {
46        $container = new Container();
47
48        $this->assertFalse($container->has(MockDependency::class));
49
50        $container->set(MockDependency::class, function (Container $container) {
51            return new MockDependency('group.param');
52        });
53
54        $this->assertFalse($container->has(MockService::class));
55        $this->assertTrue($container->has(MockDependency::class));
56    }
57
58    public function testServiceNotFound(): void
59    {
60        $container = new Container();
61
62        $this->expectException(EntryNotFoundException::class);
63
64        $container->get('foo');
65    }
66
67    public function testBadServiceEntry(): void
68    {
69        $container = new Container();
70        $container->set(\stdClass::class, '');
71
72        $this->expectException(ContainerException::class);
73        $this->expectExceptionMessage('entry must be callable');
74
75        $container->get(\stdClass::class);
76    }
77
78    public function testCircularReference(): void
79    {
80        $container = new Container();
81
82        $container->set(MockService::class, function (Container $container) {
83            return new MockService($container->get(MockService::class));
84        });
85
86        $container->set(MockService::class, function (Container $container) {
87            return new MockService($container->get(MockService::class));
88        });
89
90        $this->expectException(ContainerException::class);
91        $this->expectExceptionMessage('circular reference');
92
93        $container->get(MockService::class);
94    }
95
96    public function testParametersAndServices(): void
97    {
98        $container = new Container([
99            'foo' => 'bar',
100            'baz' => function (Container $container) {
101                return $container->get('foo');
102            },
103        ]);
104
105        $this->assertTrue($container->has('foo'));
106        $this->assertTrue($container->has('baz'));
107        $this->assertEquals('bar', $container->get('foo'));
108        $this->assertEquals('bar', $container->get('baz'));
109    }
110}
111