Skip to content

Commit 88c953f

Browse files
committed
WIP adding mocks
1 parent 1ba4ffc commit 88c953f

6 files changed

Lines changed: 154 additions & 50 deletions

File tree

app/V1Module/presenters/base/BasePresenter.php

Lines changed: 24 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@
1212
use App\Exceptions\WrongHttpMethodException;
1313
use App\Exceptions\NotImplementedException;
1414
use App\Exceptions\InternalServerException;
15-
use App\Exceptions\InvalidApiArgumentException;
1615
use App\Exceptions\FrontendErrorMappings;
1716
use App\Security\AccessManager;
1817
use App\Security\Authorizator;
@@ -27,7 +26,6 @@
2726
use App\Responses\StorageFileResponse;
2827
use App\Responses\ZipFilesResponse;
2928
use Nette\Application\Application;
30-
use Nette\Application\Request;
3129
use Nette\Http\IResponse;
3230
use Tracy\ILogger;
3331
use ReflectionClass;
@@ -130,7 +128,7 @@ public function startup()
130128
$this->tryCall($this->formatPermissionCheckMethod($this->getAction()), $this->params);
131129

132130
Validators::init();
133-
$this->processParams($this->getRequest(), $actionReflection);
131+
$this->processParams($actionReflection);
134132
}
135133

136134
protected function isRequestJson(): bool
@@ -206,30 +204,28 @@ public function getFormatInstance(): MetaFormat
206204
return $this->requestFormatInstance;
207205
}
208206

209-
private function processParams(Request $request, ReflectionMethod $reflection)
207+
private function processParams(ReflectionMethod $reflection)
210208
{
211209
// use a method specialized for formats if there is a format available
212210
$format = MetaFormatHelper::extractFormatFromAttribute($reflection);
213211
if ($format !== null) {
214-
$this->requestFormatInstance = $this->processParamsFormat($request, $format, null);
212+
$this->requestFormatInstance = $this->processParamsFormat($format, null);
215213
}
216214

217215
// handle loose parameters
218216
$paramData = MetaFormatHelper::extractRequestParamData($reflection);
219-
$this->processParamsLoose($request, $paramData);
217+
$this->processParamsLoose($paramData);
220218
}
221219

222220
/**
223221
* Processes loose parameters. Request parameters are validated, no new data is created.
224-
* @throws InvalidApiArgumentException Thrown when the request parameter values do not conform to the definition.
225-
* @param Request $request Request object holding the request data.
226222
* @param array $paramData Parameter data to be validated.
227223
*/
228-
private function processParamsLoose(Request $request, array $paramData)
224+
private function processParamsLoose(array $paramData)
229225
{
230226
// validate each param
231227
foreach ($paramData as $param) {
232-
$paramValue = $this->getValueFromParamData($request, $param);
228+
$paramValue = $this->getValueFromParamData($param);
233229

234230
// this throws when it does not conform
235231
$param->conformsToDefinition($paramValue);
@@ -239,17 +235,15 @@ private function processParamsLoose(Request $request, array $paramData)
239235
/**
240236
* Processes parameters defined by a format. Request parameters are validated and a format instance with
241237
* parameter values created.
242-
* @param Request $request Request object holding the request data.
243238
* @param string $format The format defining the parameters.
244239
* @param ?array $valueDictionary If not null, a nested format instance will be created. The values will be taken
245240
* from here instead of the request object. Format validation ignores parameter type (path, query or post).
246241
* A top-level format will be created if null.
247242
* @throws InternalServerException Thrown when the format definition is corrupted/absent.
248-
* @throws BadRequestException Thrown when the request parameter values do not meet the structural constraints.
249-
* @throws InvalidApiArgumentException Thrown when the request parameter values do not conform to the definition.
243+
* @throws BadRequestException Thrown when the request parameter values do not conform to the definition.
250244
* @return MetaFormat Returns a format instance with values filled from the request object.
251245
*/
252-
private function processParamsFormat(Request $request, string $format, ?array $valueDictionary): MetaFormat
246+
private function processParamsFormat(string $format, ?array $valueDictionary): MetaFormat
253247
{
254248
// get the parsed attribute data from the format fields
255249
$formatToFieldDefinitionsMap = FormatCache::getFormatToFieldDefinitionsMap();
@@ -265,7 +259,7 @@ private function processParamsFormat(Request $request, string $format, ?array $v
265259
$value = null;
266260
// top-level format
267261
if ($valueDictionary === null) {
268-
$value = $this->getValueFromParamData($request, $requestParamData);
262+
$value = $this->getValueFromParamData($requestParamData);
269263
// nested format
270264
} else {
271265
// Instead of retrieving the values with the getRequest call, use the provided $valueDictionary.
@@ -281,7 +275,7 @@ private function processParamsFormat(Request $request, string $format, ?array $v
281275
// replace the value dictionary stored in $value with a format instance
282276
$nestedFormatName = $requestParamData->getFormatName();
283277
if ($nestedFormatName !== null) {
284-
$value = $this->processParamsFormat($request, $nestedFormatName, $value);
278+
$value = $this->processParamsFormat($nestedFormatName, $value);
285279
}
286280

287281
// this throws if the value is invalid
@@ -298,37 +292,37 @@ private function processParamsFormat(Request $request, string $format, ?array $v
298292

299293
/**
300294
* Calls either getPostField, getQueryField or getPathField based on the provided metadata.
301-
* @param Request $request Request object holding the request data.
302295
* @param \App\Helpers\MetaFormats\RequestParamData $paramData Metadata of the request parameter.
303296
* @throws \App\Exceptions\InternalServerException Thrown when an unexpected parameter location was set.
304297
* @return mixed Returns the value from the request.
305298
*/
306-
private function getValueFromParamData(Request $request, RequestParamData $paramData): mixed
299+
private function getValueFromParamData(RequestParamData $paramData): mixed
307300
{
308301
switch ($paramData->type) {
309302
case Type::Post:
310-
return $this->getPostField($request, $paramData->name, required: $paramData->required);
303+
return $this->getPostField($paramData->name, required: $paramData->required);
311304
case Type::Query:
312-
return $this->getQueryField($request, $paramData->name, required: $paramData->required);
305+
return $this->getQueryField($paramData->name, required: $paramData->required);
313306
case Type::Path:
314-
return $this->getPathField($request, $paramData->name);
307+
return $this->getPathField($paramData->name);
315308
default:
316309
throw new InternalServerException("Unknown parameter type: {$paramData->type->name}");
317310
}
318311
}
319312

320-
private function getPostField(Request $request, $param, $required = true)
313+
private function getPostField($param, $required = true)
321314
{
322-
$post = $request->getPost();
315+
$req = $this->getRequest();
316+
$post = $req->getPost();
323317

324-
if ($request->isMethod("POST")) {
318+
if ($req->isMethod("POST")) {
325319
// nothing to see here...
326320
} else {
327-
if ($request->isMethod("PUT") || $request->isMethod("DELETE")) {
321+
if ($req->isMethod("PUT") || $req->isMethod("DELETE")) {
328322
parse_str(file_get_contents('php://input'), $post);
329323
} else {
330324
throw new WrongHttpMethodException(
331-
"Cannot get the post parameters in method '" . $request->getMethod() . "'."
325+
"Cannot get the post parameters in method '" . $req->getMethod() . "'."
332326
);
333327
}
334328
}
@@ -344,18 +338,18 @@ private function getPostField(Request $request, $param, $required = true)
344338
}
345339
}
346340

347-
private function getQueryField(Request $request, $param, $required = true)
341+
private function getQueryField($param, $required = true)
348342
{
349-
$value = $request->getParameter($param);
343+
$value = $this->getRequest()->getParameter($param);
350344
if ($value === null && $required) {
351345
throw new BadRequestException("Missing required query field $param");
352346
}
353347
return $value;
354348
}
355349

356-
private function getPathField(Request $request, $param)
350+
private function getPathField($param)
357351
{
358-
$value = $request->getParameter($param);
352+
$value = $this->getParameter($param);
359353
if ($value === null) {
360354
throw new BadRequestException("Missing required path field $param");
361355
}

app/helpers/Mocks/MockHelper.php

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use App\Helpers\Mocks\MockUserStorage;
6+
use App\Security\UserStorage;
7+
use App\V1Module\Presenters\BasePresenter;
8+
use App\V1Module\Presenters\RegistrationPresenter;
9+
use Nette\Application\Application;
10+
use Nette\Application\PresenterFactory;
11+
use Nette\Application\Request;
12+
use Nette\Application\Responses\JsonResponse;
13+
use Nette\Application\Routers\RouteList;
14+
use Nette\Http\Response;
15+
use Nette\Http\UrlScript;
16+
use Nette\Security\User;
17+
use Symfony\Component\Console\Command\Command;
18+
use Symfony\Component\Console\Input\InputInterface;
19+
use Symfony\Component\Console\Output\OutputInterface;
20+
use App\Exceptions\InvalidAccessTokenException;
21+
use App\Exceptions\InvalidArgumentException;
22+
use Nette;
23+
use Nette\Security\IIdentity;
24+
25+
class MockHelper
26+
{
27+
/**
28+
* Initializes a presenter object with empty http request, response, and user objects.
29+
* @param BasePresenter $presenter The presenter to be initialized.
30+
*/
31+
public static function initPresenter(BasePresenter $presenter)
32+
{
33+
$httpRequest = new \Nette\Http\Request(new UrlScript());
34+
$httpResponse = new Response();
35+
$user = new User(new MockUserStorage());
36+
37+
$application = new Application(new PresenterFactory(), new RouteList("V1"), $httpRequest, $httpResponse);
38+
$presenter->application = $application;
39+
40+
$factory = new MockTemplateFactory();
41+
42+
$presenter->injectPrimary($httpRequest, $httpResponse, user: $user, templateFactory: $factory);
43+
}
44+
}

app/helpers/Mocks/MockTemplate.php

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use Nette;
6+
use Nette\Application\UI\Template;
7+
use Nette\Application\UI\TemplateFactory;
8+
use Nette\Security\IIdentity;
9+
10+
class MockTemplate implements Template
11+
{
12+
public function render(): void
13+
{
14+
}
15+
16+
public function setFile(string $file): static
17+
{
18+
return $this;
19+
}
20+
21+
public function getFile(): ?string
22+
{
23+
return "test";
24+
}
25+
}
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use Nette;
6+
use Nette\Application\UI\Control;
7+
use Nette\Application\UI\Template;
8+
use Nette\Application\UI\TemplateFactory;
9+
use Nette\Security\IIdentity;
10+
11+
class MockTemplateFactory implements TemplateFactory
12+
{
13+
public function createTemplate(?Control $control = null): Template
14+
{
15+
return new MockTemplate();
16+
}
17+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use Nette;
6+
use Nette\Security\IIdentity;
7+
8+
class MockUserStorage implements Nette\Security\UserStorage
9+
{
10+
public function setExpiration(?string $expire, bool $clearIdentity): void
11+
{
12+
}
13+
14+
public function saveAuthentication(IIdentity $identity): void
15+
{
16+
}
17+
18+
public function clearAuthentication(bool $clearIdentity): void
19+
{
20+
}
21+
22+
public function getState(): array
23+
{
24+
return [false, null, 0];
25+
}
26+
}

tests/Validation/BasePresenter.phpt

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ use App\Helpers\MetaFormats\Validators\VObject;
2424
use App\Helpers\MetaFormats\Validators\VString;
2525
use App\Helpers\MetaFormats\Validators\VTimestamp;
2626
use App\Helpers\MetaFormats\Validators\VUuid;
27+
use App\Helpers\Mocks\MockHelper;
2728
use App\V1Module\Presenters\BasePresenter;
2829
use Nette\Application\Request;
2930
use Tester\Assert;
@@ -55,17 +56,20 @@ class TestPresenter extends BasePresenter
5556
#[Path("path", new VInt())]
5657
public function actionTestLoose()
5758
{
59+
$this->sendSuccessResponse("OK");
5860
}
5961

6062
#[Format(PresenterTestFormat::class)]
6163
public function actionTestFormat()
6264
{
65+
$this->sendSuccessResponse("OK");
6366
}
6467

6568
#[Format(PresenterTestFormat::class)]
6669
#[Post("loose", new VInt())]
6770
public function actionTestCombined()
6871
{
72+
$this->sendSuccessResponse("OK");
6973
}
7074
}
7175

@@ -106,44 +110,38 @@ class TestBasePresenter extends Tester\TestCase
106110
Assert::notNull(FormatCache::getFieldDefinitions($format), "Tests whether a format was injected successfully.");
107111
}
108112

109-
private static function getMethod(BasePresenter $presenter, string $methodName): ReflectionMethod
110-
{
111-
$presenterReflection = new ReflectionObject($presenter);
112-
$methodReflection = $presenterReflection->getMethod($methodName);
113-
$methodReflection->setAccessible(true);
114-
return $methodReflection;
115-
}
116-
117113
public function testLooseValid()
118114
{
119115
$presenter = new TestPresenter();
120-
$action = self::getMethod($presenter, "actionTestLoose");
121-
$processParams = self::getMethod($presenter, "processParams");
116+
MockHelper::initPresenter($presenter);
122117

123-
// create a request object and invoke the actionTestLoose method
124-
$request = new Request("name", method: "POST", params: ["path" => "1", "query" => "1"], post: ["post" => 1]);
125-
$processParams->invoke($presenter, $request, $action);
118+
// create a request object
119+
$request = new Request(
120+
"name",
121+
method: "POST",
122+
params: ["action" => "testLoose", "path" => "1", "query" => "1"],
123+
post: ["post" => 1]
124+
);
126125

127-
// check that the previous row did not throw
128-
Assert::true(true);
126+
$response = $presenter->run($request);
127+
Assert::equal("OK", $response->getPayload()["payload"]);
129128
}
130129

131130
public function testLooseInvalid()
132131
{
133132
$presenter = new TestPresenter();
134-
$action = self::getMethod($presenter, "actionTestLoose");
135-
$processParams = self::getMethod($presenter, "processParams");
133+
MockHelper::initPresenter($presenter);
136134

137135
// set an invalid parameter value and assert that the validation fails
138136
$request = new Request(
139137
"name",
140138
method: "POST",
141-
params: ["path" => "string", "query" => "1"],
139+
params: ["action" => "testLoose", "path" => "string", "query" => "1"],
142140
post: ["post" => 1]
143141
);
144142
Assert::throws(
145-
function () use ($processParams, $presenter, $request, $action) {
146-
$processParams->invoke($presenter, $request, $action);
143+
function () use ($presenter, $request) {
144+
$presenter->run($request);
147145
},
148146
InvalidApiArgumentException::class
149147
);

0 commit comments

Comments
 (0)