Skip to content

Commit dc1c07c

Browse files
committed
update test
1 parent 21db1e4 commit dc1c07c

1 file changed

Lines changed: 73 additions & 243 deletions

File tree

‎test/Unit/App/Fixture/PostLoaderTest.php‎

Lines changed: 73 additions & 243 deletions
Original file line numberDiff line numberDiff line change
@@ -16,307 +16,113 @@
1616
use Light\Blog\Entity\Author;
1717
use Light\Blog\Entity\Category;
1818
use Light\Blog\Entity\Post;
19-
use Light\Blog\Enum\PostStatusEnum;
2019
use LightTest\Unit\UnitTest;
2120
use PHPUnit\Framework\MockObject\Exception;
2221
use PHPUnit\Framework\MockObject\MockObject;
23-
use RuntimeException;
2422

25-
use function bin2hex;
23+
use function array_count_values;
2624
use function dirname;
27-
use function file_put_contents;
28-
use function is_dir;
29-
use function is_file;
30-
use function json_encode;
31-
use function mkdir;
32-
use function random_bytes;
33-
use function rmdir;
34-
use function sprintf;
35-
use function sys_get_temp_dir;
36-
use function unlink;
37-
38-
use const DIRECTORY_SEPARATOR;
39-
25+
use function file_get_contents;
26+
use function json_decode;
27+
use function preg_quote;
28+
use function preg_replace;
29+
use function strtolower;
30+
use function trim;
31+
32+
/**
33+
* Runs PostLoader against the real articles_cleaned.json, using the
34+
* "Autologin using Cookie / Remember Me in Dotkernel" article that sets post_slug.
35+
*/
4036
class PostLoaderTest extends UnitTest
4137
{
42-
private string $jsonFile;
43-
private Author $author;
44-
private Category $category;
45-
46-
protected function setUp(): void
47-
{
48-
parent::setUp();
49-
50-
$this->jsonFile = sprintf(
51-
'%s%slight-post-loader-%s%sarticles_cleaned.json',
52-
sys_get_temp_dir(),
53-
DIRECTORY_SEPARATOR,
54-
bin2hex(random_bytes(8)),
55-
DIRECTORY_SEPARATOR
56-
);
57-
58-
mkdir(dirname($this->jsonFile), 0775, true);
59-
60-
$this->author = new Author();
61-
$this->category = new Category();
62-
}
63-
64-
protected function tearDown(): void
65-
{
66-
if (is_file($this->jsonFile)) {
67-
unlink($this->jsonFile);
68-
}
69-
70-
$directory = dirname($this->jsonFile);
71-
if (is_dir($directory)) {
72-
rmdir($directory);
73-
}
74-
75-
parent::tearDown();
76-
}
38+
private const string TITLE = 'Autologin using Cookie / Remember Me in Dotkernel';
39+
private const string CUSTOM_SLUG = 'autologin-cookie-remember-me-feature';
40+
private const string TITLE_SLUG = 'autologin-using-cookie-remember-me-in-dotkernel';
7741

7842
/**
7943
* @throws Exception
8044
*/
8145
public function testLoadCreatesPostWithCustomSlugWhenNothingMatchesInDb(): void
8246
{
83-
$this->writeArticles([
84-
$this->article('A post', ['post_slug' => ' custom-slug ']),
85-
]);
86-
8747
$lookups = [];
8848
$manager = $this->createEntityManager($this->createRepository([], $lookups));
8949

90-
$persisted = null;
91-
$manager->expects($this->once())->method('persist')
92-
->willReturnCallback(function (Post $post) use (&$persisted): void {
93-
$persisted = $post;
50+
$persistedSlugs = [];
51+
$manager->expects($this->atLeastOnce())->method('persist')
52+
->willReturnCallback(function (Post $post) use (&$persistedSlugs): void {
53+
$persistedSlugs[$post->getTitle()] = $post->getSlug();
9454
});
95-
$manager->expects($this->once())->method('flush');
96-
97-
$this->expectOutputString("CREATE: A post\n");
98-
$this->runLoader($manager);
99-
100-
$this->assertInstanceOf(Post::class, $persisted);
101-
$this->assertSame('custom-slug', $persisted->getSlug());
102-
$this->assertSame(['custom-slug', 'a-post'], $lookups);
103-
}
104-
105-
/**
106-
* @throws Exception
107-
*/
108-
public function testLoadUpdatesExistingPostFoundByCustomSlug(): void
109-
{
110-
$this->writeArticles([
111-
$this->article('A post', ['post_slug' => 'custom-slug']),
112-
]);
113-
114-
$existing = $this->existingPost('custom-slug', 'A post');
115-
116-
$lookups = [];
117-
$manager = $this->createEntityManager($this->createRepository(['custom-slug' => $existing], $lookups));
118-
$manager->expects($this->never())->method('persist');
119-
120-
$this->expectOutputString("UNCHANGED: A post\n");
121-
$this->runLoader($manager);
122-
123-
$this->assertSame('custom-slug', $existing->getSlug());
124-
$this->assertSame(['custom-slug'], $lookups);
125-
}
126-
127-
/**
128-
* @throws Exception
129-
*/
130-
public function testLoadRenamesExistingPostFoundByTitleSlugToCustomSlug(): void
131-
{
132-
$this->writeArticles([
133-
$this->article('A post', ['post_slug' => 'custom-slug']),
134-
]);
135-
136-
// Imported before post_slug was added to the JSON.
137-
$existing = $this->existingPost('a-post', 'A post');
138-
139-
$lookups = [];
140-
$manager = $this->createEntityManager($this->createRepository(['a-post' => $existing], $lookups));
141-
$manager->expects($this->never())->method('persist');
142-
143-
$this->expectOutputString("UPDATE: A post\n");
144-
$this->runLoader($manager);
145-
146-
$this->assertSame('custom-slug', $existing->getSlug());
147-
$this->assertSame(['custom-slug', 'a-post'], $lookups);
148-
}
149-
150-
/**
151-
* @throws Exception
152-
*/
153-
public function testLoadFallsBackToTitleSlugWhenPostSlugIsMissingOrEmpty(): void
154-
{
155-
$this->writeArticles([
156-
$this->article('A post'),
157-
$this->article('A post', ['post_slug' => '']),
158-
]);
159-
160-
$lookups = [];
161-
$manager = $this->createEntityManager($this->createRepository([], $lookups));
162-
$manager->expects($this->exactly(2))->method('persist');
16355

164-
$this->expectOutputString("CREATE: A post\nCREATE: A post\n");
56+
$this->expectOutputRegex('/CREATE: ' . preg_quote(self::TITLE, '/') . '\n/');
16557
$this->runLoader($manager);
16658

167-
$this->assertSame(['a-post', 'a-post-2'], $lookups);
168-
}
169-
170-
/**
171-
* @throws Exception
172-
*/
173-
public function testLoadUpdatesEveryChangedFieldOnExistingPost(): void
174-
{
175-
$this->writeArticles([
176-
$this->article('A post', [
177-
'post_status' => 'archived',
178-
'post_date' => '0000-00-00 00:00:00',
179-
'excerpt' => 'New excerpt',
180-
'tl_dr' => 'New TL;DR',
181-
'isObsolete' => true,
182-
'isTwig' => true,
183-
'opengraph_img' => 'og.png',
184-
]),
185-
]);
186-
187-
$existing = $this->existingPost('a-post', 'Old title');
188-
$existing->setCategory(new Category());
189-
$existing->setAuthor(new Author());
190-
191-
$lookups = [];
192-
$manager = $this->createEntityManager($this->createRepository(['a-post' => $existing], $lookups));
193-
$manager->expects($this->never())->method('persist');
194-
195-
$this->expectOutputString("UPDATE: A post\n");
196-
$this->runLoader($manager);
59+
$this->assertSame(self::CUSTOM_SLUG, $persistedSlugs[self::TITLE]);
60+
$this->assertNotContains(self::TITLE_SLUG, $persistedSlugs);
19761

198-
$this->assertSame('A post', $existing->getTitle());
199-
$this->assertSame(PostStatusEnum::Archived, $existing->getStatus());
200-
$this->assertNotSame('2022-07-18 09:12:59', $existing->getPostDate()->format('Y-m-d H:i:s'));
201-
$this->assertSame($this->category, $existing->getCategory());
202-
$this->assertSame($this->author, $existing->getAuthor());
203-
$this->assertSame('New excerpt', $existing->getExcerpt());
204-
$this->assertSame('New TL;DR', $existing->getTlDr());
205-
$this->assertTrue($existing->isObsolete());
206-
$this->assertTrue($existing->isTwig());
207-
$this->assertSame('og.png', $existing->getOpenGraphImage());
62+
// Not found by post_slug, so the title-based slug is tried before creating.
63+
$counts = array_count_values($lookups);
64+
$this->assertSame(1, $counts[self::CUSTOM_SLUG]);
65+
$this->assertSame(1, $counts[self::TITLE_SLUG]);
20866
}
20967

21068
/**
21169
* @throws Exception
21270
*/
213-
public function testLoadMapsPostStatuses(): void
71+
public function testLoadUpdatesPostFoundByCustomSlug(): void
21472
{
215-
$this->writeArticles([
216-
$this->article('Private post', ['post_status' => 'private']),
217-
$this->article('Draft post', ['post_status' => 'unknown']),
218-
]);
73+
$existing = $this->existingPost(self::CUSTOM_SLUG, 'Old title');
21974

22075
$lookups = [];
221-
$manager = $this->createEntityManager($this->createRepository([], $lookups));
222-
223-
$statuses = [];
224-
$manager->expects($this->exactly(2))->method('persist')
225-
->willReturnCallback(function (Post $post) use (&$statuses): void {
226-
$statuses[] = $post->getStatus();
227-
});
76+
$manager = $this->createEntityManager($this->createRepository([self::CUSTOM_SLUG => $existing], $lookups));
77+
$manager->expects($this->atLeastOnce())->method('flush');
22878

229-
$this->expectOutputString("CREATE: Private post\nCREATE: Draft post\n");
79+
$this->expectOutputRegex('/UPDATE: ' . preg_quote(self::TITLE, '/') . '\n/');
23080
$this->runLoader($manager);
23181

232-
$this->assertSame([PostStatusEnum::Private, PostStatusEnum::Draft], $statuses);
82+
$this->assertSame(self::CUSTOM_SLUG, $existing->getSlug());
83+
$this->assertSame(self::TITLE, $existing->getTitle());
84+
$this->assertNotContains(self::TITLE_SLUG, $lookups);
23385
}
23486

23587
/**
23688
* @throws Exception
23789
*/
238-
public function testLoadSkipsArticleWithUnknownAuthor(): void
90+
public function testLoadRenamesPostImportedUnderTitleSlugToCustomSlug(): void
23991
{
240-
$this->writeArticles([
241-
$this->article('A post', ['author' => ['display_name' => 'nobody']]),
242-
]);
92+
// Imported before post_slug was added to the JSON.
93+
$existing = $this->existingPost(self::TITLE_SLUG, self::TITLE);
24394

24495
$lookups = [];
245-
$manager = $this->createEntityManager($this->createRepository([], $lookups));
246-
$manager->expects($this->never())->method('persist');
96+
$manager = $this->createEntityManager($this->createRepository([self::TITLE_SLUG => $existing], $lookups));
97+
$manager->expects($this->atLeastOnce())->method('flush');
24798

248-
$this->expectOutputString("SKIP (no author): A post\n");
99+
$this->expectOutputRegex('/UPDATE: ' . preg_quote(self::TITLE, '/') . '\n/');
249100
$this->runLoader($manager);
250101

251-
$this->assertSame([], $lookups);
252-
}
253-
254-
/**
255-
* @throws Exception
256-
*/
257-
public function testLoadThrowsWhenJsonFileCannotBeRead(): void
258-
{
259-
$manager = $this->createStub(EntityManagerInterface::class);
260-
261-
$this->expectException(RuntimeException::class);
262-
$this->expectExceptionMessage('Unable to read file');
263-
264-
@$this->runLoader($manager);
102+
$this->assertSame(self::CUSTOM_SLUG, $existing->getSlug());
265103
}
266104

267105
public function testDependenciesAndOrder(): void
268106
{
269-
$loader = new PostLoader($this->jsonFile);
107+
$loader = new PostLoader();
270108

271109
$this->assertSame([AuthorLoader::class, CategoryLoader::class], $loader->getDependencies());
272110
$this->assertSame(3, $loader->getOrder());
273111
}
274112

275-
/**
276-
* @param array<string, mixed> $extra
277-
* @return array<string, mixed>
278-
*/
279-
private function article(string $title, array $extra = []): array
280-
{
281-
return $extra + [
282-
'post_title' => $title,
283-
'post_date' => '2022-07-18 09:12:59',
284-
'post_status' => 'published',
285-
'author' => ['display_name' => 'arhimede'],
286-
'excerpt' => 'Excerpt',
287-
'tl_dr' => 'TL;DR',
288-
'tags' => [],
289-
];
290-
}
291-
292113
private function existingPost(string $slug, string $title): Post
293114
{
294115
$post = new Post();
295116
$post->setSlug($slug);
296117
$post->setTitle($title);
297-
$post->setPostDate(new DateTimeImmutable('2022-07-18 09:12:59'));
298-
$post->setStatus(PostStatusEnum::Published);
299-
$post->setCategory($this->category);
300-
$post->setAuthor($this->author);
301-
$post->setExcerpt('Excerpt');
302-
$post->setTldr('TL;DR');
303-
$post->setObsolete(false);
304-
$post->setTwig(false);
305-
$post->setOpenGraphImage(null);
118+
$post->setPostDate(new DateTimeImmutable());
119+
$post->setCategory(new Category());
120+
$post->setAuthor(new Author());
121+
$post->setExcerpt('');
306122

307123
return $post;
308124
}
309125

310-
/**
311-
* @param list<array<string, mixed>> $articles
312-
*/
313-
private function writeArticles(array $articles): void
314-
{
315-
file_put_contents($this->jsonFile, json_encode([
316-
['slug' => 'category', 'articles' => $articles],
317-
]));
318-
}
319-
320126
/**
321127
* @param array<string, Post> $postsBySlug
322128
* @param list<string> $lookups
@@ -336,17 +142,41 @@ function (array $criteria) use ($postsBySlug, &$lookups): ?Post {
336142
return $repository;
337143
}
338144

145+
/**
146+
* Registers the category and author references the real articles_cleaned.json needs.
147+
*/
339148
private function runLoader(EntityManagerInterface $manager): void
340149
{
150+
$contents = file_get_contents(dirname(__DIR__, 4) . '/src/App/src/Fixture/articles_cleaned.json');
151+
$this->assertIsString($contents);
152+
153+
/** @var list<array{slug: string, articles: list<array{author?: array{display_name?: string}}>}> $categories */
154+
$categories = json_decode($contents, true);
155+
341156
$referenceRepository = new ReferenceRepository($manager);
342-
$referenceRepository->addReference('category_category', $this->category);
343-
$referenceRepository->addReference('author_arhimede', $this->author);
157+
foreach ($categories as $category) {
158+
$referenceRepository->setReference('category_' . $category['slug'], new Category());
159+
160+
foreach ($category['articles'] as $article) {
161+
$authorName = $article['author']['display_name'] ?? null;
162+
if ($authorName !== null) {
163+
$referenceRepository->setReference('author_' . $this->slugify($authorName), new Author());
164+
}
165+
}
166+
}
344167

345-
$loader = new PostLoader($this->jsonFile);
168+
$loader = new PostLoader();
346169
$loader->setReferenceRepository($referenceRepository);
347170
$loader->load($manager);
348171
}
349172

173+
private function slugify(string $text): string
174+
{
175+
$text = strtolower(trim($text));
176+
$text = preg_replace('/[^a-z0-9]+/', '-', $text) ?? '';
177+
return trim($text, '-');
178+
}
179+
350180
/**
351181
* @param EntityRepository<Post> $repository
352182
* @throws Exception

0 commit comments

Comments
 (0)