Version 2.1 #41
| @ -94,10 +94,13 @@ type PostgresPageData(log: ILogger) = | |||||||
|     } |     } | ||||||
|      |      | ||||||
|     /// Find a page by its permalink for the given web log |     /// Find a page by its permalink for the given web log | ||||||
|     let findByPermalink (permalink: Permalink) webLogId = |     let findByPermalink (permalink: Permalink) webLogId = backgroundTask { | ||||||
|         log.LogTrace "Page.findByPermalink" |         log.LogTrace "Page.findByPermalink" | ||||||
|         Find.byContains<Page> Table.Page {| webLogDoc webLogId with Permalink = permalink |} |         let! page = | ||||||
|         |> tryHead |             Find.byContains<Page> Table.Page {| webLogDoc webLogId with Permalink = permalink |} | ||||||
|  |             |> tryHead | ||||||
|  |         return page |> Option.map (fun pg -> { pg with PriorPermalinks = [] }) | ||||||
|  |     } | ||||||
|      |      | ||||||
|     /// Find the current permalink within a set of potential prior permalinks for the given web log |     /// Find the current permalink within a set of potential prior permalinks for the given web log | ||||||
|     let findCurrentPermalink (permalinks: Permalink list) webLogId = backgroundTask { |     let findCurrentPermalink (permalinks: Permalink list) webLogId = backgroundTask { | ||||||
|  | |||||||
| @ -42,9 +42,12 @@ type PostgresPostData(log: ILogger) = | |||||||
|         Count.byContains Table.Post {| webLogDoc webLogId with Status = status |} |         Count.byContains Table.Post {| webLogDoc webLogId with Status = status |} | ||||||
|      |      | ||||||
|     /// Find a post by its ID for the given web log (excluding revisions) |     /// Find a post by its ID for the given web log (excluding revisions) | ||||||
|     let findById postId webLogId = |     let findById postId webLogId = backgroundTask { | ||||||
|         log.LogTrace "Post.findById" |         log.LogTrace "Post.findById" | ||||||
|         Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId |         match! Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId with | ||||||
|  |         | Some post -> return Some { post with PriorPermalinks = [] } | ||||||
|  |         | None -> return None | ||||||
|  |     } | ||||||
|      |      | ||||||
|     /// Find a post by its permalink for the given web log (excluding revisions) |     /// Find a post by its permalink for the given web log (excluding revisions) | ||||||
|     let findByPermalink (permalink: Permalink) webLogId = |     let findByPermalink (permalink: Permalink) webLogId = | ||||||
| @ -52,12 +55,12 @@ type PostgresPostData(log: ILogger) = | |||||||
|         Custom.single |         Custom.single | ||||||
|             (selectWithCriteria Table.Post) |             (selectWithCriteria Table.Post) | ||||||
|             [ jsonParam "@criteria" {| webLogDoc webLogId with Permalink = permalink |} ] |             [ jsonParam "@criteria" {| webLogDoc webLogId with Permalink = permalink |} ] | ||||||
|             fromData<Post> |             (fun row -> { fromData<Post> row with PriorPermalinks = [] }) | ||||||
|      |      | ||||||
|     /// Find a complete post by its ID for the given web log |     /// Find a complete post by its ID for the given web log | ||||||
|     let findFullById postId webLogId = backgroundTask { |     let findFullById postId webLogId = backgroundTask { | ||||||
|         log.LogTrace "Post.findFullById" |         log.LogTrace "Post.findFullById" | ||||||
|         match! findById postId webLogId with |         match! Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId with | ||||||
|         | Some post -> |         | Some post -> | ||||||
|             let! withRevisions = appendPostRevisions post |             let! withRevisions = appendPostRevisions post | ||||||
|             return Some withRevisions |             return Some withRevisions | ||||||
|  | |||||||
| @ -178,6 +178,7 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi | |||||||
|                         [| row[nameof WebLogUser.Empty.WebLogId]; row[nameof WebLogUser.Empty.Email] |] :> obj) |                         [| row[nameof WebLogUser.Empty.WebLogId]; row[nameof WebLogUser.Empty.Email] |] :> obj) | ||||||
|                     write; withRetryOnce; ignoreResult conn |                     write; withRetryOnce; ignoreResult conn | ||||||
|                 } |                 } | ||||||
|  |         do! rethink { withTable table; indexWait; result; withRetryDefault; ignoreResult conn } | ||||||
|     } |     } | ||||||
|      |      | ||||||
|     /// The batch size for restoration methods |     /// The batch size for restoration methods | ||||||
| @ -589,20 +590,27 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi | |||||||
|                     return result.Deleted > 0UL |                     return result.Deleted > 0UL | ||||||
|                 } |                 } | ||||||
|                  |                  | ||||||
|                 member _.FindById postId webLogId = |                 member _.FindById postId webLogId = backgroundTask { | ||||||
|                     rethink<Post> { |                     let! post = | ||||||
|                         withTable Table.Post |                         rethink<Post list> { | ||||||
|                         get postId |                             withTable Table.Post | ||||||
|                         without [ nameof Post.Empty.PriorPermalinks; nameof Post.Empty.Revisions ] |                             getAll [ postId ] | ||||||
|                         resultOption; withRetryOptionDefault |                             without [ nameof Post.Empty.PriorPermalinks; nameof Post.Empty.Revisions ] | ||||||
|                     } |                             result; withRetryDefault | ||||||
|                     |> verifyWebLog webLogId _.WebLogId <| conn |                         } | ||||||
|  |                         |> tryFirst <| conn | ||||||
|  |                     return | ||||||
|  |                         post | ||||||
|  |                         |> Option.filter (fun p -> p.WebLogId = webLogId) | ||||||
|  |                         |> Option.map (fun p -> { p with Revisions = []; PriorPermalinks = [] }) | ||||||
|  |                 } | ||||||
|                  |                  | ||||||
|                 member _.FindByPermalink permalink webLogId = |                 member _.FindByPermalink permalink webLogId = | ||||||
|                     rethink<Post list> { |                     rethink<Post list> { | ||||||
|                         withTable Table.Post |                         withTable Table.Post | ||||||
|                         getAll [ [| webLogId :> obj; permalink |] ] (nameof Post.Empty.Permalink) |                         getAll [ [| webLogId :> obj; permalink |] ] (nameof Post.Empty.Permalink) | ||||||
|                         without [ nameof Post.Empty.PriorPermalinks; nameof Post.Empty.Revisions ] |                         merge (r.HashMap(nameof Post.Empty.PriorPermalinks, [||]) | ||||||
|  |                                    .With(nameof Post.Empty.Revisions, [||])) | ||||||
|                         limit 1 |                         limit 1 | ||||||
|                         result; withRetryDefault |                         result; withRetryDefault | ||||||
|                     } |                     } | ||||||
|  | |||||||
| @ -102,7 +102,7 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) = | |||||||
|         conn.customSingle |         conn.customSingle | ||||||
|             $"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField linkParam "@link"}""" |             $"""{Document.Query.selectByWebLog Table.Page} AND {Query.whereByField linkParam "@link"}""" | ||||||
|             (addFieldParam "@link" linkParam [ webLogParam webLogId ]) |             (addFieldParam "@link" linkParam [ webLogParam webLogId ]) | ||||||
|             fromData<Page> |             (fun rdr -> { fromData<Page> rdr with PriorPermalinks = [] }) | ||||||
|      |      | ||||||
|     /// Find the current permalink within a set of potential prior permalinks for the given web log |     /// Find the current permalink within a set of potential prior permalinks for the given web log | ||||||
|     let findCurrentPermalink (permalinks: Permalink list) webLogId = |     let findCurrentPermalink (permalinks: Permalink list) webLogId = | ||||||
|  | |||||||
| @ -54,9 +54,12 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = | |||||||
|             (toCount >> int) |             (toCount >> int) | ||||||
|      |      | ||||||
|     /// Find a post by its ID for the given web log (excluding revisions) |     /// Find a post by its ID for the given web log (excluding revisions) | ||||||
|     let findById postId webLogId = |     let findById postId webLogId = backgroundTask { | ||||||
|         log.LogTrace "Post.findById" |         log.LogTrace "Post.findById" | ||||||
|         Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId conn |         match! Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId conn with | ||||||
|  |         | Some post -> return Some { post with PriorPermalinks = [] } | ||||||
|  |         | None -> return None | ||||||
|  |     } | ||||||
|      |      | ||||||
|     /// Find a post by its permalink for the given web log (excluding revisions) |     /// Find a post by its permalink for the given web log (excluding revisions) | ||||||
|     let findByPermalink (permalink: Permalink) webLogId = |     let findByPermalink (permalink: Permalink) webLogId = | ||||||
| @ -65,12 +68,12 @@ type SQLitePostData(conn: SqliteConnection, log: ILogger) = | |||||||
|         conn.customSingle |         conn.customSingle | ||||||
|             $"""{Document.Query.selectByWebLog Table.Post} AND {Query.whereByField linkParam "@link"}""" |             $"""{Document.Query.selectByWebLog Table.Post} AND {Query.whereByField linkParam "@link"}""" | ||||||
|             (addFieldParam "@link" linkParam [ webLogParam webLogId ]) |             (addFieldParam "@link" linkParam [ webLogParam webLogId ]) | ||||||
|             fromData<Post> |             (fun rdr -> { fromData<Post> rdr with PriorPermalinks = [] }) | ||||||
|      |      | ||||||
|     /// Find a complete post by its ID for the given web log |     /// Find a complete post by its ID for the given web log | ||||||
|     let findFullById postId webLogId = backgroundTask { |     let findFullById postId webLogId = backgroundTask { | ||||||
|         log.LogTrace "Post.findFullById" |         log.LogTrace "Post.findFullById" | ||||||
|         match! findById postId webLogId with |         match! Document.findByIdAndWebLog<PostId, Post> Table.Post postId webLogId conn with | ||||||
|         | Some post -> |         | Some post -> | ||||||
|             let! post = appendPostRevisions post |             let! post = appendPostRevisions post | ||||||
|             return Some post |             return Some post | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ type SQLiteWebLogData(conn: SqliteConnection, log: ILogger) = | |||||||
|     let delete webLogId = |     let delete webLogId = | ||||||
|         log.LogTrace "WebLog.delete" |         log.LogTrace "WebLog.delete" | ||||||
|         let webLogMatches = Query.whereByField (Field.EQ "WebLogId" "") "@webLogId" |         let webLogMatches = Query.whereByField (Field.EQ "WebLogId" "") "@webLogId" | ||||||
|         let subQuery table = $"(SELECT data ->> 'Id' FROM {table} WHERE {webLogMatches}" |         let subQuery table = $"(SELECT data ->> 'Id' FROM {table} WHERE {webLogMatches})" | ||||||
|         Custom.nonQuery |         Custom.nonQuery | ||||||
|             $"""DELETE FROM {Table.PostComment}  WHERE data ->> 'PostId' IN {subQuery Table.Post}; |             $"""DELETE FROM {Table.PostComment}  WHERE data ->> 'PostId' IN {subQuery Table.Post}; | ||||||
|                 DELETE FROM {Table.PostRevision} WHERE post_id           IN {subQuery Table.Post}; |                 DELETE FROM {Table.PostRevision} WHERE post_id           IN {subQuery Table.Post}; | ||||||
|  | |||||||
| @ -50,14 +50,9 @@ let ``Add succeeds`` (data: IData) = task { | |||||||
|     Expect.equal pg.IsInPageList page.IsInPageList "Is in page list flag 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.Template page.Template "Template not saved properly" | ||||||
|     Expect.equal pg.Text page.Text "Text not saved properly" |     Expect.equal pg.Text page.Text "Text not saved properly" | ||||||
|     Expect.hasLength pg.Metadata 1 "There should have been one meta item properly" |     Expect.equal pg.Metadata page.Metadata "Metadata not saved properly" | ||||||
|     Expect.equal pg.Metadata[0].Name page.Metadata[0].Name "Metadata name not saved properly" |     Expect.equal pg.PriorPermalinks page.PriorPermalinks "Prior permalinks not saved properly" | ||||||
|     Expect.equal pg.Metadata[0].Value page.Metadata[0].Value "Metadata value not saved properly" |     Expect.equal pg.Revisions page.Revisions "Revisions not saved properly" | ||||||
|     Expect.hasLength pg.PriorPermalinks 1 "There should have been one prior permalink" |  | ||||||
|     Expect.equal pg.PriorPermalinks[0] page.PriorPermalinks[0] "Prior permalink not saved properly" |  | ||||||
|     Expect.hasLength pg.Revisions 1 "There should have been one revision" |  | ||||||
|     Expect.equal pg.Revisions[0].AsOf page.Revisions[0].AsOf "Revision as of not saved properly" |  | ||||||
|     Expect.equal pg.Revisions[0].Text page.Revisions[0].Text "Revision text not saved properly" |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| let ``All succeeds`` (data: IData) = task { | let ``All succeeds`` (data: IData) = task { | ||||||
| @ -95,11 +90,8 @@ let ``FindById succeeds when a page is found`` (data: IData) = task { | |||||||
|     Expect.equal pg.UpdatedOn coolPagePublished "Updated 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.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.Text "<h1 id=\"a-cool-page\">A Cool Page</h1>\n<p>It really is cool!</p>\n" "Text is incorrect" | ||||||
|     Expect.hasLength pg.Metadata 2 "There should be 2 metadata items on this page" |     Expect.equal | ||||||
|     Expect.equal pg.Metadata[0].Name "Cool" "Meta item 0 name is incorrect" |         pg.Metadata [ { Name = "Cool"; Value = "true" }; { Name = "Warm"; Value = "false" } ] "Metadata is incorrect" | ||||||
|     Expect.equal pg.Metadata[0].Value "true" "Meta item 0 value is incorrect" |  | ||||||
|     Expect.equal pg.Metadata[1].Name "Warm" "Meta item 1 name is incorrect" |  | ||||||
|     Expect.equal pg.Metadata[1].Value "false" "Meta item 1 value is incorrect" |  | ||||||
|     Expect.isEmpty pg.Revisions "Revisions should not have been retrieved" |     Expect.isEmpty pg.Revisions "Revisions should not have been retrieved" | ||||||
|     Expect.isEmpty pg.PriorPermalinks "Prior permalinks should not have been retrieved" |     Expect.isEmpty pg.PriorPermalinks "Prior permalinks should not have been retrieved" | ||||||
| } | } | ||||||
| @ -119,6 +111,8 @@ let ``FindByPermalink succeeds when a page is found`` (data: IData) = task { | |||||||
|     Expect.isSome page "A page should have been returned" |     Expect.isSome page "A page should have been returned" | ||||||
|     let pg = page.Value |     let pg = page.Value | ||||||
|     Expect.equal pg.Id coolPageId "The wrong page was retrieved" |     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 ``FindByPermalink succeeds when a page is not found (incorrect weblog)`` (data: IData) = task { | ||||||
| @ -148,11 +142,11 @@ let ``FindFullById succeeds when a page is found`` (data: IData) = task { | |||||||
|     let pg = page.Value |     let pg = page.Value | ||||||
|     Expect.equal pg.Id coolPageId "The wrong page was retrieved" |     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.WebLogId rootId "The page's web log did not match the called parameter" | ||||||
|     Expect.hasLength pg.Revisions 1 "There should be 1 revision" |     Expect.equal | ||||||
|     Expect.equal pg.Revisions[0].AsOf coolPagePublished "Revision 0 as-of is incorrect" |         pg.Revisions | ||||||
|     Expect.equal pg.Revisions[0].Text (Markdown "# A Cool Page\n\nIt really is cool!") "Revision 0 text is incorrect" |         [ { AsOf = coolPagePublished; Text = Markdown "# A Cool Page\n\nIt really is cool!" } ] | ||||||
|     Expect.hasLength pg.PriorPermalinks 1 "There should be 1 prior permalink" |         "Revisions are incorrect" | ||||||
|     Expect.equal pg.PriorPermalinks[0] (Permalink "a-cool-pg.html") "Prior permalink 0 is 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 ``FindFullById succeeds when a page is not found`` (data: IData) = task { | ||||||
| @ -230,14 +224,13 @@ let ``Update succeeds when the page exists`` (data: IData) = task { | |||||||
|     Expect.equal pg.UpdatedOn (coolPagePublished + Duration.FromHours 5) "Updated 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.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.Text "<p>I have been updated" "Text is incorrect" | ||||||
|     Expect.hasLength pg.Metadata 1 "There should be 1 metadata item on this page" |     Expect.equal pg.Metadata [ { Name = "Cool"; Value = "true" } ] "Metadata is incorrect" | ||||||
|     Expect.equal pg.Metadata[0].Name "Cool" "Meta item 0 name is incorrect" |  | ||||||
|     Expect.equal pg.Metadata[0].Value "true" "Meta item 0 value is incorrect" |  | ||||||
|     Expect.equal pg.PriorPermalinks [ Permalink "a-cool-page.html" ] "Prior permalinks are incorrect" |     Expect.equal pg.PriorPermalinks [ Permalink "a-cool-page.html" ] "Prior permalinks are incorrect" | ||||||
|     Expect.hasLength pg.Revisions 2 "There should be 2 revisions" |     Expect.equal | ||||||
|     Expect.equal pg.Revisions[0].AsOf (coolPagePublished + Duration.FromHours 5) "As Of for revision 0 incorrect" |         pg.Revisions | ||||||
|     Expect.equal pg.Revisions[0].Text (Html "<p>I have been updated") "Text for revision 0 is incorrect" |         [ { AsOf = coolPagePublished + Duration.FromHours 5; Text = Html "<p>I have been updated" } | ||||||
|     Expect.equal pg.Revisions[1].AsOf coolPagePublished "As Of for revision 1 is incorrect" |           { 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 ``Update succeeds when the page does not exist`` (data: IData) = task { | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
| /// </summary>  | /// </summary>  | ||||||
| module PostDataTests | module PostDataTests | ||||||
| 
 | 
 | ||||||
|  | open System | ||||||
| open Expecto | open Expecto | ||||||
| open MyWebLog | open MyWebLog | ||||||
| open MyWebLog.Data | open MyWebLog.Data | ||||||
| @ -11,6 +12,12 @@ open NodaTime | |||||||
| /// The ID of the root web log | /// The ID of the root web log | ||||||
| let rootId = WebLogId "uSitJEuD3UyzWC9jgOHc8g" | let rootId = WebLogId "uSitJEuD3UyzWC9jgOHc8g" | ||||||
| 
 | 
 | ||||||
|  | /// The ID of podcast episode 1 | ||||||
|  | let episode1 = PostId "osxMfWGlAkyugUbJ1-xD1g" | ||||||
|  | 
 | ||||||
|  | /// The published instant for episode 1 | ||||||
|  | let episode1Published = Instant.FromDateTimeOffset(DateTimeOffset.Parse "2024-01-20T22:24:01Z") | ||||||
|  | 
 | ||||||
| let ``Add succeeds`` (data: IData) = task { | let ``Add succeeds`` (data: IData) = task { | ||||||
|     let post = |     let post = | ||||||
|         { Id              = PostId "a-new-post" |         { Id              = PostId "a-new-post" | ||||||
| @ -50,3 +57,89 @@ let ``Add succeeds`` (data: IData) = task { | |||||||
|     Expect.equal it.PriorPermalinks post.PriorPermalinks "Prior permalinks not saved properly" |     Expect.equal it.PriorPermalinks post.PriorPermalinks "Prior permalinks not saved properly" | ||||||
|     Expect.equal it.Revisions post.Revisions "Revisions 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" | ||||||
|  |     Expect.isEmpty it.PriorPermalinks "Prior permalinks should have been empty" | ||||||
|  |     Expect.isEmpty it.Revisions "Revisions should have been empty" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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" | ||||||
|  |     Expect.isEmpty it.PriorPermalinks "Prior permalinks should have been empty" | ||||||
|  |     Expect.isEmpty it.Revisions "Revisions should have been empty" | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 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" | ||||||
|  | } | ||||||
|  | |||||||
| @ -226,6 +226,39 @@ let postTests = testList "Post" [ | |||||||
|         do! freshEnvironment () |         do! freshEnvironment () | ||||||
|         do! PostDataTests.``Add succeeds`` (mkData ()) |         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 ()) | ||||||
|  |         } | ||||||
|  |     ] | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| /// Drop the throwaway PostgreSQL database | /// Drop the throwaway PostgreSQL database | ||||||
|  | |||||||
| @ -225,6 +225,39 @@ let postTests = testList "Post" [ | |||||||
|         do! freshEnvironment () |         do! freshEnvironment () | ||||||
|         do! PostDataTests.``Add succeeds`` data.Value |         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 | ||||||
|  |         } | ||||||
|  |     ] | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| /// Drop the throwaway RethinkDB database | /// Drop the throwaway RethinkDB database | ||||||
|  | |||||||
| @ -1,7 +1,7 @@ | |||||||
| module SQLiteDataTests | module SQLiteDataTests | ||||||
| 
 | 
 | ||||||
| open System.IO | open System.IO | ||||||
| open BitBadger.Documents | open BitBadger.Documents.Sqlite | ||||||
| open Expecto | open Expecto | ||||||
| open Microsoft.Extensions.Logging.Abstractions | open Microsoft.Extensions.Logging.Abstractions | ||||||
| open MyWebLog | open MyWebLog | ||||||
| @ -18,25 +18,44 @@ let dbName = | |||||||
| 
 | 
 | ||||||
| /// Create a SQLiteData instance for testing | /// Create a SQLiteData instance for testing | ||||||
| let mkData () = | let mkData () = | ||||||
|     Sqlite.Configuration.useConnectionString $"Data Source=./{dbName}" |     Configuration.useConnectionString $"Data Source=./{dbName}" | ||||||
|     let conn = Sqlite.Configuration.dbConn () |     let conn = Configuration.dbConn () | ||||||
|     SQLiteData(conn, NullLogger<SQLiteData>(), ser) :> IData |     SQLiteData(conn, NullLogger<SQLiteData>(), ser) :> IData | ||||||
| 
 | 
 | ||||||
|  | // /// Create a SQLiteData instance for testing | ||||||
|  | // let mkTraceData () = | ||||||
|  | //     Sqlite.Configuration.useConnectionString $"Data Source=./{dbName}" | ||||||
|  | //     let conn = Sqlite.Configuration.dbConn () | ||||||
|  | //     let myLogger =  | ||||||
|  | //         LoggerFactory | ||||||
|  | //             .Create(fun builder ->  | ||||||
|  | //                 builder | ||||||
|  | //                     .AddSimpleConsole() | ||||||
|  | //                     .SetMinimumLevel(LogLevel.Trace)  | ||||||
|  | //                     |> ignore) | ||||||
|  | //             .CreateLogger<SQLiteData>() | ||||||
|  | //     SQLiteData(conn, myLogger, ser) :> IData | ||||||
|  | 
 | ||||||
| /// Dispose the connection associated with the SQLiteData instance | /// Dispose the connection associated with the SQLiteData instance | ||||||
| let dispose (data: IData) = | let dispose (data: IData) = | ||||||
|     (data :?> SQLiteData).Conn.Dispose() |     (data :?> SQLiteData).Conn.Dispose() | ||||||
| 
 | 
 | ||||||
| /// Create a fresh environment from the root backup | /// Create a fresh environment from the root backup | ||||||
| let freshEnvironment (data: IData option) = task { | let freshEnvironment (data: IData option) = task { | ||||||
|     let env = |     let! env = task { | ||||||
|         match data with |         match data with | ||||||
|         | Some d -> |         | Some d -> | ||||||
|             System.Console.WriteLine "Existing data" |             return d | ||||||
|             d |  | ||||||
|         | None -> |         | None -> | ||||||
|             System.Console.WriteLine $"No data; deleting {dbName}" |             let d = mkData () | ||||||
|             File.Delete dbName |             // Thank you, kind Internet stranger... https://stackoverflow.com/a/548297 | ||||||
|             mkData () |             do! (d :?> SQLiteData).Conn.customNonQuery | ||||||
|  |                     "PRAGMA writable_schema = 1; | ||||||
|  |                      DELETE FROM sqlite_master WHERE type IN ('table', 'index'); | ||||||
|  |                      PRAGMA writable_schema = 0; | ||||||
|  |                      VACUUM" [] | ||||||
|  |             return d | ||||||
|  |         } | ||||||
|     do! env.StartUp() |     do! env.StartUp() | ||||||
|     // This exercises Restore for all implementations; all tests are dependent on it working as expected |     // 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 |     do! Maintenance.Backup.restoreBackup "root-weblog.json" None false false env | ||||||
| @ -296,6 +315,57 @@ let postTests = testList "Post" [ | |||||||
|         try do! PostDataTests.``Add succeeds`` data |         try do! PostDataTests.``Add succeeds`` data | ||||||
|         finally dispose data |         finally dispose data | ||||||
|     } |     } | ||||||
|  |     testTask "CountPostsByStatus succeeds" { | ||||||
|  |         let data = mkData () | ||||||
|  |         try do! PostDataTests.``CountByStatus succeeds`` data | ||||||
|  |         finally dispose data | ||||||
|  |     } | ||||||
|  |     testList "FindById" [ | ||||||
|  |         testTask "succeeds when a post is found" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindById succeeds when a post is found`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |         testTask "succeeds when a post is not found (incorrect weblog)" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindById succeeds when a post is not found (incorrect weblog)`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |         testTask "succeeds when a post is not found (bad post ID)" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindById succeeds when a post is not found (bad post ID)`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  |     testList "FindByPermalink" [ | ||||||
|  |         testTask "succeeds when a post is found" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindByPermalink succeeds when a post is found`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |         testTask "succeeds when a post is not found (incorrect weblog)" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindByPermalink succeeds when a post is not found (incorrect weblog)`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |         testTask "succeeds when a post is not found (no such permalink)" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindByPermalink succeeds when a post is not found (no such permalink)`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |     ] | ||||||
|  |     testList "FindCurrentPermalink" [ | ||||||
|  |         testTask "succeeds when a post is found" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindCurrentPermalink succeeds when a post is found`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |         testTask "succeeds when a post is not found" { | ||||||
|  |             let data = mkData () | ||||||
|  |             try do! PostDataTests.``FindCurrentPermalink succeeds when a post is not found`` data | ||||||
|  |             finally dispose data | ||||||
|  |         } | ||||||
|  |     ] | ||||||
| ] | ] | ||||||
| 
 | 
 | ||||||
| /// Delete the SQLite database | /// Delete the SQLite database | ||||||
|  | |||||||
| @ -272,7 +272,9 @@ | |||||||
|           "Value": "Low" |           "Value": "Low" | ||||||
|         } |         } | ||||||
|       ], |       ], | ||||||
|       "PriorPermalinks": [], |       "PriorPermalinks": [ | ||||||
|  |         "2024/ep-1.html" | ||||||
|  |       ], | ||||||
|       "Revisions": [ |       "Revisions": [ | ||||||
|         { |         { | ||||||
|           "AsOf": "2024-01-20T22:24:01Z", |           "AsOf": "2024-01-20T22:24:01Z", | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user