@@ -83,4 +83,100 @@ public function promoteUnreleasedWillMergeWithAnExistingPublishedVersion(): void
8383 self ::assertSame (['Preserve release sections ' ], $ release ->getEntriesFor (ChangelogEntryType::Fixed));
8484 self ::assertFalse ($ promoted ->getUnreleased ()->hasEntries ());
8585 }
86+
87+ /**
88+ * @return void
89+ */
90+ #[Test]
91+ public function documentAccessorsWillResolveExpectedReleaseVariants (): void
92+ {
93+ $ document = new ChangelogDocument ([new ChangelogRelease ('1.2.0 ' , '2026-04-19 ' )]);
94+
95+ self ::assertSame (ChangelogDocument::UNRELEASED_VERSION , $ document ->getUnreleased ()->getVersion ());
96+ self ::assertNull ($ document ->getRelease ('9.9.9 ' ));
97+ self ::assertSame ('1.2.0 ' , $ document ->getLatestPublishedRelease ()?->getVersion());
98+ }
99+
100+ /**
101+ * @return void
102+ */
103+ #[Test]
104+ public function getLatestPublishedReleaseWillReturnNullWhenOnlyUnreleasedExists (): void
105+ {
106+ self ::assertNull (ChangelogDocument::create ()->getLatestPublishedRelease ());
107+ }
108+
109+ /**
110+ * @return void
111+ */
112+ #[Test]
113+ public function withReleaseWillReplaceExistingVersionAndInsertUnreleasedAtTheTop (): void
114+ {
115+ $ existing = new ChangelogRelease ('1.2.0 ' , '2026-04-01 ' );
116+ $ replacement = (new ChangelogRelease ('1.2.0 ' , '2026-04-19 ' ))
117+ ->withEntry (ChangelogEntryType::Added, 'Updated note ' );
118+ $ document = (new ChangelogDocument ([$ existing ]))
119+ ->withRelease (new ChangelogRelease (ChangelogDocument::UNRELEASED_VERSION ))
120+ ->withRelease ($ replacement );
121+
122+ self ::assertSame (
123+ [ChangelogDocument::UNRELEASED_VERSION , '1.2.0 ' ],
124+ array_map (
125+ static fn (ChangelogRelease $ release ): string => $ release ->getVersion (),
126+ $ document ->getReleases (),
127+ ),
128+ );
129+ self ::assertSame (['Updated note ' ], $ document ->getRelease ('1.2.0 ' )?->getEntriesFor(ChangelogEntryType::Added));
130+ }
131+
132+ /**
133+ * @return void
134+ */
135+ #[Test]
136+ public function promoteUnreleasedWillCreatePublishedReleaseWhenDocumentIsEmpty (): void
137+ {
138+ $ document = new ChangelogDocument ([]);
139+
140+ $ promoted = $ document ->promoteUnreleased ('1.0.0 ' , '2026-04-20 ' );
141+
142+ self ::assertSame (
143+ [ChangelogDocument::UNRELEASED_VERSION , '1.0.0 ' ],
144+ array_map (
145+ static fn (ChangelogRelease $ release ): string => $ release ->getVersion (),
146+ $ promoted ->getReleases (),
147+ ),
148+ );
149+ self ::assertSame ('2026-04-20 ' , $ promoted ->getRelease ('1.0.0 ' )?->getDate());
150+ }
151+
152+ /**
153+ * @return void
154+ */
155+ #[Test]
156+ public function releaseHelpersWillNormalizeEntriesAndSupportImmutableMutators (): void
157+ {
158+ $ release = new ChangelogRelease ('1.3.0 ' , null , [
159+ ChangelogEntryType::Added->value => ['Ship feature ' , 'Ship feature ' ],
160+ ]);
161+
162+ self ::assertSame ('1.3.0 ' , $ release ->getVersion ());
163+ self ::assertNull ($ release ->getDate ());
164+ self ::assertFalse ($ release ->isUnreleased ());
165+ self ::assertTrue ($ release ->hasEntries ());
166+ self ::assertSame (['Ship feature ' ], $ release ->getEntriesFor (ChangelogEntryType::Added));
167+
168+ $ sameRelease = $ release ->withEntry (ChangelogEntryType::Fixed, ' ' );
169+ self ::assertSame ($ release , $ sameRelease );
170+
171+ $ updated = $ release
172+ ->withEntry (ChangelogEntryType::Fixed, 'Repair behavior ' )
173+ ->withEntries ([
174+ ChangelogEntryType::Security->value => ['Security hardening ' ],
175+ ])
176+ ->withDate ('2026-04-20 ' );
177+
178+ self ::assertSame ('2026-04-20 ' , $ updated ->getDate ());
179+ self ::assertSame (['Security hardening ' ], $ updated ->getEntriesFor (ChangelogEntryType::Security));
180+ self ::assertSame ([], $ updated ->getEntriesFor (ChangelogEntryType::Added));
181+ }
86182}
0 commit comments