using ItWorx.KnowledgeHub.Domain.Knowledge; namespace ItWorx.KnowledgeHub.Domain.Tests; public sealed class PageTests { private static readonly DateTimeOffset Now = new(2026, 7, 31, 12, 0, 0, TimeSpan.Zero); [Fact] public void Create_sets_the_initial_revision_number_and_publishes_immediately() { var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Voorbeeldpagina"), DocumentType.WikiPage, "# Hallo", Guid.NewGuid(), Now); Assert.Equal(1, page.CurrentRevisionNumber); Assert.Equal(Now, page.PublishedAt); Assert.False(page.IsTrashed); Assert.Equal("voorbeeldpagina", page.Slug.Value); } [Fact] public void Publish_increments_the_revision_number_and_updates_the_slug() { var editor = Guid.NewGuid(); var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Oude titel"), DocumentType.WikiPage, "Inhoud", Guid.NewGuid(), Now); page.Publish(PageTitle.Create("Nieuwe titel"), "Bijgewerkte inhoud", DocumentType.WikiPage, DocumentStatus.Active, editor, Now.AddMinutes(5)); Assert.Equal(2, page.CurrentRevisionNumber); Assert.Equal("nieuwe-titel", page.Slug.Value); Assert.Equal(DocumentStatus.Active, page.DocumentStatus); Assert.Equal(editor, page.UpdatedBy); } [Fact] public void MoveTo_rejects_a_page_becoming_its_own_parent() { var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Pagina"), DocumentType.WikiPage, "Inhoud", Guid.NewGuid(), Now); Assert.Throws( () => page.MoveTo(page.Id, SortKey.First(), Guid.NewGuid(), Now)); } [Fact] public void A_trashed_page_cannot_be_edited_or_moved_until_restored() { var actor = Guid.NewGuid(); var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Pagina"), DocumentType.WikiPage, "Inhoud", actor, Now); page.Trash(actor, Now, TimeSpan.FromDays(30)); Assert.True(page.IsTrashed); Assert.Throws( () => page.Publish(PageTitle.Create("Pagina"), "Nieuw", DocumentType.WikiPage, DocumentStatus.Active, actor, Now)); Assert.Throws( () => page.MoveTo(null, SortKey.First(), actor, Now)); page.Restore(actor, Now.AddMinutes(1)); Assert.False(page.IsTrashed); } [Fact] public void Trashing_twice_is_a_harmless_no_op() { var actor = Guid.NewGuid(); var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Pagina"), DocumentType.WikiPage, "Inhoud", actor, Now); page.Trash(actor, Now, TimeSpan.FromDays(30)); var purgeAfterFirst = page.PurgeAfter; page.Trash(actor, Now.AddDays(1), TimeSpan.FromDays(30)); Assert.Equal(purgeAfterFirst, page.PurgeAfter); } [Fact] public void ReplaceTags_trims_deduplicates_case_insensitively_and_keeps_first_seen_casing() { var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Pagina"), DocumentType.WikiPage, "Inhoud", Guid.NewGuid(), Now); page.ReplaceTags([" Netwerk ", "netwerk", "Archief", "", " "]); Assert.Equal(["Netwerk", "Archief"], page.Tags); } [Fact] public void ReplaceTags_bounds_the_number_of_tags_and_the_length_of_each_tag() { var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Pagina"), DocumentType.WikiPage, "Inhoud", Guid.NewGuid(), Now); var tooMany = Enumerable.Range(0, Page.MaxTagCount + 10).Select(i => $"tag{i}"); page.ReplaceTags(tooMany); Assert.Equal(Page.MaxTagCount, page.Tags.Count); var tooLong = new string('a', Page.MaxTagLength + 20); page.ReplaceTags([tooLong]); Assert.Equal(Page.MaxTagLength, page.Tags[0].Length); } [Fact] public void ReplaceTags_does_not_advance_the_revision_number_or_change_UpdatedAt() { var page = Page.Create( Guid.NewGuid(), null, SortKey.First(), PageTitle.Create("Pagina"), DocumentType.WikiPage, "Inhoud", Guid.NewGuid(), Now); page.ReplaceTags(["Runbook"]); Assert.Equal(1, page.CurrentRevisionNumber); Assert.Equal(Now, page.UpdatedAt); } }