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

  • 15.3. Edit two one
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

  • 15.3. Edit two one
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(),