77use DateTimeImmutable ;
88use DateTimeZone ;
99use Light \App \Service \SitemapGenerator ;
10+ use Light \Blog \Entity \Author ;
1011use Light \Blog \Entity \Category ;
1112use Light \Blog \Entity \Post ;
13+ use Light \Blog \Repository \AuthorRepository ;
14+ use Light \Blog \Repository \CategoryRepository ;
1215use Light \Blog \Repository \PostRepository ;
1316use LightTest \Unit \UnitTest ;
1417use PHPUnit \Framework \MockObject \Exception ;
3134
3235class SitemapGeneratorTest extends UnitTest
3336{
37+ /** Homepage, /blog/, /categories/ and the packages-lifecycle page are always present. */
38+ private const FIXED_URL_COUNT = 4 ;
39+
3440 private string $ sitemapFile ;
3541
3642 protected function setUp (): void
@@ -64,37 +70,76 @@ protected function tearDown(): void
6470
6571 public function testGetSitemapFileReturnsTheConfiguredPath (): void
6672 {
67- $ this ->assertSame ($ this ->sitemapFile , $ this ->createGenerator ([] )->getSitemapFile ());
73+ $ this ->assertSame ($ this ->sitemapFile , $ this ->createGenerator ()->getSitemapFile ());
6874 }
6975
7076 /**
71- * The count includes the homepage entry in addition to one entry per post.
72- *
7377 * @throws Exception
7478 */
75- public function testWriteReturnsTheNumberOfPostsPlusTheHomepage (): void
79+ public function testWriteAlwaysIncludesTheFixedPagesEvenWithoutAnyContent (): void
7680 {
77- $ generator = $ this ->createGenerator ([
78- $ this ->createPost ('first-post ' , 'news ' ),
79- $ this ->createPost ('second-post ' , 'news ' ),
80- ]);
81+ $ generator = $ this ->createGenerator ();
82+
83+ $ this ->assertSame (self ::FIXED_URL_COUNT , $ generator ->write ());
8184
82- $ this ->assertSame (3 , $ generator ->write ());
85+ $ urls = $ this ->loadSitemap ()->url ;
86+ $ this ->assertCount (self ::FIXED_URL_COUNT , $ urls );
87+ $ this ->assertSame ('https://example.test/ ' , (string ) $ urls [0 ]->loc );
88+ $ this ->assertSame ('https://example.test/blog/ ' , (string ) $ urls [1 ]->loc );
89+ $ this ->assertSame ('https://example.test/categories/ ' , (string ) $ urls [2 ]->loc );
90+ $ this ->assertSame (
91+ 'https://example.test/dotkernel-packages-oss-lifecycle/ ' ,
92+ (string ) $ urls [3 ]->loc
93+ );
94+ $ this ->assertCount (0 , $ urls [0 ]->lastmod );
8395 }
8496
8597 /**
8698 * @throws Exception
8799 */
88- public function testWriteAlwaysIncludesTheHomepageEvenWithoutPosts (): void
100+ public function testWriteAddsOneUrlEntryPerConfiguredStaticPage (): void
89101 {
90- $ generator = $ this ->createGenerator ([ ]);
102+ $ generator = $ this ->createGenerator (pageRoutes: [ ' contact ' ]);
91103
92- $ this ->assertSame (1 , $ generator ->write ());
104+ $ this ->assertSame (self :: FIXED_URL_COUNT + 1 , $ generator ->write ());
93105
94106 $ urls = $ this ->loadSitemap ()->url ;
95- $ this ->assertCount (1 , $ urls );
96- $ this ->assertSame ('https://example.test ' , (string ) $ urls [0 ]->loc );
97- $ this ->assertCount (0 , $ urls [0 ]->lastmod );
107+ $ this ->assertSame ('https://example.test/contact/ ' , (string ) $ urls [self ::FIXED_URL_COUNT ]->loc );
108+ }
109+
110+ /**
111+ * @throws Exception
112+ */
113+ public function testWriteAddsOneUrlEntryPerCategoryWithItsLastModifiedDate (): void
114+ {
115+ $ category = $ this ->createCategory ('news ' , '2026-08-01 10:00:00 ' );
116+ $ generator = $ this ->createGenerator (categories: [$ category ]);
117+
118+ $ this ->assertSame (self ::FIXED_URL_COUNT + 1 , $ generator ->write ());
119+
120+ $ urls = $ this ->loadSitemap ()->url ;
121+ $ this ->assertSame ('https://example.test/category/news/ ' , (string ) $ urls [self ::FIXED_URL_COUNT ]->loc );
122+ $ this ->assertSame (
123+ '2026-08-01T10:00:00+00:00 ' ,
124+ (string ) $ urls [self ::FIXED_URL_COUNT ]->lastmod
125+ );
126+ }
127+
128+ /**
129+ * @throws Exception
130+ */
131+ public function testWriteAddsOneUrlEntryPerAuthor (): void
132+ {
133+ $ author = $ this ->createStub (Author::class);
134+ $ author ->method ('getSlug ' )->willReturn ('jane-doe ' );
135+
136+ $ generator = $ this ->createGenerator (authors: [$ author ]);
137+
138+ $ this ->assertSame (self ::FIXED_URL_COUNT + 1 , $ generator ->write ());
139+
140+ $ urls = $ this ->loadSitemap ()->url ;
141+ $ this ->assertSame ('https://example.test/author/jane-doe/ ' , (string ) $ urls [self ::FIXED_URL_COUNT ]->loc );
142+ $ this ->assertCount (0 , $ urls [self ::FIXED_URL_COUNT ]->lastmod );
98143 }
99144
100145 /**
@@ -103,13 +148,19 @@ public function testWriteAlwaysIncludesTheHomepageEvenWithoutPosts(): void
103148 public function testWriteAddsOneUrlEntryPerPostWithACategoryQualifiedLink (): void
104149 {
105150 $ post = $ this ->createPost ('a-post ' , 'news ' , '2026-08-01 10:00:00 ' );
106- $ this ->createGenerator ([$ post ])->write ();
151+ $ this ->createGenerator (posts: [$ post ])->write ();
107152
108153 $ urls = $ this ->loadSitemap ()->url ;
109154
110- $ this ->assertCount (2 , $ urls );
111- $ this ->assertSame ('https://example.test/news/a-post/ ' , (string ) $ urls [1 ]->loc );
112- $ this ->assertSame ('2026-08-01T10:00:00+00:00 ' , (string ) $ urls [1 ]->lastmod );
155+ $ this ->assertCount (self ::FIXED_URL_COUNT + 1 , $ urls );
156+ $ this ->assertSame (
157+ 'https://example.test/news/a-post/ ' ,
158+ (string ) $ urls [self ::FIXED_URL_COUNT ]->loc
159+ );
160+ $ this ->assertSame (
161+ '2026-08-01T10:00:00+00:00 ' ,
162+ (string ) $ urls [self ::FIXED_URL_COUNT ]->lastmod
163+ );
113164 }
114165
115166 /**
@@ -120,7 +171,7 @@ public function testWriteAddsOneUrlEntryPerPostWithACategoryQualifiedLink(): voi
120171 */
121172 public function testWriteThrowsWhenTheSitemapFileCannotBeWritten (): void
122173 {
123- $ generator = $ this ->createGenerator ([], sitemapFile: '/nonexistent-directory/sitemap.xml ' );
174+ $ generator = $ this ->createGenerator (sitemapFile: '/nonexistent-directory/sitemap.xml ' );
124175
125176 $ this ->expectException (RuntimeException::class);
126177 $ this ->expectExceptionMessage ('Unable to write sitemap. ' );
@@ -130,15 +181,32 @@ public function testWriteThrowsWhenTheSitemapFileCannotBeWritten(): void
130181
131182 /**
132183 * @param list<Post> $posts
184+ * @param list<Category> $categories
185+ * @param list<Author> $authors
186+ * @param list<string> $pageRoutes
133187 * @throws Exception
134188 */
135- private function createGenerator (array $ posts , ?string $ sitemapFile = null ): SitemapGenerator
136- {
189+ private function createGenerator (
190+ array $ posts = [],
191+ array $ categories = [],
192+ array $ authors = [],
193+ array $ pageRoutes = [],
194+ ?string $ sitemapFile = null ,
195+ ): SitemapGenerator {
137196 $ postRepository = $ this ->createStub (PostRepository::class);
138197 $ postRepository ->method ('getPublishedPosts ' )->willReturn ($ posts );
139198
199+ $ categoryRepository = $ this ->createStub (CategoryRepository::class);
200+ $ categoryRepository ->method ('getCategories ' )->willReturn ($ categories );
201+
202+ $ authorRepository = $ this ->createStub (AuthorRepository::class);
203+ $ authorRepository ->method ('getAuthorsWithPublishedPosts ' )->willReturn ($ authors );
204+
140205 return new SitemapGenerator (
141206 $ postRepository ,
207+ $ categoryRepository ,
208+ $ authorRepository ,
209+ $ pageRoutes ,
142210 $ sitemapFile ?? $ this ->sitemapFile ,
143211 'https://example.test ' ,
144212 );
@@ -160,6 +228,19 @@ private function createPost(string $slug, string $categorySlug, string $postDate
160228 return $ post ;
161229 }
162230
231+ /**
232+ * @throws Exception
233+ */
234+ private function createCategory (string $ slug , string $ updated ): Category
235+ {
236+ $ category = $ this ->createStub (Category::class);
237+ $ category ->method ('getSlug ' )->willReturn ($ slug );
238+ $ category ->method ('getUpdated ' )->willReturn (new DateTimeImmutable ($ updated , new DateTimeZone ('UTC ' )));
239+ $ category ->method ('getCreated ' )->willReturn (new DateTimeImmutable ($ updated , new DateTimeZone ('UTC ' )));
240+
241+ return $ category ;
242+ }
243+
163244 private function loadSitemap (): SimpleXMLElement
164245 {
165246 $ this ->assertFileExists ($ this ->sitemapFile );
0 commit comments