Code Coverage |
||||||||||
Lines |
Functions and Methods |
Classes and Traits |
||||||||
Total | |
92.55% |
87 / 94 |
|
71.43% |
10 / 14 |
CRAP | |
0.00% |
0 / 1 |
LaravelTestApplication | |
92.55% |
87 / 94 |
|
71.43% |
10 / 14 |
20.17 | |
0.00% |
0 / 1 |
__construct | |
0.00% |
0 / 1 |
|
0.00% |
0 / 1 |
2 | |||
getConsoleApplication | |
100.00% |
6 / 6 |
|
100.00% |
1 / 1 |
1 | |||
getApplicationConfig | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
defineEnvironment | |
100.00% |
36 / 36 |
|
100.00% |
1 / 1 |
1 | |||
bootApplication | |
76.92% |
10 / 13 |
|
0.00% |
0 / 1 |
2.05 | |||
getServiceContainer | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
cleanApplication | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
httpRequestGet | |
100.00% |
5 / 5 |
|
100.00% |
1 / 1 |
2 | |||
handleResponse | |
100.00% |
11 / 11 |
|
100.00% |
1 / 1 |
3 | |||
getPackageProviders | |
100.00% |
1 / 1 |
|
100.00% |
1 / 1 |
1 | |||
httpRequest | |
100.00% |
8 / 8 |
|
100.00% |
1 / 1 |
2 | |||
loginAs | |
100.00% |
2 / 2 |
|
100.00% |
1 / 1 |
1 | |||
logout | |
0.00% |
0 / 2 |
|
0.00% |
0 / 1 |
2 | |||
getLoggedInAs | |
83.33% |
5 / 6 |
|
0.00% |
0 / 1 |
2.02 |
1 | <?php |
2 | namespace Apie\IntegrationTests\Applications\Laravel; |
3 | |
4 | use Apie\AiInstructor\AiClient; |
5 | use Apie\Common\Events\AddAuthenticationCookie; |
6 | use Apie\Common\IntegrationTestLogger; |
7 | use Apie\Common\ValueObjects\DecryptedAuthenticatedUser; |
8 | use Apie\Common\Wrappers\TextEncrypter; |
9 | use Apie\Core\Other\FileWriterInterface; |
10 | use Apie\Core\Other\MockFileWriter; |
11 | use Apie\IntegrationTests\Applications\MockFactory; |
12 | use Apie\IntegrationTests\Concerns\ItRunsApplications; |
13 | use Apie\IntegrationTests\Config\ApplicationConfig; |
14 | use Apie\IntegrationTests\Config\BoundedContextConfig; |
15 | use Apie\IntegrationTests\Interfaces\TestApplicationInterface; |
16 | use Apie\IntegrationTests\Requests\TestRequestInterface; |
17 | use Apie\LaravelApie\ApieServiceProvider; |
18 | use Illuminate\Contracts\Config\Repository; |
19 | use Illuminate\Contracts\Http\Kernel as HttpKernel; |
20 | use Illuminate\Http\Request; |
21 | use Illuminate\Support\Facades\Auth; |
22 | use Nyholm\Psr7\Factory\Psr17Factory as NyholmPsr17Factory; |
23 | use Orchestra\Testbench\TestCase; |
24 | use Psr\Container\ContainerInterface; |
25 | use Psr\Http\Message\ResponseInterface; |
26 | use Symfony\Bridge\PsrHttpMessage\Factory\HttpFoundationFactory; |
27 | use Symfony\Bridge\PsrHttpMessage\Factory\PsrHttpFactory; |
28 | use Symfony\Component\Console\Application; |
29 | use Symfony\Component\HttpFoundation\Cookie; |
30 | use Symfony\Component\HttpFoundation\Response as HttpFoundationResponse; |
31 | use Symfony\Component\HttpFoundation\ResponseHeaderBag; |
32 | use Symfony\Component\HttpKernel\Exception\NotFoundHttpException; |
33 | |
34 | class LaravelTestApplication extends TestCase implements TestApplicationInterface |
35 | { |
36 | use ItRunsApplications; |
37 | |
38 | /** |
39 | * @phpstan-ignore method.parentMethodFinalByPhpDoc |
40 | */ |
41 | public function __construct( |
42 | private readonly ApplicationConfig $applicationConfig, |
43 | private readonly BoundedContextConfig $boundedContextConfig |
44 | ) { |
45 | parent::__construct('Laravel application'); |
46 | } |
47 | |
48 | public function getConsoleApplication(): Application |
49 | { |
50 | $application = new \Illuminate\Console\Application( |
51 | $this->getServiceContainer(), |
52 | $this->getServiceContainer()->get(\Illuminate\Contracts\Events\Dispatcher::class), |
53 | 'test' |
54 | ); |
55 | return $application; |
56 | } |
57 | |
58 | public function getApplicationConfig(): ApplicationConfig |
59 | { |
60 | return $this->applicationConfig; |
61 | } |
62 | |
63 | protected function defineEnvironment($app): void |
64 | { |
65 | tap($app->make('config'), function (Repository $config) { |
66 | $config->set('app.key', 'base64:/aNEFWQbsYwDslb4Xw1RKKj9oCdZdbNhvcyUpVgXPz4='); |
67 | $config->set('apie.encryption_key', 'test'); |
68 | $config->set( |
69 | 'apie.bounded_contexts', |
70 | $this->boundedContextConfig->toArray() |
71 | ); |
72 | $config->set( |
73 | 'apie.scan_bounded_contexts', |
74 | [] |
75 | ); |
76 | // this has to be explicit because of some code execution order issues |
77 | $config->set('apie.enable_ai_instructor', true); |
78 | $config->set( |
79 | 'apie.ai', |
80 | [ |
81 | 'base_url' => 'http://localbost:11434', |
82 | 'api_key' => 'test', |
83 | ] |
84 | ); |
85 | $config->set( |
86 | 'apie.datalayers', |
87 | [ |
88 | 'default_datalayer' => $this->applicationConfig->getDatalayerImplementation()->name, |
89 | ] |
90 | ); |
91 | $config->set( |
92 | 'apie.doctrine', |
93 | [ |
94 | 'build_once' => false, |
95 | 'run_migrations' => true, |
96 | 'connection_params' => [ |
97 | 'driver' => 'pdo_sqlite' |
98 | ] |
99 | ] |
100 | ); |
101 | }); |
102 | } |
103 | |
104 | /** |
105 | * Boot application. Should be called at the start of the test. |
106 | */ |
107 | public function bootApplication(): void |
108 | { |
109 | IntegrationTestLogger::resetLoggedException(); |
110 | $this->setUp(); |
111 | $this->session([]); |
112 | if (getenv('PHPUNIT_LOG_INTEGRATION_OUTPUT')) { |
113 | $this->withoutExceptionHandling([ |
114 | NotFoundHttpException::class |
115 | ]); |
116 | } |
117 | $this->app->instance(FileWriterInterface::class, new MockFileWriter()); |
118 | $this->app->instance( |
119 | AiClient::class, |
120 | MockFactory::createMockAiClient() |
121 | ); |
122 | unset($this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME]); |
123 | } |
124 | |
125 | /** |
126 | * Gets service container of application. Should be used as little as possible. |
127 | */ |
128 | public function getServiceContainer(): ContainerInterface |
129 | { |
130 | return $this->app; |
131 | } |
132 | |
133 | /** |
134 | * Cleans up application. Should be called at the end of the test. |
135 | */ |
136 | public function cleanApplication(): void |
137 | { |
138 | $this->tearDown(); |
139 | } |
140 | |
141 | /** |
142 | * Does a HTTP request on the application and returns the response. |
143 | */ |
144 | public function httpRequestGet(string $uri): ResponseInterface |
145 | { |
146 | //workaround against spacebar search test and Laravel doing unneeded things |
147 | if (str_ends_with($uri, ' ')) { |
148 | $uri .= '&dummy=1'; |
149 | } |
150 | $testResponse = $this->get($uri); |
151 | $laravelResponse = $testResponse->baseResponse; |
152 | return $this->handleResponse($laravelResponse); |
153 | } |
154 | |
155 | private function handleResponse(HttpFoundationResponse $laravelResponse): ResponseInterface |
156 | { |
157 | $cookie = $laravelResponse->headers->getCookies( |
158 | ResponseHeaderBag::COOKIES_ARRAY |
159 | )[""]["/"][AddAuthenticationCookie::COOKIE_NAME] ?? null; |
160 | if ($cookie !== null) { |
161 | $cookie = Cookie::fromString($cookie)->getValue(); |
162 | } |
163 | if ($cookie) { |
164 | $this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME] = $cookie; |
165 | } else { |
166 | unset($this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME]); |
167 | } |
168 | |
169 | $psrFactory = new NyholmPsr17Factory(); |
170 | $factory = new PsrHttpFactory($psrFactory, $psrFactory, $psrFactory, $psrFactory); |
171 | return $factory->createResponse($laravelResponse); |
172 | } |
173 | |
174 | protected function getPackageProviders($app): array |
175 | { |
176 | return [ApieServiceProvider::class]; |
177 | } |
178 | |
179 | public function httpRequest(TestRequestInterface $testRequest): ResponseInterface |
180 | { |
181 | $psrRequest = $testRequest->getRequest(); |
182 | $factory = new HttpFoundationFactory(); |
183 | $sfRequest = $factory->createRequest($psrRequest); |
184 | $laravelRequest = Request::createFromBase($sfRequest); |
185 | if (isset($this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME])) { |
186 | $laravelRequest->cookies->set(AddAuthenticationCookie::COOKIE_NAME, $this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME]); |
187 | } |
188 | $laravelResponse = $this->app->make(HttpKernel::class)->handle($laravelRequest); |
189 | return $this->handleResponse($laravelResponse); |
190 | } |
191 | |
192 | public function loginAs(DecryptedAuthenticatedUser $user): void |
193 | { |
194 | $textEncrypter = new TextEncrypter('test'); |
195 | $this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME] = $textEncrypter->encrypt($user->toNative()); |
196 | } |
197 | |
198 | /** |
199 | * Forget that you are logged in. |
200 | */ |
201 | public function logout(): void |
202 | { |
203 | Auth::logout(); |
204 | unset($this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME]); |
205 | } |
206 | |
207 | public function getLoggedInAs(): ?DecryptedAuthenticatedUser |
208 | { |
209 | if (empty($this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME])) { |
210 | return null; |
211 | } |
212 | $textEncrypter = new TextEncrypter('test'); |
213 | return DecryptedAuthenticatedUser::fromNative( |
214 | $textEncrypter->decrypt($this->defaultCookies[AddAuthenticationCookie::COOKIE_NAME]), |
215 | ); |
216 | } |
217 | } |