Version 2.1 (#41)

- Add full chapter support (#6)
- Add built-in redirect functionality (#39)
- Support building Docker containers for release (#38)
- Support canonical domain configuration (#37)
- Add unit tests for domain/models and integration tests for all three data stores
- Convert SQLite storage to use JSON documents, similar to PostgreSQL
- Convert admin templates to Giraffe View Engine (from Liquid)
- Add .NET 8 support
This commit was merged in pull request #41.
This commit is contained in:
2024-03-26 20:13:28 -04:00
committed by GitHub
parent 7b325dc19e
commit f1a7e55f3e
116 changed files with 14807 additions and 8249 deletions

View File

@@ -0,0 +1,150 @@
/// <summary>
/// Integration tests for <see cref="ICategoryData" /> implementations
/// </summary>
module CategoryDataTests
open Expecto
open MyWebLog
open MyWebLog.Data
/// The ID of the root web log
let rootId = WebLogId "uSitJEuD3UyzWC9jgOHc8g"
/// The ID of the Favorites category
let private favoritesId = CategoryId "S5JflPsJ9EG7gA2LD4m92A"
let ``Add succeeds`` (data: IData) = task {
let category =
{ Category.Empty with Id = CategoryId "added-cat"; WebLogId = WebLogId "test"; Name = "Added"; Slug = "added" }
do! data.Category.Add category
let! stored = data.Category.FindById (CategoryId "added-cat") (WebLogId "test")
Expect.isSome stored "The category should have been added"
}
let ``CountAll succeeds when categories exist`` (data: IData) = task {
let! count = data.Category.CountAll rootId
Expect.equal count 3 "There should have been 3 categories"
}
let ``CountAll succeeds when categories do not exist`` (data: IData) = task {
let! count = data.Category.CountAll WebLogId.Empty
Expect.equal count 0 "There should have been no categories"
}
let ``CountTopLevel succeeds when top-level categories exist`` (data: IData) = task {
let! count = data.Category.CountTopLevel rootId
Expect.equal count 2 "There should have been 2 top-level categories"
}
let ``CountTopLevel succeeds when no top-level categories exist`` (data: IData) = task {
let! count = data.Category.CountTopLevel WebLogId.Empty
Expect.equal count 0 "There should have been no top-level categories"
}
let ``FindAllForView succeeds`` (data: IData) = task {
let! all = data.Category.FindAllForView rootId
Expect.equal all.Length 3 "There should have been 3 categories returned"
Expect.equal all[0].Name "Favorites" "The first category is incorrect"
Expect.equal all[0].PostCount 1 "There should be one post in this category"
Expect.equal all[1].Name "Spitball" "The second category is incorrect"
Expect.equal all[1].PostCount 2 "There should be two posts in this category"
Expect.equal all[2].Name "Moonshot" "The third category is incorrect"
Expect.equal all[2].PostCount 1 "There should be one post in this category"
}
let ``FindById succeeds when a category is found`` (data: IData) = task {
let! cat = data.Category.FindById favoritesId rootId
Expect.isSome cat "There should have been a category returned"
Expect.equal cat.Value.Name "Favorites" "The category retrieved is incorrect"
Expect.equal cat.Value.Slug "favorites" "The slug is incorrect"
Expect.equal cat.Value.Description (Some "Favorite posts") "The description is incorrect"
Expect.isNone cat.Value.ParentId "There should have been no parent ID"
}
let ``FindById succeeds when a category is not found`` (data: IData) = task {
let! cat = data.Category.FindById CategoryId.Empty rootId
Expect.isNone cat "There should not have been a category returned"
}
let ``FindByWebLog succeeds when categories exist`` (data: IData) = task {
let! cats = data.Category.FindByWebLog rootId
Expect.equal cats.Length 3 "There should be 3 categories"
Expect.exists cats (fun it -> it.Name = "Favorites") "Favorites category not found"
Expect.exists cats (fun it -> it.Name = "Spitball") "Spitball category not found"
Expect.exists cats (fun it -> it.Name = "Moonshot") "Moonshot category not found"
}
let ``FindByWebLog succeeds when no categories exist`` (data: IData) = task {
let! cats = data.Category.FindByWebLog WebLogId.Empty
Expect.isEmpty cats "There should have been no categories returned"
}
let ``Update succeeds`` (data: IData) = task {
match! data.Category.FindById favoritesId rootId with
| Some cat ->
do! data.Category.Update { cat with Name = "My Favorites"; Slug = "my-favorites"; Description = None }
match! data.Category.FindById favoritesId rootId with
| Some updated ->
Expect.equal updated.Name "My Favorites" "Name not updated properly"
Expect.equal updated.Slug "my-favorites" "Slug not updated properly"
Expect.isNone updated.Description "Description should have been removed"
| None -> Expect.isTrue false "The updated favorites category could not be retrieved"
| None -> Expect.isTrue false "The favorites category could not be retrieved"
}
let ``Delete succeeds when the category is deleted (no posts)`` (data: IData) = task {
let! result = data.Category.Delete (CategoryId "added-cat") (WebLogId "test")
Expect.equal result CategoryDeleted "The category should have been deleted"
let! cat = data.Category.FindById (CategoryId "added-cat") (WebLogId "test")
Expect.isNone cat "The deleted category should not still exist"
}
let ``Delete succeeds when the category does not exist`` (data: IData) = task {
let! result = data.Category.Delete CategoryId.Empty (WebLogId "none")
Expect.equal result CategoryNotFound "The category should not have been found"
}
let ``Delete succeeds when reassigning parent category to None`` (data: IData) = task {
let moonshotId = CategoryId "ScVpyu1e7UiP7bDdge3ZEw"
let spitballId = CategoryId "jw6N69YtTEWVHAO33jHU-w"
let! result = data.Category.Delete spitballId rootId
Expect.equal result ReassignedChildCategories "Child categories should have been reassigned"
match! data.Category.FindById moonshotId rootId with
| Some cat -> Expect.isNone cat.ParentId "Parent ID should have been cleared"
| None -> Expect.isTrue false "Unable to find former child category"
}
let ``Delete succeeds when reassigning parent category to Some`` (data: IData) = task {
do! data.Category.Add { Category.Empty with Id = CategoryId "a"; WebLogId = WebLogId "test"; Name = "A" }
do! data.Category.Add
{ Category.Empty with
Id = CategoryId "b"
WebLogId = WebLogId "test"
Name = "B"
ParentId = Some (CategoryId "a") }
do! data.Category.Add
{ Category.Empty with
Id = CategoryId "c"
WebLogId = WebLogId "test"
Name = "C"
ParentId = Some (CategoryId "b") }
let! result = data.Category.Delete (CategoryId "b") (WebLogId "test")
Expect.equal result ReassignedChildCategories "Child categories should have been reassigned"
match! data.Category.FindById (CategoryId "c") (WebLogId "test") with
| Some cat -> Expect.equal cat.ParentId (Some (CategoryId "a")) "Parent category ID not reassigned properly"
| None -> Expect.isTrue false "Expected former child category not found"
}
let ``Delete succeeds and removes category from posts`` (data: IData) = task {
let moonshotId = CategoryId "ScVpyu1e7UiP7bDdge3ZEw"
let postId = PostId "RCsCU2puYEmkpzotoi8p4g"
match! data.Post.FindById postId rootId with
| Some post ->
Expect.equal post.CategoryIds [ moonshotId ] "Post category IDs are not as expected"
let! result = data.Category.Delete moonshotId rootId
Expect.equal result CategoryDeleted "The category should have been deleted (no children)"
match! data.Post.FindById postId rootId with
| Some p -> Expect.isEmpty p.CategoryIds "Category ID was not removed"
| None -> Expect.isTrue false "The expected updated post was not found"
| None -> Expect.isTrue false "The expected test post was not found"
}

View File

@@ -0,0 +1,296 @@
module ConvertersTests
open Expecto
open Microsoft.FSharpLu.Json
open MyWebLog
open MyWebLog.Converters.Json
open Newtonsoft.Json
/// Unit tests for the CategoryIdConverter type
let categoryIdConverterTests = testList "CategoryIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(CategoryIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(CategoryId "test-cat-id", opts)
Expect.equal after "\"test-cat-id\"" "Category ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<CategoryId>("\"test-cat-id\"", opts)
Expect.equal after (CategoryId "test-cat-id") "Category ID not serialized incorrectly"
}
]
/// Unit tests for the CommentIdConverter type
let commentIdConverterTests = testList "CommentIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(CommentIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(CommentId "test-id", opts)
Expect.equal after "\"test-id\"" "Comment ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<CommentId>("\"my-test\"", opts)
Expect.equal after (CommentId "my-test") "Comment ID deserialized incorrectly"
}
]
/// Unit tests for the CommentStatusConverter type
let commentStatusConverterTests = testList "CommentStatusConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(CommentStatusConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(Approved, opts)
Expect.equal after "\"Approved\"" "Comment status serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<CommentStatus>("\"Spam\"", opts)
Expect.equal after Spam "Comment status deserialized incorrectly"
}
]
/// Unit tests for the CustomFeedIdConverter type
let customFeedIdConverterTests = testList "CustomFeedIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(CustomFeedIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(CustomFeedId "my-feed", opts)
Expect.equal after "\"my-feed\"" "Custom feed ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<CustomFeedId>("\"feed-me\"", opts)
Expect.equal after (CustomFeedId "feed-me") "Custom feed ID deserialized incorrectly"
}
]
/// Unit tests for the CustomFeedSourceConverter type
let customFeedSourceConverterTests = testList "CustomFeedSourceConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(CustomFeedSourceConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(Category (CategoryId "abc-123"), opts)
Expect.equal after "\"category:abc-123\"" "Custom feed source serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<CustomFeedSource>("\"tag:testing\"", opts)
Expect.equal after (Tag "testing") "Custom feed source deserialized incorrectly"
}
]
/// Unit tests for the ExplicitRating type
let explicitRatingConverterTests = testList "ExplicitRatingConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(ExplicitRatingConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(Yes, opts)
Expect.equal after "\"yes\"" "Explicit rating serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<ExplicitRating>("\"clean\"", opts)
Expect.equal after Clean "Explicit rating deserialized incorrectly"
}
]
/// Unit tests for the MarkupText type
let markupTextConverterTests = testList "MarkupTextConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(MarkupTextConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(Html "<h4>test</h4>", opts)
Expect.equal after "\"HTML: <h4>test</h4>\"" "Markup text serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<MarkupText>("\"Markdown: #### test\"", opts)
Expect.equal after (Markdown "#### test") "Markup text deserialized incorrectly"
}
]
/// Unit tests for the PermalinkConverter type
let permalinkConverterTests = testList "PermalinkConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(PermalinkConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(Permalink "2022/test", opts)
Expect.equal after "\"2022/test\"" "Permalink serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<Permalink>("\"2023/unit.html\"", opts)
Expect.equal after (Permalink "2023/unit.html") "Permalink deserialized incorrectly"
}
]
/// Unit tests for the PageIdConverter type
let pageIdConverterTests = testList "PageIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(PageIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(PageId "test-page", opts)
Expect.equal after "\"test-page\"" "Page ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<PageId>("\"page-test\"", opts)
Expect.equal after (PageId "page-test") "Page ID deserialized incorrectly"
}
]
/// Unit tests for the PodcastMedium type
let podcastMediumConverterTests = testList "PodcastMediumConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(PodcastMediumConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(Audiobook, opts)
Expect.equal after "\"audiobook\"" "Podcast medium serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<PodcastMedium>("\"newsletter\"", opts)
Expect.equal after Newsletter "Podcast medium deserialized incorrectly"
}
]
/// Unit tests for the PostIdConverter type
let postIdConverterTests = testList "PostIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(PostIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(PostId "test-post", opts)
Expect.equal after "\"test-post\"" "Post ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<PostId>("\"post-test\"", opts)
Expect.equal after (PostId "post-test") "Post ID deserialized incorrectly"
}
]
/// Unit tests for the TagMapIdConverter type
let tagMapIdConverterTests = testList "TagMapIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(TagMapIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(TagMapId "test-map", opts)
Expect.equal after "\"test-map\"" "Tag map ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<TagMapId>("\"map-test\"", opts)
Expect.equal after (TagMapId "map-test") "Tag map ID deserialized incorrectly"
}
]
/// Unit tests for the ThemeAssetIdConverter type
let themeAssetIdConverterTests = testList "ThemeAssetIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(ThemeAssetIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(ThemeAssetId (ThemeId "test", "unit.jpg"), opts)
Expect.equal after "\"test/unit.jpg\"" "Theme asset ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<ThemeAssetId>("\"theme/test.png\"", opts)
Expect.equal after (ThemeAssetId (ThemeId "theme", "test.png")) "Theme asset ID deserialized incorrectly"
}
]
/// Unit tests for the ThemeIdConverter type
let themeIdConverterTests = testList "ThemeIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(ThemeIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(ThemeId "test-theme", opts)
Expect.equal after "\"test-theme\"" "Theme ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<ThemeId>("\"theme-test\"", opts)
Expect.equal after (ThemeId "theme-test") "Theme ID deserialized incorrectly"
}
]
/// Unit tests for the UploadIdConverter type
let uploadIdConverterTests = testList "UploadIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(UploadIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(UploadId "test-up", opts)
Expect.equal after "\"test-up\"" "Upload ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<UploadId>("\"up-test\"", opts)
Expect.equal after (UploadId "up-test") "Upload ID deserialized incorrectly"
}
]
/// Unit tests for the WebLogIdConverter type
let webLogIdConverterTests = testList "WebLogIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(WebLogIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(WebLogId "test-web", opts)
Expect.equal after "\"test-web\"" "Web log ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<WebLogId>("\"web-test\"", opts)
Expect.equal after (WebLogId "web-test") "Web log ID deserialized incorrectly"
}
]
/// Unit tests for the WebLogUserIdConverter type
let webLogUserIdConverterTests = testList "WebLogUserIdConverter" [
let opts = JsonSerializerSettings()
opts.Converters.Add(WebLogUserIdConverter())
test "succeeds when serializing" {
let after = JsonConvert.SerializeObject(WebLogUserId "test-user", opts)
Expect.equal after "\"test-user\"" "Web log user ID serialized incorrectly"
}
test "succeeds when deserializing" {
let after = JsonConvert.DeserializeObject<WebLogUserId>("\"user-test\"", opts)
Expect.equal after (WebLogUserId "user-test") "Web log user ID deserialized incorrectly"
}
]
open NodaTime.Serialization.JsonNet
/// Unit tests for the Json.configure function
let configureTests = test "Json.configure succeeds" {
let has typ (converter: JsonConverter) = converter.GetType() = typ
let ser = configure (JsonSerializer.Create())
Expect.hasCountOf ser.Converters 1u (has typeof<CategoryIdConverter>) "Category ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<CommentIdConverter>) "Comment ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<CommentStatusConverter>) "Comment status converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<CustomFeedIdConverter>) "Custom feed ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<CustomFeedSourceConverter>) "Custom feed source converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<ExplicitRatingConverter>) "Explicit rating converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<MarkupTextConverter>) "Markup text converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<PermalinkConverter>) "Permalink converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<PageIdConverter>) "Page ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<PodcastMediumConverter>) "Podcast medium converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<PostIdConverter>) "Post ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<TagMapIdConverter>) "Tag map ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<ThemeAssetIdConverter>) "Theme asset ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<ThemeIdConverter>) "Theme ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<UploadIdConverter>) "Upload ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<WebLogIdConverter>) "Web log ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<WebLogUserIdConverter>) "Web log user ID converter not found"
Expect.hasCountOf ser.Converters 1u (has typeof<CompactUnionJsonConverter>) "F# type converter not found"
Expect.hasCountOf ser.Converters 1u (has (NodaConverters.InstantConverter.GetType())) "NodaTime converter not found"
Expect.equal ser.NullValueHandling NullValueHandling.Ignore "Null handling set incorrectly"
Expect.equal ser.MissingMemberHandling MissingMemberHandling.Ignore "Missing member handling set incorrectly"
}
/// All tests for the Data.Converters file
let all = testList "Converters" [
categoryIdConverterTests
commentIdConverterTests
commentStatusConverterTests
customFeedIdConverterTests
customFeedSourceConverterTests
explicitRatingConverterTests
markupTextConverterTests
permalinkConverterTests
pageIdConverterTests
podcastMediumConverterTests
postIdConverterTests
tagMapIdConverterTests
themeAssetIdConverterTests
themeIdConverterTests
uploadIdConverterTests
webLogIdConverterTests
webLogUserIdConverterTests
configureTests
]

View File

@@ -0,0 +1,267 @@
/// <summary>
/// Integration tests for <see cref="IPageData" /> implementations
/// </summary>
module PageDataTests
open System
open Expecto
open MyWebLog
open MyWebLog.Data
open NodaTime
/// The ID of the root web log
let private rootId = CategoryDataTests.rootId
/// The ID of the "A cool page" page
let coolPageId = PageId "hgc_BLEZ50SoAWLuPNISvA"
/// The published and updated time of the "A cool page" page
let private coolPagePublished = Instant.FromDateTimeOffset(DateTimeOffset.Parse "2024-01-20T22:14:28Z")
/// The ID of the "Yet Another Page" page
let private otherPageId = PageId "KouRjvSmm0Wz6TMD8xf67A"
let ``Add succeeds`` (data: IData) = task {
let page =
{ Id = PageId "added-page"
WebLogId = WebLogId "test"
AuthorId = WebLogUserId "the-author"
Title = "A New Page"
Permalink = Permalink "2024/the-page.htm"
PublishedOn = Noda.epoch + Duration.FromDays 3
UpdatedOn = Noda.epoch + Duration.FromDays 3 + Duration.FromMinutes 2L
IsInPageList = true
Template = Some "new-page-template"
Text = "<h1>A new page</h1>"
Metadata = [ { Name = "Meta Item"; Value = "Meta Value" } ]
PriorPermalinks = [ Permalink "2024/the-new-page.htm" ]
Revisions = [ { AsOf = Noda.epoch + Duration.FromDays 3; Text = Html "<h1>A new page</h1>" } ] }
do! data.Page.Add page
let! stored = data.Page.FindFullById (PageId "added-page") (WebLogId "test")
Expect.isSome stored "The page should have been added"
let pg = stored.Value
Expect.equal pg.Id page.Id "ID not saved properly"
Expect.equal pg.WebLogId page.WebLogId "Web log ID not saved properly"
Expect.equal pg.AuthorId page.AuthorId "Author ID not saved properly"
Expect.equal pg.Title page.Title "Title not saved properly"
Expect.equal pg.Permalink page.Permalink "Permalink not saved properly"
Expect.equal pg.PublishedOn page.PublishedOn "Published On not saved properly"
Expect.equal pg.UpdatedOn page.UpdatedOn "Updated On not saved properly"
Expect.equal pg.IsInPageList page.IsInPageList "Is in page list flag not saved properly"
Expect.equal pg.Template page.Template "Template not saved properly"
Expect.equal pg.Text page.Text "Text not saved properly"
Expect.equal pg.Metadata page.Metadata "Metadata not saved properly"
Expect.equal pg.PriorPermalinks page.PriorPermalinks "Prior permalinks not saved properly"
Expect.equal pg.Revisions page.Revisions "Revisions not saved properly"
}
let ``All succeeds`` (data: IData) = task {
let! pages = data.Page.All rootId
Expect.hasLength pages 2 "There should have been 2 pages retrieved"
pages |> List.iteri (fun idx pg ->
Expect.equal pg.Text "" $"Page {idx} should have had no text"
Expect.isEmpty pg.Metadata $"Page {idx} should have had no metadata"
Expect.isEmpty pg.Revisions $"Page {idx} should have had no revisions"
Expect.isEmpty pg.PriorPermalinks $"Page {idx} should have had no prior permalinks")
let! others = data.Page.All (WebLogId "not-there")
Expect.isEmpty others "There should not be pages retrieved"
}
let ``CountAll succeeds`` (data: IData) = task {
let! pages = data.Page.CountAll rootId
Expect.equal pages 2 "There should have been 2 pages counted"
}
let ``CountListed succeeds`` (data: IData) = task {
let! pages = data.Page.CountListed rootId
Expect.equal pages 1 "There should have been 1 page in the page list"
}
let ``FindById succeeds when a page is found`` (data: IData) = task {
let! page = data.Page.FindById coolPageId rootId
Expect.isSome page "A page should have been returned"
let pg = page.Value
Expect.equal pg.Id coolPageId "The wrong page was retrieved"
Expect.equal pg.WebLogId rootId "The page's web log did not match the called parameter"
Expect.equal pg.AuthorId (WebLogUserId "5EM2rimH9kONpmd2zQkiVA") "Author ID is incorrect"
Expect.equal pg.Title "Page Title" "Title is incorrect"
Expect.equal pg.Permalink (Permalink "a-cool-page.html") "Permalink is incorrect"
Expect.equal pg.PublishedOn coolPagePublished "Published On is incorrect"
Expect.equal pg.UpdatedOn coolPagePublished "Updated On is incorrect"
Expect.isFalse pg.IsInPageList "Is in page list flag should not have been set"
Expect.equal pg.Text "<h1 id=\"a-cool-page\">A Cool Page</h1>\n<p>It really is cool!</p>\n" "Text is incorrect"
Expect.equal
pg.Metadata [ { Name = "Cool"; Value = "true" }; { Name = "Warm"; Value = "false" } ] "Metadata is incorrect"
Expect.isEmpty pg.Revisions "Revisions should not have been retrieved"
Expect.isEmpty pg.PriorPermalinks "Prior permalinks should not have been retrieved"
}
let ``FindById succeeds when a page is not found (incorrect weblog)`` (data: IData) = task {
let! page = data.Page.FindById coolPageId (WebLogId "wrong")
Expect.isNone page "The page should not have been retrieved"
}
let ``FindById succeeds when a page is not found (bad page ID)`` (data: IData) = task {
let! page = data.Page.FindById (PageId "missing") rootId
Expect.isNone page "The page should not have been retrieved"
}
let ``FindByPermalink succeeds when a page is found`` (data: IData) = task {
let! page = data.Page.FindByPermalink (Permalink "a-cool-page.html") rootId
Expect.isSome page "A page should have been returned"
let pg = page.Value
Expect.equal pg.Id coolPageId "The wrong page was retrieved"
Expect.isEmpty pg.Revisions "Revisions should not have been retrieved"
Expect.isEmpty pg.PriorPermalinks "Prior permalinks should not have been retrieved"
}
let ``FindByPermalink succeeds when a page is not found (incorrect weblog)`` (data: IData) = task {
let! page = data.Page.FindByPermalink (Permalink "a-cool-page.html") (WebLogId "wrong")
Expect.isNone page "The page should not have been retrieved"
}
let ``FindByPermalink succeeds when a page is not found (no such permalink)`` (data: IData) = task {
let! page = data.Page.FindByPermalink (Permalink "1970/no-www-then.html") rootId
Expect.isNone page "The page should not have been retrieved"
}
let ``FindCurrentPermalink succeeds when a page is found`` (data: IData) = task {
let! link = data.Page.FindCurrentPermalink [ Permalink "a-cool-pg.html"; Permalink "a-cool-pg.html/" ] rootId
Expect.isSome link "A permalink should have been returned"
Expect.equal link (Some (Permalink "a-cool-page.html")) "The wrong permalink was retrieved"
}
let ``FindCurrentPermalink succeeds when a page is not found`` (data: IData) = task {
let! link = data.Page.FindCurrentPermalink [ Permalink "blah/"; Permalink "blah" ] rootId
Expect.isNone link "A permalink should not have been returned"
}
let ``FindFullById succeeds when a page is found`` (data: IData) = task {
let! page = data.Page.FindFullById coolPageId rootId
Expect.isSome page "A page should have been returned"
let pg = page.Value
Expect.equal pg.Id coolPageId "The wrong page was retrieved"
Expect.equal pg.WebLogId rootId "The page's web log did not match the called parameter"
Expect.equal
pg.Revisions
[ { AsOf = coolPagePublished; Text = Markdown "# A Cool Page\n\nIt really is cool!" } ]
"Revisions are incorrect"
Expect.equal pg.PriorPermalinks [ Permalink "a-cool-pg.html" ] "Prior permalinks are incorrect"
}
let ``FindFullById succeeds when a page is not found`` (data: IData) = task {
let! page = data.Page.FindFullById (PageId "not-there") rootId
Expect.isNone page "A page should not have been retrieved"
}
let ``FindFullByWebLog succeeds when pages are found`` (data: IData) = task {
let! pages = data.Page.FindFullByWebLog rootId
Expect.hasLength pages 2 "There should have been 2 pages returned"
pages |> List.iter (fun pg ->
Expect.contains [ coolPageId; otherPageId ] pg.Id $"Page ID {pg.Id} unexpected"
if pg.Id = coolPageId then
Expect.isNonEmpty pg.Metadata "Metadata should have been retrieved"
Expect.isNonEmpty pg.PriorPermalinks "Prior permalinks should have been retrieved"
Expect.isNonEmpty pg.Revisions "Revisions should have been retrieved")
}
let ``FindFullByWebLog succeeds when pages are not found`` (data: IData) = task {
let! pages = data.Page.FindFullByWebLog (WebLogId "does-not-exist")
Expect.isEmpty pages "No pages should have been retrieved"
}
let ``FindListed succeeds when pages are found`` (data: IData) = task {
let! pages = data.Page.FindListed rootId
Expect.hasLength pages 1 "There should have been 1 page returned"
Expect.equal pages[0].Id otherPageId "An unexpected page was returned"
Expect.equal pages[0].Text "" "Text should not have been returned"
Expect.isEmpty pages[0].PriorPermalinks "Prior permalinks should not have been retrieved"
Expect.isEmpty pages[0].Revisions "Revisions should not have been retrieved"
}
let ``FindListed succeeds when pages are not found`` (data: IData) = task {
let! pages = data.Page.FindListed (WebLogId "none")
Expect.isEmpty pages "No pages should have been retrieved"
}
let ``FindPageOfPages succeeds when pages are found`` (data: IData) = task {
let! pages = data.Page.FindPageOfPages rootId 1
Expect.hasLength pages 2 "There should have been 2 page returned"
Expect.equal pages[0].Id coolPageId "Pages not sorted correctly"
pages |> List.iteri (fun idx pg ->
Expect.notEqual pg.Text "" $"Text for page {idx} should have been retrieved"
Expect.isEmpty pg.Metadata $"Metadata for page {idx} should not have been retrieved"
Expect.isEmpty pg.PriorPermalinks $"Prior permalinks for page {idx} should not have been retrieved"
Expect.isEmpty pg.Revisions $"Revisions for page {idx} should not have been retrieved")
}
let ``FindPageOfPages succeeds when pages are not found`` (data: IData) = task {
let! pages = data.Page.FindPageOfPages rootId 2
Expect.isEmpty pages "No pages should have been retrieved"
}
let ``Update succeeds when the page exists`` (data: IData) = task {
let! page = data.Page.FindFullById coolPageId rootId
Expect.isSome page "A page should have been returned"
do! data.Page.Update
{ page.Value with
Title = "This Is Neat"
Permalink = Permalink "neat-page.html"
UpdatedOn = page.Value.PublishedOn + Duration.FromHours 5
IsInPageList = true
Text = "<p>I have been updated"
Metadata = [ List.head page.Value.Metadata ]
PriorPermalinks = [ Permalink "a-cool-page.html" ]
Revisions =
{ AsOf = page.Value.PublishedOn + Duration.FromHours 5; Text = Html "<p>I have been updated" }
:: page.Value.Revisions }
let! updated = data.Page.FindFullById coolPageId rootId
Expect.isSome updated "The updated page should have been returned"
let pg = updated.Value
Expect.equal pg.Title "This Is Neat" "Title is incorrect"
Expect.equal pg.Permalink (Permalink "neat-page.html") "Permalink is incorrect"
Expect.equal pg.PublishedOn coolPagePublished "Published On is incorrect"
Expect.equal pg.UpdatedOn (coolPagePublished + Duration.FromHours 5) "Updated On is incorrect"
Expect.isTrue pg.IsInPageList "Is in page list flag should have been set"
Expect.equal pg.Text "<p>I have been updated" "Text is incorrect"
Expect.equal pg.Metadata [ { Name = "Cool"; Value = "true" } ] "Metadata is incorrect"
Expect.equal pg.PriorPermalinks [ Permalink "a-cool-page.html" ] "Prior permalinks are incorrect"
Expect.equal
pg.Revisions
[ { AsOf = coolPagePublished + Duration.FromHours 5; Text = Html "<p>I have been updated" }
{ AsOf = coolPagePublished; Text = Markdown "# A Cool Page\n\nIt really is cool!" } ]
"Revisions are incorrect"
}
let ``Update succeeds when the page does not exist`` (data: IData) = task {
let pageId = PageId "missing-page"
do! data.Page.Update { Page.Empty with Id = pageId; WebLogId = rootId }
let! page = data.Page.FindById pageId rootId
Expect.isNone page "A page should not have been retrieved"
}
let ``UpdatePriorPermalinks succeeds when the page exists`` (data: IData) = task {
let links = [ Permalink "link-1.html"; Permalink "link-1.aspx"; Permalink "link-3.php" ]
let! found = data.Page.UpdatePriorPermalinks otherPageId rootId links
Expect.isTrue found "The permalinks should have been updated"
let! page = data.Page.FindFullById otherPageId rootId
Expect.isSome page "The page should have been found"
Expect.equal page.Value.PriorPermalinks links "The prior permalinks were not correct"
}
let ``UpdatePriorPermalinks succeeds when the page does not exist`` (data: IData) = task {
let! found =
data.Page.UpdatePriorPermalinks (PageId "no-page") WebLogId.Empty
[ Permalink "link-1.html"; Permalink "link-1.aspx"; Permalink "link-3.php" ]
Expect.isFalse found "The permalinks should not have been updated"
}
let ``Delete succeeds when a page is deleted`` (data: IData) = task {
let! deleted = data.Page.Delete coolPageId rootId
Expect.isTrue deleted "The page should have been deleted"
}
let ``Delete succeeds when a page is not deleted`` (data: IData) = task {
let! deleted = data.Page.Delete coolPageId rootId // this was deleted above
Expect.isFalse deleted "A page should not have been deleted"
}

View File

@@ -0,0 +1,431 @@
/// <summary>
/// Integration tests for <see cref="IPostData" /> implementations
/// </summary>
module PostDataTests
open System
open Expecto
open MyWebLog
open MyWebLog.Data
open NodaTime
/// The ID of the root web log
let private rootId = CategoryDataTests.rootId
/// The ID of podcast episode 1
let private episode1 = PostId "osxMfWGlAkyugUbJ1-xD1g"
/// The published instant for episode 1
let private episode1Published = Instant.FromDateTimeOffset(DateTimeOffset.Parse "2024-01-20T22:24:01Z")
/// The ID of podcast episode 2
let episode2 = PostId "l4_Eh4aFO06SqqJjOymNzA"
/// The ID of "Something May Happen" post
let private something = PostId "QweKbWQiOkqqrjEdgP9wwg"
/// The published instant for "Something May Happen" post
let private somethingPublished = Instant.FromDateTimeOffset(DateTimeOffset.Parse "2024-01-20T22:32:59Z")
/// The ID of "An Incomplete Thought" post
let private incomplete = PostId "VweKbWQiOkqqrjEdgP9wwg"
/// The ID of "Test Post 1" post
let private testPost1 = PostId "RCsCU2puYEmkpzotoi8p4g"
/// The published instant for "Test Post 1" post
let private testPost1Published = Instant.FromDateTimeOffset(DateTimeOffset.Parse "2024-01-20T22:17:29Z")
/// The category IDs for "Spitball" (parent) and "Moonshot"
let private testCatIds = [ CategoryId "jw6N69YtTEWVHAO33jHU-w"; CategoryId "ScVpyu1e7UiP7bDdge3ZEw" ]
/// Ensure that a list of posts has text for each post
let private ensureHasText (posts: Post list) =
for post in posts do Expect.isNotEmpty post.Text $"Text should not be blank (post ID {post.Id})"
/// Ensure that a list of posts has no revisions or prior permalinks
let private ensureEmpty posts =
for post in posts do
Expect.isEmpty post.Revisions $"There should have been no revisions (post ID {post.Id})"
Expect.isEmpty post.PriorPermalinks $"There should have been no prior permalinks (post ID {post.Id})"
let ``Add succeeds`` (data: IData) = task {
let post =
{ Id = PostId "a-new-post"
WebLogId = WebLogId "test"
AuthorId = WebLogUserId "test-author"
Status = Published
Title = "A New Test Post"
Permalink = Permalink "2020/test-post.html"
PublishedOn = Some (Noda.epoch + Duration.FromMinutes 1L)
UpdatedOn = Noda.epoch + Duration.FromMinutes 3L
Template = Some "fancy"
Text = "<p>Test text here"
CategoryIds = [ CategoryId "a"; CategoryId "b" ]
Tags = [ "x"; "y"; "zed" ]
Episode = Some { Episode.Empty with Media = "test-ep.mp3" }
Metadata = [ { Name = "Meta"; Value = "Data" } ]
PriorPermalinks = [ Permalink "2020/test-post-a.html" ]
Revisions = [ { AsOf = Noda.epoch + Duration.FromMinutes 1L; Text = Html "<p>Test text here" } ] }
do! data.Post.Add post
let! stored = data.Post.FindFullById post.Id post.WebLogId
Expect.isSome stored "The added post should have been retrieved"
let it = stored.Value
Expect.equal it.Id post.Id "ID not saved properly"
Expect.equal it.WebLogId post.WebLogId "Web log ID not saved properly"
Expect.equal it.AuthorId post.AuthorId "Author ID not saved properly"
Expect.equal it.Status post.Status "Status not saved properly"
Expect.equal it.Title post.Title "Title not saved properly"
Expect.equal it.Permalink post.Permalink "Permalink not saved properly"
Expect.equal it.PublishedOn post.PublishedOn "Published On not saved properly"
Expect.equal it.UpdatedOn post.UpdatedOn "Updated On not saved properly"
Expect.equal it.Template post.Template "Template not saved properly"
Expect.equal it.Text post.Text "Text not saved properly"
Expect.equal it.CategoryIds post.CategoryIds "Category IDs not saved properly"
Expect.equal it.Tags post.Tags "Tags not saved properly"
Expect.equal it.Episode post.Episode "Episode not saved properly"
Expect.equal it.Metadata post.Metadata "Metadata items not saved properly"
Expect.equal it.PriorPermalinks post.PriorPermalinks "Prior permalinks not saved properly"
Expect.equal it.Revisions post.Revisions "Revisions not saved properly"
}
let ``CountByStatus succeeds`` (data: IData) = task {
let! count = data.Post.CountByStatus Published rootId
Expect.equal count 4 "There should be 4 published posts"
}
let ``FindById succeeds when a post is found`` (data: IData) = task {
let! post = data.Post.FindById episode1 rootId
Expect.isSome post "There should have been a post returned"
let it = post.Value
Expect.equal it.Id episode1 "An incorrect post was retrieved"
Expect.equal it.WebLogId rootId "The post belongs to an incorrect web log"
Expect.equal it.AuthorId (WebLogUserId "5EM2rimH9kONpmd2zQkiVA") "Author ID is incorrect"
Expect.equal it.Status Published "Status is incorrect"
Expect.equal it.Title "Episode 1" "Title is incorrect"
Expect.equal it.Permalink (Permalink "2024/episode-1.html") "Permalink is incorrect"
Expect.equal it.PublishedOn (Some episode1Published) "Published On is incorrect"
Expect.equal it.UpdatedOn episode1Published "Updated On is incorrect"
Expect.equal it.Text "<p>It's the launch of my new podcast - y'all come listen!" "Text is incorrect"
Expect.equal it.CategoryIds [ CategoryId "S5JflPsJ9EG7gA2LD4m92A" ] "Category IDs are incorrect"
Expect.equal it.Tags [ "general"; "podcast" ] "Tags are incorrect"
Expect.isSome it.Episode "There should be an episode associated with this post"
let ep = it.Episode.Value
Expect.equal ep.Media "episode-1.mp3" "Episode media is incorrect"
Expect.equal ep.Length 124302L "Episode length is incorrect"
Expect.equal
ep.Duration (Some (Duration.FromMinutes 12L + Duration.FromSeconds 22L)) "Episode duration is incorrect"
Expect.equal ep.ImageUrl (Some "images/ep1-cover.png") "Episode image URL is incorrect"
Expect.equal ep.Subtitle (Some "An introduction to this podcast") "Episode subtitle is incorrect"
Expect.equal ep.Explicit (Some Clean) "Episode explicit rating is incorrect"
Expect.equal ep.ChapterFile (Some "uploads/chapters.json") "Episode chapter file is incorrect"
Expect.equal ep.TranscriptUrl (Some "uploads/transcript.srt") "Episode transcript URL is incorrect"
Expect.equal ep.TranscriptType (Some "application/srt") "Episode transcript type is incorrect"
Expect.equal ep.TranscriptLang (Some "en") "Episode transcript language is incorrect"
Expect.equal ep.TranscriptCaptions (Some true) "Episode transcript caption flag is incorrect"
Expect.equal ep.SeasonNumber (Some 1) "Episode season number is incorrect"
Expect.equal ep.SeasonDescription (Some "The First Season") "Episode season description is incorrect"
Expect.equal ep.EpisodeNumber (Some 1.) "Episode number is incorrect"
Expect.equal ep.EpisodeDescription (Some "The first episode ever!") "Episode description is incorrect"
Expect.equal
it.Metadata
[ { Name = "Density"; Value = "Non-existent" }; { Name = "Intensity"; Value = "Low" } ]
"Metadata is incorrect"
ensureEmpty [ it ]
}
let ``FindById succeeds when a post is not found (incorrect weblog)`` (data: IData) = task {
let! post = data.Post.FindById episode1 (WebLogId "wrong")
Expect.isNone post "The post should not have been retrieved"
}
let ``FindById succeeds when a post is not found (bad post ID)`` (data: IData) = task {
let! post = data.Post.FindById (PostId "absent") rootId
Expect.isNone post "The post should not have been retrieved"
}
let ``FindByPermalink succeeds when a post is found`` (data: IData) = task {
let! post = data.Post.FindByPermalink (Permalink "2024/episode-1.html") rootId
Expect.isSome post "A post should have been returned"
let it = post.Value
Expect.equal it.Id episode1 "The wrong post was retrieved"
ensureEmpty [ it ]
}
let ``FindByPermalink succeeds when a post is not found (incorrect weblog)`` (data: IData) = task {
let! post = data.Post.FindByPermalink (Permalink "2024/episode-1.html") (WebLogId "incorrect")
Expect.isNone post "The post should not have been retrieved"
}
let ``FindByPermalink succeeds when a post is not found (no such permalink)`` (data: IData) = task {
let! post = data.Post.FindByPermalink (Permalink "404") rootId
Expect.isNone post "The post should not have been retrieved"
}
let ``FindCurrentPermalink succeeds when a post is found`` (data: IData) = task {
let! link = data.Post.FindCurrentPermalink [ Permalink "2024/ep-1.html"; Permalink "2024/ep-1.html/" ] rootId
Expect.isSome link "A permalink should have been returned"
Expect.equal link (Some (Permalink "2024/episode-1.html")) "The wrong permalink was retrieved"
}
let ``FindCurrentPermalink succeeds when a post is not found`` (data: IData) = task {
let! link = data.Post.FindCurrentPermalink [ Permalink "oops/"; Permalink "oops" ] rootId
Expect.isNone link "A permalink should not have been returned"
}
let ``FindFullById succeeds when a post is found`` (data: IData) = task {
let! post = data.Post.FindFullById episode1 rootId
Expect.isSome post "A post should have been returned"
let it = post.Value
Expect.equal it.Id episode1 "The wrong post was retrieved"
Expect.equal it.WebLogId rootId "The post's web log did not match the called parameter"
Expect.equal
it.Revisions
[ { AsOf = episode1Published; Text = Html "<p>It's the launch of my new podcast - y'all come listen!" } ]
"Revisions are incorrect"
Expect.equal it.PriorPermalinks [ Permalink "2024/ep-1.html" ] "Prior permalinks are incorrect"
}
let ``FindFullById succeeds when a post is not found`` (data: IData) = task {
let! post = data.Post.FindFullById (PostId "no-post") rootId
Expect.isNone post "A page should not have been retrieved"
}
let ``FindFullByWebLog succeeds when posts are found`` (data: IData) = task {
let! posts = data.Post.FindFullByWebLog rootId
Expect.hasLength posts 5 "There should have been 5 posts returned"
let allPosts = [ testPost1; episode1; episode2; something; incomplete ]
posts |> List.iter (fun it ->
Expect.contains allPosts it.Id $"Post ID {it.Id} unexpected"
if it.Id = episode1 then
Expect.isNonEmpty it.Metadata "Metadata should have been retrieved"
Expect.isNonEmpty it.PriorPermalinks "Prior permalinks should have been retrieved"
Expect.isNonEmpty it.Revisions "Revisions should have been retrieved")
}
let ``FindFullByWebLog succeeds when posts are not found`` (data: IData) = task {
let! posts = data.Post.FindFullByWebLog (WebLogId "nonexistent")
Expect.isEmpty posts "No posts should have been retrieved"
}
let ``FindPageOfCategorizedPosts succeeds when posts are found`` (data: IData) = task {
let! posts = data.Post.FindPageOfCategorizedPosts rootId testCatIds 1 1
Expect.hasLength posts 2 "There should be 2 posts returned"
Expect.equal posts[0].Id something "The wrong post was returned for page 1"
ensureEmpty posts
let! posts = data.Post.FindPageOfCategorizedPosts rootId testCatIds 2 1
Expect.hasLength posts 1 "There should be 1 post returned"
Expect.equal posts[0].Id testPost1 "The wrong post was returned for page 2"
ensureEmpty posts
}
let ``FindPageOfCategorizedPosts succeeds when finding a too-high page number`` (data: IData) = task {
let! posts = data.Post.FindPageOfCategorizedPosts rootId testCatIds 17 2
Expect.hasLength posts 0 "There should have been no posts returned (not enough posts)"
}
let ``FindPageOfCategorizedPosts succeeds when a category has no posts`` (data: IData) = task {
let! posts = data.Post.FindPageOfCategorizedPosts rootId [ CategoryId "nope" ] 1 1
Expect.hasLength posts 0 "There should have been no posts returned (none match)"
}
let ``FindPageOfPosts succeeds when posts are found`` (data: IData) = task {
let ensureNoText (posts: Post list) =
for post in posts do Expect.equal post.Text "" $"There should be no text (post ID {post.Id})"
let! posts = data.Post.FindPageOfPosts rootId 1 2
Expect.hasLength posts 3 "There should have been 3 posts returned for page 1"
Expect.equal posts[0].Id incomplete "Page 1, post 1 is incorrect"
Expect.equal posts[1].Id something "Page 1, post 2 is incorrect"
Expect.equal posts[2].Id episode2 "Page 1, post 3 is incorrect"
ensureNoText posts
ensureEmpty posts
let! posts = data.Post.FindPageOfPosts rootId 2 2
Expect.hasLength posts 3 "There should have been 3 posts returned for page 2"
Expect.equal posts[0].Id episode2 "Page 2, post 1 is incorrect"
Expect.equal posts[1].Id episode1 "Page 2, post 2 is incorrect"
Expect.equal posts[2].Id testPost1 "Page 2, post 3 is incorrect"
ensureNoText posts
ensureEmpty posts
let! posts = data.Post.FindPageOfPosts rootId 3 2
Expect.hasLength posts 1 "There should have been 1 post returned for page 3"
Expect.equal posts[0].Id testPost1 "Page 3, post 1 is incorrect"
ensureNoText posts
ensureEmpty posts
}
let ``FindPageOfPosts succeeds when finding a too-high page number`` (data: IData) = task {
let! posts = data.Post.FindPageOfPosts rootId 88 3
Expect.isEmpty posts "There should have been no posts returned (not enough posts)"
}
let ``FindPageOfPosts succeeds when there are no posts`` (data: IData) = task {
let! posts = data.Post.FindPageOfPosts (WebLogId "no-posts") 1 25
Expect.isEmpty posts "There should have been no posts returned (no posts)"
}
let ``FindPageOfPublishedPosts succeeds when posts are found`` (data: IData) = task {
let! posts = data.Post.FindPageOfPublishedPosts rootId 1 3
Expect.hasLength posts 4 "There should have been 4 posts returned for page 1"
Expect.equal posts[0].Id something "Page 1, post 1 is incorrect"
Expect.equal posts[1].Id episode2 "Page 1, post 2 is incorrect"
Expect.equal posts[2].Id episode1 "Page 1, post 3 is incorrect"
Expect.equal posts[3].Id testPost1 "Page 1, post 4 is incorrect"
ensureHasText posts
ensureEmpty posts
let! posts = data.Post.FindPageOfPublishedPosts rootId 2 2
Expect.hasLength posts 2 "There should have been 2 posts returned for page 2"
Expect.equal posts[0].Id episode1 "Page 2, post 1 is incorrect"
Expect.equal posts[1].Id testPost1 "Page 2, post 2 is incorrect"
ensureHasText posts
ensureEmpty posts
}
let ``FindPageOfPublishedPosts succeeds when finding a too-high page number`` (data: IData) = task {
let! posts = data.Post.FindPageOfPublishedPosts rootId 7 22
Expect.isEmpty posts "There should have been no posts returned (not enough posts)"
}
let ``FindPageOfPublishedPosts succeeds when there are no posts`` (data: IData) = task {
let! posts = data.Post.FindPageOfPublishedPosts (WebLogId "empty") 1 8
Expect.isEmpty posts "There should have been no posts returned (no posts)"
}
let ``FindPageOfTaggedPosts succeeds when posts are found`` (data: IData) = task {
let! posts = data.Post.FindPageOfTaggedPosts rootId "f#" 1 1
Expect.hasLength posts 2 "There should have been 2 posts returned"
Expect.equal posts[0].Id something "Page 1, post 1 is incorrect"
Expect.equal posts[1].Id testPost1 "Page 1, post 2 is incorrect"
ensureHasText posts
ensureEmpty posts
let! posts = data.Post.FindPageOfTaggedPosts rootId "f#" 2 1
Expect.hasLength posts 1 "There should have been 1 posts returned"
Expect.equal posts[0].Id testPost1 "Page 2, post 1 is incorrect"
ensureHasText posts
ensureEmpty posts
}
let ``FindPageOfTaggedPosts succeeds when posts are found (excluding drafts)`` (data: IData) = task {
let! posts = data.Post.FindPageOfTaggedPosts rootId "speculation" 1 10
Expect.hasLength posts 1 "There should have been 1 post returned"
Expect.equal posts[0].Id something "Post 1 is incorrect"
ensureHasText posts
ensureEmpty posts
}
let ``FindPageOfTaggedPosts succeeds when finding a too-high page number`` (data: IData) = task {
let! posts = data.Post.FindPageOfTaggedPosts rootId "f#" 436 18
Expect.isEmpty posts "There should have been no posts returned (not enough posts)"
}
let ``FindPageOfTaggedPosts succeeds when there are no posts`` (data: IData) = task {
let! posts = data.Post.FindPageOfTaggedPosts rootId "non-existent-tag" 1 8
Expect.isEmpty posts "There should have been no posts returned (no posts)"
}
let ``FindSurroundingPosts succeeds when there is no next newer post`` (data: IData) = task {
let! older, newer = data.Post.FindSurroundingPosts rootId somethingPublished
Expect.isSome older "There should have been an older post"
Expect.equal older.Value.Id episode2 "The next older post is incorrect"
ensureHasText [ older.Value ]
ensureEmpty [ older.Value ]
Expect.isNone newer "There should not have been a newer post"
}
let ``FindSurroundingPosts succeeds when there is no next older post`` (data: IData) = task {
let! older, newer = data.Post.FindSurroundingPosts rootId testPost1Published
Expect.isNone older "There should not have been an older post"
Expect.isSome newer "There should have been a newer post"
Expect.equal newer.Value.Id episode1 "The next newer post is incorrect"
ensureHasText [ newer.Value ]
ensureEmpty [ newer.Value ]
}
let ``FindSurroundingPosts succeeds when older and newer exist`` (data: IData) = task {
let! older, newer = data.Post.FindSurroundingPosts rootId episode1Published
Expect.isSome older "There should have been an older post"
Expect.equal older.Value.Id testPost1 "The next older post is incorrect"
Expect.isSome newer "There should have been a newer post"
Expect.equal newer.Value.Id episode2 "The next newer post is incorrect"
ensureHasText [ older.Value; newer.Value ]
ensureEmpty [ older.Value; newer.Value ]
}
let ``Update succeeds when the post exists`` (data: IData) = task {
let! before = data.Post.FindFullById (PostId "a-new-post") (WebLogId "test")
Expect.isSome before "The post to be updated should have been found"
do! data.Post.Update
{ before.Value with
AuthorId = WebLogUserId "someone-else"
Status = Draft
Title = "An Updated Test Post"
Permalink = Permalink "2021/updated-post.html"
PublishedOn = None
UpdatedOn = Noda.epoch + Duration.FromDays 4
Template = Some "other"
Text = "<p>Updated text here"
CategoryIds = [ CategoryId "c"; CategoryId "d"; CategoryId "e" ]
Tags = [ "alpha"; "beta"; "nu"; "zeta" ]
Episode = None
Metadata = [ { Name = "Howdy"; Value = "Pardner" } ]
PriorPermalinks = Permalink "2020/test-post.html" :: before.Value.PriorPermalinks
Revisions =
{ AsOf = Noda.epoch + Duration.FromDays 4; Text = Html "<p>Updated text here" }
:: before.Value.Revisions }
let! after = data.Post.FindFullById (PostId "a-new-post") (WebLogId "test")
Expect.isSome after "The updated post should have been found"
let post = after.Value
Expect.equal post.AuthorId (WebLogUserId "someone-else") "Updated author is incorrect"
Expect.equal post.Status Draft "Updated status is incorrect"
Expect.equal post.Title "An Updated Test Post" "Updated title is incorrect"
Expect.equal post.Permalink (Permalink "2021/updated-post.html") "Updated permalink is incorrect"
Expect.isNone post.PublishedOn "Updated post should not have had a published-on date/time"
Expect.equal post.UpdatedOn (Noda.epoch + Duration.FromDays 4) "Updated updated-on date/time is incorrect"
Expect.equal post.Template (Some "other") "Updated template is incorrect"
Expect.equal post.Text "<p>Updated text here" "Updated text is incorrect"
Expect.equal
post.CategoryIds [ CategoryId "c"; CategoryId "d"; CategoryId "e" ] "Updated category IDs are incorrect"
Expect.equal post.Tags [ "alpha"; "beta"; "nu"; "zeta" ] "Updated tags are incorrect"
Expect.isNone post.Episode "Update episode is incorrect"
Expect.equal post.Metadata [ { Name = "Howdy"; Value = "Pardner" } ] "Updated metadata is incorrect"
Expect.equal
post.PriorPermalinks
[ Permalink "2020/test-post.html"; Permalink "2020/test-post-a.html" ]
"Updated prior permalinks are incorrect"
Expect.equal
post.Revisions
[ { AsOf = Noda.epoch + Duration.FromDays 4; Text = Html "<p>Updated text here" }
{ AsOf = Noda.epoch + Duration.FromMinutes 1L; Text = Html "<p>Test text here" } ]
"Updated revisions are incorrect"
}
let ``Update succeeds when the post does not exist`` (data: IData) = task {
let postId = PostId "lost-post"
do! data.Post.Update { Post.Empty with Id = postId; WebLogId = rootId }
let! post = data.Post.FindById postId rootId
Expect.isNone post "A post should not have been retrieved"
}
let ``UpdatePriorPermalinks succeeds when the post exists`` (data: IData) = task {
let links = [ Permalink "2024/ep-1.html"; Permalink "2023/ep-1.html" ]
let! found = data.Post.UpdatePriorPermalinks episode1 rootId links
Expect.isTrue found "The permalinks should have been updated"
let! post = data.Post.FindFullById episode1 rootId
Expect.isSome post "The post should have been found"
Expect.equal post.Value.PriorPermalinks links "The prior permalinks were not correct"
}
let ``UpdatePriorPermalinks succeeds when the post does not exist`` (data: IData) = task {
let! found =
data.Post.UpdatePriorPermalinks (PostId "silence") WebLogId.Empty [ Permalink "a.html"; Permalink "b.html" ]
Expect.isFalse found "The permalinks should not have been updated"
}
let ``Delete succeeds when a post is deleted`` (data: IData) = task {
let! deleted = data.Post.Delete episode2 rootId
Expect.isTrue deleted "The post should have been deleted"
}
let ``Delete succeeds when a post is not deleted`` (data: IData) = task {
let! deleted = data.Post.Delete episode2 rootId // this was deleted above
Expect.isFalse deleted "A post should not have been deleted"
}

View File

@@ -0,0 +1,722 @@
module PostgresDataTests
open BitBadger.Documents.Postgres
open Expecto
open Microsoft.Extensions.Logging.Abstractions
open MyWebLog
open MyWebLog.Converters
open MyWebLog.Data
open Newtonsoft.Json
open Npgsql
open ThrowawayDb.Postgres
/// JSON serializer
let private ser = Json.configure (JsonSerializer.CreateDefault())
/// The throwaway database (deleted when disposed)
let mutable private db: ThrowawayDatabase option = None
/// Create a PostgresData instance for testing
let private mkData () =
PostgresData(NullLogger<PostgresData>(), ser) :> IData
/// The host for the PostgreSQL test database (defaults to localhost)
let private testHost =
RethinkDbDataTests.env "PG_HOST" "localhost"
/// The database name for the PostgreSQL test database (defaults to postgres)
let private testDb =
RethinkDbDataTests.env "PG_DB" "postgres"
/// The user ID for the PostgreSQL test database (defaults to postgres)
let private testUser =
RethinkDbDataTests.env "PG_USER" "postgres"
/// The password for the PostgreSQL test database (defaults to postgres)
let private testPw =
RethinkDbDataTests.env "PG_PW" "postgres"
/// Create a fresh environment from the root backup
let private freshEnvironment () = task {
if Option.isSome db then db.Value.Dispose()
db <- Some (ThrowawayDatabase.Create $"Host={testHost};Database={testDb};User ID={testUser};Password={testPw}")
let source = NpgsqlDataSourceBuilder db.Value.ConnectionString
let _ = source.UseNodaTime()
Configuration.useDataSource (source.Build())
let env = mkData ()
do! env.StartUp()
// This exercises Restore for all implementations; all tests are dependent on it working as expected
do! Maintenance.Backup.restoreBackup "root-weblog.json" None false false env
}
/// Set up the environment for the PostgreSQL tests
let private environmentSetUp = testTask "creating database" {
do! freshEnvironment ()
}
/// Integration tests for the Category implementation in PostgreSQL
let private categoryTests = testList "Category" [
testTask "Add succeeds" {
do! CategoryDataTests.``Add succeeds`` (mkData ())
}
testList "CountAll" [
testTask "succeeds when categories exist" {
do! CategoryDataTests.``CountAll succeeds when categories exist`` (mkData ())
}
testTask "succeeds when categories do not exist" {
do! CategoryDataTests.``CountAll succeeds when categories do not exist`` (mkData ())
}
]
testList "CountTopLevel" [
testTask "succeeds when top-level categories exist" {
do! CategoryDataTests.``CountTopLevel succeeds when top-level categories exist`` (mkData ())
}
testTask "succeeds when no top-level categories exist" {
do! CategoryDataTests.``CountTopLevel succeeds when no top-level categories exist`` (mkData ())
}
]
testTask "FindAllForView succeeds" {
do! CategoryDataTests.``FindAllForView succeeds`` (mkData ())
}
testList "FindById" [
testTask "succeeds when a category is found" {
do! CategoryDataTests.``FindById succeeds when a category is found`` (mkData ())
}
testTask "succeeds when a category is not found" {
do! CategoryDataTests.``FindById succeeds when a category is not found`` (mkData ())
}
]
testList "FindByWebLog" [
testTask "succeeds when categories exist" {
do! CategoryDataTests.``FindByWebLog succeeds when categories exist`` (mkData ())
}
testTask "succeeds when no categories exist" {
do! CategoryDataTests.``FindByWebLog succeeds when no categories exist`` (mkData ())
}
]
testTask "Update succeeds" {
do! CategoryDataTests.``Update succeeds`` (mkData ())
}
testList "Delete" [
testTask "succeeds when the category is deleted (no posts)" {
do! CategoryDataTests.``Delete succeeds when the category is deleted (no posts)`` (mkData ())
}
testTask "succeeds when the category does not exist" {
do! CategoryDataTests.``Delete succeeds when the category does not exist`` (mkData ())
}
testTask "succeeds when reassigning parent category to None" {
do! CategoryDataTests.``Delete succeeds when reassigning parent category to None`` (mkData ())
}
testTask "succeeds when reassigning parent category to Some" {
do! CategoryDataTests.``Delete succeeds when reassigning parent category to Some`` (mkData ())
}
testTask "succeeds and removes category from posts" {
do! CategoryDataTests.``Delete succeeds and removes category from posts`` (mkData ())
}
]
]
/// Integration tests for the Page implementation in PostgreSQL
let private pageTests = testList "Page" [
testTask "Add succeeds" {
do! PageDataTests.``Add succeeds`` (mkData ())
}
testTask "All succeeds" {
do! PageDataTests.``All succeeds`` (mkData ())
}
testTask "CountAll succeeds" {
do! PageDataTests.``CountAll succeeds`` (mkData ())
}
testTask "CountListed succeeds" {
do! PageDataTests.``CountListed succeeds`` (mkData ())
}
testList "FindById" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindById succeeds when a page is found`` (mkData ())
}
testTask "succeeds when a page is not found (incorrect weblog)" {
do! PageDataTests.``FindById succeeds when a page is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a page is not found (bad page ID)" {
do! PageDataTests.``FindById succeeds when a page is not found (bad page ID)`` (mkData ())
}
]
testList "FindByPermalink" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindByPermalink succeeds when a page is found`` (mkData ())
}
testTask "succeeds when a page is not found (incorrect weblog)" {
do! PageDataTests.``FindByPermalink succeeds when a page is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a page is not found (no such permalink)" {
do! PageDataTests.``FindByPermalink succeeds when a page is not found (no such permalink)`` (mkData ())
}
]
testList "FindCurrentPermalink" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindCurrentPermalink succeeds when a page is found`` (mkData ())
}
testTask "succeeds when a page is not found" {
do! PageDataTests.``FindCurrentPermalink succeeds when a page is not found`` (mkData ())
}
]
testList "FindFullById" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindFullById succeeds when a page is found`` (mkData ())
}
testTask "succeeds when a page is not found" {
do! PageDataTests.``FindFullById succeeds when a page is not found`` (mkData ())
}
]
testList "FindFullByWebLog" [
testTask "succeeds when pages are found" {
do! PageDataTests.``FindFullByWebLog succeeds when pages are found`` (mkData ())
}
testTask "succeeds when a pages are not found" {
do! PageDataTests.``FindFullByWebLog succeeds when pages are not found`` (mkData ())
}
]
testList "FindListed" [
testTask "succeeds when pages are found" {
do! PageDataTests.``FindListed succeeds when pages are found`` (mkData ())
}
testTask "succeeds when a pages are not found" {
do! PageDataTests.``FindListed succeeds when pages are not found`` (mkData ())
}
]
testList "FindPageOfPages" [
testTask "succeeds when pages are found" {
do! PageDataTests.``FindPageOfPages succeeds when pages are found`` (mkData ())
}
testTask "succeeds when a pages are not found" {
do! PageDataTests.``FindPageOfPages succeeds when pages are not found`` (mkData ())
}
]
testList "Update" [
testTask "succeeds when the page exists" {
do! PageDataTests.``Update succeeds when the page exists`` (mkData ())
}
testTask "succeeds when the page does not exist" {
do! PageDataTests.``Update succeeds when the page does not exist`` (mkData ())
}
]
testList "UpdatePriorPermalinks" [
testTask "succeeds when the page exists" {
do! PageDataTests.``UpdatePriorPermalinks succeeds when the page exists`` (mkData ())
}
testTask "succeeds when the page does not exist" {
do! PageDataTests.``UpdatePriorPermalinks succeeds when the page does not exist`` (mkData ())
}
]
testList "Delete" [
testTask "succeeds when a page is deleted" {
do! PageDataTests.``Delete succeeds when a page is deleted`` (mkData ())
let! revisions =
Custom.scalar
"SELECT COUNT(*) AS it FROM page_revision WHERE page_id = @id"
[ idParam PageDataTests.coolPageId ]
toCount
Expect.equal revisions 0 "All revisions for the page should have been deleted"
}
testTask "succeeds when a page is not deleted" {
do! PageDataTests.``Delete succeeds when a page is not deleted`` (mkData ())
}
]
]
/// Integration tests for the Post implementation in PostgreSQL
let private postTests = testList "Post" [
testTask "Add succeeds" {
// We'll need the root website categories restored for these tests
do! freshEnvironment ()
do! PostDataTests.``Add succeeds`` (mkData ())
}
testTask "CountByStatus succeeds" {
do! PostDataTests.``CountByStatus succeeds`` (mkData ())
}
testList "FindById" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindById succeeds when a post is found`` (mkData ())
}
testTask "succeeds when a post is not found (incorrect weblog)" {
do! PostDataTests.``FindById succeeds when a post is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a post is not found (bad post ID)" {
do! PostDataTests.``FindById succeeds when a post is not found (bad post ID)`` (mkData ())
}
]
testList "FindByPermalink" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindByPermalink succeeds when a post is found`` (mkData ())
}
testTask "succeeds when a post is not found (incorrect weblog)" {
do! PostDataTests.``FindByPermalink succeeds when a post is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a post is not found (no such permalink)" {
do! PostDataTests.``FindByPermalink succeeds when a post is not found (no such permalink)`` (mkData ())
}
]
testList "FindCurrentPermalink" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindCurrentPermalink succeeds when a post is found`` (mkData ())
}
testTask "succeeds when a post is not found" {
do! PostDataTests.``FindCurrentPermalink succeeds when a post is not found`` (mkData ())
}
]
testList "FindFullById" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindFullById succeeds when a post is found`` (mkData ())
}
testTask "succeeds when a post is not found" {
do! PostDataTests.``FindFullById succeeds when a post is not found`` (mkData ())
}
]
testList "FindFullByWebLog" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindFullByWebLog succeeds when posts are found`` (mkData ())
}
testTask "succeeds when a posts are not found" {
do! PostDataTests.``FindFullByWebLog succeeds when posts are not found`` (mkData ())
}
]
testList "FindPageOfCategorizedPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfCategorizedPosts succeeds when posts are found`` (mkData ())
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfCategorizedPosts succeeds when finding a too-high page number`` (mkData ())
}
testTask "succeeds when a category has no posts" {
do! PostDataTests.``FindPageOfCategorizedPosts succeeds when a category has no posts`` (mkData ())
}
]
testList "FindPageOfPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfPosts succeeds when posts are found`` (mkData ())
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfPosts succeeds when finding a too-high page number`` (mkData ())
}
testTask "succeeds when there are no posts" {
do! PostDataTests.``FindPageOfPosts succeeds when there are no posts`` (mkData ())
}
]
testList "FindPageOfPublishedPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfPublishedPosts succeeds when posts are found`` (mkData ())
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfPublishedPosts succeeds when finding a too-high page number`` (mkData ())
}
testTask "succeeds when there are no posts" {
do! PostDataTests.``FindPageOfPublishedPosts succeeds when there are no posts`` (mkData ())
}
]
testList "FindPageOfTaggedPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when posts are found`` (mkData ())
}
testTask "succeeds when posts are found (excluding drafts)" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when posts are found (excluding drafts)`` (mkData ())
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when finding a too-high page number`` (mkData ())
}
testTask "succeeds when there are no posts" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when there are no posts`` (mkData ())
}
]
testList "FindSurroundingPosts" [
testTask "succeeds when there is no next newer post" {
do! PostDataTests.``FindSurroundingPosts succeeds when there is no next newer post`` (mkData ())
}
testTask "succeeds when there is no next older post" {
do! PostDataTests.``FindSurroundingPosts succeeds when there is no next older post`` (mkData ())
}
testTask "succeeds when older and newer exist" {
do! PostDataTests.``FindSurroundingPosts succeeds when older and newer exist`` (mkData ())
}
]
testList "Update" [
testTask "succeeds when the post exists" {
do! PostDataTests.``Update succeeds when the post exists`` (mkData ())
}
testTask "succeeds when the post does not exist" {
do! PostDataTests.``Update succeeds when the post does not exist`` (mkData ())
}
]
testList "UpdatePriorPermalinks" [
testTask "succeeds when the post exists" {
do! PostDataTests.``UpdatePriorPermalinks succeeds when the post exists`` (mkData ())
}
testTask "succeeds when the post does not exist" {
do! PostDataTests.``UpdatePriorPermalinks succeeds when the post does not exist`` (mkData ())
}
]
testList "Delete" [
testTask "succeeds when a post is deleted" {
do! PostDataTests.``Delete succeeds when a post is deleted`` (mkData ())
let! revisions =
Custom.scalar
"SELECT COUNT(*) AS it FROM post_revision WHERE post_id = @id"
[ idParam PostDataTests.episode2 ]
toCount
Expect.equal revisions 0 "All revisions for the post should have been deleted"
}
testTask "succeeds when a post is not deleted" {
do! PostDataTests.``Delete succeeds when a post is not deleted`` (mkData ())
}
]
]
let private tagMapTests = testList "TagMap" [
testList "FindById" [
testTask "succeeds when a tag mapping is found" {
do! TagMapDataTests.``FindById succeeds when a tag mapping is found`` (mkData ())
}
testTask "succeeds when a tag mapping is not found (incorrect weblog)" {
do! TagMapDataTests.``FindById succeeds when a tag mapping is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a tag mapping is not found (bad tag map ID)" {
do! TagMapDataTests.``FindById succeeds when a tag mapping is not found (bad tag map ID)`` (mkData ())
}
]
testList "FindByUrlValue" [
testTask "succeeds when a tag mapping is found" {
do! TagMapDataTests.``FindByUrlValue succeeds when a tag mapping is found`` (mkData ())
}
testTask "succeeds when a tag mapping is not found (incorrect weblog)" {
do! TagMapDataTests.``FindByUrlValue succeeds when a tag mapping is not found (incorrect weblog)``
(mkData ())
}
testTask "succeeds when a tag mapping is not found (no such value)" {
do! TagMapDataTests.``FindByUrlValue succeeds when a tag mapping is not found (no such value)`` (mkData ())
}
]
testList "FindByWebLog" [
testTask "succeeds when tag mappings are found" {
do! TagMapDataTests.``FindByWebLog succeeds when tag mappings are found`` (mkData ())
}
testTask "succeeds when no tag mappings are found" {
do! TagMapDataTests.``FindByWebLog succeeds when no tag mappings are found`` (mkData ())
}
]
testList "FindMappingForTags" [
testTask "succeeds when mappings exist" {
do! TagMapDataTests.``FindMappingForTags succeeds when mappings exist`` (mkData ())
}
testTask "succeeds when no mappings exist" {
do! TagMapDataTests.``FindMappingForTags succeeds when no mappings exist`` (mkData ())
}
]
testList "Save" [
testTask "succeeds when adding a tag mapping" {
do! TagMapDataTests.``Save succeeds when adding a tag mapping`` (mkData ())
}
testTask "succeeds when updating a tag mapping" {
do! TagMapDataTests.``Save succeeds when updating a tag mapping`` (mkData ())
}
]
testList "Delete" [
testTask "succeeds when a tag mapping is deleted" {
do! TagMapDataTests.``Delete succeeds when a tag mapping is deleted`` (mkData ())
}
testTask "succeeds when a tag mapping is not deleted" {
do! TagMapDataTests.``Delete succeeds when a tag mapping is not deleted`` (mkData ())
}
]
]
let private themeTests = testList "Theme" [
testTask "All succeeds" {
do! ThemeDataTests.``All succeeds`` (mkData ())
}
testList "Exists" [
testTask "succeeds when the theme exists" {
do! ThemeDataTests.``Exists succeeds when the theme exists`` (mkData ())
}
testTask "succeeds when the theme does not exist" {
do! ThemeDataTests.``Exists succeeds when the theme does not exist`` (mkData ())
}
]
testList "FindById" [
testTask "succeeds when the theme exists" {
do! ThemeDataTests.``FindById succeeds when the theme exists`` (mkData ())
}
testTask "succeeds when the theme does not exist" {
do! ThemeDataTests.``FindById succeeds when the theme does not exist`` (mkData ())
}
]
testList "FindByIdWithoutText" [
testTask "succeeds when the theme exists" {
do! ThemeDataTests.``FindByIdWithoutText succeeds when the theme exists`` (mkData ())
}
testTask "succeeds when the theme does not exist" {
do! ThemeDataTests.``FindByIdWithoutText succeeds when the theme does not exist`` (mkData ())
}
]
testList "Save" [
testTask "succeeds when adding a theme" {
do! ThemeDataTests.``Save succeeds when adding a theme`` (mkData ())
}
testTask "succeeds when updating a theme" {
do! ThemeDataTests.``Save succeeds when updating a theme`` (mkData ())
}
]
testList "Delete" [
testTask "succeeds when a theme is deleted" {
do! ThemeDataTests.``Delete succeeds when a theme is deleted`` (mkData ())
}
testTask "succeeds when a theme is not deleted" {
do! ThemeDataTests.``Delete succeeds when a theme is not deleted`` (mkData ())
}
]
]
let private themeAssetTests = testList "ThemeAsset" [
testList "Save" [
testTask "succeeds when adding an asset" {
do! ThemeDataTests.Asset.``Save succeeds when adding an asset`` (mkData ())
}
testTask "succeeds when updating an asset" {
do! ThemeDataTests.Asset.``Save succeeds when updating an asset`` (mkData ())
}
]
testTask "All succeeds" {
do! ThemeDataTests.Asset.``All succeeds`` (mkData ())
}
testList "FindById" [
testTask "succeeds when an asset is found" {
do! ThemeDataTests.Asset.``FindById succeeds when an asset is found`` (mkData ())
}
testTask "succeeds when an asset is not found" {
do! ThemeDataTests.Asset.``FindById succeeds when an asset is not found`` (mkData ())
}
]
testList "FindByTheme" [
testTask "succeeds when assets exist" {
do! ThemeDataTests.Asset.``FindByTheme succeeds when assets exist`` (mkData ())
}
testTask "succeeds when assets do not exist" {
do! ThemeDataTests.Asset.``FindByTheme succeeds when assets do not exist`` (mkData ())
}
]
testList "FindByThemeWithData" [
testTask "succeeds when assets exist" {
do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets exist`` (mkData ())
}
testTask "succeeds when assets do not exist" {
do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets do not exist`` (mkData ())
}
]
testList "DeleteByTheme" [
testTask "succeeds when assets are deleted" {
do! ThemeDataTests.Asset.``DeleteByTheme succeeds when assets are deleted`` (mkData ())
}
testTask "succeeds when no assets are deleted" {
do! ThemeDataTests.Asset.``DeleteByTheme succeeds when no assets are deleted`` (mkData ())
}
]
]
let private uploadTests = testList "Upload" [
testTask "Add succeeds" {
do! UploadDataTests.``Add succeeds`` (mkData ())
}
testList "FindByPath" [
testTask "succeeds when an upload is found" {
do! UploadDataTests.``FindByPath succeeds when an upload is found`` (mkData ())
}
testTask "succeeds when an upload is not found (incorrect weblog)" {
do! UploadDataTests.``FindByPath succeeds when an upload is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when an upload is not found (bad path)" {
do! UploadDataTests.``FindByPath succeeds when an upload is not found (bad path)`` (mkData ())
}
]
testList "FindByWebLog" [
testTask "succeeds when uploads exist" {
do! UploadDataTests.``FindByWebLog succeeds when uploads exist`` (mkData ())
}
testTask "succeeds when no uploads exist" {
do! UploadDataTests.``FindByWebLog succeeds when no uploads exist`` (mkData ())
}
]
testList "FindByWebLogWithData" [
testTask "succeeds when uploads exist" {
do! UploadDataTests.``FindByWebLogWithData succeeds when uploads exist`` (mkData ())
}
testTask "succeeds when no uploads exist" {
do! UploadDataTests.``FindByWebLogWithData succeeds when no uploads exist`` (mkData ())
}
]
testList "Delete" [
testTask "succeeds when an upload is deleted" {
do! UploadDataTests.``Delete succeeds when an upload is deleted`` (mkData ())
}
testTask "succeeds when an upload is not deleted" {
do! UploadDataTests.``Delete succeeds when an upload is not deleted`` (mkData ())
}
]
]
let private webLogUserTests = testList "WebLogUser" [
testTask "Add succeeds" {
// This restore ensures all the posts and pages exist
do! freshEnvironment ()
do! WebLogUserDataTests.``Add succeeds`` (mkData ())
}
testList "FindByEmail" [
testTask "succeeds when a user is found" {
do! WebLogUserDataTests.``FindByEmail succeeds when a user is found`` (mkData ())
}
testTask "succeeds when a user is not found (incorrect weblog)" {
do! WebLogUserDataTests.``FindByEmail succeeds when a user is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a user is not found (bad email)" {
do! WebLogUserDataTests.``FindByEmail succeeds when a user is not found (bad email)`` (mkData ())
}
]
testList "FindById" [
testTask "succeeds when a user is found" {
do! WebLogUserDataTests.``FindById succeeds when a user is found`` (mkData ())
}
testTask "succeeds when a user is not found (incorrect weblog)" {
do! WebLogUserDataTests.``FindById succeeds when a user is not found (incorrect weblog)`` (mkData ())
}
testTask "succeeds when a user is not found (bad ID)" {
do! WebLogUserDataTests.``FindById succeeds when a user is not found (bad ID)`` (mkData ())
}
]
testList "FindByWebLog" [
testTask "succeeds when users exist" {
do! WebLogUserDataTests.``FindByWebLog succeeds when users exist`` (mkData ())
}
testTask "succeeds when no users exist" {
do! WebLogUserDataTests.``FindByWebLog succeeds when no users exist`` (mkData ())
}
]
testList "FindNames" [
testTask "succeeds when users exist" {
do! WebLogUserDataTests.``FindNames succeeds when users exist`` (mkData ())
}
testTask "succeeds when users do not exist" {
do! WebLogUserDataTests.``FindNames succeeds when users do not exist`` (mkData ())
}
]
testList "SetLastSeen" [
testTask "succeeds when the user exists" {
do! WebLogUserDataTests.``SetLastSeen succeeds when the user exists`` (mkData ())
}
testTask "succeeds when the user does not exist" {
do! WebLogUserDataTests.``SetLastSeen succeeds when the user does not exist`` (mkData ())
}
]
testList "Update" [
testTask "succeeds when the user exists" {
do! WebLogUserDataTests.``Update succeeds when the user exists`` (mkData ())
}
testTask "succeeds when the user does not exist" {
do! WebLogUserDataTests.``Update succeeds when the user does not exist`` (mkData ())
}
]
testList "Delete" [
testTask "fails when the user is the author of a page" {
do! WebLogUserDataTests.``Delete fails when the user is the author of a page`` (mkData ())
}
testTask "fails when the user is the author of a post" {
do! WebLogUserDataTests.``Delete fails when the user is the author of a post`` (mkData ())
}
testTask "succeeds when the user is not an author" {
do! WebLogUserDataTests.``Delete succeeds when the user is not an author`` (mkData ())
}
testTask "succeeds when the user does not exist" {
do! WebLogUserDataTests.``Delete succeeds when the user does not exist`` (mkData ())
}
]
]
let private webLogTests = testList "WebLog" [
testTask "Add succeeds" {
do! WebLogDataTests.``Add succeeds`` (mkData ())
}
testTask "All succeeds" {
do! WebLogDataTests.``All succeeds`` (mkData ())
}
testList "FindByHost" [
testTask "succeeds when a web log is found" {
do! WebLogDataTests.``FindByHost succeeds when a web log is found`` (mkData ())
}
testTask "succeeds when a web log is not found" {
do! WebLogDataTests.``FindByHost succeeds when a web log is not found`` (mkData ())
}
]
testList "FindById" [
testTask "succeeds when a web log is found" {
do! WebLogDataTests.``FindById succeeds when a web log is found`` (mkData ())
}
testTask "succeeds when a web log is not found" {
do! WebLogDataTests.``FindById succeeds when a web log is not found`` (mkData ())
}
]
testList "UpdateRedirectRules" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``UpdateRedirectRules succeeds when the web log exists`` (mkData ())
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``UpdateRedirectRules succeeds when the web log does not exist`` (mkData ())
}
]
testList "UpdateRssOptions" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``UpdateRssOptions succeeds when the web log exists`` (mkData ())
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``UpdateRssOptions succeeds when the web log does not exist`` (mkData ())
}
]
testList "UpdateSettings" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``UpdateSettings succeeds when the web log exists`` (mkData ())
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``UpdateSettings succeeds when the web log does not exist`` (mkData ())
}
]
testList "Delete" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``Delete succeeds when the web log exists`` (mkData ())
let! revisions =
Custom.scalar
"SELECT (SELECT COUNT(*) FROM page_revision) + (SELECT COUNT(*) FROM post_revision) AS it"
[]
toCount
Expect.equal revisions 0 "All revisions should be deleted"
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``Delete succeeds when the web log does not exist`` (mkData ())
}
]
]
/// Drop the throwaway PostgreSQL database
let private environmentCleanUp = test "Clean Up" {
if db.IsSome then db.Value.Dispose()
}
/// All PostgreSQL data tests
let all =
testList "PostgresData"
[ environmentSetUp
categoryTests
pageTests
postTests
tagMapTests
themeTests
themeAssetTests
uploadTests
webLogUserTests
webLogTests
environmentCleanUp ]
|> testSequenced

View File

@@ -0,0 +1,704 @@
module RethinkDbDataTests
open System
open Expecto
open Microsoft.Extensions.Logging.Abstractions
open MyWebLog
open MyWebLog.Converters
open MyWebLog.Data
open RethinkDb.Driver.FSharp
open RethinkDb.Driver.Net
/// Get an environment variable, using the given value as the default if it is not set
let env name value =
match Environment.GetEnvironmentVariable $"MWL_TEST_{name}" with
| null -> value
| it when it.Trim() = "" -> value
| it -> it
/// The data configuration for the test database
let private dataCfg =
DataConfig.FromUri (env "RETHINK_URI" "rethinkdb://172.17.0.2/mwl_test")
/// The active data instance to use for testing
let mutable private data: IData option = None
/// Dispose the existing data
let private disposeData () = task {
if data.IsSome then
let conn = (data.Value :?> RethinkDbData).Conn
do! rethink { dbDrop dataCfg.Database; write; withRetryOnce; ignoreResult conn }
conn.Dispose()
data <- None
}
/// Create a new data implementation instance
let private newData () =
let log = NullLogger<RethinkDbData>()
let conn = dataCfg.CreateConnection log
RethinkDbData(conn, dataCfg, log)
/// Create a fresh environment from the root backup
let private freshEnvironment () = task {
do! disposeData ()
data <- Some (newData ())
do! data.Value.StartUp()
// This exercises Restore for all implementations; all tests are dependent on it working as expected
do! Maintenance.Backup.restoreBackup "root-weblog.json" None false false data.Value
}
/// Set up the environment for the RethinkDB tests
let private environmentSetUp = testTask "creating database" {
let _ = Json.configure Converter.Serializer
do! freshEnvironment ()
}
/// Integration tests for the Category implementation in RethinkDB
let private categoryTests = testList "Category" [
testTask "Add succeeds" {
do! CategoryDataTests.``Add succeeds`` data.Value
}
testList "CountAll" [
testTask "succeeds when categories exist" {
do! CategoryDataTests.``CountAll succeeds when categories exist`` data.Value
}
testTask "succeeds when categories do not exist" {
do! CategoryDataTests.``CountAll succeeds when categories do not exist`` data.Value
}
]
testList "CountTopLevel" [
testTask "succeeds when top-level categories exist" {
do! CategoryDataTests.``CountTopLevel succeeds when top-level categories exist`` data.Value
}
testTask "succeeds when no top-level categories exist" {
do! CategoryDataTests.``CountTopLevel succeeds when no top-level categories exist`` data.Value
}
]
testTask "FindAllForView succeeds" {
do! CategoryDataTests.``FindAllForView succeeds`` data.Value
}
testList "FindById" [
testTask "succeeds when a category is found" {
do! CategoryDataTests.``FindById succeeds when a category is found`` data.Value
}
testTask "succeeds when a category is not found" {
do! CategoryDataTests.``FindById succeeds when a category is not found`` data.Value
}
]
testList "FindByWebLog" [
testTask "succeeds when categories exist" {
do! CategoryDataTests.``FindByWebLog succeeds when categories exist`` data.Value
}
testTask "succeeds when no categories exist" {
do! CategoryDataTests.``FindByWebLog succeeds when no categories exist`` data.Value
}
]
testTask "Update succeeds" {
do! CategoryDataTests.``Update succeeds`` data.Value
}
testList "Delete" [
testTask "succeeds when the category is deleted (no posts)" {
do! CategoryDataTests.``Delete succeeds when the category is deleted (no posts)`` data.Value
}
testTask "succeeds when the category does not exist" {
do! CategoryDataTests.``Delete succeeds when the category does not exist`` data.Value
}
testTask "succeeds when reassigning parent category to None" {
do! CategoryDataTests.``Delete succeeds when reassigning parent category to None`` data.Value
}
testTask "succeeds when reassigning parent category to Some" {
do! CategoryDataTests.``Delete succeeds when reassigning parent category to Some`` data.Value
}
testTask "succeeds and removes category from posts" {
do! CategoryDataTests.``Delete succeeds and removes category from posts`` data.Value
}
]
]
/// Integration tests for the Page implementation in RethinkDB
let private pageTests = testList "Page" [
testTask "Add succeeds" {
do! PageDataTests.``Add succeeds`` data.Value
}
testTask "All succeeds" {
do! PageDataTests.``All succeeds`` data.Value
}
testTask "CountAll succeeds" {
do! PageDataTests.``CountAll succeeds`` data.Value
}
testTask "CountListed succeeds" {
do! PageDataTests.``CountListed succeeds`` data.Value
}
testList "FindById" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindById succeeds when a page is found`` data.Value
}
testTask "succeeds when a page is not found (incorrect weblog)" {
do! PageDataTests.``FindById succeeds when a page is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a page is not found (bad page ID)" {
do! PageDataTests.``FindById succeeds when a page is not found (bad page ID)`` data.Value
}
]
testList "FindByPermalink" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindByPermalink succeeds when a page is found`` data.Value
}
testTask "succeeds when a page is not found (incorrect weblog)" {
do! PageDataTests.``FindByPermalink succeeds when a page is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a page is not found (no such permalink)" {
do! PageDataTests.``FindByPermalink succeeds when a page is not found (no such permalink)`` data.Value
}
]
testList "FindCurrentPermalink" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindCurrentPermalink succeeds when a page is found`` data.Value
}
testTask "succeeds when a page is not found" {
do! PageDataTests.``FindCurrentPermalink succeeds when a page is not found`` data.Value
}
]
testList "FindFullById" [
testTask "succeeds when a page is found" {
do! PageDataTests.``FindFullById succeeds when a page is found`` data.Value
}
testTask "succeeds when a page is not found" {
do! PageDataTests.``FindFullById succeeds when a page is not found`` data.Value
}
]
testList "FindFullByWebLog" [
testTask "succeeds when pages are found" {
do! PageDataTests.``FindFullByWebLog succeeds when pages are found`` data.Value
}
testTask "succeeds when a pages are not found" {
do! PageDataTests.``FindFullByWebLog succeeds when pages are not found`` data.Value
}
]
testList "FindListed" [
testTask "succeeds when pages are found" {
do! PageDataTests.``FindListed succeeds when pages are found`` data.Value
}
testTask "succeeds when a pages are not found" {
do! PageDataTests.``FindListed succeeds when pages are not found`` data.Value
}
]
testList "FindPageOfPages" [
testTask "succeeds when pages are found" {
do! PageDataTests.``FindPageOfPages succeeds when pages are found`` data.Value
}
testTask "succeeds when a pages are not found" {
do! PageDataTests.``FindPageOfPages succeeds when pages are not found`` data.Value
}
]
testList "Update" [
testTask "succeeds when the page exists" {
do! PageDataTests.``Update succeeds when the page exists`` data.Value
}
testTask "succeeds when the page does not exist" {
do! PageDataTests.``Update succeeds when the page does not exist`` data.Value
}
]
testList "UpdatePriorPermalinks" [
testTask "succeeds when the page exists" {
do! PageDataTests.``UpdatePriorPermalinks succeeds when the page exists`` data.Value
}
testTask "succeeds when the page does not exist" {
do! PageDataTests.``UpdatePriorPermalinks succeeds when the page does not exist`` data.Value
}
]
testList "Delete" [
testTask "succeeds when a page is deleted" {
do! PageDataTests.``Delete succeeds when a page is deleted`` data.Value
}
testTask "succeeds when a page is not deleted" {
do! PageDataTests.``Delete succeeds when a page is not deleted`` data.Value
}
]
]
/// Integration tests for the Post implementation in RethinkDB
let private postTests = testList "Post" [
testTask "Add succeeds" {
// We'll need the root website categories restored for these tests
do! freshEnvironment ()
do! PostDataTests.``Add succeeds`` data.Value
}
testTask "CountByStatus succeeds" {
do! PostDataTests.``CountByStatus succeeds`` data.Value
}
testList "FindById" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindById succeeds when a post is found`` data.Value
}
testTask "succeeds when a post is not found (incorrect weblog)" {
do! PostDataTests.``FindById succeeds when a post is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a post is not found (bad post ID)" {
do! PostDataTests.``FindById succeeds when a post is not found (bad post ID)`` data.Value
}
]
testList "FindByPermalink" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindByPermalink succeeds when a post is found`` data.Value
}
testTask "succeeds when a post is not found (incorrect weblog)" {
do! PostDataTests.``FindByPermalink succeeds when a post is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a post is not found (no such permalink)" {
do! PostDataTests.``FindByPermalink succeeds when a post is not found (no such permalink)`` data.Value
}
]
testList "FindCurrentPermalink" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindCurrentPermalink succeeds when a post is found`` data.Value
}
testTask "succeeds when a post is not found" {
do! PostDataTests.``FindCurrentPermalink succeeds when a post is not found`` data.Value
}
]
testList "FindFullById" [
testTask "succeeds when a post is found" {
do! PostDataTests.``FindFullById succeeds when a post is found`` data.Value
}
testTask "succeeds when a post is not found" {
do! PostDataTests.``FindFullById succeeds when a post is not found`` data.Value
}
]
testList "FindFullByWebLog" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindFullByWebLog succeeds when posts are found`` data.Value
}
testTask "succeeds when a posts are not found" {
do! PostDataTests.``FindFullByWebLog succeeds when posts are not found`` data.Value
}
]
testList "FindPageOfCategorizedPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfCategorizedPosts succeeds when posts are found`` data.Value
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfCategorizedPosts succeeds when finding a too-high page number`` data.Value
}
testTask "succeeds when a category has no posts" {
do! PostDataTests.``FindPageOfCategorizedPosts succeeds when a category has no posts`` data.Value
}
]
testList "FindPageOfPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfPosts succeeds when posts are found`` data.Value
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfPosts succeeds when finding a too-high page number`` data.Value
}
testTask "succeeds when there are no posts" {
do! PostDataTests.``FindPageOfPosts succeeds when there are no posts`` data.Value
}
]
testList "FindPageOfPublishedPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfPublishedPosts succeeds when posts are found`` data.Value
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfPublishedPosts succeeds when finding a too-high page number`` data.Value
}
testTask "succeeds when there are no posts" {
do! PostDataTests.``FindPageOfPublishedPosts succeeds when there are no posts`` data.Value
}
]
testList "FindPageOfTaggedPosts" [
testTask "succeeds when posts are found" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when posts are found`` data.Value
}
testTask "succeeds when posts are found (excluding drafts)" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when posts are found (excluding drafts)`` data.Value
}
testTask "succeeds when finding a too-high page number" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when finding a too-high page number`` data.Value
}
testTask "succeeds when there are no posts" {
do! PostDataTests.``FindPageOfTaggedPosts succeeds when there are no posts`` data.Value
}
]
testList "FindSurroundingPosts" [
testTask "succeeds when there is no next newer post" {
do! PostDataTests.``FindSurroundingPosts succeeds when there is no next newer post`` data.Value
}
testTask "succeeds when there is no next older post" {
do! PostDataTests.``FindSurroundingPosts succeeds when there is no next older post`` data.Value
}
testTask "succeeds when older and newer exist" {
do! PostDataTests.``FindSurroundingPosts succeeds when older and newer exist`` data.Value
}
]
testList "Update" [
testTask "succeeds when the post exists" {
do! PostDataTests.``Update succeeds when the post exists`` data.Value
}
testTask "succeeds when the post does not exist" {
do! PostDataTests.``Update succeeds when the post does not exist`` data.Value
}
]
testList "UpdatePriorPermalinks" [
testTask "succeeds when the post exists" {
do! PostDataTests.``UpdatePriorPermalinks succeeds when the post exists`` data.Value
}
testTask "succeeds when the post does not exist" {
do! PostDataTests.``UpdatePriorPermalinks succeeds when the post does not exist`` data.Value
}
]
testList "Delete" [
testTask "succeeds when a post is deleted" {
do! PostDataTests.``Delete succeeds when a post is deleted`` data.Value
}
testTask "succeeds when a post is not deleted" {
do! PostDataTests.``Delete succeeds when a post is not deleted`` data.Value
}
]
]
let private tagMapTests = testList "TagMap" [
testList "FindById" [
testTask "succeeds when a tag mapping is found" {
do! TagMapDataTests.``FindById succeeds when a tag mapping is found`` data.Value
}
testTask "succeeds when a tag mapping is not found (incorrect weblog)" {
do! TagMapDataTests.``FindById succeeds when a tag mapping is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a tag mapping is not found (bad tag map ID)" {
do! TagMapDataTests.``FindById succeeds when a tag mapping is not found (bad tag map ID)`` data.Value
}
]
testList "FindByUrlValue" [
testTask "succeeds when a tag mapping is found" {
do! TagMapDataTests.``FindByUrlValue succeeds when a tag mapping is found`` data.Value
}
testTask "succeeds when a tag mapping is not found (incorrect weblog)" {
do! TagMapDataTests.``FindByUrlValue succeeds when a tag mapping is not found (incorrect weblog)``
data.Value
}
testTask "succeeds when a tag mapping is not found (no such value)" {
do! TagMapDataTests.``FindByUrlValue succeeds when a tag mapping is not found (no such value)`` data.Value
}
]
testList "FindByWebLog" [
testTask "succeeds when tag mappings are found" {
do! TagMapDataTests.``FindByWebLog succeeds when tag mappings are found`` data.Value
}
testTask "succeeds when no tag mappings are found" {
do! TagMapDataTests.``FindByWebLog succeeds when no tag mappings are found`` data.Value
}
]
testList "FindMappingForTags" [
testTask "succeeds when mappings exist" {
do! TagMapDataTests.``FindMappingForTags succeeds when mappings exist`` data.Value
}
testTask "succeeds when no mappings exist" {
do! TagMapDataTests.``FindMappingForTags succeeds when no mappings exist`` data.Value
}
]
testList "Save" [
testTask "succeeds when adding a tag mapping" {
do! TagMapDataTests.``Save succeeds when adding a tag mapping`` data.Value
}
testTask "succeeds when updating a tag mapping" {
do! TagMapDataTests.``Save succeeds when updating a tag mapping`` data.Value
}
]
testList "Delete" [
testTask "succeeds when a tag mapping is deleted" {
do! TagMapDataTests.``Delete succeeds when a tag mapping is deleted`` data.Value
}
testTask "succeeds when a tag mapping is not deleted" {
do! TagMapDataTests.``Delete succeeds when a tag mapping is not deleted`` data.Value
}
]
]
let private themeTests = testList "Theme" [
testTask "All succeeds" {
do! ThemeDataTests.``All succeeds`` data.Value
}
testList "Exists" [
testTask "succeeds when the theme exists" {
do! ThemeDataTests.``Exists succeeds when the theme exists`` data.Value
}
testTask "succeeds when the theme does not exist" {
do! ThemeDataTests.``Exists succeeds when the theme does not exist`` data.Value
}
]
testList "FindById" [
testTask "succeeds when the theme exists" {
do! ThemeDataTests.``FindById succeeds when the theme exists`` data.Value
}
testTask "succeeds when the theme does not exist" {
do! ThemeDataTests.``FindById succeeds when the theme does not exist`` data.Value
}
]
testList "FindByIdWithoutText" [
testTask "succeeds when the theme exists" {
do! ThemeDataTests.``FindByIdWithoutText succeeds when the theme exists`` data.Value
}
testTask "succeeds when the theme does not exist" {
do! ThemeDataTests.``FindByIdWithoutText succeeds when the theme does not exist`` data.Value
}
]
testList "Save" [
testTask "succeeds when adding a theme" {
do! ThemeDataTests.``Save succeeds when adding a theme`` data.Value
}
testTask "succeeds when updating a theme" {
do! ThemeDataTests.``Save succeeds when updating a theme`` data.Value
}
]
testList "Delete" [
testTask "succeeds when a theme is deleted" {
do! ThemeDataTests.``Delete succeeds when a theme is deleted`` data.Value
}
testTask "succeeds when a theme is not deleted" {
do! ThemeDataTests.``Delete succeeds when a theme is not deleted`` data.Value
}
]
]
let private themeAssetTests = testList "ThemeAsset" [
testList "Save" [
testTask "succeeds when adding an asset" {
do! ThemeDataTests.Asset.``Save succeeds when adding an asset`` data.Value
}
testTask "succeeds when updating an asset" {
do! ThemeDataTests.Asset.``Save succeeds when updating an asset`` data.Value
}
]
testTask "All succeeds" {
do! ThemeDataTests.Asset.``All succeeds`` data.Value
}
testList "FindById" [
testTask "succeeds when an asset is found" {
do! ThemeDataTests.Asset.``FindById succeeds when an asset is found`` data.Value
}
testTask "succeeds when an asset is not found" {
do! ThemeDataTests.Asset.``FindById succeeds when an asset is not found`` data.Value
}
]
testList "FindByTheme" [
testTask "succeeds when assets exist" {
do! ThemeDataTests.Asset.``FindByTheme succeeds when assets exist`` data.Value
}
testTask "succeeds when assets do not exist" {
do! ThemeDataTests.Asset.``FindByTheme succeeds when assets do not exist`` data.Value
}
]
testList "FindByThemeWithData" [
testTask "succeeds when assets exist" {
do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets exist`` data.Value
}
testTask "succeeds when assets do not exist" {
do! ThemeDataTests.Asset.``FindByThemeWithData succeeds when assets do not exist`` data.Value
}
]
testList "DeleteByTheme" [
testTask "succeeds when assets are deleted" {
do! ThemeDataTests.Asset.``DeleteByTheme succeeds when assets are deleted`` data.Value
}
testTask "succeeds when no assets are deleted" {
do! ThemeDataTests.Asset.``DeleteByTheme succeeds when no assets are deleted`` data.Value
}
]
]
let private uploadTests = testList "Upload" [
testTask "Add succeeds" {
do! UploadDataTests.``Add succeeds`` data.Value
}
testList "FindByPath" [
testTask "succeeds when an upload is found" {
do! UploadDataTests.``FindByPath succeeds when an upload is found`` data.Value
}
testTask "succeeds when an upload is not found (incorrect weblog)" {
do! UploadDataTests.``FindByPath succeeds when an upload is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when an upload is not found (bad path)" {
do! UploadDataTests.``FindByPath succeeds when an upload is not found (bad path)`` data.Value
}
]
testList "FindByWebLog" [
testTask "succeeds when uploads exist" {
do! UploadDataTests.``FindByWebLog succeeds when uploads exist`` data.Value
}
testTask "succeeds when no uploads exist" {
do! UploadDataTests.``FindByWebLog succeeds when no uploads exist`` data.Value
}
]
testList "FindByWebLogWithData" [
testTask "succeeds when uploads exist" {
do! UploadDataTests.``FindByWebLogWithData succeeds when uploads exist`` data.Value
}
testTask "succeeds when no uploads exist" {
do! UploadDataTests.``FindByWebLogWithData succeeds when no uploads exist`` data.Value
}
]
testList "Delete" [
testTask "succeeds when an upload is deleted" {
do! UploadDataTests.``Delete succeeds when an upload is deleted`` data.Value
}
testTask "succeeds when an upload is not deleted" {
do! UploadDataTests.``Delete succeeds when an upload is not deleted`` data.Value
}
]
]
let private webLogUserTests = testList "WebLogUser" [
testTask "Add succeeds" {
// This restore ensures all the posts and pages exist
do! freshEnvironment ()
do! WebLogUserDataTests.``Add succeeds`` data.Value
}
testList "FindByEmail" [
testTask "succeeds when a user is found" {
do! WebLogUserDataTests.``FindByEmail succeeds when a user is found`` data.Value
}
testTask "succeeds when a user is not found (incorrect weblog)" {
do! WebLogUserDataTests.``FindByEmail succeeds when a user is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a user is not found (bad email)" {
do! WebLogUserDataTests.``FindByEmail succeeds when a user is not found (bad email)`` data.Value
}
]
testList "FindById" [
testTask "succeeds when a user is found" {
do! WebLogUserDataTests.``FindById succeeds when a user is found`` data.Value
}
testTask "succeeds when a user is not found (incorrect weblog)" {
do! WebLogUserDataTests.``FindById succeeds when a user is not found (incorrect weblog)`` data.Value
}
testTask "succeeds when a user is not found (bad ID)" {
do! WebLogUserDataTests.``FindById succeeds when a user is not found (bad ID)`` data.Value
}
]
testList "FindByWebLog" [
testTask "succeeds when users exist" {
do! WebLogUserDataTests.``FindByWebLog succeeds when users exist`` data.Value
}
testTask "succeeds when no users exist" {
do! WebLogUserDataTests.``FindByWebLog succeeds when no users exist`` data.Value
}
]
testList "FindNames" [
testTask "succeeds when users exist" {
do! WebLogUserDataTests.``FindNames succeeds when users exist`` data.Value
}
testTask "succeeds when users do not exist" {
do! WebLogUserDataTests.``FindNames succeeds when users do not exist`` data.Value
}
]
testList "SetLastSeen" [
testTask "succeeds when the user exists" {
do! WebLogUserDataTests.``SetLastSeen succeeds when the user exists`` data.Value
}
testTask "succeeds when the user does not exist" {
do! WebLogUserDataTests.``SetLastSeen succeeds when the user does not exist`` data.Value
}
]
testList "Update" [
testTask "succeeds when the user exists" {
do! WebLogUserDataTests.``Update succeeds when the user exists`` data.Value
}
testTask "succeeds when the user does not exist" {
do! WebLogUserDataTests.``Update succeeds when the user does not exist`` data.Value
}
]
testList "Delete" [
testTask "fails when the user is the author of a page" {
do! WebLogUserDataTests.``Delete fails when the user is the author of a page`` data.Value
}
testTask "fails when the user is the author of a post" {
do! WebLogUserDataTests.``Delete fails when the user is the author of a post`` data.Value
}
testTask "succeeds when the user is not an author" {
do! WebLogUserDataTests.``Delete succeeds when the user is not an author`` data.Value
}
testTask "succeeds when the user does not exist" {
do! WebLogUserDataTests.``Delete succeeds when the user does not exist`` data.Value
}
]
]
let private webLogTests = testList "WebLog" [
testTask "Add succeeds" {
do! WebLogDataTests.``Add succeeds`` data.Value
}
testTask "All succeeds" {
do! WebLogDataTests.``All succeeds`` data.Value
}
testList "FindByHost" [
testTask "succeeds when a web log is found" {
do! WebLogDataTests.``FindByHost succeeds when a web log is found`` data.Value
}
testTask "succeeds when a web log is not found" {
do! WebLogDataTests.``FindByHost succeeds when a web log is not found`` data.Value
}
]
testList "FindById" [
testTask "succeeds when a web log is found" {
do! WebLogDataTests.``FindById succeeds when a web log is found`` data.Value
}
testTask "succeeds when a web log is not found" {
do! WebLogDataTests.``FindById succeeds when a web log is not found`` data.Value
}
]
testList "UpdateRedirectRules" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``UpdateRedirectRules succeeds when the web log exists`` data.Value
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``UpdateRedirectRules succeeds when the web log does not exist`` data.Value
}
]
testList "UpdateRssOptions" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``UpdateRssOptions succeeds when the web log exists`` data.Value
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``UpdateRssOptions succeeds when the web log does not exist`` data.Value
}
]
testList "UpdateSettings" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``UpdateSettings succeeds when the web log exists`` data.Value
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``UpdateSettings succeeds when the web log does not exist`` data.Value
}
]
testList "Delete" [
testTask "succeeds when the web log exists" {
do! WebLogDataTests.``Delete succeeds when the web log exists`` data.Value
}
testTask "succeeds when the web log does not exist" {
do! WebLogDataTests.``Delete succeeds when the web log does not exist`` data.Value
}
]
]
/// Drop the throwaway RethinkDB database
let private environmentCleanUp = testTask "Clean Up" {
do! disposeData ()
}
/// All RethinkDB data tests
let all =
testList "RethinkDbData"
[ environmentSetUp
categoryTests
pageTests
postTests
tagMapTests
themeTests
themeAssetTests
uploadTests
webLogUserTests
webLogTests
environmentCleanUp ]
|> testSequenced

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,112 @@
/// <summary>
/// Integration tests for <see cref="ITagMapData" /> implementations
/// </summary>
module TagMapDataTests
open Expecto
open MyWebLog
open MyWebLog.Data
/// The ID of the root web log
let private rootId = CategoryDataTests.rootId
/// The ID of the f# tag
let private fSharpId = TagMapId "Icm027noqE-rPHKZA98vAw"
/// The ID of the ghoti tag
let private fishId = TagMapId "GdryXh-S0kGsNBs2RIacGA"
let ``FindById succeeds when a tag mapping is found`` (data: IData) = task {
let! tagMap = data.TagMap.FindById fSharpId rootId
Expect.isSome tagMap "There should have been a tag mapping returned"
let tag = tagMap.Value
Expect.equal tag.Id fSharpId "ID is incorrect"
Expect.equal tag.WebLogId rootId "Web log ID is incorrect"
Expect.equal tag.Tag "f#" "Tag is incorrect"
Expect.equal tag.UrlValue "f-sharp" "URL value is incorrect"
}
let ``FindById succeeds when a tag mapping is not found (incorrect weblog)`` (data: IData) = task {
let! tagMap = data.TagMap.FindById fSharpId (WebLogId "wrong")
Expect.isNone tagMap "There should not have been a tag mapping returned"
}
let ``FindById succeeds when a tag mapping is not found (bad tag map ID)`` (data: IData) = task {
let! tagMap = data.TagMap.FindById (TagMapId "out") rootId
Expect.isNone tagMap "There should not have been a tag mapping returned"
}
let ``FindByUrlValue succeeds when a tag mapping is found`` (data: IData) = task {
let! tagMap = data.TagMap.FindByUrlValue "f-sharp" rootId
Expect.isSome tagMap "There should have been a tag mapping returned"
Expect.equal tagMap.Value.Id fSharpId "ID is incorrect"
}
let ``FindByUrlValue succeeds when a tag mapping is not found (incorrect weblog)`` (data: IData) = task {
let! tagMap = data.TagMap.FindByUrlValue "f-sharp" (WebLogId "incorrect")
Expect.isNone tagMap "There should not have been a tag mapping returned"
}
let ``FindByUrlValue succeeds when a tag mapping is not found (no such value)`` (data: IData) = task {
let! tagMap = data.TagMap.FindByUrlValue "c-sharp" rootId
Expect.isNone tagMap "There should not have been a tag mapping returned"
}
let ``FindByWebLog succeeds when tag mappings are found`` (data: IData) = task {
let! mappings = data.TagMap.FindByWebLog rootId
Expect.hasLength mappings 2 "There should have been 2 tag mappings returned"
for mapping in mappings do
Expect.contains [ fSharpId; fishId ] mapping.Id $"Unexpected mapping ID ({mapping.Id})"
Expect.equal mapping.WebLogId rootId "Web log ID is incorrect"
Expect.isNotEmpty mapping.Tag "Tag should not have been blank"
Expect.isNotEmpty mapping.UrlValue "URL value should not have been blank"
}
let ``FindByWebLog succeeds when no tag mappings are found`` (data: IData) = task {
let! mappings = data.TagMap.FindByWebLog (WebLogId "no-maps")
Expect.isEmpty mappings "There should have been no tag mappings returned"
}
let ``FindMappingForTags succeeds when mappings exist`` (data: IData) = task {
let! mappings = data.TagMap.FindMappingForTags [ "f#"; "testing"; "unit" ] rootId
Expect.hasLength mappings 1 "There should have been one mapping returned"
Expect.equal mappings[0].Id fSharpId "The wrong mapping was returned"
}
let ``FindMappingForTags succeeds when no mappings exist`` (data: IData) = task {
let! mappings = data.TagMap.FindMappingForTags [ "c#"; "turkey"; "ham" ] rootId
Expect.isEmpty mappings "There should have been no tag mappings returned"
}
let ``Save succeeds when adding a tag mapping`` (data: IData) = task {
let mapId = TagMapId "test"
do! data.TagMap.Save { Id = mapId; WebLogId = rootId; Tag = "c#"; UrlValue = "c-sharp" }
let! mapping = data.TagMap.FindById mapId rootId
Expect.isSome mapping "The mapping should have been retrieved"
let tag = mapping.Value
Expect.equal tag.Id mapId "ID is incorrect"
Expect.equal tag.WebLogId rootId "Web log ID is incorrect"
Expect.equal tag.Tag "c#" "Tag is incorrect"
Expect.equal tag.UrlValue "c-sharp" "URL value is incorrect"
}
let ``Save succeeds when updating a tag mapping`` (data: IData) = task {
do! data.TagMap.Save { Id = fishId; WebLogId = rootId; Tag = "halibut"; UrlValue = "mackerel" }
let! mapping = data.TagMap.FindById fishId rootId
Expect.isSome mapping "The mapping should have been retrieved"
let tag = mapping.Value
Expect.equal tag.Id fishId "ID is incorrect"
Expect.equal tag.WebLogId rootId "Web log ID is incorrect"
Expect.equal tag.Tag "halibut" "Tag is incorrect"
Expect.equal tag.UrlValue "mackerel" "URL value is incorrect"
}
let ``Delete succeeds when a tag mapping is deleted`` (data: IData) = task {
let! deleted = data.TagMap.Delete fSharpId rootId
Expect.isTrue deleted "The tag mapping should have been deleted"
}
let ``Delete succeeds when a tag mapping is not deleted`` (data: IData) = task {
let! deleted = data.TagMap.Delete fSharpId rootId // this was deleted above
Expect.isFalse deleted "A tag mapping should not have been deleted"
}

View File

@@ -0,0 +1,234 @@
/// <summary>
/// Integration tests for <see cref="IThemeData" /> implementations
/// </summary>
module ThemeDataTests
open System.IO
open Expecto
open MyWebLog
open MyWebLog.Data
open NodaTime
/// The ID of the default theme (restored from root-weblog.json)
let private defaultId = ThemeId "default"
/// The ID of the test theme loaded and manipulated by these tests
let private testId = ThemeId "test-theme"
/// The dark version of the myWebLog logo
let private darkFile = File.ReadAllBytes "../admin-theme/wwwroot/logo-dark.png"
/// The light version of the myWebLog logo
let private lightFile = File.ReadAllBytes "../admin-theme/wwwroot/logo-light.png"
/// Ensure that theme templates do not have any text
let private ensureNoText theme =
for template in theme.Templates do
Expect.equal template.Text "" $"Text for template {template.Name} should have been blank"
let ``All succeeds`` (data: IData) = task {
let! themes = data.Theme.All()
Expect.hasLength themes 1 "There should have been one theme returned"
Expect.equal themes[0].Id defaultId "ID was incorrect"
Expect.equal themes[0].Name "myWebLog Default Theme" "Name was incorrect"
Expect.equal themes[0].Version "2.1.0" "Version was incorrect"
ensureNoText themes[0]
}
let ``Exists succeeds when the theme exists`` (data: IData) = task {
let! exists = data.Theme.Exists defaultId
Expect.isTrue exists "The \"default\" theme should have existed"
}
let ``Exists succeeds when the theme does not exist`` (data: IData) = task {
let! exists = data.Theme.Exists (ThemeId "fancy")
Expect.isFalse exists "The \"fancy\" theme should not have existed"
}
let ``FindById succeeds when the theme exists`` (data: IData) = task {
let! theme = data.Theme.FindById defaultId
Expect.isSome theme "The theme should have been found"
let it = theme.Value
Expect.equal it.Id defaultId "ID was incorrect"
Expect.equal it.Name "myWebLog Default Theme" "Name was incorrect"
Expect.equal it.Version "2.1.0" "Version was incorrect"
for template in it.Templates do
Expect.isNotEmpty template.Text $"Text for template {template.Name} should not have been blank"
}
let ``FindById succeeds when the theme does not exist`` (data: IData) = task {
let! theme = data.Theme.FindById (ThemeId "missing")
Expect.isNone theme "There should not have been a theme found"
}
let ``FindByIdWithoutText succeeds when the theme exists`` (data: IData) = task {
let! theme = data.Theme.FindByIdWithoutText defaultId
Expect.isSome theme "The theme should have been found"
let it = theme.Value
Expect.equal it.Id defaultId "ID was incorrect"
ensureNoText it
}
let ``FindByIdWithoutText succeeds when the theme does not exist`` (data: IData) = task {
let! theme = data.Theme.FindByIdWithoutText (ThemeId "ornate")
Expect.isNone theme "There should not have been a theme found"
}
let ``Save succeeds when adding a theme`` (data: IData) = task {
do! data.Theme.Save
{ Id = testId
Name = "Test Theme"
Version = "evergreen"
Templates =
[ { Name = "index"; Text = "<h1>{{ values_here }}</h1>" }
{ Name = "single-post"; Text = "<p>{{ the_post }}" } ] }
let! saved = data.Theme.FindById testId
Expect.isSome saved "There should have been a theme returned"
let it = saved.Value
Expect.equal it.Id testId "ID was incorrect"
Expect.equal it.Name "Test Theme" "Name was incorrect"
Expect.equal it.Version "evergreen" "Version was incorrect"
Expect.hasLength it.Templates 2 "There should have been 2 templates"
Expect.equal it.Templates[0].Name "index" "Template 0 name incorrect"
Expect.equal it.Templates[0].Text "<h1>{{ values_here }}</h1>" "Template 0 text incorrect"
Expect.equal it.Templates[1].Name "single-post" "Template 1 name incorrect"
Expect.equal it.Templates[1].Text "<p>{{ the_post }}" "Template 1 text incorrect"
}
let ``Save succeeds when updating a theme`` (data: IData) = task {
do! data.Theme.Save
{ Id = testId
Name = "Updated Theme"
Version = "still evergreen"
Templates =
[ { Name = "index"; Text = "<h1>{{ values_there }}</h1>" }
{ Name = "layout"; Text = "<!DOCTYPE html><etc />" }
{ Name = "single-post"; Text = "<p>{{ the_post }}" } ] }
let! updated = data.Theme.FindById testId
Expect.isSome updated "The updated theme should have been returned"
let it = updated.Value
Expect.equal it.Id testId "ID was incorrect"
Expect.equal it.Name "Updated Theme" "Name was incorrect"
Expect.equal it.Version "still evergreen" "Version was incorrect"
Expect.hasLength it.Templates 3 "There should have been 3 templates"
Expect.equal it.Templates[0].Name "index" "Template 0 name incorrect"
Expect.equal it.Templates[0].Text "<h1>{{ values_there }}</h1>" "Template 0 text incorrect"
Expect.equal it.Templates[1].Name "layout" "Template 1 name incorrect"
Expect.equal it.Templates[1].Text "<!DOCTYPE html><etc />" "Template 1 text incorrect"
Expect.equal it.Templates[2].Name "single-post" "Template 2 name incorrect"
Expect.equal it.Templates[2].Text "<p>{{ the_post }}" "Template 2 text incorrect"
}
let ``Delete succeeds when a theme is deleted`` (data: IData) = task {
// Delete should also delete assets associated with the theme
do! data.ThemeAsset.Save { Id = ThemeAssetId (testId, "logo-dark.png"); UpdatedOn = Noda.epoch; Data = darkFile }
do! data.ThemeAsset.Save { Id = ThemeAssetId (testId, "logo-light.png"); UpdatedOn = Noda.epoch; Data = lightFile }
let! deleted = data.Theme.Delete testId
Expect.isTrue deleted "The theme should have been deleted"
let! assets = data.ThemeAsset.FindByTheme testId
Expect.isEmpty assets "The theme's assets should have been deleted"
}
let ``Delete succeeds when a theme is not deleted`` (data: IData) = task {
let! deleted = data.Theme.Delete (ThemeId "test-theme") // already deleted above
Expect.isFalse deleted "The theme should not have been deleted"
}
/// <summary>
/// Integration tests for <see cref="IThemeAssetData" /> implementations
/// </summary>
module Asset =
/// The theme ID for which assets will be tested
let private assetThemeId = ThemeId "asset-test"
/// The asset ID for the dark logo
let private darkId = ThemeAssetId (assetThemeId, "logo-dark.png")
/// The asset ID for the light logo
let private lightId = ThemeAssetId (assetThemeId, "logo-light.png")
let ``Save succeeds when adding an asset`` (data: IData) = task {
do! data.Theme.Save { Theme.Empty with Id = assetThemeId }
do! data.ThemeAsset.Save { Id = lightId; UpdatedOn = Noda.epoch + Duration.FromDays 18; Data = lightFile }
let! asset = data.ThemeAsset.FindById lightId
Expect.isSome asset "The asset should have been found"
let it = asset.Value
Expect.equal it.Id lightId "ID was incorrect"
Expect.equal it.UpdatedOn (Noda.epoch + Duration.FromDays 18) "Updated on was incorrect"
Expect.equal it.Data lightFile "Data was incorrect"
}
let ``Save succeeds when updating an asset`` (data: IData) = task {
do! data.ThemeAsset.Save { Id = lightId; UpdatedOn = Noda.epoch + Duration.FromDays 20; Data = darkFile }
let! asset = data.ThemeAsset.FindById lightId
Expect.isSome asset "The asset should have been found"
let it = asset.Value
Expect.equal it.Id lightId "ID was incorrect"
Expect.equal it.UpdatedOn (Noda.epoch + Duration.FromDays 20) "Updated on was incorrect"
Expect.equal it.Data darkFile "Data was incorrect"
}
let ``All succeeds`` (data: IData) = task {
let! all = data.ThemeAsset.All()
Expect.hasLength all 2 "There should have been 2 assets retrieved"
for asset in all do
Expect.contains
[ ThemeAssetId (defaultId, "style.css"); lightId ] asset.Id $"Unexpected asset found ({asset.Id})"
Expect.isEmpty asset.Data $"Asset {asset.Id} should not have had data"
}
let ``FindById succeeds when an asset is found`` (data: IData) = task {
let! asset = data.ThemeAsset.FindById lightId
Expect.isSome asset "The asset should have been found"
let it = asset.Value
Expect.equal it.Id lightId "ID was incorrect"
Expect.equal it.UpdatedOn (Noda.epoch + Duration.FromDays 20) "Updated on was incorrect"
Expect.equal it.Data darkFile "Data was incorrect"
}
let ``FindById succeeds when an asset is not found`` (data: IData) = task {
let! asset = data.ThemeAsset.FindById (ThemeAssetId (assetThemeId, "404.jpg"))
Expect.isNone asset "There should not have been an asset returned"
}
let ``FindByTheme succeeds when assets exist`` (data: IData) = task {
do! data.ThemeAsset.Save { Id = darkId; UpdatedOn = Noda.epoch; Data = darkFile }
do! data.ThemeAsset.Save { Id = lightId; UpdatedOn = Noda.epoch; Data = lightFile }
let! assets = data.ThemeAsset.FindByTheme assetThemeId
Expect.hasLength assets 2 "There should have been 2 assets returned"
for asset in assets do
Expect.contains [ darkId; lightId ] asset.Id $"Unexpected asset found ({asset.Id})"
Expect.equal asset.UpdatedOn Noda.epoch $"Updated on was incorrect ({asset.Id})"
Expect.isEmpty asset.Data $"Data should not have been retrieved ({asset.Id})"
}
let ``FindByTheme succeeds when assets do not exist`` (data: IData) = task {
let! assets = data.ThemeAsset.FindByTheme (ThemeId "no-assets-here")
Expect.isEmpty assets "There should have been no assets returned"
}
let ``FindByThemeWithData succeeds when assets exist`` (data: IData) = task {
let! assets = data.ThemeAsset.FindByThemeWithData assetThemeId
Expect.hasLength assets 2 "There should have been 2 assets returned"
let darkLogo = assets |> List.find (fun it -> it.Id = darkId)
Expect.equal darkLogo.Data darkFile "The dark asset's data is incorrect"
let lightLogo = assets |> List.find (fun it -> it.Id = lightId)
Expect.equal lightLogo.Data lightFile "The light asset's data is incorrect"
}
let ``FindByThemeWithData succeeds when assets do not exist`` (data: IData) = task {
let! assets = data.ThemeAsset.FindByThemeWithData (ThemeId "still-no-assets")
Expect.isEmpty assets "There should have been no assets returned"
}
let ``DeleteByTheme succeeds when assets are deleted`` (data: IData) = task {
do! data.ThemeAsset.DeleteByTheme assetThemeId
let! assets = data.ThemeAsset.FindByTheme assetThemeId
Expect.isEmpty assets "There should be no assets remaining"
}
let ``DeleteByTheme succeeds when no assets are deleted`` (data: IData) = task {
do! data.ThemeAsset.DeleteByTheme assetThemeId // already deleted above
Expect.isTrue true "The above did not raise an exception; that's the test"
}

View File

@@ -0,0 +1,95 @@
/// <summary>
/// Integration tests for <see cref="IUploadData" /> implementations
/// </summary>
module UploadDataTests
open System
open System.IO
open Expecto
open MyWebLog
open MyWebLog.Data
open NodaTime
/// The ID of the root web log
let private rootId = CategoryDataTests.rootId
/// The ID of the favicon upload
let private faviconId = UploadId "XweKbWQiOkqqrjEdgP9wwg"
let ``Add succeeds`` (data: IData) = task {
let file = File.ReadAllBytes "../admin-theme/wwwroot/logo-dark.png"
do! data.Upload.Add
{ Id = UploadId "new-upload"
WebLogId = rootId
UpdatedOn = Noda.epoch + Duration.FromDays 30
Path = Permalink "1970/01/logo-dark.png"
Data = file }
let! added = data.Upload.FindByPath "1970/01/logo-dark.png" rootId
Expect.isSome added "There should have been an upload returned"
let upload = added.Value
Expect.equal upload.Id (UploadId "new-upload") "ID is incorrect"
Expect.equal upload.WebLogId rootId "Web log ID is incorrect"
Expect.equal upload.UpdatedOn (Noda.epoch + Duration.FromDays 30) "Updated on is incorrect"
Expect.equal upload.Path (Permalink "1970/01/logo-dark.png") "Path is incorrect"
Expect.equal upload.Data file "Data is incorrect"
}
let ``FindByPath succeeds when an upload is found`` (data: IData) = task {
let! upload = data.Upload.FindByPath "2022/06/favicon.ico" rootId
Expect.isSome upload "There should have been an upload returned"
let it = upload.Value
Expect.equal it.Id faviconId "ID is incorrect"
Expect.equal it.WebLogId rootId "Web log ID is incorrect"
Expect.equal
it.UpdatedOn (Instant.FromDateTimeOffset(DateTimeOffset.Parse "2022-06-23T21:15:40Z")) "Updated on is incorrect"
Expect.equal it.Path (Permalink "2022/06/favicon.ico") "Path is incorrect"
Expect.isNonEmpty it.Data "Data should have been retrieved"
}
let ``FindByPath succeeds when an upload is not found (incorrect weblog)`` (data: IData) = task {
let! upload = data.Upload.FindByPath "2022/06/favicon.ico" (WebLogId "wrong")
Expect.isNone upload "There should not have been an upload returned"
}
let ``FindByPath succeeds when an upload is not found (bad path)`` (data: IData) = task {
let! upload = data.Upload.FindByPath "2022/07/favicon.ico" rootId
Expect.isNone upload "There should not have been an upload returned"
}
let ``FindByWebLog succeeds when uploads exist`` (data: IData) = task {
let! uploads = data.Upload.FindByWebLog rootId
Expect.hasLength uploads 2 "There should have been 2 uploads returned"
for upload in uploads do
Expect.contains [ faviconId; UploadId "new-upload" ] upload.Id $"Unexpected upload returned ({upload.Id})"
Expect.isEmpty upload.Data $"Upload should not have had its data ({upload.Id})"
}
let ``FindByWebLog succeeds when no uploads exist`` (data: IData) = task {
let! uploads = data.Upload.FindByWebLog (WebLogId "nothing")
Expect.isEmpty uploads "There should have been no uploads returned"
}
let ``FindByWebLogWithData succeeds when uploads exist`` (data: IData) = task {
let! uploads = data.Upload.FindByWebLogWithData rootId
Expect.hasLength uploads 2 "There should have been 2 uploads returned"
for upload in uploads do
Expect.contains [ faviconId; UploadId "new-upload" ] upload.Id $"Unexpected upload returned ({upload.Id})"
Expect.isNonEmpty upload.Data $"Upload should have had its data ({upload.Id})"
}
let ``FindByWebLogWithData succeeds when no uploads exist`` (data: IData) = task {
let! uploads = data.Upload.FindByWebLogWithData (WebLogId "data-nope")
Expect.isEmpty uploads "There should have been no uploads returned"
}
let ``Delete succeeds when an upload is deleted`` (data: IData) = task {
match! data.Upload.Delete faviconId rootId with
| Ok path -> Expect.equal path "2022/06/favicon.ico" "The path of the deleted upload was incorrect"
| Error it -> Expect.isTrue false $"Upload deletion should have succeeded (message {it})"
}
let ``Delete succeeds when an upload is not deleted`` (data: IData) = task {
match! data.Upload.Delete faviconId rootId with
| Ok it -> Expect.isTrue false $"Upload deletion should not have succeeded (path {it})"
| Error msg -> Expect.equal msg $"Upload ID {faviconId} not found" "Error message was incorrect"
}

View File

@@ -0,0 +1,96 @@
module UtilsTests
open Expecto
open MyWebLog
open MyWebLog.Data
open NodaTime
/// Unit tests for the orderByHierarchy function
let orderByHierarchyTests = test "orderByHierarchy succeeds" {
let rawCats =
[ { Category.Empty with Id = CategoryId "a"; Name = "Audio"; Slug = "audio"; ParentId = Some (CategoryId "p") }
{ Category.Empty with
Id = CategoryId "b"
Name = "Breaking"
Description = Some "Breaking News"
Slug = "breaking"
ParentId = Some (CategoryId "n") }
{ Category.Empty with Id = CategoryId "l"; Name = "Local"; Slug = "local"; ParentId = Some (CategoryId "b") }
{ Category.Empty with Id = CategoryId "n"; Name = "News"; Slug = "news" }
{ Category.Empty with Id = CategoryId "p"; Name = "Podcast"; Slug = "podcast" }
{ Category.Empty with Id = CategoryId "v"; Name = "Video"; Slug = "vid"; ParentId = Some (CategoryId "p") } ]
let cats = Utils.orderByHierarchy rawCats None None [] |> List.ofSeq
Expect.equal cats.Length 6 "There should have been 6 categories"
Expect.equal cats[0].Id "n" "The first top-level category should have been News"
Expect.equal cats[0].Slug "news" "Slug for News not filled properly"
Expect.isEmpty cats[0].ParentNames "Parent names for News not filled properly"
Expect.equal cats[1].Id "b" "Breaking should have been just below News"
Expect.equal cats[1].Slug "news/breaking" "Slug for Breaking not filled properly"
Expect.equal cats[1].Name "Breaking" "Name not filled properly"
Expect.equal cats[1].Description (Some "Breaking News") "Description not filled properly"
Expect.equal cats[1].ParentNames [| "News" |] "Parent names for Breaking not filled properly"
Expect.equal cats[2].Id "l" "Local should have been just below Breaking"
Expect.equal cats[2].Slug "news/breaking/local" "Slug for Local not filled properly"
Expect.equal cats[2].ParentNames [| "News"; "Breaking" |] "Parent names for Local not filled properly"
Expect.equal cats[3].Id "p" "Podcast should have been the next top-level category"
Expect.equal cats[3].Slug "podcast" "Slug for Podcast not filled properly"
Expect.isEmpty cats[3].ParentNames "Parent names for Podcast not filled properly"
Expect.equal cats[4].Id "a" "Audio should have been just below Podcast"
Expect.equal cats[4].Slug "podcast/audio" "Slug for Audio not filled properly"
Expect.equal cats[4].ParentNames [| "Podcast" |] "Parent names for Audio not filled properly"
Expect.equal cats[5].Id "v" "Video should have been below Audio"
Expect.equal cats[5].Slug "podcast/vid" "Slug for Video not filled properly"
Expect.equal cats[5].ParentNames [| "Podcast" |] "Parent names for Video not filled properly"
Expect.hasCountOf cats 6u (fun it -> it.PostCount = 0) "All post counts should have been 0"
}
/// Unit tests for the diffLists function
let diffListsTests = testList "diffLists" [
test "succeeds with identical lists" {
let removed, added = Utils.diffLists [ 1; 2; 3 ] [ 1; 2; 3 ] id
Expect.isEmpty removed "There should have been no removed items returned"
Expect.isEmpty added "There should have been no added items returned"
}
test "succeeds with differing lists" {
let removed, added = Utils.diffLists [ 1; 2; 3 ] [ 3; 4; 5 ] string
Expect.equal removed [ 1; 2 ] "Removed items incorrect"
Expect.equal added [ 4; 5 ] "Added items incorrect"
}
]
/// Unit tests for the diffRevisions function
let diffRevisionsTests = testList "diffRevisions" [
test "succeeds with identical lists" {
let oldItems =
[ { AsOf = Noda.epoch + Duration.FromDays 3; Text = Html "<p>test" }
{ AsOf = Noda.epoch; Text = Html "<p>test test" } ]
let newItems =
[ { AsOf = Noda.epoch; Text = Html "<p>test test" }
{ AsOf = Noda.epoch + Duration.FromDays 3; Text = Html "<p>test" } ]
let removed, added = Utils.diffRevisions oldItems newItems
Expect.isEmpty removed "There should have been no removed items returned"
Expect.isEmpty added "There should have been no added items returned"
}
test "succeeds with differing lists" {
let oldItems =
[ { AsOf = Noda.epoch + Duration.FromDays 3; Text = Html "<p>test" }
{ AsOf = Noda.epoch + Duration.FromDays 2; Text = Html "<p>tests" }
{ AsOf = Noda.epoch; Text = Html "<p>test test" } ]
let newItems =
[ { AsOf = Noda.epoch + Duration.FromDays 4; Text = Html "<p>tests" }
{ AsOf = Noda.epoch + Duration.FromDays 3; Text = Html "<p>test" }
{ AsOf = Noda.epoch; Text = Html "<p>test test" } ]
let removed, added = Utils.diffRevisions oldItems newItems
Expect.equal removed.Length 1 "There should be 1 removed item"
Expect.equal removed[0].AsOf (Noda.epoch + Duration.FromDays 2) "Expected removed item incorrect"
Expect.equal added.Length 1 "There should be 1 added item"
Expect.equal added[0].AsOf (Noda.epoch + Duration.FromDays 4) "Expected added item incorrect"
}
]
/// All tests for the Utils file
let all = testList "Utils" [
orderByHierarchyTests
diffListsTests
diffRevisionsTests
]

View File

@@ -0,0 +1,198 @@
/// <summary>
/// Integration tests for <see cref="IWebLogData" /> implementations
/// </summary>
module WebLogDataTests
open System
open Expecto
open MyWebLog
open MyWebLog.Data
/// The ID of the root web log
let private rootId = CategoryDataTests.rootId
let ``Add succeeds`` (data: IData) = task {
do! data.WebLog.Add
{ Id = WebLogId "new-weblog"
Name = "Test Web Log"
Slug = "test-web-log"
Subtitle = None
DefaultPage = ""
PostsPerPage = 7
ThemeId = ThemeId "default"
UrlBase = "https://example.com/new"
TimeZone = "America/Los_Angeles"
Rss =
{ IsFeedEnabled = true
FeedName = "my-feed.xml"
ItemsInFeed = None
IsCategoryEnabled = false
IsTagEnabled = false
Copyright = Some "go for it"
CustomFeeds = [] }
AutoHtmx = true
Uploads = Disk
RedirectRules = [ { From = "/here"; To = "/there"; IsRegex = false } ] }
let! webLog = data.WebLog.FindById (WebLogId "new-weblog")
Expect.isSome webLog "The web log should have been returned"
let it = webLog.Value
Expect.equal it.Id (WebLogId "new-weblog") "ID is incorrect"
Expect.equal it.Name "Test Web Log" "Name is incorrect"
Expect.equal it.Slug "test-web-log" "Slug is incorrect"
Expect.isNone it.Subtitle "Subtitle is incorrect"
Expect.equal it.DefaultPage "" "Default page is incorrect"
Expect.equal it.PostsPerPage 7 "Posts per page is incorrect"
Expect.equal it.ThemeId (ThemeId "default") "Theme ID is incorrect"
Expect.equal it.UrlBase "https://example.com/new" "URL base is incorrect"
Expect.equal it.TimeZone "America/Los_Angeles" "Time zone is incorrect"
Expect.isTrue it.AutoHtmx "Auto htmx flag is incorrect"
Expect.equal it.Uploads Disk "Upload destination is incorrect"
Expect.equal it.RedirectRules [ { From = "/here"; To = "/there"; IsRegex = false } ] "Redirect rules are incorrect"
let rss = it.Rss
Expect.isTrue rss.IsFeedEnabled "Is feed enabled flag is incorrect"
Expect.equal rss.FeedName "my-feed.xml" "Feed name is incorrect"
Expect.isNone rss.ItemsInFeed "Items in feed is incorrect"
Expect.isFalse rss.IsCategoryEnabled "Is category enabled flag is incorrect"
Expect.isFalse rss.IsTagEnabled "Is tag enabled flag is incorrect"
Expect.equal rss.Copyright (Some "go for it") "Copyright is incorrect"
Expect.isEmpty rss.CustomFeeds "Custom feeds are incorrect"
}
let ``All succeeds`` (data: IData) = task {
let! webLogs = data.WebLog.All()
Expect.hasLength webLogs 2 "There should have been 2 web logs returned"
for webLog in webLogs do
Expect.contains [ rootId; WebLogId "new-weblog" ] webLog.Id $"Unexpected web log returned ({webLog.Id})"
}
let ``FindByHost succeeds when a web log is found`` (data: IData) = task {
let! webLog = data.WebLog.FindByHost "http://localhost:8081"
Expect.isSome webLog "A web log should have been returned"
Expect.equal webLog.Value.Id rootId "The wrong web log was returned"
}
let ``FindByHost succeeds when a web log is not found`` (data: IData) = task {
let! webLog = data.WebLog.FindByHost "https://test.units"
Expect.isNone webLog "There should not have been a web log returned"
}
let ``FindById succeeds when a web log is found`` (data: IData) = task {
let! webLog = data.WebLog.FindById rootId
Expect.isSome webLog "There should have been a web log returned"
let it = webLog.Value
Expect.equal it.Id rootId "ID is incorrect"
Expect.equal it.Name "Root WebLog" "Name is incorrect"
Expect.equal it.Slug "root-weblog" "Slug is incorrect"
Expect.equal it.Subtitle (Some "This is the main one") "Subtitle is incorrect"
Expect.equal it.DefaultPage "posts" "Default page is incorrect"
Expect.equal it.PostsPerPage 9 "Posts per page is incorrect"
Expect.equal it.ThemeId (ThemeId "default") "Theme ID is incorrect"
Expect.equal it.UrlBase "http://localhost:8081" "URL base is incorrect"
Expect.equal it.TimeZone "America/Denver" "Time zone is incorrect"
Expect.isTrue it.AutoHtmx "Auto htmx flag is incorrect"
Expect.equal it.Uploads Database "Upload destination is incorrect"
Expect.isEmpty it.RedirectRules "Redirect rules are incorrect"
let rss = it.Rss
Expect.isTrue rss.IsFeedEnabled "Is feed enabled flag is incorrect"
Expect.equal rss.FeedName "feed" "Feed name is incorrect"
Expect.equal rss.ItemsInFeed (Some 7) "Items in feed is incorrect"
Expect.isTrue rss.IsCategoryEnabled "Is category enabled flag is incorrect"
Expect.isTrue rss.IsTagEnabled "Is tag enabled flag is incorrect"
Expect.equal rss.Copyright (Some "CC40-NC-BY") "Copyright is incorrect"
Expect.hasLength rss.CustomFeeds 1 "There should be 1 custom feed"
Expect.equal rss.CustomFeeds[0].Id (CustomFeedId "isPQ6drbDEydxohQzaiYtQ") "Custom feed ID incorrect"
Expect.equal rss.CustomFeeds[0].Source (Tag "podcast") "Custom feed source is incorrect"
Expect.equal rss.CustomFeeds[0].Path (Permalink "podcast-feed") "Custom feed path is incorrect"
Expect.isSome rss.CustomFeeds[0].Podcast "There should be podcast settings for this custom feed"
let pod = rss.CustomFeeds[0].Podcast.Value
Expect.equal pod.Title "Root Podcast" "Podcast title is incorrect"
Expect.equal pod.ItemsInFeed 23 "Podcast items in feed is incorrect"
Expect.equal pod.Summary "All things that happen in the domain root" "Podcast summary is incorrect"
Expect.equal pod.DisplayedAuthor "Podcaster Extraordinaire" "Podcast author is incorrect"
Expect.equal pod.Email "podcaster@example.com" "Podcast e-mail is incorrect"
Expect.equal pod.ImageUrl (Permalink "images/cover-art.png") "Podcast image URL is incorrect"
Expect.equal pod.AppleCategory "Fiction" "Podcast Apple category is incorrect"
Expect.equal pod.AppleSubcategory (Some "Drama") "Podcast Apple subcategory is incorrect"
Expect.equal pod.Explicit No "Podcast explicit rating is incorrect"
Expect.equal pod.DefaultMediaType (Some "audio/mpeg") "Podcast default media type is incorrect"
Expect.equal pod.MediaBaseUrl (Some "https://media.example.com/root/") "Podcast media base URL is incorrect"
Expect.equal pod.PodcastGuid (Some (Guid.Parse "10fd7f79-c719-4e1d-9da7-10405dd4fd96")) "Podcast GUID is incorrect"
Expect.equal pod.FundingUrl (Some "https://example.com/support-us") "Podcast funding URL is incorrect"
Expect.equal pod.FundingText (Some "Support Our Work") "Podcast funding text is incorrect"
Expect.equal pod.Medium (Some Newsletter) "Podcast medium is incorrect"
}
let ``FindById succeeds when a web log is not found`` (data: IData) = task {
let! webLog = data.WebLog.FindById (WebLogId "no-web-log")
Expect.isNone webLog "There should not have been a web log returned"
}
let ``UpdateRedirectRules succeeds when the web log exists`` (data: IData) = task {
let! webLog = data.WebLog.FindById (WebLogId "new-weblog")
Expect.isSome webLog "The test web log should have been returned"
do! data.WebLog.UpdateRedirectRules
{ webLog.Value with
RedirectRules = { From = "/now"; To = "/later"; IsRegex = false } :: webLog.Value.RedirectRules }
let! updated = data.WebLog.FindById (WebLogId "new-weblog")
Expect.isSome updated "The updated web log should have been returned"
Expect.equal
updated.Value.RedirectRules
[ { From = "/now"; To = "/later"; IsRegex = false }; { From = "/here"; To = "/there"; IsRegex = false } ]
"Redirect rules not updated correctly"
}
let ``UpdateRedirectRules succeeds when the web log does not exist`` (data: IData) = task {
do! data.WebLog.UpdateRedirectRules { WebLog.Empty with Id = WebLogId "no-rules" }
Expect.isTrue true "This not raising an exception is the test"
}
let ``UpdateRssOptions succeeds when the web log exists`` (data: IData) = task {
let! webLog = data.WebLog.FindById rootId
Expect.isSome webLog "The root web log should have been returned"
do! data.WebLog.UpdateRssOptions { webLog.Value with Rss = { webLog.Value.Rss with CustomFeeds = [] } }
let! updated = data.WebLog.FindById rootId
Expect.isSome updated "The updated web log should have been returned"
Expect.isEmpty updated.Value.Rss.CustomFeeds "RSS options not updated correctly"
}
let ``UpdateRssOptions succeeds when the web log does not exist`` (data: IData) = task {
do! data.WebLog.UpdateRssOptions { WebLog.Empty with Id = WebLogId "rss-less" }
Expect.isTrue true "This not raising an exception is the test"
}
let ``UpdateSettings succeeds when the web log exists`` (data: IData) = task {
let! webLog = data.WebLog.FindById rootId
Expect.isSome webLog "The root web log should have been returned"
do! data.WebLog.UpdateSettings { webLog.Value with AutoHtmx = false; Subtitle = None }
let! updated = data.WebLog.FindById rootId
Expect.isSome updated "The updated web log should have been returned"
Expect.isFalse updated.Value.AutoHtmx "Auto htmx flag not updated correctly"
Expect.isNone updated.Value.Subtitle "Subtitle not updated correctly"
}
let ``UpdateSettings succeeds when the web log does not exist`` (data: IData) = task {
do! data.WebLog.UpdateRedirectRules { WebLog.Empty with Id = WebLogId "no-settings" }
let! webLog = data.WebLog.FindById (WebLogId "no-settings")
Expect.isNone webLog "Updating settings should not have created a web log"
}
let ``Delete succeeds when the web log exists`` (data: IData) = task {
do! data.WebLog.Delete rootId
let! cats = data.Category.FindByWebLog rootId
Expect.isEmpty cats "There should be no categories remaining"
let! pages = data.Page.FindFullByWebLog rootId
Expect.isEmpty pages "There should be no pages remaining"
let! posts = data.Post.FindFullByWebLog rootId
Expect.isEmpty posts "There should be no posts remaining"
let! tagMappings = data.TagMap.FindByWebLog rootId
Expect.isEmpty tagMappings "There should be no tag mappings remaining"
let! uploads = data.Upload.FindByWebLog rootId
Expect.isEmpty uploads "There should be no uploads remaining"
let! users = data.WebLogUser.FindByWebLog rootId
Expect.isEmpty users "There should be no users remaining"
}
let ``Delete succeeds when the web log does not exist`` (data: IData) = task {
do! data.WebLog.Delete rootId // already deleted above
Expect.isTrue true "This not raising an exception is the test"
}

View File

@@ -0,0 +1,184 @@
/// <summary>
/// Integration tests for <see cref="IWebLogUserData" /> implementations
/// </summary>
module WebLogUserDataTests
open Expecto
open MyWebLog
open MyWebLog.Data
open NodaTime
/// The ID of the root web log
let private rootId = CategoryDataTests.rootId
/// The ID of the admin user
let private adminId = WebLogUserId "5EM2rimH9kONpmd2zQkiVA"
/// The ID of the editor user
let private editorId = WebLogUserId "GPbJaSOwTkKt14ZKYyveKA"
/// The ID of the author user
let private authorId = WebLogUserId "iIRNLSeY0EanxRPyqGuwVg"
/// The ID of the user added during the run of these tests
let private newId = WebLogUserId "new-user"
let ``Add succeeds`` (data: IData) = task {
do! data.WebLogUser.Add
{ Id = newId
WebLogId = rootId
Email = "new@example.com"
FirstName = "New"
LastName = "User"
PreferredName = "n00b"
PasswordHash = "hashed-password"
Url = Some "https://example.com/~new"
AccessLevel = Author
CreatedOn = Noda.epoch + Duration.FromDays 365
LastSeenOn = None }
let! user = data.WebLogUser.FindById newId rootId
Expect.isSome user "There should have been a user returned"
let it = user.Value
Expect.equal it.Id newId "ID is incorrect"
Expect.equal it.WebLogId rootId "Web log ID is incorrect"
Expect.equal it.Email "new@example.com" "E-mail address is incorrect"
Expect.equal it.FirstName "New" "First name is incorrect"
Expect.equal it.LastName "User" "Last name is incorrect"
Expect.equal it.PreferredName "n00b" "Preferred name is incorrect"
Expect.equal it.PasswordHash "hashed-password" "Password hash is incorrect"
Expect.equal it.Url (Some "https://example.com/~new") "URL is incorrect"
Expect.equal it.AccessLevel Author "Access level is incorrect"
Expect.equal it.CreatedOn (Noda.epoch + Duration.FromDays 365) "Created on is incorrect"
Expect.isNone it.LastSeenOn "Last seen on should not have had a value"
}
let ``FindByEmail succeeds when a user is found`` (data: IData) = task {
let! user = data.WebLogUser.FindByEmail "root@example.com" rootId
Expect.isSome user "There should have been a user returned"
Expect.equal user.Value.Id adminId "The wrong user was returned"
}
let ``FindByEmail succeeds when a user is not found (incorrect weblog)`` (data: IData) = task {
let! user = data.WebLogUser.FindByEmail "root@example.com" (WebLogId "other")
Expect.isNone user "There should not have been a user returned"
}
let ``FindByEmail succeeds when a user is not found (bad email)`` (data: IData) = task {
let! user = data.WebLogUser.FindByEmail "wwwdata@example.com" rootId
Expect.isNone user "There should not have been a user returned"
}
let ``FindById succeeds when a user is found`` (data: IData) = task {
let! user = data.WebLogUser.FindById adminId rootId
Expect.isSome user "There should have been a user returned"
Expect.equal user.Value.Id adminId "The wrong user was returned"
// The remainder of field population is tested in the "Add succeeds" test above
}
let ``FindById succeeds when a user is not found (incorrect weblog)`` (data: IData) = task {
let! user = data.WebLogUser.FindById adminId (WebLogId "not-admin")
Expect.isNone user "There should not have been a user returned"
}
let ``FindById succeeds when a user is not found (bad ID)`` (data: IData) = task {
let! user = data.WebLogUser.FindById (WebLogUserId "tom") rootId
Expect.isNone user "There should not have been a user returned"
}
let ``FindByWebLog succeeds when users exist`` (data: IData) = task {
let! users = data.WebLogUser.FindByWebLog rootId
Expect.hasLength users 4 "There should have been 4 users returned"
for user in users do
Expect.contains [ adminId; editorId; authorId; newId ] user.Id $"Unexpected user returned ({user.Id})"
}
let ``FindByWebLog succeeds when no users exist`` (data: IData) = task {
let! users = data.WebLogUser.FindByWebLog (WebLogId "no-users")
Expect.isEmpty users "There should have been no users returned"
}
let ``FindNames succeeds when users exist`` (data: IData) = task {
let! names = data.WebLogUser.FindNames rootId [ editorId; authorId ]
let expected =
[ { Name = string editorId; Value = "Edits It-Or" }; { Name = string authorId; Value = "Mister Dude" } ]
Expect.hasLength names 2 "There should have been 2 names returned"
for name in names do Expect.contains expected name $"Unexpected name returned ({name.Name}|{name.Value})"
}
let ``FindNames succeeds when users do not exist`` (data: IData) = task {
let! names = data.WebLogUser.FindNames rootId [ WebLogUserId "nope"; WebLogUserId "no" ]
Expect.isEmpty names "There should have been no names returned"
}
let ``SetLastSeen succeeds when the user exists`` (data: IData) = task {
let now = Noda.now ()
do! data.WebLogUser.SetLastSeen newId rootId
let! user = data.WebLogUser.FindById newId rootId
Expect.isSome user "The user should have been returned"
let it = user.Value
Expect.isSome it.LastSeenOn "Last seen on should have been set"
Expect.isGreaterThanOrEqual it.LastSeenOn.Value now "The last seen on date/time was not set correctly"
}
let ``SetLastSeen succeeds when the user does not exist`` (data: IData) = task {
do! data.WebLogUser.SetLastSeen (WebLogUserId "matt") rootId
Expect.isTrue true "This not raising an exception is the test"
}
let ``Update succeeds when the user exists`` (data: IData) = task {
let! currentUser = data.WebLogUser.FindById newId rootId
Expect.isSome currentUser "The current user should have been found"
do! data.WebLogUser.Update
{ currentUser.Value with
Email = "newish@example.com"
FirstName = "New-ish"
LastName = "User-ish"
PreferredName = "n00b-ish"
PasswordHash = "hashed-ish-password"
Url = None
AccessLevel = Editor }
let! updated = data.WebLogUser.FindById newId rootId
Expect.isSome updated "The updated user should have been returned"
let it = updated.Value
Expect.equal it.Id newId "ID is incorrect"
Expect.equal it.WebLogId rootId "Web log ID is incorrect"
Expect.equal it.Email "newish@example.com" "E-mail address is incorrect"
Expect.equal it.FirstName "New-ish" "First name is incorrect"
Expect.equal it.LastName "User-ish" "Last name is incorrect"
Expect.equal it.PreferredName "n00b-ish" "Preferred name is incorrect"
Expect.equal it.PasswordHash "hashed-ish-password" "Password hash is incorrect"
Expect.isNone it.Url "URL is incorrect"
Expect.equal it.AccessLevel Editor "Access level is incorrect"
Expect.equal it.CreatedOn (Noda.epoch + Duration.FromDays 365) "Created on is incorrect"
Expect.isSome it.LastSeenOn "Last seen on should have had a value"
}
let ``Update succeeds when the user does not exist`` (data: IData) = task {
do! data.WebLogUser.Update { WebLogUser.Empty with Id = WebLogUserId "nothing"; WebLogId = rootId }
let! updated = data.WebLogUser.FindById (WebLogUserId "nothing") rootId
Expect.isNone updated "The update of a missing user should not have created the user"
}
let ``Delete fails when the user is the author of a page`` (data: IData) = task {
match! data.WebLogUser.Delete adminId rootId with
| Ok _ -> Expect.isTrue false "Deletion should have failed because the user is a page author"
| Error msg -> Expect.equal msg "User has pages or posts; cannot delete" "Error message is incorrect"
}
let ``Delete fails when the user is the author of a post`` (data: IData) = task {
match! data.WebLogUser.Delete authorId rootId with
| Ok _ -> Expect.isTrue false "Deletion should have failed because the user is a post author"
| Error msg -> Expect.equal msg "User has pages or posts; cannot delete" "Error message is incorrect"
}
let ``Delete succeeds when the user is not an author`` (data: IData) = task {
match! data.WebLogUser.Delete newId rootId with
| Ok _ -> Expect.isTrue true "This is the expected outcome"
| Error msg -> Expect.isTrue false $"Deletion unexpectedly failed (message {msg})"
}
let ``Delete succeeds when the user does not exist`` (data: IData) = task {
match! data.WebLogUser.Delete newId rootId with // already deleted above
| Ok _ -> Expect.isTrue false "Deletion should have failed because the user does not exist"
| Error msg -> Expect.equal msg "User does not exist" "Error message is incorrect"
}

View File

@@ -0,0 +1,87 @@
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 ]

View File

@@ -0,0 +1,415 @@
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<ArgumentException>
(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<ArgumentException>
(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"
}
]
]
/// Tests for the ExplicitRating type
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<ArgumentException>
(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: <p>howdy</p>"
match txt with
| Html it when it = "<p>howdy</p>" -> ()
| _ -> 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<ArgumentException>
(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: <h1>HTML</h1>")) "HTML: <h1>HTML</h1>" "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: <h1>The Heading</h1>").AsHtml()) "<h1>The Heading</h1>" "HTML value incorrect"
}
test "succeeds for Markdown" {
Expect.equal
((MarkupText.Parse "Markdown: *emphasis*").AsHtml())
"<p><em>emphasis</em></p>\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<ArgumentException>
(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<ArgumentException>
(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<ArgumentException>
(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<ArgumentException>
(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<ArgumentException>
(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
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,38 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
</PropertyGroup>
<ItemGroup>
<Compile Include="Domain\SupportTypesTests.fs" />
<Compile Include="Domain\DataTypesTests.fs" />
<Compile Include="Domain\ViewModelsTests.fs" />
<Compile Include="Data\ConvertersTests.fs" />
<Compile Include="Data\UtilsTests.fs" />
<Compile Include="Data\CategoryDataTests.fs" />
<Compile Include="Data\PageDataTests.fs" />
<Compile Include="Data\PostDataTests.fs" />
<Compile Include="Data\TagMapDataTests.fs" />
<Compile Include="Data\ThemeDataTests.fs" />
<Compile Include="Data\UploadDataTests.fs" />
<Compile Include="Data\WebLogUserDataTests.fs" />
<Compile Include="Data\WebLogDataTests.fs" />
<Compile Include="Data\RethinkDbDataTests.fs" />
<Compile Include="Data\SQLiteDataTests.fs" />
<Compile Include="Data\PostgresDataTests.fs" />
<Compile Include="Program.fs" />
<None Include="root-weblog.json" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Expecto" Version="10.2.1" />
<PackageReference Include="ThrowawayDb.Postgres" Version="1.4.0" />
<PackageReference Update="FSharp.Core" Version="8.0.200" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyWebLog\MyWebLog.fsproj" />
</ItemGroup>
</Project>

View File

@@ -0,0 +1,31 @@
open Expecto
/// Whether to only run RethinkDB data tests
let rethinkOnly = (RethinkDbDataTests.env "RETHINK_ONLY" "0") = "1"
/// Whether to only run SQLite data tests
let sqliteOnly = (RethinkDbDataTests.env "SQLITE_ONLY" "0") = "1"
/// Whether to only run PostgreSQL data tests
let postgresOnly = (RethinkDbDataTests.env "PG_ONLY" "0") = "1"
/// Whether any of the data tests are being isolated
let dbOnly = rethinkOnly || sqliteOnly || postgresOnly
/// Whether to only run the unit tests (skip database/integration tests)
let unitOnly = (RethinkDbDataTests.env "UNIT_ONLY" "0") = "1"
let allTests = testList "MyWebLog" [
if not dbOnly then testList "Domain" [ SupportTypesTests.all; DataTypesTests.all; ViewModelsTests.all ]
if not unitOnly then
testList "Data" [
if not dbOnly then ConvertersTests.all
if not dbOnly then UtilsTests.all
if not dbOnly || (dbOnly && rethinkOnly) then RethinkDbDataTests.all
if not dbOnly || (dbOnly && sqliteOnly) then SQLiteDataTests.all
if not dbOnly || (dbOnly && postgresOnly) then PostgresDataTests.all
]
]
[<EntryPoint>]
let main args = runTestsWithCLIArgs [] args allTests

File diff suppressed because one or more lines are too long