diff --git a/src/MyWebLog.Tests/DataTypesTests.fs b/src/MyWebLog.Tests/DataTypesTests.fs new file mode 100644 index 0000000..0a5f904 --- /dev/null +++ b/src/MyWebLog.Tests/DataTypesTests.fs @@ -0,0 +1,90 @@ +module DataTypesTests + +open Expecto +open MyWebLog + +/// Unit tests for the WebLog type +let webLogTests = + testList "WebLog" [ + testList "ExtraPath" [ + test "succeeds for blank URL base" { + Expect.equal WebLog.Empty.ExtraPath "" "Extra path should have been blank for blank URL base" + } + test "succeeds for domain root URL" { + Expect.equal + { WebLog.Empty with UrlBase = "https://example.com" }.ExtraPath + "" + "Extra path should have been blank for domain root" + } + test "succeeds for single subdirectory" { + Expect.equal + { WebLog.Empty with UrlBase = "https://a.com/sub" }.ExtraPath + "/sub" + "Extra path incorrect for a single subdirectory" + } + test "succeeds for deeper nesting" { + Expect.equal + { WebLog.Empty with UrlBase = "https://b.com/users/test/units" }.ExtraPath + "/users/test/units" + "Extra path incorrect for deeper nesting" + } + ] + test "AbsoluteUrl succeeds" { + Expect.equal + ({ WebLog.Empty with UrlBase = "https://my.site" }.AbsoluteUrl(Permalink "blog/page.html")) + "https://my.site/blog/page.html" + "Absolute URL is incorrect" + } + testList "RelativeUrl" [ + test "succeeds for domain root URL" { + Expect.equal + ({ WebLog.Empty with UrlBase = "https://test.me" }.RelativeUrl(Permalink "about.htm")) + "/about.htm" + "Relative URL is incorrect for domain root site" + } + test "succeeds for domain non-root URL" { + Expect.equal + ({ WebLog.Empty with UrlBase = "https://site.page/a/b/c" }.RelativeUrl(Permalink "x/y/z")) + "/a/b/c/x/y/z" + "Relative URL is incorrect for domain non-root site" + } + ] + testList "LocalTime" [ + test "succeeds when no time zone is set" { + Expect.equal + (WebLog.Empty.LocalTime(Noda.epoch)) + (Noda.epoch.ToDateTimeUtc()) + "Reference should be UTC when no time zone is specified" + } + test "succeeds when time zone is set" { + Expect.equal + ({ WebLog.Empty with TimeZone = "Etc/GMT-1" }.LocalTime(Noda.epoch)) + (Noda.epoch.ToDateTimeUtc().AddHours 1) + "The time should have been adjusted by one hour" + } + ] + ] + +/// Unit tests for the WebLogUser type +let webLogUserTests = + testList "WebLogUser" [ + testList "DisplayName" [ + test "succeeds when a preferred name is present" { + Expect.equal + { WebLogUser.Empty with + FirstName = "Thomas"; PreferredName = "Tom"; LastName = "Tester" }.DisplayName + "Tom Tester" + "Display name incorrect when preferred name is present" + } + test "succeeds when a preferred name is absent" { + Expect.equal + { WebLogUser.Empty with FirstName = "Test"; LastName = "Units" }.DisplayName + "Test Units" + "Display name incorrect when preferred name is absent" + } + ] + ] + +/// All tests for the Domain.DataTypes file +let all = + testList "DataTypes" [ webLogTests; webLogUserTests ] diff --git a/src/MyWebLog.Tests/Domain.fs b/src/MyWebLog.Tests/Domain.fs deleted file mode 100644 index 812fe19..0000000 --- a/src/MyWebLog.Tests/Domain.fs +++ /dev/null @@ -1,807 +0,0 @@ -module Domain - -open System -open Expecto -open MyWebLog -open NodaTime - -// --- SUPPORT TYPES --- - -/// Tests for the NodaTime-wrapping module -let nodaTests = - testList "Noda" [ - test "epoch succeeds" { - Expect.equal - (Noda.epoch.ToDateTimeUtc()) - (DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)) - "The Unix epoch value is not correct" - } - test "toSecondsPrecision succeeds" { - let testDate = Instant.FromDateTimeUtc(DateTime(1970, 1, 1, 0, 0, 0, 444, DateTimeKind.Utc)) - // testDate. - Expect.equal - ((Noda.toSecondsPrecision testDate).ToDateTimeUtc()) - (Noda.epoch.ToDateTimeUtc()) - "Instant value was not rounded to seconds precision" - } - test "fromDateTime succeeds" { - let testDate = DateTime(1970, 1, 1, 0, 0, 0, 444, DateTimeKind.Utc) - Expect.equal (Noda.fromDateTime testDate) Noda.epoch "fromDateTime did not truncate to seconds" - } - ] - -/// Tests for the AccessLevel type -let accessLevelTests = - testList "AccessLevel" [ - testList "Parse" [ - test "succeeds for \"Author\"" { - Expect.equal Author (AccessLevel.Parse "Author") "Author not parsed correctly" - } - test "succeeds for \"Editor\"" { - Expect.equal Editor (AccessLevel.Parse "Editor") "Editor not parsed correctly" - } - test "succeeds for \"WebLogAdmin\"" { - Expect.equal WebLogAdmin (AccessLevel.Parse "WebLogAdmin") "WebLogAdmin not parsed correctly" - } - test "succeeds for \"Administrator\"" { - Expect.equal Administrator (AccessLevel.Parse "Administrator") "Administrator not parsed correctly" - } - test "fails when given an unrecognized value" { - Expect.throwsT - (fun () -> ignore (AccessLevel.Parse "Hacker")) "Invalid value should have raised an exception" - } - ] - testList "ToString" [ - test "Author succeeds" { - Expect.equal (string Author) "Author" "Author string incorrect" - } - test "Editor succeeds" { - Expect.equal (string Editor) "Editor" "Editor string incorrect" - } - test "WebLogAdmin succeeds" { - Expect.equal (string WebLogAdmin) "WebLogAdmin" "WebLogAdmin string incorrect" - } - test "Administrator succeeds" { - Expect.equal (string Administrator) "Administrator" "Administrator string incorrect" - } - ] - testList "HasAccess" [ - test "Author has Author access" { - Expect.isTrue (Author.HasAccess Author) "Author should have Author access" - } - test "Author does not have Editor access" { - Expect.isFalse (Author.HasAccess Editor) "Author should not have Editor access" - } - test "Author does not have WebLogAdmin access" { - Expect.isFalse (Author.HasAccess WebLogAdmin) "Author should not have WebLogAdmin access" - } - test "Author does not have Administrator access" { - Expect.isFalse (Author.HasAccess Administrator) "Author should not have Administrator access" - } - test "Editor has Author access" { - Expect.isTrue (Editor.HasAccess Author) "Editor should have Author access" - } - test "Editor has Editor access" { - Expect.isTrue (Editor.HasAccess Editor) "Editor should have Editor access" - } - test "Editor does not have WebLogAdmin access" { - Expect.isFalse (Editor.HasAccess WebLogAdmin) "Editor should not have WebLogAdmin access" - } - test "Editor does not have Administrator access" { - Expect.isFalse (Editor.HasAccess Administrator) "Editor should not have Administrator access" - } - test "WebLogAdmin has Author access" { - Expect.isTrue (WebLogAdmin.HasAccess Author) "WebLogAdmin should have Author access" - } - test "WebLogAdmin has Editor access" { - Expect.isTrue (WebLogAdmin.HasAccess Editor) "WebLogAdmin should have Editor access" - } - test "WebLogAdmin has WebLogAdmin access" { - Expect.isTrue (WebLogAdmin.HasAccess WebLogAdmin) "WebLogAdmin should have WebLogAdmin access" - } - test "WebLogAdmin does not have Administrator access" { - Expect.isFalse (WebLogAdmin.HasAccess Administrator) "WebLogAdmin should not have Administrator access" - } - test "Administrator has Author access" { - Expect.isTrue (Administrator.HasAccess Author) "Administrator should have Author access" - } - test "Administrator has Editor access" { - Expect.isTrue (Administrator.HasAccess Editor) "Administrator should have Editor access" - } - test "Administrator has WebLogAdmin access" { - Expect.isTrue (Administrator.HasAccess WebLogAdmin) "Administrator should have WebLogAdmin access" - } - test "Administrator has Administrator access" { - Expect.isTrue (Administrator.HasAccess Administrator) "Administrator should have Administrator access" - } - ] - ] - -/// Tests for the CommentStatus type -let commentStatusTests = - testList "CommentStatus" [ - testList "Parse" [ - test "succeeds for \"Approved\"" { - Expect.equal Approved (CommentStatus.Parse "Approved") "Approved not parsed correctly" - } - test "succeeds for \"Pending\"" { - Expect.equal Pending (CommentStatus.Parse "Pending") "Pending not parsed correctly" - } - test "succeeds for \"Spam\"" { - Expect.equal Spam (CommentStatus.Parse "Spam") "Spam not parsed correctly" - } - test "fails for unrecognized value" { - Expect.throwsT - (fun () -> ignore (CommentStatus.Parse "Live")) "Invalid value should have raised an exception" - } - ] - testList "ToString" [ - test "Approved succeeds" { - Expect.equal (string Approved) "Approved" "Approved string incorrect" - } - test "Pending succeeds" { - Expect.equal (string Pending) "Pending" "Pending string incorrect" - } - test "Spam succeeds" { - Expect.equal (string Spam) "Spam" "Spam string incorrect" - } - ] - ] - -let explicitRatingTests = - testList "ExplicitRating" [ - testList "Parse" [ - test "succeeds for \"yes\"" { - Expect.equal Yes (ExplicitRating.Parse "yes") "\"yes\" not parsed correctly" - } - test "succeeds for \"no\"" { - Expect.equal No (ExplicitRating.Parse "no") "\"no\" not parsed correctly" - } - test "succeeds for \"clean\"" { - Expect.equal Clean (ExplicitRating.Parse "clean") "\"clean\" not parsed correctly" - } - test "fails for unrecognized value" { - Expect.throwsT - (fun () -> ignore (ExplicitRating.Parse "maybe")) "Invalid value should have raised an exception" - } - ] - testList "ToString" [ - test "Yes succeeds" { - Expect.equal (string Yes) "yes" "Yes string incorrect" - } - test "No succeeds" { - Expect.equal (string No) "no" "No string incorrect" - } - test "Clean succeeds" { - Expect.equal (string Clean) "clean" "Clean string incorrect" - } - ] - ] - -/// Tests for the Episode type -let episodeTests = - testList "Episode" [ - testList "FormatDuration" [ - test "succeeds when no duration is specified" { - Expect.isNone (Episode.Empty.FormatDuration()) "A missing duration should have returned None" - } - test "succeeds when duration is specified" { - Expect.equal - ({ Episode.Empty with - Duration = Some (Duration.FromMinutes 3L + Duration.FromSeconds 13L) }.FormatDuration()) - (Some "0:03:13") - "Duration not formatted correctly" - } - test "succeeds when duration is > 10 hours" { - Expect.equal - ({ Episode.Empty with Duration = Some (Duration.FromHours 11) }.FormatDuration()) - (Some "11:00:00") - "Duration not formatted correctly" - } - ] - ] - -/// Unit tests for the MarkupText type -let markupTextTests = - testList "MarkupText" [ - testList "Parse" [ - test "succeeds with HTML content" { - let txt = MarkupText.Parse "HTML:

howdy

" - match txt with - | Html it when it = "

howdy

" -> () - | _ -> Expect.isTrue false $"Unexpected parse result for HTML: %A{txt}" - } - test "succeeds with Markdown content" { - let txt = MarkupText.Parse "Markdown: # A Title" - match txt with - | Markdown it when it = "# A Title" -> () - | _ -> Expect.isTrue false $"Unexpected parse result for Markdown: %A{txt}" - } - test "fails with unexpected content" { - Expect.throwsT - (fun () -> ignore (MarkupText.Parse "LaTEX: nope")) "Invalid value should have raised an exception" - } - ] - testList "SourceType" [ - test "succeeds for HTML" { - Expect.equal (MarkupText.Parse "HTML: something").SourceType "HTML" "HTML source type incorrect" - } - test "succeeds for Markdown" { - Expect.equal (MarkupText.Parse "Markdown: blah").SourceType "Markdown" "Markdown source type incorrect" - } - ] - testList "Text" [ - test "succeeds for HTML" { - Expect.equal (MarkupText.Parse "HTML: test").Text "test" "HTML text incorrect" - } - test "succeeds for Markdown" { - Expect.equal (MarkupText.Parse "Markdown: test!").Text "test!" "Markdown text incorrect" - } - ] - testList "ToString" [ - test "succeeds for HTML" { - Expect.equal - (string (MarkupText.Parse "HTML:

HTML

")) - "HTML:

HTML

" - "HTML string value incorrect" - } - test "succeeds for Markdown" { - Expect.equal - (string (MarkupText.Parse "Markdown: # Some Content")) - "Markdown: # Some Content" - "Markdown string value incorrect" - } - ] - testList "AsHtml" [ - test "succeeds for HTML" { - Expect.equal - ((MarkupText.Parse "HTML:

The Heading

").AsHtml()) - "

The Heading

" - "HTML value incorrect" - } - test "succeeds for Markdown" { - Expect.equal - ((MarkupText.Parse "Markdown: *emphasis*").AsHtml()) - "

emphasis

\n" - "Markdown HTML value incorrect" - } - ] - ] - -/// Unit tests for the PodcastMedium type -let podcastMediumTests = - testList "PodcastMedium" [ - testList "Parse" [ - test "succeeds for \"podcast\"" { - Expect.equal (PodcastMedium.Parse "podcast") Podcast "\"podcast\" not parsed correctly" - } - test "succeeds for \"music\"" { - Expect.equal (PodcastMedium.Parse "music") Music "\"music\" not parsed correctly" - } - test "succeeds for \"video\"" { - Expect.equal (PodcastMedium.Parse "video") Video "\"video\" not parsed correctly" - } - test "succeeds for \"film\"" { - Expect.equal (PodcastMedium.Parse "film") Film "\"film\" not parsed correctly" - } - test "succeeds for \"audiobook\"" { - Expect.equal (PodcastMedium.Parse "audiobook") Audiobook "\"audiobook\" not parsed correctly" - } - test "succeeds for \"newsletter\"" { - Expect.equal (PodcastMedium.Parse "newsletter") Newsletter "\"newsletter\" not parsed correctly" - } - test "succeeds for \"blog\"" { - Expect.equal (PodcastMedium.Parse "blog") Blog "\"blog\" not parsed correctly" - } - test "fails for invalid type" { - Expect.throwsT - (fun () -> ignore (PodcastMedium.Parse "laser")) "Invalid value should have raised an exception" - } - ] - testList "ToString" [ - test "succeeds for Podcast" { - Expect.equal (string Podcast) "podcast" "Podcast string incorrect" - } - test "succeeds for Music" { - Expect.equal (string Music) "music" "Music string incorrect" - } - test "succeeds for Video" { - Expect.equal (string Video) "video" "Video string incorrect" - } - test "succeeds for Film" { - Expect.equal (string Film) "film" "Film string incorrect" - } - test "succeeds for Audiobook" { - Expect.equal (string Audiobook) "audiobook" "Audiobook string incorrect" - } - test "succeeds for Newsletter" { - Expect.equal (string Newsletter) "newsletter" "Newsletter string incorrect" - } - test "succeeds for Blog" { - Expect.equal (string Blog) "blog" "Blog string incorrect" - } - ] - ] - -/// Unit tests for the PostStatus type -let postStatusTests = - testList "PostStatus" [ - testList "Parse" [ - test "succeeds for \"Draft\"" { - Expect.equal (PostStatus.Parse "Draft") Draft "\"Draft\" not parsed correctly" - } - test "succeeds for \"Published\"" { - Expect.equal (PostStatus.Parse "Published") Published "\"Published\" not parsed correctly" - } - test "fails for unrecognized value" { - Expect.throwsT - (fun () -> ignore (PostStatus.Parse "Rescinded")) "Invalid value should have raised an exception" - } - ] - ] - -/// Unit tests for the CustomFeedSource type -let customFeedSourceTests = - testList "CustomFeedSource" [ - testList "Parse" [ - test "succeeds for category feeds" { - Expect.equal - (CustomFeedSource.Parse "category:abc123") - (Category (CategoryId "abc123")) - "Category feed not parsed correctly" - } - test "succeeds for tag feeds" { - Expect.equal (CustomFeedSource.Parse "tag:turtles") (Tag "turtles") "Tag feed not parsed correctly" - } - test "fails for unknown type" { - Expect.throwsT - (fun () -> ignore (CustomFeedSource.Parse "nasa:sat1")) - "Invalid value should have raised an exception" - } - ] - testList "ToString" [ - test "succeeds for category feed" { - Expect.equal - (string (CustomFeedSource.Parse "category:fish")) "category:fish" "Category feed string incorrect" - } - test "succeeds for tag feed" { - Expect.equal (string (CustomFeedSource.Parse "tag:rocks")) "tag:rocks" "Tag feed string incorrect" - } - ] - ] - -/// Unit tests for the ThemeAssetId type -let themeAssetIdTests = - testList "ThemeAssetId" [ - testList "Parse" [ - test "succeeds with expected values" { - Expect.equal - (ThemeAssetId.Parse "test-theme/the-asset") - (ThemeAssetId ((ThemeId "test-theme"), "the-asset")) - "Theme asset ID not parsed correctly" - } - test "fails if no slash is present" { - Expect.throwsT - (fun () -> ignore (ThemeAssetId.Parse "my-theme-asset")) - "Invalid value should have raised an exception" - } - ] - test "ToString succeeds" { - Expect.equal - (string (ThemeAssetId ((ThemeId "howdy"), "pardner"))) "howdy/pardner" "Theme asset ID string incorrect" - } - ] - -/// Unit tests for the UploadDestination type -let uploadDestinationTests = - testList "UploadDestination" [ - testList "Parse" [ - test "succeeds for \"Database\"" { - Expect.equal (UploadDestination.Parse "Database") Database "\"Database\" not parsed correctly" - } - test "succeeds for \"Disk\"" { - Expect.equal (UploadDestination.Parse "Disk") Disk "\"Disk\" not parsed correctly" - } - test "fails for unrecognized value" { - Expect.throwsT - (fun () -> ignore (UploadDestination.Parse "Azure")) "Invalid value should have raised an exception" - } - ] - testList "ToString" [ - test "succeeds for Database" { - Expect.equal (string Database) "Database" "Database string incorrect" - } - test "succeeds for Disk" { - Expect.equal (string Disk) "Disk" "Disk string incorrect" - } - ] - ] - -// --- DATA TYPES --- - -/// Unit tests for the WebLog type -let webLogTests = - testList "WebLog" [ - testList "ExtraPath" [ - test "succeeds for blank URL base" { - Expect.equal WebLog.Empty.ExtraPath "" "Extra path should have been blank for blank URL base" - } - test "succeeds for domain root URL" { - Expect.equal - { WebLog.Empty with UrlBase = "https://example.com" }.ExtraPath - "" - "Extra path should have been blank for domain root" - } - test "succeeds for single subdirectory" { - Expect.equal - { WebLog.Empty with UrlBase = "https://a.com/sub" }.ExtraPath - "/sub" - "Extra path incorrect for a single subdirectory" - } - test "succeeds for deeper nesting" { - Expect.equal - { WebLog.Empty with UrlBase = "https://b.com/users/test/units" }.ExtraPath - "/users/test/units" - "Extra path incorrect for deeper nesting" - } - ] - test "AbsoluteUrl succeeds" { - Expect.equal - ({ WebLog.Empty with UrlBase = "https://my.site" }.AbsoluteUrl(Permalink "blog/page.html")) - "https://my.site/blog/page.html" - "Absolute URL is incorrect" - } - testList "RelativeUrl" [ - test "succeeds for domain root URL" { - Expect.equal - ({ WebLog.Empty with UrlBase = "https://test.me" }.RelativeUrl(Permalink "about.htm")) - "/about.htm" - "Relative URL is incorrect for domain root site" - } - test "succeeds for domain non-root URL" { - Expect.equal - ({ WebLog.Empty with UrlBase = "https://site.page/a/b/c" }.RelativeUrl(Permalink "x/y/z")) - "/a/b/c/x/y/z" - "Relative URL is incorrect for domain non-root site" - } - ] - testList "LocalTime" [ - test "succeeds when no time zone is set" { - Expect.equal - (WebLog.Empty.LocalTime(Noda.epoch)) - (Noda.epoch.ToDateTimeUtc()) - "Reference should be UTC when no time zone is specified" - } - test "succeeds when time zone is set" { - Expect.equal - ({ WebLog.Empty with TimeZone = "Etc/GMT-1" }.LocalTime(Noda.epoch)) - (Noda.epoch.ToDateTimeUtc().AddHours 1) - "The time should have been adjusted by one hour" - } - ] - ] - -/// Unit tests for the WebLogUser type -let webLogUserTests = - testList "WebLogUser" [ - testList "DisplayName" [ - test "succeeds when a preferred name is present" { - Expect.equal - { WebLogUser.Empty with - FirstName = "Thomas"; PreferredName = "Tom"; LastName = "Tester" }.DisplayName - "Tom Tester" - "Display name incorrect when preferred name is present" - } - test "succeeds when a preferred name is absent" { - Expect.equal - { WebLogUser.Empty with FirstName = "Test"; LastName = "Units" }.DisplayName - "Test Units" - "Display name incorrect when preferred name is absent" - } - ] - ] - -// --- VIEW MODELS --- - -open MyWebLog.ViewModels - -/// Unit tests for the addBaseToRelativeUrls helper function -let addBaseToRelativeUrlsTests = - testList "PublicHelpers.addBaseToRelativeUrls" [ - test "succeeds when there is no extra URL path" { - let testText = """Howdy>""" - let modified = addBaseToRelativeUrls "" testText - Expect.equal modified testText "The text should not have been modified" - } - test "succeeds with an extra URL path" { - let testText = - """Click Me!""" - Metadata = [ { Name = "unit"; Value = "test" } ] } - testList "FromPageMinimal" [ - test "succeeds when page is default page" { - let webLog = { WebLog.Empty with TimeZone = "Etc/GMT-1"; DefaultPage = "my-page" } - let model = DisplayPage.FromPageMinimal webLog page - Expect.equal model.Id "my-page" "Id not filled properly" - Expect.equal model.AuthorId "jim" "AuthorId not filled properly" - Expect.equal model.Title "A Fine Example" "Title not filled properly" - Expect.equal model.Permalink "about/a-fine-example.html" "Permalink not filled properly" - Expect.equal - model.PublishedOn - ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) - "PublishedOn not filled properly" - Expect.equal - model.UpdatedOn - ((Noda.epoch + Duration.FromHours 2).ToDateTimeUtc()) - "UpdatedOn not filled properly" - Expect.isFalse model.IsInPageList "IsInPageList should not have been set" - Expect.isTrue model.IsDefault "IsDefault should have been set" - Expect.equal model.Text "" "Text should have been blank" - Expect.isEmpty model.Metadata "Metadata should have been empty" - } - test "succeeds when page is not the default page" { - let model = DisplayPage.FromPageMinimal { WebLog.Empty with DefaultPage = "posts" } page - Expect.isFalse model.IsDefault "IsDefault should not have been set" - } - ] - testList "FromPage" [ - test "succeeds when the web log is on the domain root" { - let webLog = { WebLog.Empty with TimeZone = "Etc/GMT-4"; UrlBase = "https://example.com" } - let model = DisplayPage.FromPage webLog page - Expect.equal model.Id "my-page" "Id not filled properly" - Expect.equal model.AuthorId "jim" "AuthorId not filled properly" - Expect.equal model.Title "A Fine Example" "Title not filled properly" - Expect.equal model.Permalink "about/a-fine-example.html" "Permalink not filled properly" - Expect.equal - model.PublishedOn - ((Noda.epoch + Duration.FromHours 4).ToDateTimeUtc()) - "PublishedOn not filled properly" - Expect.equal - model.UpdatedOn - ((Noda.epoch + Duration.FromHours 5).ToDateTimeUtc()) - "UpdatedOn not filled properly" - Expect.isFalse model.IsInPageList "IsInPageList should not have been set" - Expect.isFalse model.IsDefault "IsDefault should not have been set" - Expect.equal model.Text """Click Me!""" "Text not filled properly" - Expect.equal model.Metadata.Length 1 "Metadata not filled properly" - } - test "succeeds when the web log is not on the domain root" { - let model = DisplayPage.FromPage { WebLog.Empty with UrlBase = "https://example.com/a/b/c" } page - Expect.equal model.Text """Click Me!""" "Text not filled properly" - } - ] - ] - -/// Unit tests for the DisplayRevision type -let displayRevisionTests = - test "DisplayRevision.FromRevision succeeds" { - let model = - DisplayRevision.FromRevision - { WebLog.Empty with TimeZone = "Etc/GMT+1" } - { Text = Html "howdy"; AsOf = Noda.epoch } - Expect.equal model.AsOf (Noda.epoch.ToDateTimeUtc()) "AsOf not filled properly" - Expect.equal - model.AsOfLocal ((Noda.epoch - Duration.FromHours 1).ToDateTimeUtc()) "AsOfLocal not filled properly" - Expect.equal model.Format "HTML" "Format not filled properly" - } - -open System.IO - -/// Unit tests for the DisplayTheme type -let displayThemeTests = - testList "DisplayTheme.FromTheme" [ - let theme = - { Id = ThemeId "the-theme" - Name = "Test Theme" - Version = "v0.1.2" - Templates = [ ThemeTemplate.Empty; ThemeTemplate.Empty ] } - test "succeeds when theme is in use and not on disk" { - let model = - DisplayTheme.FromTheme - (fun it -> Expect.equal it (ThemeId "the-theme") "The theme ID not passed correctly"; true) theme - Expect.equal model.Id "the-theme" "Id not filled properly" - Expect.equal model.Name "Test Theme" "Name not filled properly" - Expect.equal model.Version "v0.1.2" "Version not filled properly" - Expect.equal model.TemplateCount 2 "TemplateCount not filled properly" - Expect.isTrue model.IsInUse "IsInUse should have been set" - Expect.isFalse model.IsOnDisk "IsOnDisk should not have been set" - } - test "succeeds when the theme is not in use as is on disk" { - let file = File.Create "another-theme.zip" - try - let model = DisplayTheme.FromTheme (fun _ -> false) { theme with Id = ThemeId "another" } - Expect.isFalse model.IsInUse "IsInUse should not have been set" - Expect.isTrue model.IsOnDisk "IsOnDisk should have been set" - finally - file.Close() - file.Dispose() - File.Delete "another-theme.zip" - } - ] - -/// Unit tests for the DisplayUpload type -let displayUploadTests = - test "DisplayUpload.FromUpload succeeds" { - let upload = - { Upload.Empty with - Id = UploadId "test-up" - Path = Permalink "2022/04/my-pic.jpg" - UpdatedOn = Noda.epoch } - let model = DisplayUpload.FromUpload { WebLog.Empty with TimeZone = "Etc/GMT-1" } Database upload - Expect.equal model.Id "test-up" "Id not filled properly" - Expect.equal model.Name "my-pic.jpg" "Name not filled properly" - Expect.equal model.Path "2022/04/" "Path not filled properly" - Expect.equal model.Source "Database" "Source not filled properly" - Expect.isSome model.UpdatedOn "There should have been an UpdatedOn value" - Expect.equal - model.UpdatedOn.Value ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "UpdatedOn not filled properly" - } - -/// Unit tests for the DisplayUser type -let displayUserTests = - testList "DisplayUser.FromUser" [ - let minimalUser = - { WebLogUser.Empty with - Id = WebLogUserId "test-user" - Email = "jim.james@example.com" - FirstName = "Jim" - LastName = "James" - PreferredName = "John" - AccessLevel = Editor - CreatedOn = Noda.epoch } - test "succeeds when the user has minimal information" { - let model = DisplayUser.FromUser WebLog.Empty minimalUser - Expect.equal model.Id "test-user" "Id not filled properly" - Expect.equal model.Email "jim.james@example.com" "Email not filled properly" - Expect.equal model.FirstName "Jim" "FirstName not filled properly" - Expect.equal model.LastName "James" "LastName not filled properly" - Expect.equal model.PreferredName "John" "PreferredName not filled properly" - Expect.equal model.Url "" "Url not filled properly" - Expect.equal model.AccessLevel "Editor" "AccessLevel not filled properly" - Expect.equal model.CreatedOn (Noda.epoch.ToDateTimeUtc()) "CreatedOn not filled properly" - Expect.isFalse model.LastSeenOn.HasValue "LastSeenOn should have been null" - } - test "succeeds when the user has all information" { - let model = - DisplayUser.FromUser - { WebLog.Empty with TimeZone = "Etc/GMT-1" } - { minimalUser with - Url = Some "https://my.site" - LastSeenOn = Some (Noda.epoch + Duration.FromDays 4) } - Expect.equal model.Url "https://my.site" "Url not filled properly" - Expect.equal - model.CreatedOn ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "CreatedOn not filled properly" - Expect.isTrue model.LastSeenOn.HasValue "LastSeenOn should not have been null" - Expect.equal - model.LastSeenOn.Value - ((Noda.epoch + Duration.FromDays 4 + Duration.FromHours 1).ToDateTimeUtc()) - "LastSeenOn not filled properly" - } - ] - -/// Unit tests for the EditCategoryModel type -let editCategoryModelTests = - testList "EditCategoryModel" [ - testList "FromCategory" [ - let minimalCat = { Category.Empty with Id = CategoryId "test-cat"; Name = "test"; Slug = "test-slug" } - test "succeeds with minimal information" { - let model = EditCategoryModel.FromCategory minimalCat - Expect.equal model.CategoryId "test-cat" "CategoryId not filled properly" - Expect.equal model.Name "test" "Name not filled properly" - Expect.equal model.Slug "test-slug" "Slug not filled properly" - Expect.equal model.Description "" "Description not filled properly" - Expect.equal model.ParentId "" "ParentId not filled properly" - } - test "succeeds with complete information" { - let model = - EditCategoryModel.FromCategory - { minimalCat with Description = Some "Testing"; ParentId = Some (CategoryId "parent") } - Expect.equal model.Description "Testing" "Description not filled properly" - Expect.equal model.ParentId "parent" "ParentId not filled properly" - } - ] - testList "IsNew" [ - test "succeeds for a new category" { - let model = EditCategoryModel.FromCategory { Category.Empty with Id = CategoryId "new" } - Expect.isTrue model.IsNew "Category should have been considered new" - } - test "succeeds for a non-new category" { - let model = EditCategoryModel.FromCategory Category.Empty - Expect.isFalse model.IsNew "Category should not have been considered new" - } - ] - ] - -/// All tests for the Domain namespace -let all = - testList - "Domain" - [ // support types - nodaTests - accessLevelTests - commentStatusTests - explicitRatingTests - episodeTests - markupTextTests - podcastMediumTests - postStatusTests - customFeedSourceTests - themeAssetIdTests - uploadDestinationTests - // data types - webLogTests - webLogUserTests - // view models - addBaseToRelativeUrlsTests - displayCustomFeedTests - displayPageTests - displayRevisionTests - displayThemeTests - displayUploadTests - displayUserTests - editCategoryModelTests ] diff --git a/src/MyWebLog.Tests/MyWebLog.Tests.fsproj b/src/MyWebLog.Tests/MyWebLog.Tests.fsproj index 1cae610..d415d68 100644 --- a/src/MyWebLog.Tests/MyWebLog.Tests.fsproj +++ b/src/MyWebLog.Tests/MyWebLog.Tests.fsproj @@ -5,7 +5,9 @@ - + + + diff --git a/src/MyWebLog.Tests/Program.fs b/src/MyWebLog.Tests/Program.fs index 5a5a4be..e7d56ba 100644 --- a/src/MyWebLog.Tests/Program.fs +++ b/src/MyWebLog.Tests/Program.fs @@ -3,7 +3,7 @@ let allTests = testList "MyWebLog" - [ Domain.all ] + [ testList "Domain" [ SupportTypesTests.all; DataTypesTests.all; ViewModelsTests.all ] ] [] let main args = runTestsWithCLIArgs [] args allTests diff --git a/src/MyWebLog.Tests/SupportTypesTests.fs b/src/MyWebLog.Tests/SupportTypesTests.fs new file mode 100644 index 0000000..09c1fc4 --- /dev/null +++ b/src/MyWebLog.Tests/SupportTypesTests.fs @@ -0,0 +1,432 @@ +module SupportTypesTests + +open System +open Expecto +open MyWebLog +open NodaTime + +/// Tests for the NodaTime-wrapping module +let nodaTests = + testList "Noda" [ + test "epoch succeeds" { + Expect.equal + (Noda.epoch.ToDateTimeUtc()) + (DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc)) + "The Unix epoch value is not correct" + } + test "toSecondsPrecision succeeds" { + let testDate = Instant.FromDateTimeUtc(DateTime(1970, 1, 1, 0, 0, 0, 444, DateTimeKind.Utc)) + // testDate. + Expect.equal + ((Noda.toSecondsPrecision testDate).ToDateTimeUtc()) + (Noda.epoch.ToDateTimeUtc()) + "Instant value was not rounded to seconds precision" + } + test "fromDateTime succeeds" { + let testDate = DateTime(1970, 1, 1, 0, 0, 0, 444, DateTimeKind.Utc) + Expect.equal (Noda.fromDateTime testDate) Noda.epoch "fromDateTime did not truncate to seconds" + } + ] + +/// Tests for the AccessLevel type +let accessLevelTests = + testList "AccessLevel" [ + testList "Parse" [ + test "succeeds for \"Author\"" { + Expect.equal Author (AccessLevel.Parse "Author") "Author not parsed correctly" + } + test "succeeds for \"Editor\"" { + Expect.equal Editor (AccessLevel.Parse "Editor") "Editor not parsed correctly" + } + test "succeeds for \"WebLogAdmin\"" { + Expect.equal WebLogAdmin (AccessLevel.Parse "WebLogAdmin") "WebLogAdmin not parsed correctly" + } + test "succeeds for \"Administrator\"" { + Expect.equal Administrator (AccessLevel.Parse "Administrator") "Administrator not parsed correctly" + } + test "fails when given an unrecognized value" { + Expect.throwsT + (fun () -> ignore (AccessLevel.Parse "Hacker")) "Invalid value should have raised an exception" + } + ] + testList "ToString" [ + test "Author succeeds" { + Expect.equal (string Author) "Author" "Author string incorrect" + } + test "Editor succeeds" { + Expect.equal (string Editor) "Editor" "Editor string incorrect" + } + test "WebLogAdmin succeeds" { + Expect.equal (string WebLogAdmin) "WebLogAdmin" "WebLogAdmin string incorrect" + } + test "Administrator succeeds" { + Expect.equal (string Administrator) "Administrator" "Administrator string incorrect" + } + ] + testList "HasAccess" [ + test "Author has Author access" { + Expect.isTrue (Author.HasAccess Author) "Author should have Author access" + } + test "Author does not have Editor access" { + Expect.isFalse (Author.HasAccess Editor) "Author should not have Editor access" + } + test "Author does not have WebLogAdmin access" { + Expect.isFalse (Author.HasAccess WebLogAdmin) "Author should not have WebLogAdmin access" + } + test "Author does not have Administrator access" { + Expect.isFalse (Author.HasAccess Administrator) "Author should not have Administrator access" + } + test "Editor has Author access" { + Expect.isTrue (Editor.HasAccess Author) "Editor should have Author access" + } + test "Editor has Editor access" { + Expect.isTrue (Editor.HasAccess Editor) "Editor should have Editor access" + } + test "Editor does not have WebLogAdmin access" { + Expect.isFalse (Editor.HasAccess WebLogAdmin) "Editor should not have WebLogAdmin access" + } + test "Editor does not have Administrator access" { + Expect.isFalse (Editor.HasAccess Administrator) "Editor should not have Administrator access" + } + test "WebLogAdmin has Author access" { + Expect.isTrue (WebLogAdmin.HasAccess Author) "WebLogAdmin should have Author access" + } + test "WebLogAdmin has Editor access" { + Expect.isTrue (WebLogAdmin.HasAccess Editor) "WebLogAdmin should have Editor access" + } + test "WebLogAdmin has WebLogAdmin access" { + Expect.isTrue (WebLogAdmin.HasAccess WebLogAdmin) "WebLogAdmin should have WebLogAdmin access" + } + test "WebLogAdmin does not have Administrator access" { + Expect.isFalse (WebLogAdmin.HasAccess Administrator) "WebLogAdmin should not have Administrator access" + } + test "Administrator has Author access" { + Expect.isTrue (Administrator.HasAccess Author) "Administrator should have Author access" + } + test "Administrator has Editor access" { + Expect.isTrue (Administrator.HasAccess Editor) "Administrator should have Editor access" + } + test "Administrator has WebLogAdmin access" { + Expect.isTrue (Administrator.HasAccess WebLogAdmin) "Administrator should have WebLogAdmin access" + } + test "Administrator has Administrator access" { + Expect.isTrue (Administrator.HasAccess Administrator) "Administrator should have Administrator access" + } + ] + ] + +/// Tests for the CommentStatus type +let commentStatusTests = + testList "CommentStatus" [ + testList "Parse" [ + test "succeeds for \"Approved\"" { + Expect.equal Approved (CommentStatus.Parse "Approved") "Approved not parsed correctly" + } + test "succeeds for \"Pending\"" { + Expect.equal Pending (CommentStatus.Parse "Pending") "Pending not parsed correctly" + } + test "succeeds for \"Spam\"" { + Expect.equal Spam (CommentStatus.Parse "Spam") "Spam not parsed correctly" + } + test "fails for unrecognized value" { + Expect.throwsT + (fun () -> ignore (CommentStatus.Parse "Live")) "Invalid value should have raised an exception" + } + ] + testList "ToString" [ + test "Approved succeeds" { + Expect.equal (string Approved) "Approved" "Approved string incorrect" + } + test "Pending succeeds" { + Expect.equal (string Pending) "Pending" "Pending string incorrect" + } + test "Spam succeeds" { + Expect.equal (string Spam) "Spam" "Spam string incorrect" + } + ] + ] + +let explicitRatingTests = + testList "ExplicitRating" [ + testList "Parse" [ + test "succeeds for \"yes\"" { + Expect.equal Yes (ExplicitRating.Parse "yes") "\"yes\" not parsed correctly" + } + test "succeeds for \"no\"" { + Expect.equal No (ExplicitRating.Parse "no") "\"no\" not parsed correctly" + } + test "succeeds for \"clean\"" { + Expect.equal Clean (ExplicitRating.Parse "clean") "\"clean\" not parsed correctly" + } + test "fails for unrecognized value" { + Expect.throwsT + (fun () -> ignore (ExplicitRating.Parse "maybe")) "Invalid value should have raised an exception" + } + ] + testList "ToString" [ + test "Yes succeeds" { + Expect.equal (string Yes) "yes" "Yes string incorrect" + } + test "No succeeds" { + Expect.equal (string No) "no" "No string incorrect" + } + test "Clean succeeds" { + Expect.equal (string Clean) "clean" "Clean string incorrect" + } + ] + ] + +/// Tests for the Episode type +let episodeTests = + testList "Episode" [ + testList "FormatDuration" [ + test "succeeds when no duration is specified" { + Expect.isNone (Episode.Empty.FormatDuration()) "A missing duration should have returned None" + } + test "succeeds when duration is specified" { + Expect.equal + ({ Episode.Empty with + Duration = Some (Duration.FromMinutes 3L + Duration.FromSeconds 13L) }.FormatDuration()) + (Some "0:03:13") + "Duration not formatted correctly" + } + test "succeeds when duration is > 10 hours" { + Expect.equal + ({ Episode.Empty with Duration = Some (Duration.FromHours 11) }.FormatDuration()) + (Some "11:00:00") + "Duration not formatted correctly" + } + ] + ] + +/// Unit tests for the MarkupText type +let markupTextTests = + testList "MarkupText" [ + testList "Parse" [ + test "succeeds with HTML content" { + let txt = MarkupText.Parse "HTML:

howdy

" + match txt with + | Html it when it = "

howdy

" -> () + | _ -> Expect.isTrue false $"Unexpected parse result for HTML: %A{txt}" + } + test "succeeds with Markdown content" { + let txt = MarkupText.Parse "Markdown: # A Title" + match txt with + | Markdown it when it = "# A Title" -> () + | _ -> Expect.isTrue false $"Unexpected parse result for Markdown: %A{txt}" + } + test "fails with unexpected content" { + Expect.throwsT + (fun () -> ignore (MarkupText.Parse "LaTEX: nope")) "Invalid value should have raised an exception" + } + ] + testList "SourceType" [ + test "succeeds for HTML" { + Expect.equal (MarkupText.Parse "HTML: something").SourceType "HTML" "HTML source type incorrect" + } + test "succeeds for Markdown" { + Expect.equal (MarkupText.Parse "Markdown: blah").SourceType "Markdown" "Markdown source type incorrect" + } + ] + testList "Text" [ + test "succeeds for HTML" { + Expect.equal (MarkupText.Parse "HTML: test").Text "test" "HTML text incorrect" + } + test "succeeds for Markdown" { + Expect.equal (MarkupText.Parse "Markdown: test!").Text "test!" "Markdown text incorrect" + } + ] + testList "ToString" [ + test "succeeds for HTML" { + Expect.equal + (string (MarkupText.Parse "HTML:

HTML

")) + "HTML:

HTML

" + "HTML string value incorrect" + } + test "succeeds for Markdown" { + Expect.equal + (string (MarkupText.Parse "Markdown: # Some Content")) + "Markdown: # Some Content" + "Markdown string value incorrect" + } + ] + testList "AsHtml" [ + test "succeeds for HTML" { + Expect.equal + ((MarkupText.Parse "HTML:

The Heading

").AsHtml()) + "

The Heading

" + "HTML value incorrect" + } + test "succeeds for Markdown" { + Expect.equal + ((MarkupText.Parse "Markdown: *emphasis*").AsHtml()) + "

emphasis

\n" + "Markdown HTML value incorrect" + } + ] + ] + +/// Unit tests for the PodcastMedium type +let podcastMediumTests = + testList "PodcastMedium" [ + testList "Parse" [ + test "succeeds for \"podcast\"" { + Expect.equal (PodcastMedium.Parse "podcast") Podcast "\"podcast\" not parsed correctly" + } + test "succeeds for \"music\"" { + Expect.equal (PodcastMedium.Parse "music") Music "\"music\" not parsed correctly" + } + test "succeeds for \"video\"" { + Expect.equal (PodcastMedium.Parse "video") Video "\"video\" not parsed correctly" + } + test "succeeds for \"film\"" { + Expect.equal (PodcastMedium.Parse "film") Film "\"film\" not parsed correctly" + } + test "succeeds for \"audiobook\"" { + Expect.equal (PodcastMedium.Parse "audiobook") Audiobook "\"audiobook\" not parsed correctly" + } + test "succeeds for \"newsletter\"" { + Expect.equal (PodcastMedium.Parse "newsletter") Newsletter "\"newsletter\" not parsed correctly" + } + test "succeeds for \"blog\"" { + Expect.equal (PodcastMedium.Parse "blog") Blog "\"blog\" not parsed correctly" + } + test "fails for invalid type" { + Expect.throwsT + (fun () -> ignore (PodcastMedium.Parse "laser")) "Invalid value should have raised an exception" + } + ] + testList "ToString" [ + test "succeeds for Podcast" { + Expect.equal (string Podcast) "podcast" "Podcast string incorrect" + } + test "succeeds for Music" { + Expect.equal (string Music) "music" "Music string incorrect" + } + test "succeeds for Video" { + Expect.equal (string Video) "video" "Video string incorrect" + } + test "succeeds for Film" { + Expect.equal (string Film) "film" "Film string incorrect" + } + test "succeeds for Audiobook" { + Expect.equal (string Audiobook) "audiobook" "Audiobook string incorrect" + } + test "succeeds for Newsletter" { + Expect.equal (string Newsletter) "newsletter" "Newsletter string incorrect" + } + test "succeeds for Blog" { + Expect.equal (string Blog) "blog" "Blog string incorrect" + } + ] + ] + +/// Unit tests for the PostStatus type +let postStatusTests = + testList "PostStatus" [ + testList "Parse" [ + test "succeeds for \"Draft\"" { + Expect.equal (PostStatus.Parse "Draft") Draft "\"Draft\" not parsed correctly" + } + test "succeeds for \"Published\"" { + Expect.equal (PostStatus.Parse "Published") Published "\"Published\" not parsed correctly" + } + test "fails for unrecognized value" { + Expect.throwsT + (fun () -> ignore (PostStatus.Parse "Rescinded")) "Invalid value should have raised an exception" + } + ] + ] + +/// Unit tests for the CustomFeedSource type +let customFeedSourceTests = + testList "CustomFeedSource" [ + testList "Parse" [ + test "succeeds for category feeds" { + Expect.equal + (CustomFeedSource.Parse "category:abc123") + (Category (CategoryId "abc123")) + "Category feed not parsed correctly" + } + test "succeeds for tag feeds" { + Expect.equal (CustomFeedSource.Parse "tag:turtles") (Tag "turtles") "Tag feed not parsed correctly" + } + test "fails for unknown type" { + Expect.throwsT + (fun () -> ignore (CustomFeedSource.Parse "nasa:sat1")) + "Invalid value should have raised an exception" + } + ] + testList "ToString" [ + test "succeeds for category feed" { + Expect.equal + (string (CustomFeedSource.Parse "category:fish")) "category:fish" "Category feed string incorrect" + } + test "succeeds for tag feed" { + Expect.equal (string (CustomFeedSource.Parse "tag:rocks")) "tag:rocks" "Tag feed string incorrect" + } + ] + ] + +/// Unit tests for the ThemeAssetId type +let themeAssetIdTests = + testList "ThemeAssetId" [ + testList "Parse" [ + test "succeeds with expected values" { + Expect.equal + (ThemeAssetId.Parse "test-theme/the-asset") + (ThemeAssetId ((ThemeId "test-theme"), "the-asset")) + "Theme asset ID not parsed correctly" + } + test "fails if no slash is present" { + Expect.throwsT + (fun () -> ignore (ThemeAssetId.Parse "my-theme-asset")) + "Invalid value should have raised an exception" + } + ] + test "ToString succeeds" { + Expect.equal + (string (ThemeAssetId ((ThemeId "howdy"), "pardner"))) "howdy/pardner" "Theme asset ID string incorrect" + } + ] + +/// Unit tests for the UploadDestination type +let uploadDestinationTests = + testList "UploadDestination" [ + testList "Parse" [ + test "succeeds for \"Database\"" { + Expect.equal (UploadDestination.Parse "Database") Database "\"Database\" not parsed correctly" + } + test "succeeds for \"Disk\"" { + Expect.equal (UploadDestination.Parse "Disk") Disk "\"Disk\" not parsed correctly" + } + test "fails for unrecognized value" { + Expect.throwsT + (fun () -> ignore (UploadDestination.Parse "Azure")) "Invalid value should have raised an exception" + } + ] + testList "ToString" [ + test "succeeds for Database" { + Expect.equal (string Database) "Database" "Database string incorrect" + } + test "succeeds for Disk" { + Expect.equal (string Disk) "Disk" "Disk string incorrect" + } + ] + ] + +/// All tests for the Domain.SupportTypes file +let all = + testList + "SupportTypes" + [ nodaTests + accessLevelTests + commentStatusTests + explicitRatingTests + episodeTests + markupTextTests + podcastMediumTests + postStatusTests + customFeedSourceTests + themeAssetIdTests + uploadDestinationTests ] diff --git a/src/MyWebLog.Tests/ViewModelsTests.fs b/src/MyWebLog.Tests/ViewModelsTests.fs new file mode 100644 index 0000000..bddae3c --- /dev/null +++ b/src/MyWebLog.Tests/ViewModelsTests.fs @@ -0,0 +1,292 @@ +module ViewModelsTests + +open System +open Expecto +open MyWebLog +open MyWebLog.ViewModels +open NodaTime + +/// Unit tests for the addBaseToRelativeUrls helper function +let addBaseToRelativeUrlsTests = + testList "PublicHelpers.addBaseToRelativeUrls" [ + test "succeeds when there is no extra URL path" { + let testText = """Howdy>""" + let modified = addBaseToRelativeUrls "" testText + Expect.equal modified testText "The text should not have been modified" + } + test "succeeds with an extra URL path" { + let testText = + """Click Me!""" + Metadata = [ { Name = "unit"; Value = "test" } ] } + testList "FromPageMinimal" [ + test "succeeds when page is default page" { + let webLog = { WebLog.Empty with TimeZone = "Etc/GMT-1"; DefaultPage = "my-page" } + let model = DisplayPage.FromPageMinimal webLog page + Expect.equal model.Id "my-page" "Id not filled properly" + Expect.equal model.AuthorId "jim" "AuthorId not filled properly" + Expect.equal model.Title "A Fine Example" "Title not filled properly" + Expect.equal model.Permalink "about/a-fine-example.html" "Permalink not filled properly" + Expect.equal + model.PublishedOn + ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) + "PublishedOn not filled properly" + Expect.equal + model.UpdatedOn + ((Noda.epoch + Duration.FromHours 2).ToDateTimeUtc()) + "UpdatedOn not filled properly" + Expect.isFalse model.IsInPageList "IsInPageList should not have been set" + Expect.isTrue model.IsDefault "IsDefault should have been set" + Expect.equal model.Text "" "Text should have been blank" + Expect.isEmpty model.Metadata "Metadata should have been empty" + } + test "succeeds when page is not the default page" { + let model = DisplayPage.FromPageMinimal { WebLog.Empty with DefaultPage = "posts" } page + Expect.isFalse model.IsDefault "IsDefault should not have been set" + } + ] + testList "FromPage" [ + test "succeeds when the web log is on the domain root" { + let webLog = { WebLog.Empty with TimeZone = "Etc/GMT-4"; UrlBase = "https://example.com" } + let model = DisplayPage.FromPage webLog page + Expect.equal model.Id "my-page" "Id not filled properly" + Expect.equal model.AuthorId "jim" "AuthorId not filled properly" + Expect.equal model.Title "A Fine Example" "Title not filled properly" + Expect.equal model.Permalink "about/a-fine-example.html" "Permalink not filled properly" + Expect.equal + model.PublishedOn + ((Noda.epoch + Duration.FromHours 4).ToDateTimeUtc()) + "PublishedOn not filled properly" + Expect.equal + model.UpdatedOn + ((Noda.epoch + Duration.FromHours 5).ToDateTimeUtc()) + "UpdatedOn not filled properly" + Expect.isFalse model.IsInPageList "IsInPageList should not have been set" + Expect.isFalse model.IsDefault "IsDefault should not have been set" + Expect.equal model.Text """Click Me!""" "Text not filled properly" + Expect.equal model.Metadata.Length 1 "Metadata not filled properly" + } + test "succeeds when the web log is not on the domain root" { + let model = DisplayPage.FromPage { WebLog.Empty with UrlBase = "https://example.com/a/b/c" } page + Expect.equal model.Text """Click Me!""" "Text not filled properly" + } + ] + ] + +/// Unit tests for the DisplayRevision type +let displayRevisionTests = + test "DisplayRevision.FromRevision succeeds" { + let model = + DisplayRevision.FromRevision + { WebLog.Empty with TimeZone = "Etc/GMT+1" } + { Text = Html "howdy"; AsOf = Noda.epoch } + Expect.equal model.AsOf (Noda.epoch.ToDateTimeUtc()) "AsOf not filled properly" + Expect.equal + model.AsOfLocal ((Noda.epoch - Duration.FromHours 1).ToDateTimeUtc()) "AsOfLocal not filled properly" + Expect.equal model.Format "HTML" "Format not filled properly" + } + +open System.IO + +/// Unit tests for the DisplayTheme type +let displayThemeTests = + testList "DisplayTheme.FromTheme" [ + let theme = + { Id = ThemeId "the-theme" + Name = "Test Theme" + Version = "v0.1.2" + Templates = [ ThemeTemplate.Empty; ThemeTemplate.Empty ] } + test "succeeds when theme is in use and not on disk" { + let model = + DisplayTheme.FromTheme + (fun it -> Expect.equal it (ThemeId "the-theme") "The theme ID not passed correctly"; true) theme + Expect.equal model.Id "the-theme" "Id not filled properly" + Expect.equal model.Name "Test Theme" "Name not filled properly" + Expect.equal model.Version "v0.1.2" "Version not filled properly" + Expect.equal model.TemplateCount 2 "TemplateCount not filled properly" + Expect.isTrue model.IsInUse "IsInUse should have been set" + Expect.isFalse model.IsOnDisk "IsOnDisk should not have been set" + } + test "succeeds when the theme is not in use as is on disk" { + let file = File.Create "another-theme.zip" + try + let model = DisplayTheme.FromTheme (fun _ -> false) { theme with Id = ThemeId "another" } + Expect.isFalse model.IsInUse "IsInUse should not have been set" + Expect.isTrue model.IsOnDisk "IsOnDisk should have been set" + finally + file.Close() + file.Dispose() + File.Delete "another-theme.zip" + } + ] + +/// Unit tests for the DisplayUpload type +let displayUploadTests = + test "DisplayUpload.FromUpload succeeds" { + let upload = + { Upload.Empty with + Id = UploadId "test-up" + Path = Permalink "2022/04/my-pic.jpg" + UpdatedOn = Noda.epoch } + let model = DisplayUpload.FromUpload { WebLog.Empty with TimeZone = "Etc/GMT-1" } Database upload + Expect.equal model.Id "test-up" "Id not filled properly" + Expect.equal model.Name "my-pic.jpg" "Name not filled properly" + Expect.equal model.Path "2022/04/" "Path not filled properly" + Expect.equal model.Source "Database" "Source not filled properly" + Expect.isSome model.UpdatedOn "There should have been an UpdatedOn value" + Expect.equal + model.UpdatedOn.Value ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "UpdatedOn not filled properly" + } + +/// Unit tests for the DisplayUser type +let displayUserTests = + testList "DisplayUser.FromUser" [ + let minimalUser = + { WebLogUser.Empty with + Id = WebLogUserId "test-user" + Email = "jim.james@example.com" + FirstName = "Jim" + LastName = "James" + PreferredName = "John" + AccessLevel = Editor + CreatedOn = Noda.epoch } + test "succeeds when the user has minimal information" { + let model = DisplayUser.FromUser WebLog.Empty minimalUser + Expect.equal model.Id "test-user" "Id not filled properly" + Expect.equal model.Email "jim.james@example.com" "Email not filled properly" + Expect.equal model.FirstName "Jim" "FirstName not filled properly" + Expect.equal model.LastName "James" "LastName not filled properly" + Expect.equal model.PreferredName "John" "PreferredName not filled properly" + Expect.equal model.Url "" "Url not filled properly" + Expect.equal model.AccessLevel "Editor" "AccessLevel not filled properly" + Expect.equal model.CreatedOn (Noda.epoch.ToDateTimeUtc()) "CreatedOn not filled properly" + Expect.isFalse model.LastSeenOn.HasValue "LastSeenOn should have been null" + } + test "succeeds when the user has all information" { + let model = + DisplayUser.FromUser + { WebLog.Empty with TimeZone = "Etc/GMT-1" } + { minimalUser with + Url = Some "https://my.site" + LastSeenOn = Some (Noda.epoch + Duration.FromDays 4) } + Expect.equal model.Url "https://my.site" "Url not filled properly" + Expect.equal + model.CreatedOn ((Noda.epoch + Duration.FromHours 1).ToDateTimeUtc()) "CreatedOn not filled properly" + Expect.isTrue model.LastSeenOn.HasValue "LastSeenOn should not have been null" + Expect.equal + model.LastSeenOn.Value + ((Noda.epoch + Duration.FromDays 4 + Duration.FromHours 1).ToDateTimeUtc()) + "LastSeenOn not filled properly" + } + ] + +/// Unit tests for the EditCategoryModel type +let editCategoryModelTests = + testList "EditCategoryModel" [ + testList "FromCategory" [ + let minimalCat = { Category.Empty with Id = CategoryId "test-cat"; Name = "test"; Slug = "test-slug" } + test "succeeds with minimal information" { + let model = EditCategoryModel.FromCategory minimalCat + Expect.equal model.CategoryId "test-cat" "CategoryId not filled properly" + Expect.equal model.Name "test" "Name not filled properly" + Expect.equal model.Slug "test-slug" "Slug not filled properly" + Expect.equal model.Description "" "Description not filled properly" + Expect.equal model.ParentId "" "ParentId not filled properly" + } + test "succeeds with complete information" { + let model = + EditCategoryModel.FromCategory + { minimalCat with Description = Some "Testing"; ParentId = Some (CategoryId "parent") } + Expect.equal model.Description "Testing" "Description not filled properly" + Expect.equal model.ParentId "parent" "ParentId not filled properly" + } + ] + testList "IsNew" [ + test "succeeds for a new category" { + let model = EditCategoryModel.FromCategory { Category.Empty with Id = CategoryId "new" } + Expect.isTrue model.IsNew "Category should have been considered new" + } + test "succeeds for a non-new category" { + let model = EditCategoryModel.FromCategory Category.Empty + Expect.isFalse model.IsNew "Category should not have been considered new" + } + ] + ] + +/// All tests for the Domain namespace +let all = + testList + "ViewModels" + [ addBaseToRelativeUrlsTests + displayCustomFeedTests + displayPageTests + displayRevisionTests + displayThemeTests + displayUploadTests + displayUserTests + editCategoryModelTests ]