diff --git a/app/composer.json b/app/composer.json
index f2ece6a1f..e3c072d96 100644
--- a/app/composer.json
+++ b/app/composer.json
@@ -2,7 +2,6 @@
"name": "spaze/michalspacek.cz",
"type": "project",
"description": "Source code for my site",
- "version": "1.3.3.7",
"license": "MIT",
"require": {
"php": "^8.3",
@@ -61,7 +60,7 @@
"phpstan/phpstan": "^2.0",
"phpstan/phpstan-deprecation-rules": "^2.0",
"phpstan/phpstan-nette": "^2.0",
- "psalm/phar": "^5.14",
+ "psalm/phar": "^5.26.1",
"roave/security-advisories": "dev-latest",
"shipmonk/composer-dependency-analyser": "^1.3",
"spaze/coding-standard": "^1.7.1",
diff --git a/app/composer.lock b/app/composer.lock
index 8dd695866..9e8f25004 100644
--- a/app/composer.lock
+++ b/app/composer.lock
@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "cd65d0895faaabd1296d4b6333ff4863",
+ "content-hash": "1066b808a967f8e5a38aca04159b39c3",
"packages": [
{
"name": "composer/pcre",
diff --git a/app/psalm-baseline.xml b/app/psalm-baseline.xml
deleted file mode 100644
index dd6cc57e4..000000000
--- a/app/psalm-baseline.xml
+++ /dev/null
@@ -1,52 +0,0 @@
-
-
-
-
- getValue()]]>
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- filename ?? '']]>
- filenameAlternative ?? '']]>
-
-
-
-
-
-
-
-
-
- ranking]]>
-
-
-
-
-
-
-
-
-
-
- entry]]>
- entry]]>
- entry[0]]]>
- entry[0]]]>
- entry[0]]]>
- entry[1]]]>
- entry[1]]]>
- entry[1]]]>
-
-
-
diff --git a/app/psalm.xml b/app/psalm.xml
index fd368729b..2f98f0c44 100644
--- a/app/psalm.xml
+++ b/app/psalm.xml
@@ -9,7 +9,6 @@
findUnusedCode="true"
findUnusedPsalmSuppress="true"
maxStringLength="2600"
- errorBaseline="psalm-baseline.xml"
>
@@ -50,17 +49,6 @@
-
-
-
-
-
-
-
-
-
-
-
@@ -70,25 +58,13 @@
-
-
-
-
-
-
-
-
-
-
-
-
@@ -98,8 +74,6 @@
-
-
@@ -111,7 +85,6 @@
-
diff --git a/app/src/EasterEgg/WinterIsComing.php b/app/src/EasterEgg/WinterIsComing.php
index e6594e5d5..1013f5b55 100644
--- a/app/src/EasterEgg/WinterIsComing.php
+++ b/app/src/EasterEgg/WinterIsComing.php
@@ -38,11 +38,12 @@ class WinterIsComing
public function ruleEmail(): callable
{
return function (TextInput $input) {
+ $value = $input->getValue();
if (
- is_string($input->getValue())
+ is_string($value)
&& (
- Arrays::contains(self::EMAILS, $input->getValue())
- || Regex::isMatch('/@(' . implode('|', array_map('preg_quote', self::HOSTS)) . ')$/', $input->getValue())
+ Arrays::contains(self::EMAILS, $value)
+ || Regex::isMatch('/@(' . implode('|', array_map('preg_quote', self::HOSTS)) . ')$/', $value)
)
) {
$this->sendSyntaxError($input);
diff --git a/app/src/Formatter/TexyFormatter.php b/app/src/Formatter/TexyFormatter.php
index a5b6b19a6..695dec6ae 100644
--- a/app/src/Formatter/TexyFormatter.php
+++ b/app/src/Formatter/TexyFormatter.php
@@ -190,6 +190,7 @@ private function replace(string $key, Texy $texy, callable $callback): Html
$result = Regex::replaceCallbackStrictGroups(
'~\*\*([^:]+):([^*]+)\*\*~',
+ /** @param array $matches */
function (array $matches) use ($replacements): string {
return (isset($replacements[$matches[1]]) ? $replacements[$matches[1]]($matches[2]) : '');
},
diff --git a/app/src/Pulse/Passwords/PasswordsSorting.php b/app/src/Pulse/Passwords/PasswordsSorting.php
index 4415930be..169a4b0b2 100644
--- a/app/src/Pulse/Passwords/PasswordsSorting.php
+++ b/app/src/Pulse/Passwords/PasswordsSorting.php
@@ -45,46 +45,38 @@ public function __construct()
public function sort(StorageRegistry $storages, string $sort): StorageRegistry
{
- switch ($sort) {
- case self::RATING_A_F:
- case self::RATING_F_A:
- $sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
- return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
- $result = $sort === self::RATING_A_F ? $siteA->getRating()->name <=> $siteB->getRating()->name : $siteB->getRating()->name <=> $siteA->getRating()->name;
+ $sorter = match ($sort) {
+ self::RATING_A_F, self::RATING_F_A => function (Storage $a, Storage $b) use ($storages, $sort): int {
+ return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
+ $result = $sort === self::RATING_A_F ? $siteA->getRating()->name <=> $siteB->getRating()->name : $siteB->getRating()->name <=> $siteA->getRating()->name;
+ if ($result === 0) {
+ $result = $this->collator->getSortKey($storages->getCompany($siteA->getCompany()->getId())->getSortName()) <=> $this->collator->getSortKey($storages->getCompany($siteB->getCompany()->getId())->getSortName());
if ($result === 0) {
- $result = $this->collator->getSortKey($storages->getCompany($siteA->getCompany()->getId())->getSortName()) <=> $this->collator->getSortKey($storages->getCompany($siteB->getCompany()->getId())->getSortName());
- if ($result === 0) {
- $subKeyA = $siteA instanceof StorageSpecificSite ? $siteA->getUrl() : $siteA->getLatestAlgorithm()->getAlias();
- $subKeyB = $siteB instanceof StorageSpecificSite ? $siteB->getUrl() : $siteB->getLatestAlgorithm()->getAlias();
- $result = $subKeyA <=> $subKeyB;
- }
+ $subKeyA = $siteA instanceof StorageSpecificSite ? $siteA->getUrl() : $siteA->getLatestAlgorithm()->getAlias();
+ $subKeyB = $siteB instanceof StorageSpecificSite ? $siteB->getUrl() : $siteB->getLatestAlgorithm()->getAlias();
+ $result = $subKeyA <=> $subKeyB;
}
- return $result;
- });
- };
- break;
- case self::NEWEST_DISCLOSURES_FIRST:
- case self::NEWEST_DISCLOSURES_LAST:
- $sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
- return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
- return $sort === self::NEWEST_DISCLOSURES_LAST
- ? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished()
- : $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
- });
- };
- break;
- case self::NEWLY_ADDED_FIRST:
- case self::NEWLY_ADDED_LAST:
- $sorter = function (Storage $a, Storage $b) use ($storages, $sort): int {
- return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
- $addedA = $siteA->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
- $addedB = $siteB->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
- return $sort === self::NEWLY_ADDED_LAST ? $addedA <=> $addedB : $addedB <=> $addedA;
- });
- };
- break;
- }
- if (isset($sorter)) {
+ }
+ return $result;
+ });
+ },
+ self::NEWEST_DISCLOSURES_FIRST, self::NEWEST_DISCLOSURES_LAST => function (Storage $a, Storage $b) use ($storages, $sort): int {
+ return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
+ return $sort === self::NEWEST_DISCLOSURES_LAST
+ ? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished()
+ : $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished() <=> $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
+ });
+ },
+ self::NEWLY_ADDED_FIRST, self::NEWLY_ADDED_LAST => function (Storage $a, Storage $b) use ($storages, $sort): int {
+ return $this->sortSites($storages, $a, $b, $sort, function (StorageRegistry $storages, StorageSite $siteA, StorageSite $siteB, string $sort): int {
+ $addedA = $siteA->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteA->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
+ $addedB = $siteB->getLatestAlgorithm()->getLatestDisclosure()->getAdded() ?? $siteB->getLatestAlgorithm()->getLatestDisclosure()->getPublished();
+ return $sort === self::NEWLY_ADDED_LAST ? $addedA <=> $addedB : $addedB <=> $addedA;
+ });
+ },
+ default => null,
+ };
+ if ($sorter !== null) {
$storages->sortStorages($sorter);
}
return $storages;
diff --git a/app/src/Talks/Slides/TalkSlides.php b/app/src/Talks/Slides/TalkSlides.php
index 1c5dea6fe..857cd1406 100644
--- a/app/src/Talks/Slides/TalkSlides.php
+++ b/app/src/Talks/Slides/TalkSlides.php
@@ -267,6 +267,8 @@ private function updateSlides(int $talkId, TalkSlideCollection $originalSlides,
assert(is_string($slide->speakerNotes));
$width = self::SLIDE_MAX_WIDTH;
$height = self::SLIDE_MAX_HEIGHT;
+ $slideFilename = $slide->filename;
+ $slideFilenameAlternative = $slide->filenameAlternative;
if (isset($slide->replace, $slide->replaceAlternative)) {
$replace = $this->replaceSlideImage($talkId, $slide->replace, $this->supportedImageFileFormats->getMainExtensionByContentType(...), $removeFiles, $originalSlides->getByNumber($slide->number)->getFilename(), $width, $height);
@@ -279,11 +281,11 @@ private function updateSlides(int $talkId, TalkSlideCollection $originalSlides,
}
}
} else {
- $replace = $replaceAlternative = $slide->filename = $slide->filenameAlternative = null;
+ $replace = $replaceAlternative = $slideFilename = $slideFilenameAlternative = null;
}
try {
- $this->updateSlidesRow($talkId, $slide->alias, $slide->number, $replace ?? $slide->filename ?? '', $replaceAlternative ?? $slide->filenameAlternative ?? '', $slide->title, $slide->speakerNotes, $id);
+ $this->updateSlidesRow($talkId, $slide->alias, $slide->number, $replace ?? $slideFilename ?? '', $replaceAlternative ?? $slideFilenameAlternative ?? '', $slide->title, $slide->speakerNotes, $id);
} catch (UniqueConstraintViolationException $e) {
throw new DuplicatedSlideException($slide->number, previous: $e);
}
diff --git a/app/src/Tls/CertificateGatherer.php b/app/src/Tls/CertificateGatherer.php
index f71ec6158..951baa522 100644
--- a/app/src/Tls/CertificateGatherer.php
+++ b/app/src/Tls/CertificateGatherer.php
@@ -44,12 +44,13 @@ public function fetchCertificates(string $hostname, bool $includeIpv6): array
$certificates = [];
$records = $this->dnsResolver->getRecords($hostname, $includeIpv6 ? DNS_A | DNS_AAAA : DNS_A);
foreach ($records as $record) {
+ $ipAddress = null;
if ($record->getIpv6() !== null) {
$ipAddress = "[{$record->getIpv6()}]";
} elseif ($record->getIp() !== null) {
$ipAddress = $record->getIp();
}
- if (!isset($ipAddress)) {
+ if ($ipAddress === null) {
throw new DnsGetRecordException("No IPv4/v6 address for {$hostname}");
}
$certificates[$ipAddress] = $this->fetchCertificate($hostname, $ipAddress);
diff --git a/app/src/Training/Exceptions/TrainingReviewRankingInvalidException.php b/app/src/Training/Exceptions/TrainingReviewRankingInvalidException.php
new file mode 100644
index 000000000..bee3d57d7
--- /dev/null
+++ b/app/src/Training/Exceptions/TrainingReviewRankingInvalidException.php
@@ -0,0 +1,16 @@
+
+ * @throws TrainingReviewRankingInvalidException
*/
public function getVisibleReviews(int $id, ?int $limit = null): array
{
@@ -62,6 +64,7 @@ public function getVisibleReviews(int $id, ?int $limit = null): array
* Get all reviews including hidden by training id.
*
* @return list
+ * @throws TrainingReviewRankingInvalidException
*/
public function getAllReviews(int $id): array
{
@@ -95,6 +98,7 @@ public function getAllReviews(int $id): array
/**
* @throws TrainingReviewNotFoundException
+ * @throws TrainingReviewRankingInvalidException
*/
public function getReview(int $reviewId): TrainingReview
{
@@ -127,6 +131,7 @@ public function getReview(int $reviewId): TrainingReview
/**
* @return list
+ * @throws TrainingReviewRankingInvalidException
*/
public function getReviewsByDateId(int $dateId): array
{
@@ -198,6 +203,9 @@ public function addReview(int $dateId, string $name, string $company, ?string $j
}
+ /**
+ * @throws TrainingReviewRankingInvalidException
+ */
private function createFromDatabaseRow(Row $row): TrainingReview
{
assert(is_int($row->id));
@@ -205,13 +213,14 @@ private function createFromDatabaseRow(Row $row): TrainingReview
assert(is_string($row->company));
assert($row->jobTitle === null || is_string($row->jobTitle));
assert(is_string($row->review));
- assert(is_string($row->reviewTexy));
assert($row->href === null || is_string($row->href));
assert(is_int($row->hidden));
- assert($row->ranking === null || is_int($row->ranking) && $row->ranking > 0);
+ assert($row->ranking === null || is_int($row->ranking));
assert($row->note === null || is_string($row->note));
assert(is_int($row->dateId));
-
+ if ($row->ranking !== null && $row->ranking <= 0) {
+ throw new TrainingReviewRankingInvalidException($row->id, $row->ranking);
+ }
return new TrainingReview(
$row->id,
$row->name,
diff --git a/app/src/UpcKeys/Presenters/HomepagePresenter.php b/app/src/UpcKeys/Presenters/HomepagePresenter.php
index dcb0bde8f..08cd3c549 100644
--- a/app/src/UpcKeys/Presenters/HomepagePresenter.php
+++ b/app/src/UpcKeys/Presenters/HomepagePresenter.php
@@ -37,6 +37,8 @@ public function __construct(
public function actionDefault(?string $ssid = null, string $format = 'html'): void
{
$this->ssid = $ssid;
+ $error = null;
+ $keys = [];
if ($this->ssid !== null) {
if ($this->upcKeys->isValidSsid($this->ssid)) {
if ($this->ssid !== strtoupper($this->ssid)) {
@@ -53,12 +55,12 @@ public function actionDefault(?string $ssid = null, string $format = 'html'): vo
}
$this->template->ssid = $this->ssid;
- $this->template->error = $error ?? null;
- $this->template->keys = isset($keys) && !isset($error) ? $keys : null;
+ $this->template->error = $error;
+ $this->template->keys = $keys !== [] && $error === null ? $keys : null;
switch ($format) {
case 'text':
$this->httpResponse->setContentType('text/plain');
- $this->sendResponse($this->upcKeys->getTextResponse($this->ssid, $error ?? null, $keys ?? []));
+ $this->sendResponse($this->upcKeys->getTextResponse($this->ssid, $error, $keys));
// no break, Presenter::sendResponse() is in earlyTerminatingMethodCalls defined in the phpstan-nette extension config
case 'html':
$this->template->filterTypes = WiFiBand::getKnown();
@@ -69,8 +71,8 @@ public function actionDefault(?string $ssid = null, string $format = 'html'): vo
case 'json':
$this->sendJson(array_filter([
'ssid' => $this->ssid,
- 'error' => $error ?? null,
- 'keys' => $keys ?? null,
+ 'error' => $error,
+ 'keys' => $keys,
]));
// no break, Presenter::sendJson() is in earlyTerminatingMethodCalls defined in the phpstan-nette extension config
default:
diff --git a/app/tests/Feed/ExportsTest.phpt b/app/tests/Feed/ExportsTest.phpt
index 274dbad1b..29c96b6a7 100644
--- a/app/tests/Feed/ExportsTest.phpt
+++ b/app/tests/Feed/ExportsTest.phpt
@@ -15,6 +15,7 @@ use Nette\Caching\Storage;
use Nette\Utils\Html;
use Override;
use SimpleXMLElement;
+use Spaze\Exports\Atom\Feed;
use Tester\Assert;
use Tester\TestCase;
@@ -70,14 +71,7 @@ class ExportsTest extends TestCase
{
$this->articles->addBlogPost(1, new DateTime(), 'one');
$this->articles->addBlogPost(2, new DateTime(), 'two');
- $feed = $this->exports->getArticles('https://example.com/');
- $xml = simplexml_load_string((string)$feed);
- if ($xml === false || $xml->entry === null || !isset($xml->entry[0], $xml->entry[1])) {
- Assert::fail('Cannot load the feed');
- } else {
- $this->assertEntry($xml->entry[0], 'one');
- $this->assertEntry($xml->entry[1], 'two');
- }
+ $this->assertEntries('one', null, 'two', null);
}
@@ -92,14 +86,12 @@ class ExportsTest extends TestCase
];
$this->articles->addBlogPost(1, new DateTime(), 'one', $editsOne);
$this->articles->addBlogPost(2, new DateTime(), 'two', $editsTwo);
- $feed = $this->exports->getArticles('https://example.com/');
- $xml = simplexml_load_string((string)$feed);
- if ($xml === false || $xml->entry === null || !isset($xml->entry[0], $xml->entry[1])) {
- Assert::fail('Cannot load the feed');
- } else {
- $this->assertEntry($xml->entry[0], 'one', 'messages.blog.post.edits
- 14.3. Edit one one
- 14.4. Edit one two
Text one');
- $this->assertEntry($xml->entry[1], 'two', 'messages.blog.post.edits
Text two');
- }
+ $this->assertEntries(
+ 'one',
+ 'messages.blog.post.edits
- 14.3. Edit one one
- 14.4. Edit one two
Text one',
+ 'two',
+ 'messages.blog.post.edits
Text two',
+ );
}
@@ -108,16 +100,7 @@ class ExportsTest extends TestCase
$this->articles->addBlogPost(1, new DateTime(), 'one');
$this->articles->addBlogPost(2, new DateTime(), 'two', omitExports: true);
$this->articles->addBlogPost(3, new DateTime(), 'three', omitExports: false);
- $feed = $this->exports->getArticles('https://example.com/');
- $xml = simplexml_load_string((string)$feed);
- if ($xml === false || $xml->entry === null || !isset($xml->entry[0], $xml->entry[1])) {
- Assert::fail('Cannot load the feed');
- } else {
- $this->assertEntry($xml->entry[0], 'one');
- $this->assertEntry($xml->entry[1], 'three');
- Assert::count(2, $xml->entry);
- Assert::notNull($feed->getUpdated());
- }
+ $this->assertEntries('one', null, 'three', null);
}
@@ -126,25 +109,42 @@ class ExportsTest extends TestCase
$this->articles->addBlogPost(1, new DateTime(), 'one', omitExports: true);
$this->articles->addBlogPost(2, new DateTime(), 'two', omitExports: true);
$this->articles->addBlogPost(3, new DateTime(), 'three', omitExports: true);
+ [$feed, $count] = $this->getEntries();
+ Assert::same(0, $count);
+ Assert::null($feed->getUpdated());
+ }
+
+
+ /**
+ * @return array{0: Feed, 1:non-negative-int, 2: SimpleXMLElement|null, 3: SimpleXMLElement|null}
+ */
+ private function getEntries(): array
+ {
$feed = $this->exports->getArticles('https://example.com/');
$xml = simplexml_load_string((string)$feed);
- if ($xml === false || $xml->entry === null) {
- Assert::fail('Cannot load the feed');
- } else {
- Assert::count(0, $xml->entry);
- Assert::null($feed->getUpdated());
- }
+ assert($xml instanceof SimpleXMLElement);
+ assert($xml->entry instanceof SimpleXMLElement);
+ return [$feed, count($xml->entry), $xml->entry[0], $xml->entry[1]];
}
- private function assertEntry(SimpleXMLElement $entry, string $suffix, ?string $text = null): void
+ private function assertEntries(string $suffix1, ?string $text1, string $suffix2, ?string $text2): void
{
- $link = "https://example.com/{$suffix}";
- Assert::same($link, (string)$entry->id, $suffix);
- Assert::same("Excerpt {$suffix}", (string)$entry->summary, $suffix);
- Assert::same("Title {$suffix}", (string)$entry->title, $suffix);
- Assert::same($link, $entry->link !== null ? (string)$entry->link['href'] : null, $suffix);
- Assert::same($text ?? "Text {$suffix}", (string)$entry->content, $suffix);
+ [$feed, $count, $entry1, $entry2] = $this->getEntries();
+ assert($entry1 instanceof SimpleXMLElement);
+ assert($entry2 instanceof SimpleXMLElement);
+ Assert::same(2, $count);
+ Assert::same("https://example.com/$suffix1", (string)$entry1->id);
+ Assert::same("Excerpt $suffix1", (string)$entry1->summary);
+ Assert::same("Title $suffix1", (string)$entry1->title);
+ Assert::same("https://example.com/$suffix1", $entry1->link !== null ? (string)$entry1->link['href'] : null);
+ Assert::same($text1 ?? "Text $suffix1", (string)$entry1->content);
+ Assert::same("https://example.com/$suffix2", (string)$entry2->id);
+ Assert::same("Excerpt $suffix2", (string)$entry2->summary);
+ Assert::same("Title $suffix2", (string)$entry2->title);
+ Assert::same("https://example.com/$suffix2", $entry2->link !== null ? (string)$entry2->link['href'] : null);
+ Assert::same($text2 ?? "Text $suffix2", (string)$entry2->content);
+ Assert::notNull($feed->getUpdated());
}
diff --git a/app/tests/Training/Reviews/TrainingReviewsTest.phpt b/app/tests/Training/Reviews/TrainingReviewsTest.phpt
new file mode 100644
index 000000000..bf0e1144a
--- /dev/null
+++ b/app/tests/Training/Reviews/TrainingReviewsTest.phpt
@@ -0,0 +1,224 @@
+database->setFetchAllDefaultResult([
+ [
+ 'id' => 11,
+ 'name' => 'Name 1',
+ 'company' => 'Company 1',
+ 'jobTitle' => 'Title',
+ 'review' => 'Review //1//',
+ 'href' => 'https://example.com/1',
+ 'hidden' => 0,
+ 'ranking' => 1,
+ 'note' => 'Note 1',
+ 'dateId' => 21,
+ ],
+ [
+ 'id' => 12,
+ 'name' => 'Name 2',
+ 'company' => 'Company 2',
+ 'jobTitle' => null,
+ 'review' => 'Review //2//',
+ 'href' => 'https://example.com/2',
+ 'hidden' => 1,
+ 'ranking' => null,
+ 'note' => null,
+ 'dateId' => 22,
+ ],
+ ]);
+ }
+
+
+ #[Override]
+ protected function tearDown(): void
+ {
+ $this->database->reset();
+ }
+
+
+ public function testGetVisibleReviews(): void
+ {
+ $reviews = $this->trainingReviews->getVisibleReviews(3, 4);
+ $this->assertReviews($reviews);
+ }
+
+
+ public function testGetAllReviews(): void
+ {
+ $reviews = $this->trainingReviews->getAllReviews(3);
+ $this->assertReviews($reviews);
+ }
+
+
+ public function testGetReviewsById(): void
+ {
+ $reviews = $this->trainingReviews->getReviewsByDateId(5);
+ $this->assertReviews($reviews);
+ }
+
+
+ public function testGetAllReviewsInvalidRanking(): void
+ {
+ $this->database->setFetchAllDefaultResult([
+ [
+ 'id' => 11,
+ 'name' => 'Name 1',
+ 'company' => 'Company 1',
+ 'jobTitle' => 'Title',
+ 'review' => 'Review //1//',
+ 'href' => 'https://example.com/1',
+ 'hidden' => 0,
+ 'ranking' => -1,
+ 'note' => 'Note 1',
+ 'dateId' => 21,
+ ],
+ ]);
+ Assert::exception(function (): void {
+ $this->trainingReviews->getAllReviews(3);
+ }, TrainingReviewRankingInvalidException::class, "The rating of the training review id '11' is invalid: '-1'");
+ }
+
+
+ public function testGetReview(): void
+ {
+ $this->database->setFetchDefaultResult([
+ 'id' => 11,
+ 'name' => 'Name 1',
+ 'company' => 'Company 1',
+ 'jobTitle' => 'Title',
+ 'review' => 'Review //1//',
+ 'href' => 'https://example.com/1',
+ 'hidden' => 0,
+ 'ranking' => 1,
+ 'note' => 'Note 1',
+ 'dateId' => 21,
+ ]);
+ $review = $this->trainingReviews->getReview(5);
+ Assert::same(11, $review->getId());
+ Assert::same('Name 1', $review->getName());
+ Assert::same('Company 1', $review->getCompany());
+ Assert::same('Title', $review->getJobTitle());
+ Assert::same('Review 1', $review->getReview()->getHtml());
+ Assert::same('https://example.com/1', $review->getHref());
+ Assert::false($review->isHidden());
+ Assert::same(1, $review->getRanking());
+ Assert::same('Note 1', $review->getNote());
+ Assert::same(21, $review->getDateId());
+ }
+
+
+ public function testGetReviewNotFound(): void
+ {
+ Assert::exception(function (): void {
+ $this->trainingReviews->getReview(6);
+ }, TrainingReviewNotFoundException::class, "Training review id '6' doesn't exist");
+ }
+
+
+ public function testUpdateReview(): void
+ {
+ $this->trainingReviews->updateReview(
+ 2,
+ 3,
+ 'Name',
+ 'Company',
+ null,
+ 'Review',
+ 'https://review.example',
+ false,
+ null,
+ null,
+ );
+ Assert::count(1, $this->database->getParamsArrayForQuery('UPDATE training_reviews SET ? WHERE id_review = ?'));
+ }
+
+
+ public function testAddReview(): void
+ {
+ $date = new DateTimeImmutable();
+ $this->dateTimeMachineFactory->setDateTime($date);
+ $this->trainingReviews->addReview(
+ 4,
+ 'Name',
+ 'Company',
+ null,
+ 'Review',
+ 'https://review.example',
+ false,
+ null,
+ null,
+ );
+ $params = $this->database->getParamsArrayForQuery('INSERT INTO training_reviews ?');
+ Assert::count(1, $params);
+ Assert::same($date->format(DateTimeFormat::MYSQL), $params[0]['added']);
+ Assert::same($date->getTimezone()->getName(), $params[0]['added_timezone']);
+ }
+
+
+ /**
+ * @param list $reviews
+ */
+ private function assertReviews(array $reviews): void
+ {
+ Assert::count(2, $reviews);
+
+ Assert::same(11, $reviews[0]->getId());
+ Assert::same('Name 1', $reviews[0]->getName());
+ Assert::same('Company 1', $reviews[0]->getCompany());
+ Assert::same('Title', $reviews[0]->getJobTitle());
+ Assert::same('Review 1', $reviews[0]->getReview()->getHtml());
+ Assert::same('https://example.com/1', $reviews[0]->getHref());
+ Assert::false($reviews[0]->isHidden());
+ Assert::same(1, $reviews[0]->getRanking());
+ Assert::same('Note 1', $reviews[0]->getNote());
+ Assert::same(21, $reviews[0]->getDateId());
+
+ Assert::same(12, $reviews[1]->getId());
+ Assert::same('Name 2', $reviews[1]->getName());
+ Assert::same('Company 2', $reviews[1]->getCompany());
+ Assert::null($reviews[1]->getJobTitle());
+ Assert::same('Review 2', $reviews[1]->getReview()->getHtml());
+ Assert::same('https://example.com/2', $reviews[1]->getHref());
+ Assert::true($reviews[1]->isHidden());
+ Assert::null($reviews[1]->getRanking());
+ Assert::null($reviews[1]->getNote());
+ Assert::same(22, $reviews[1]->getDateId());
+ }
+
+}
+
+TestCaseRunner::run(TrainingReviewsTest::class);
diff --git a/app/vendor/composer/installed.php b/app/vendor/composer/installed.php
index 671c4caf7..60035a9b2 100644
--- a/app/vendor/composer/installed.php
+++ b/app/vendor/composer/installed.php
@@ -1,9 +1,9 @@
array(
'name' => 'spaze/michalspacek.cz',
- 'pretty_version' => '1.3.3.7',
- 'version' => '1.3.3.7',
- 'reference' => null,
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => '53ccdd4e3bfed40f4fe0a708fec39406a888c21a',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),
@@ -466,9 +466,9 @@
'dev_requirement' => false,
),
'spaze/michalspacek.cz' => array(
- 'pretty_version' => '1.3.3.7',
- 'version' => '1.3.3.7',
- 'reference' => null,
+ 'pretty_version' => 'dev-main',
+ 'version' => 'dev-main',
+ 'reference' => '53ccdd4e3bfed40f4fe0a708fec39406a888c21a',
'type' => 'project',
'install_path' => __DIR__ . '/../../',
'aliases' => array(),