Skip to content

Commit 8c56741

Browse files
committed
Merge branch 'meta-testing' into validation-bugfixes
2 parents 2328fe9 + ef0840e commit 8c56741

File tree

12 files changed

+1173
-44
lines changed

12 files changed

+1173
-44
lines changed

app/V1Module/presenters/UploadedFilesPresenter.php

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -264,6 +264,7 @@ public function actionContent(string $id, ?string $entry = null)
264264
// Remove UTF BOM prefix...
265265
$utf8bom = "\xef\xbb\xbf";
266266
$contents = Strings::replace($contents, "~^$utf8bom~");
267+
$contents = str_replace("\r\n", "\n", $contents); // normalize line endings
267268

268269
$fixedContents = @mb_convert_encoding($contents, 'UTF-8', 'UTF-8');
269270

app/config/permissions.neon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,7 @@ permissions:
309309

310310
- allow: true
311311
resource: group
312-
role: empowered-supervisor
312+
role: supervisor
313313
actions:
314314
- viewExamLocksIPs
315315
conditions:

app/helpers/MetaFormats/MetaFormat.php

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
namespace App\Helpers\MetaFormats;
44

5+
use App\Exceptions\BadRequestException;
56
use App\Exceptions\InternalServerException;
67
use App\Exceptions\InvalidApiArgumentException;
78

@@ -42,29 +43,26 @@ public function checkedAssign(string $fieldName, mixed $value)
4243

4344
/**
4445
* Validates the given format.
45-
* @return bool Returns whether the format and all nested formats are valid.
46+
* @throws InvalidApiArgumentException Thrown when a value is not assignable.
47+
* @throws BadRequestException Thrown when the structural constraints were not met.
4648
*/
4749
public function validate()
4850
{
4951
// check whether all higher level contracts hold
5052
if (!$this->validateStructure()) {
51-
return false;
53+
throw new BadRequestException("The structural constraints of the format were not met.");
5254
}
5355

5456
// go through all fields and check whether they were assigned properly
5557
$fieldFormats = FormatCache::getFieldDefinitions(get_class($this));
5658
foreach ($fieldFormats as $fieldName => $fieldFormat) {
57-
if (!$this->checkIfAssignable($fieldName, $this->$fieldName)) {
58-
return false;
59-
}
59+
$this->checkIfAssignable($fieldName, $this->$fieldName);
6060

6161
// check nested formats recursively
62-
if ($this->$fieldName instanceof MetaFormat && !$this->$fieldName->validate()) {
63-
return false;
62+
if ($this->$fieldName instanceof MetaFormat) {
63+
$this->$fieldName->validate();
6464
}
6565
}
66-
67-
return true;
6866
}
6967

7068
/**

app/helpers/Mocks/MockHelper.php

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use App\Helpers\MetaFormats\FormatCache;
6+
use App\Helpers\MetaFormats\MetaFormatHelper;
7+
use App\Helpers\Mocks\MockUserStorage;
8+
use App\V1Module\Presenters\BasePresenter;
9+
use Nette\Application\Application;
10+
use Nette\Application\PresenterFactory;
11+
use Nette\Application\Routers\RouteList;
12+
use Nette\Http\Response;
13+
use Nette\Http\UrlScript;
14+
use Nette\Security\User;
15+
use Nette;
16+
use ReflectionProperty;
17+
18+
class MockHelper
19+
{
20+
/**
21+
* Initializes a presenter object with empty http request, response, and user objects.
22+
* This is intended to be called right after presenter instantiation and before calling the Presenter::run method.
23+
* @param BasePresenter $presenter The presenter to be initialized.
24+
*/
25+
public static function initPresenter(BasePresenter $presenter)
26+
{
27+
$httpRequest = new \Nette\Http\Request(new UrlScript());
28+
$httpResponse = new Response();
29+
$user = new User(new MockUserStorage());
30+
31+
$application = new Application(new PresenterFactory(), new RouteList("V1"), $httpRequest, $httpResponse);
32+
$presenter->application = $application;
33+
34+
$factory = new MockTemplateFactory();
35+
36+
$presenter->injectPrimary($httpRequest, $httpResponse, user: $user, templateFactory: $factory);
37+
}
38+
39+
/**
40+
* Injects a Format class to the FormatCache.
41+
* This method must not be used outside of testing, normal Format classes are discovered automatically.
42+
* @param string $format The Format class name.
43+
*/
44+
public static function injectFormat(string $format)
45+
{
46+
// make sure the cache is initialized (it uses lazy loading)
47+
FormatCache::getFormatToFieldDefinitionsMap();
48+
FormatCache::getFormatNamesHashSet();
49+
50+
// inject the format name
51+
$hashSetReflector = new ReflectionProperty(FormatCache::class, "formatNamesHashSet");
52+
$hashSetReflector->setAccessible(true);
53+
$formatNamesHashSet = $hashSetReflector->getValue();
54+
$formatNamesHashSet[$format] = true;
55+
$hashSetReflector->setValue(null, $formatNamesHashSet);
56+
57+
// inject the format definitions
58+
$formatMapReflector = new ReflectionProperty(FormatCache::class, "formatToFieldFormatsMap");
59+
$formatMapReflector->setAccessible(true);
60+
$formatToFieldFormatsMap = $formatMapReflector->getValue();
61+
$formatToFieldFormatsMap[$format] = MetaFormatHelper::createNameToFieldDefinitionsMap($format);
62+
$formatMapReflector->setValue(null, $formatToFieldFormatsMap);
63+
}
64+
}

app/helpers/Mocks/MockTemplate.php

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use Nette\Application\UI\Template;
6+
7+
class MockTemplate implements Template
8+
{
9+
public function render(): void
10+
{
11+
}
12+
13+
public function setFile(string $file): static
14+
{
15+
return $this;
16+
}
17+
18+
public function getFile(): ?string
19+
{
20+
return "test";
21+
}
22+
}
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
<?php
2+
3+
namespace App\Helpers\Mocks;
4+
5+
use Nette\Application\UI\Control;
6+
use Nette\Application\UI\Template;
7+
use Nette\Application\UI\TemplateFactory;
8+
9+
class MockTemplateFactory implements TemplateFactory
10+
{
11+
public function createTemplate(?Control $control = null): Template
12+
{
13+
return new MockTemplate();
14+
}
15+
}
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+
}

recodex-api.spec

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
%define name recodex-core
22
%define short_name api
33
%define install_dir /opt/%{name}
4-
%define version 2.16.1
5-
%define unmangled_version 1e145d5253dacbe64a8ed1050883834b05a8deeb
6-
%define release 2
4+
%define version 2.17.0
5+
%define unmangled_version 8886b04634ad87c2657f7efed1211c2e4d2bbb65
6+
%define release 1
77

88
Summary: ReCodEx core API component
99
Name: %{name}

tests/Presenters/GroupsPresenter.phpt

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1788,40 +1788,9 @@ class TestGroupsPresenter extends Tester\TestCase
17881788
);
17891789
}
17901790

1791-
public function testGetExamLocks()
1792-
{
1793-
$group = $this->prepExamGroup(); // logged in as supervisor
1794-
$student = $this->presenter->users->getByEmail("demoGroupMember1@example.com");
1795-
1796-
$now = (new DateTime())->getTimestamp();
1797-
$begin = $now - 7200;
1798-
$end = $now - 3600;
1799-
$group->setExamPeriod(DateTime::createFromFormat('U', $begin), DateTime::createFromFormat('U', $end));
1800-
$this->presenter->groups->persist($group);
1801-
1802-
$exam = new GroupExam($group, DateTime::createFromFormat('U', $begin), DateTime::createFromFormat('U', $end), false);
1803-
$this->presenter->groupExams->persist($exam);
1804-
1805-
$lock = new GroupExamLock($exam, $student, '1.2.3.4');
1806-
$this->presenter->groupExamLocks->persist($lock);
1807-
1808-
$payload = PresenterTestHelper::performPresenterRequest(
1809-
$this->presenter,
1810-
'V1:Groups',
1811-
'GET',
1812-
['action' => 'getExamLocks', 'id' => $group->getId(), 'examId' => $exam->getId()],
1813-
);
1814-
1815-
Assert::count(1, $payload);
1816-
Assert::equal($exam->getId(), $payload[0]['groupExamId']);
1817-
Assert::equal($student->getId(), $payload[0]['studentId']);
1818-
Assert::false(array_key_exists('remoteAddr', $payload[0])); // supervisor cannot se IPs
1819-
}
1820-
18211791
public function testGetExamLocksWithIPs()
18221792
{
18231793
$group = $this->prepExamGroup();
1824-
PresenterTestHelper::loginDefaultAdmin($this->container); // admin can see IPs
18251794
$student = $this->presenter->users->getByEmail("demoGroupMember1@example.com");
18261795

18271796
$now = (new DateTime())->getTimestamp();

0 commit comments

Comments
 (0)