Add person data access, integ tests, backup/restore (#17)
This commit is contained in:
@@ -9,6 +9,14 @@ module Json =
|
|||||||
|
|
||||||
open Newtonsoft.Json
|
open Newtonsoft.Json
|
||||||
|
|
||||||
|
/// <summary>Converter for the <see cref="AccessLevel" /> type</summary>
|
||||||
|
type AccessLevelConverter() =
|
||||||
|
inherit JsonConverter<AccessLevel>()
|
||||||
|
override _.WriteJson(writer: JsonWriter, value: AccessLevel, _: JsonSerializer) =
|
||||||
|
writer.WriteValue(string value)
|
||||||
|
override _.ReadJson(reader: JsonReader, _: Type, _: AccessLevel, _: bool, _: JsonSerializer) =
|
||||||
|
(string >> AccessLevel.Parse) reader.Value
|
||||||
|
|
||||||
/// <summary>Converter for the <see cref="AutoHtmxType" /> type</summary>
|
/// <summary>Converter for the <see cref="AutoHtmxType" /> type</summary>
|
||||||
type AutoHtmxTypeConverter() =
|
type AutoHtmxTypeConverter() =
|
||||||
inherit JsonConverter<AutoHtmxType>()
|
inherit JsonConverter<AutoHtmxType>()
|
||||||
@@ -195,7 +203,8 @@ module Json =
|
|||||||
/// <summary>Configure a serializer to use these converters (and other settings)</summary>
|
/// <summary>Configure a serializer to use these converters (and other settings)</summary>
|
||||||
let configure (ser: JsonSerializer) =
|
let configure (ser: JsonSerializer) =
|
||||||
// Our converters
|
// Our converters
|
||||||
[ AutoHtmxTypeConverter() :> JsonConverter
|
[ AccessLevelConverter() :> JsonConverter
|
||||||
|
AutoHtmxTypeConverter()
|
||||||
CategoryIdConverter()
|
CategoryIdConverter()
|
||||||
CommentIdConverter()
|
CommentIdConverter()
|
||||||
CommentStatusConverter()
|
CommentStatusConverter()
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ type PostgresPersonData(log: ILogger) =
|
|||||||
Find.byFieldsOrdered<Person>
|
Find.byFieldsOrdered<Person>
|
||||||
Table.Person
|
Table.Person
|
||||||
All
|
All
|
||||||
[ Field.In (nameof Person.Empty.Id) (assignments |> List.map _.PersonId) ]
|
[ Field.In (nameof Person.Empty.Id) (assignments |> List.map (fun it -> string it.PersonId)) ]
|
||||||
[ Field.Named (nameof Person.Empty.Name) ]
|
[ Field.Named (nameof Person.Empty.Name) ]
|
||||||
|
|
||||||
/// Find a person by their ID for the given web log
|
/// Find a person by their ID for the given web log
|
||||||
@@ -46,7 +46,7 @@ type PostgresPersonData(log: ILogger) =
|
|||||||
/// Find all persons for the given web log
|
/// Find all persons for the given web log
|
||||||
let findByWebLog webLogId =
|
let findByWebLog webLogId =
|
||||||
log.LogTrace "Person.findByWebLog"
|
log.LogTrace "Person.findByWebLog"
|
||||||
Find.byContainsOrdered<Person> Table.Person (webLogContains webLogId) [ Field.Named (nameof Person.Empty.Name) ]
|
Find.byContainsOrdered<Person> Table.Person (webLogDoc webLogId) [ Field.Named (nameof Person.Empty.Name) ]
|
||||||
|
|
||||||
|
|
||||||
/// Restore persons from a backup
|
/// Restore persons from a backup
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ type SQLitePersonData(conn: SqliteConnection, log: ILogger) =
|
|||||||
log.LogTrace "Person.findByAssignment"
|
log.LogTrace "Person.findByAssignment"
|
||||||
conn.findByFieldsOrdered<Person>
|
conn.findByFieldsOrdered<Person>
|
||||||
Table.Person All
|
Table.Person All
|
||||||
[ Field.In "PersonId" (assignments |> List.map _.PersonId) ]
|
[ Field.In (nameof Person.Empty.Id) (assignments |> List.map (fun it -> string it.PersonId)) ]
|
||||||
[ Field.Named (nameof Person.Empty.Name) ]
|
[ Field.Named (nameof Person.Empty.Name) ]
|
||||||
|
|
||||||
/// Find a person by their ID
|
/// Find a person by their ID
|
||||||
|
|||||||
@@ -414,7 +414,7 @@ type PersonId =
|
|||||||
|
|
||||||
/// <summary>Create a new person ID</summary>
|
/// <summary>Create a new person ID</summary>
|
||||||
/// <returns>A new person ID</returns>
|
/// <returns>A new person ID</returns>
|
||||||
static member Create =
|
static member Create() =
|
||||||
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-')[..21] |> PersonId
|
Convert.ToBase64String(Guid.NewGuid().ToByteArray()).Replace('/', '_').Replace('+', '-')[..21] |> PersonId
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@@ -88,7 +88,9 @@ type AccessLevel =
|
|||||||
/// <exception cref="InvalidArgumentException">If the string is not valid</exception>
|
/// <exception cref="InvalidArgumentException">If the string is not valid</exception>
|
||||||
static member Parse level =
|
static member Parse level =
|
||||||
match level with
|
match level with
|
||||||
|
// Legacy name; superseded by Author on PersonRole
|
||||||
| "Author" -> ContentAuthor
|
| "Author" -> ContentAuthor
|
||||||
|
| "ContentAuthor" -> ContentAuthor
|
||||||
| "Editor" -> Editor
|
| "Editor" -> Editor
|
||||||
| "WebLogAdmin" -> WebLogAdmin
|
| "WebLogAdmin" -> WebLogAdmin
|
||||||
| "Administrator" -> Administrator
|
| "Administrator" -> Administrator
|
||||||
@@ -97,7 +99,7 @@ type AccessLevel =
|
|||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
override this.ToString() =
|
override this.ToString() =
|
||||||
match this with
|
match this with
|
||||||
| ContentAuthor -> "Author"
|
| ContentAuthor -> "ContentAuthor"
|
||||||
| Editor -> "Editor"
|
| Editor -> "Editor"
|
||||||
| WebLogAdmin -> "WebLogAdmin"
|
| WebLogAdmin -> "WebLogAdmin"
|
||||||
| Administrator -> "Administrator"
|
| Administrator -> "Administrator"
|
||||||
|
|||||||
@@ -6,6 +6,20 @@ open MyWebLog
|
|||||||
open MyWebLog.Converters.Json
|
open MyWebLog.Converters.Json
|
||||||
open Newtonsoft.Json
|
open Newtonsoft.Json
|
||||||
|
|
||||||
|
/// Unit tests for the AutoHtmxTypeConverter type
|
||||||
|
let accessLevelConverterTests = testList "AccessLevelConverter" [
|
||||||
|
let opts = JsonSerializerSettings()
|
||||||
|
opts.Converters.Add(AccessLevelConverter())
|
||||||
|
test "succeeds when serializing" {
|
||||||
|
let after = JsonConvert.SerializeObject(Editor, opts)
|
||||||
|
Expect.equal after "\"Editor\"" "Access Level serialized incorrectly"
|
||||||
|
}
|
||||||
|
test "succeeds when deserializing" {
|
||||||
|
let after = JsonConvert.DeserializeObject<AccessLevel>("\"Author\"", opts)
|
||||||
|
Expect.equal after ContentAuthor "Access Level deserialized incorrectly"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
/// Unit tests for the AutoHtmxTypeConverter type
|
/// Unit tests for the AutoHtmxTypeConverter type
|
||||||
let autoHtmxTypeConverterTests = testList "AutoHtmxTypeConverter" [
|
let autoHtmxTypeConverterTests = testList "AutoHtmxTypeConverter" [
|
||||||
let opts = JsonSerializerSettings()
|
let opts = JsonSerializerSettings()
|
||||||
@@ -324,6 +338,7 @@ open NodaTime.Serialization.JsonNet
|
|||||||
let configureTests = test "Json.configure succeeds" {
|
let configureTests = test "Json.configure succeeds" {
|
||||||
let has typ (converter: JsonConverter) = converter.GetType() = typ
|
let has typ (converter: JsonConverter) = converter.GetType() = typ
|
||||||
let ser = configure (JsonSerializer.Create())
|
let ser = configure (JsonSerializer.Create())
|
||||||
|
Expect.hasCountOf ser.Converters 1u (has typeof<AccessLevelConverter>) "Access Level converter not found"
|
||||||
Expect.hasCountOf ser.Converters 1u (has typeof<AutoHtmxTypeConverter>) "Auto htmx type converter not found"
|
Expect.hasCountOf ser.Converters 1u (has typeof<AutoHtmxTypeConverter>) "Auto htmx type converter not found"
|
||||||
Expect.hasCountOf ser.Converters 1u (has typeof<CategoryIdConverter>) "Category ID converter not found"
|
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<CommentIdConverter>) "Comment ID converter not found"
|
||||||
@@ -354,6 +369,7 @@ let configureTests = test "Json.configure succeeds" {
|
|||||||
|
|
||||||
/// All tests for the Data.Converters file
|
/// All tests for the Data.Converters file
|
||||||
let all = testList "Converters" [
|
let all = testList "Converters" [
|
||||||
|
accessLevelConverterTests
|
||||||
autoHtmxTypeConverterTests
|
autoHtmxTypeConverterTests
|
||||||
categoryIdConverterTests
|
categoryIdConverterTests
|
||||||
commentIdConverterTests
|
commentIdConverterTests
|
||||||
|
|||||||
@@ -0,0 +1,104 @@
|
|||||||
|
/// <summary>Integration tests for <see cref="IPersonData" /> implementations</summary>
|
||||||
|
module PersonDataTests
|
||||||
|
|
||||||
|
open System
|
||||||
|
open Expecto
|
||||||
|
open MyWebLog
|
||||||
|
open MyWebLog.Data
|
||||||
|
|
||||||
|
/// The ID of the root web log
|
||||||
|
let private rootId = CategoryDataTests.rootId
|
||||||
|
|
||||||
|
/// The person ID for "Crackpot"
|
||||||
|
let private crackpotPersonId = PersonId "Q-6ElUj4cEyg5_wQeMpH_A"
|
||||||
|
|
||||||
|
/// The person ID for "Buzzkill" (RIP, JCD)
|
||||||
|
let private buzzkillPersonId = PersonId "a8M4gN58R0evXnJEt9j95w"
|
||||||
|
|
||||||
|
let ``Add succeeds`` (data: IData) = task {
|
||||||
|
let person =
|
||||||
|
{ Id = PersonId "person-x"
|
||||||
|
WebLogId = WebLogId "web-log-x"
|
||||||
|
Name = "Anonny Moose"
|
||||||
|
Url = None
|
||||||
|
ImageUrl = Some "sunglasses.jpg" }
|
||||||
|
do! data.Person.Add person
|
||||||
|
let! stored = data.Person.FindById person.Id person.WebLogId
|
||||||
|
Expect.isSome stored "The added person should have been retrieved"
|
||||||
|
let it = stored.Value
|
||||||
|
Expect.equal it.Id person.Id "ID not saved properly"
|
||||||
|
Expect.equal it.WebLogId person.WebLogId "Web Log ID not saved properly"
|
||||||
|
Expect.equal it.Name person.Name "Name not saved properly"
|
||||||
|
Expect.equal it.Url person.Url "URL not saved properly"
|
||||||
|
Expect.equal it.ImageUrl person.ImageUrl "Image URL not saved properly"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindByAssignment succeeds when assignments are found`` (data: IData) = task {
|
||||||
|
let assignments =
|
||||||
|
[ { PersonId = crackpotPersonId; Role = Producer }; { PersonId = buzzkillPersonId; Role = Producer } ]
|
||||||
|
let! people = data.Person.FindByAssignment assignments
|
||||||
|
Expect.hasLength people 2 "There should have been two people returned"
|
||||||
|
Expect.equal (people |> List.head).Name "Buzzkill" "The list was not sorted correctly"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindByAssignment succeeds when no assignments are found`` (data: IData) = task {
|
||||||
|
let assignments =
|
||||||
|
[ { PersonId = PersonId "missing"; Role = Guest }
|
||||||
|
{ PersonId = PersonId "still-missing"; Role = GuestHost } ]
|
||||||
|
let! people = data.Person.FindByAssignment assignments
|
||||||
|
Expect.hasLength people 0 "There should not have been any people returned"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindById succeeds when a person is found`` (data: IData) = task {
|
||||||
|
let! person = data.Person.FindById buzzkillPersonId rootId
|
||||||
|
Expect.isSome person "There should have been a person returned"
|
||||||
|
let it = person.Value
|
||||||
|
Expect.equal it.Id buzzkillPersonId "ID retrieved incorrectly"
|
||||||
|
Expect.equal it.WebLogId rootId "Web Log ID retrieved incorrectly"
|
||||||
|
Expect.equal it.Name "Buzzkill" "Name retrieved incorrectly"
|
||||||
|
Expect.isNone it.Url "URL should have been blank"
|
||||||
|
Expect.isNone it.ImageUrl "Image URL should have been blank"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindById succeeds when a person is not found (incorrect weblog)`` (data: IData) = task {
|
||||||
|
let! person = data.Person.FindById crackpotPersonId (WebLogId "incorrect")
|
||||||
|
Expect.isNone person "There should not have been a person returned"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindById succeeds when a person is not found (bad person ID)`` (data: IData) = task {
|
||||||
|
let! person = data.Person.FindById (PersonId "bad") rootId
|
||||||
|
Expect.isNone person "There should not have been a person returned"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindByWebLog succeeds when people are found`` (data: IData) = task {
|
||||||
|
let! people = data.Person.FindByWebLog rootId
|
||||||
|
Expect.hasLength people 2 "There should have been 2 people returned"
|
||||||
|
Expect.equal (people |> List.head).Name "Buzzkill" "The people were not sorted correctly"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``FindByWebLog succeeds when no people are found`` (data: IData) = task {
|
||||||
|
let! people = data.Person.FindByWebLog (WebLogId "no-folks")
|
||||||
|
Expect.hasLength people 0 "There should have been no people returned"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``Update succeeds when person is updated`` (data: IData) = task {
|
||||||
|
let! person = data.Person.FindById crackpotPersonId rootId
|
||||||
|
Expect.isSome person "The original person should have been returned"
|
||||||
|
do! data.Person.Update { person.Value with Name = "Crackpot!" }
|
||||||
|
let! updated = data.Person.FindById crackpotPersonId rootId
|
||||||
|
Expect.isSome updated "The updated person should have been returned"
|
||||||
|
Expect.equal updated.Value.Name "Crackpot!" "Name was not updated"
|
||||||
|
// Put the name back
|
||||||
|
do! data.Person.Update person.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``Update succeeds when person is not updated`` (data: IData) = task {
|
||||||
|
let! person = data.Person.FindById crackpotPersonId rootId
|
||||||
|
Expect.isSome person "The original person should have been returned"
|
||||||
|
do! data.Person.Update { person.Value with Name = "New Name"; WebLogId = WebLogId "invalid" }
|
||||||
|
let! updated = data.Person.FindById crackpotPersonId rootId
|
||||||
|
Expect.isSome updated "The updated person should have been returned"
|
||||||
|
Expect.equal updated.Value.Name "Crackpot" "The update should not have occurred"
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: write delete tests when they cover podcasts and episodes as well
|
||||||
@@ -1,6 +1,4 @@
|
|||||||
/// <summary>
|
/// <summary>Integration tests for <see cref="IPostData" /> implementations</summary>
|
||||||
/// Integration tests for <see cref="IPostData" /> implementations
|
|
||||||
/// </summary>
|
|
||||||
module PostDataTests
|
module PostDataTests
|
||||||
|
|
||||||
open System
|
open System
|
||||||
@@ -39,6 +37,12 @@ let private testPost1Published = Instant.FromDateTimeOffset(DateTimeOffset.Parse
|
|||||||
/// The category IDs for "Spitball" (parent) and "Moonshot"
|
/// The category IDs for "Spitball" (parent) and "Moonshot"
|
||||||
let private testCatIds = [ CategoryId "jw6N69YtTEWVHAO33jHU-w"; CategoryId "ScVpyu1e7UiP7bDdge3ZEw" ]
|
let private testCatIds = [ CategoryId "jw6N69YtTEWVHAO33jHU-w"; CategoryId "ScVpyu1e7UiP7bDdge3ZEw" ]
|
||||||
|
|
||||||
|
/// The person ID for "Crackpot"
|
||||||
|
let private crackpotPersonId = PersonId "Q-6ElUj4cEyg5_wQeMpH_A"
|
||||||
|
|
||||||
|
/// The person ID for "Buzzkill" (RIP, JCD)
|
||||||
|
let private buzzkillPersonId = PersonId "a8M4gN58R0evXnJEt9j95w"
|
||||||
|
|
||||||
/// Ensure that a list of posts has text for each post
|
/// Ensure that a list of posts has text for each post
|
||||||
let private ensureHasText (posts: Post list) =
|
let private ensureHasText (posts: Post list) =
|
||||||
for post in posts do Expect.isNotEmpty post.Text $"Text should not be blank (post ID {post.Id})"
|
for post in posts do Expect.isNotEmpty post.Text $"Text should not be blank (post ID {post.Id})"
|
||||||
@@ -129,6 +133,11 @@ let ``FindById succeeds when a post is found`` (data: IData) = task {
|
|||||||
Expect.equal ep.SeasonDescription (Some "The First Season") "Episode season description 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.EpisodeNumber (Some 1.) "Episode number is incorrect"
|
||||||
Expect.equal ep.EpisodeDescription (Some "The first episode ever!") "Episode description is incorrect"
|
Expect.equal ep.EpisodeDescription (Some "The first episode ever!") "Episode description is incorrect"
|
||||||
|
Expect.isSome ep.People "There should have been people on the episode"
|
||||||
|
let ppl = ep.People.Value
|
||||||
|
Expect.hasLength ppl 2 "There should have been 2 people on the episode"
|
||||||
|
Expect.contains ppl { PersonId = crackpotPersonId; Role = CoHost } "Crackpot not found on episode"
|
||||||
|
Expect.contains ppl { PersonId = buzzkillPersonId; Role = VoiceActor } "Buzzkill not found on episode"
|
||||||
Expect.equal
|
Expect.equal
|
||||||
it.Metadata
|
it.Metadata
|
||||||
[ { Name = "Density"; Value = "Non-existent" }; { Name = "Intensity"; Value = "Low" } ]
|
[ { Name = "Density"; Value = "Non-existent" }; { Name = "Intensity"; Value = "Low" } ]
|
||||||
|
|||||||
@@ -224,6 +224,40 @@ let private pageTests = testList "Page" [
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Person implementation in PostgreSQL
|
||||||
|
let private personTests = testList "Person" [
|
||||||
|
testTask "Add succeeds" {
|
||||||
|
do! PersonDataTests.``Add succeeds`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindByAssignment succeeds when assignments are found" {
|
||||||
|
do! PersonDataTests.``FindByAssignment succeeds when assignments are found`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindByAssignment succeeds when no assignments are found" {
|
||||||
|
do! PersonDataTests.``FindByAssignment succeeds when no assignments are found`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is found" {
|
||||||
|
do! PersonDataTests.``FindById succeeds when a person is found`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is not found (incorrect weblog)" {
|
||||||
|
do! PersonDataTests.``FindById succeeds when a person is not found (incorrect weblog)`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is not found (bad person ID)" {
|
||||||
|
do! PersonDataTests.``FindById succeeds when a person is not found (bad person ID)`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindByWebLog succeeds when people are found" {
|
||||||
|
do! PersonDataTests.``FindByWebLog succeeds when people are found`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "FindByWebLog succeeds when no people are found" {
|
||||||
|
do! PersonDataTests.``FindByWebLog succeeds when no people are found`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "Update succeeds when person is updated" {
|
||||||
|
do! PersonDataTests.``Update succeeds when person is updated`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "Update succeeds when person is not updated" {
|
||||||
|
do! PersonDataTests.``Update succeeds when person is not updated`` (mkData ())
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
/// Integration tests for the Post implementation in PostgreSQL
|
/// Integration tests for the Post implementation in PostgreSQL
|
||||||
let private postTests = testList "Post" [
|
let private postTests = testList "Post" [
|
||||||
testTask "Add succeeds" {
|
testTask "Add succeeds" {
|
||||||
@@ -711,6 +745,7 @@ let all =
|
|||||||
[ environmentSetUp
|
[ environmentSetUp
|
||||||
categoryTests
|
categoryTests
|
||||||
pageTests
|
pageTests
|
||||||
|
personTests
|
||||||
postTests
|
postTests
|
||||||
tagMapTests
|
tagMapTests
|
||||||
themeTests
|
themeTests
|
||||||
|
|||||||
@@ -218,6 +218,40 @@ let private pageTests = testList "Page" [
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Post implementation in RethinkDB
|
||||||
|
let private personTests = testList "Person" [
|
||||||
|
testTask "Add succeeds" {
|
||||||
|
do! PersonDataTests.``Add succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindByAssignment succeeds when assignments are found" {
|
||||||
|
do! PersonDataTests.``FindByAssignment succeeds when assignments are found`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindByAssignment succeeds when no assignments are found" {
|
||||||
|
do! PersonDataTests.``FindByAssignment succeeds when no assignments are found`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is found" {
|
||||||
|
do! PersonDataTests.``FindById succeeds when a person is found`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is not found (incorrect weblog)" {
|
||||||
|
do! PersonDataTests.``FindById succeeds when a person is not found (incorrect weblog)`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is not found (bad person ID)" {
|
||||||
|
do! PersonDataTests.``FindById succeeds when a person is not found (bad person ID)`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindByWebLog succeeds when people are found" {
|
||||||
|
do! PersonDataTests.``FindByWebLog succeeds when people are found`` data.Value
|
||||||
|
}
|
||||||
|
testTask "FindByWebLog succeeds when no people are found" {
|
||||||
|
do! PersonDataTests.``FindByWebLog succeeds when no people are found`` data.Value
|
||||||
|
}
|
||||||
|
testTask "Update succeeds when person is updated" {
|
||||||
|
do! PersonDataTests.``Update succeeds when person is updated`` data.Value
|
||||||
|
}
|
||||||
|
testTask "Update succeeds when person is not updated" {
|
||||||
|
do! PersonDataTests.``Update succeeds when person is not updated`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
/// Integration tests for the Post implementation in RethinkDB
|
/// Integration tests for the Post implementation in RethinkDB
|
||||||
let private postTests = testList "Post" [
|
let private postTests = testList "Post" [
|
||||||
testTask "Add succeeds" {
|
testTask "Add succeeds" {
|
||||||
@@ -693,6 +727,7 @@ let all =
|
|||||||
[ environmentSetUp
|
[ environmentSetUp
|
||||||
categoryTests
|
categoryTests
|
||||||
pageTests
|
pageTests
|
||||||
|
personTests
|
||||||
postTests
|
postTests
|
||||||
tagMapTests
|
tagMapTests
|
||||||
themeTests
|
themeTests
|
||||||
|
|||||||
@@ -326,6 +326,60 @@ let private pageTests = testList "Page" [
|
|||||||
]
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Person implementation in SQLite
|
||||||
|
let private personTests = testList "Person" [
|
||||||
|
testTask "Add succeeds" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``Add succeeds`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindByAssignment succeeds when assignments are found" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindByAssignment succeeds when assignments are found`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindByAssignment succeeds when no assignments are found" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindByAssignment succeeds when no assignments are found`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is found" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindById succeeds when a person is found`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is not found (incorrect weblog)" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindById succeeds when a person is not found (incorrect weblog)`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindById succeeds when a person is not found (bad person ID)" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindById succeeds when a person is not found (bad person ID)`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindByWebLog succeeds when people are found" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindByWebLog succeeds when people are found`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "FindByWebLog succeeds when no people are found" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``FindByWebLog succeeds when no people are found`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "Update succeeds when person is updated" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``Update succeeds when person is updated`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "Update succeeds when person is not updated" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PersonDataTests.``Update succeeds when person is not updated`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
/// Integration tests for the Post implementation in SQLite
|
/// Integration tests for the Post implementation in SQLite
|
||||||
let private postTests = testList "Post" [
|
let private postTests = testList "Post" [
|
||||||
testTask "Add succeeds" {
|
testTask "Add succeeds" {
|
||||||
@@ -1043,6 +1097,7 @@ let all =
|
|||||||
[ environmentSetUp
|
[ environmentSetUp
|
||||||
categoryTests
|
categoryTests
|
||||||
pageTests
|
pageTests
|
||||||
|
personTests
|
||||||
postTests
|
postTests
|
||||||
tagMapTests
|
tagMapTests
|
||||||
themeTests
|
themeTests
|
||||||
|
|||||||
@@ -1,6 +1,4 @@
|
|||||||
/// <summary>
|
/// <summary>Integration tests for <see cref="IWebLogData" /> implementations</summary>
|
||||||
/// Integration tests for <see cref="IWebLogData" /> implementations
|
|
||||||
/// </summary>
|
|
||||||
module WebLogDataTests
|
module WebLogDataTests
|
||||||
|
|
||||||
open System
|
open System
|
||||||
@@ -122,6 +120,11 @@ let ``FindById succeeds when a web log is found`` (data: IData) = task {
|
|||||||
Expect.equal pod.FundingUrl (Some "https://example.com/support-us") "Podcast funding URL 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.FundingText (Some "Support Our Work") "Podcast funding text is incorrect"
|
||||||
Expect.equal pod.Medium (Some Newsletter) "Podcast medium is incorrect"
|
Expect.equal pod.Medium (Some Newsletter) "Podcast medium is incorrect"
|
||||||
|
Expect.isSome pod.People "There should have been people associated with the podcast"
|
||||||
|
let ppl = pod.People.Value
|
||||||
|
Expect.hasLength ppl 2 "There should have been 2 people associatged with the podcast"
|
||||||
|
Expect.contains ppl { PersonId = PersonId "Q-6ElUj4cEyg5_wQeMpH_A"; Role = Host } "Crackpot not found"
|
||||||
|
Expect.contains ppl { PersonId = PersonId "a8M4gN58R0evXnJEt9j95w"; Role = Host } "Buzzkill not found :("
|
||||||
}
|
}
|
||||||
|
|
||||||
let ``FindById succeeds when a web log is not found`` (data: IData) = task {
|
let ``FindById succeeds when a web log is not found`` (data: IData) = task {
|
||||||
|
|||||||
@@ -33,6 +33,10 @@ let accessLevelTests = testList "AccessLevel" [
|
|||||||
test "succeeds for \"Author\"" {
|
test "succeeds for \"Author\"" {
|
||||||
Expect.equal (AccessLevel.Parse "Author") AccessLevel.ContentAuthor "Author not parsed correctly"
|
Expect.equal (AccessLevel.Parse "Author") AccessLevel.ContentAuthor "Author not parsed correctly"
|
||||||
}
|
}
|
||||||
|
test "succeeds for \"ContentAuthor\"" {
|
||||||
|
Expect.equal
|
||||||
|
(AccessLevel.Parse "ContentAuthor") AccessLevel.ContentAuthor "ContentAuthor not parsed correctly"
|
||||||
|
}
|
||||||
test "succeeds for \"Editor\"" {
|
test "succeeds for \"Editor\"" {
|
||||||
Expect.equal (AccessLevel.Parse "Editor") Editor "Editor not parsed correctly"
|
Expect.equal (AccessLevel.Parse "Editor") Editor "Editor not parsed correctly"
|
||||||
}
|
}
|
||||||
@@ -49,7 +53,7 @@ let accessLevelTests = testList "AccessLevel" [
|
|||||||
]
|
]
|
||||||
testList "ToString" [
|
testList "ToString" [
|
||||||
test "ContentAuthor succeeds" {
|
test "ContentAuthor succeeds" {
|
||||||
Expect.equal (string AccessLevel.ContentAuthor) "Author" "ContentAuthor string incorrect"
|
Expect.equal (string AccessLevel.ContentAuthor) "ContentAuthor" "ContentAuthor string incorrect"
|
||||||
}
|
}
|
||||||
test "Editor succeeds" {
|
test "Editor succeeds" {
|
||||||
Expect.equal (string Editor) "Editor" "Editor string incorrect"
|
Expect.equal (string Editor) "Editor" "Editor string incorrect"
|
||||||
|
|||||||
@@ -13,6 +13,7 @@
|
|||||||
<Compile Include="Data\UtilsTests.fs" />
|
<Compile Include="Data\UtilsTests.fs" />
|
||||||
<Compile Include="Data\CategoryDataTests.fs" />
|
<Compile Include="Data\CategoryDataTests.fs" />
|
||||||
<Compile Include="Data\PageDataTests.fs" />
|
<Compile Include="Data\PageDataTests.fs" />
|
||||||
|
<Compile Include="Data\PersonDataTests.fs" />
|
||||||
<Compile Include="Data\PostDataTests.fs" />
|
<Compile Include="Data\PostDataTests.fs" />
|
||||||
<Compile Include="Data\TagMapDataTests.fs" />
|
<Compile Include="Data\TagMapDataTests.fs" />
|
||||||
<Compile Include="Data\ThemeDataTests.fs" />
|
<Compile Include="Data\ThemeDataTests.fs" />
|
||||||
|
|||||||
@@ -36,7 +36,17 @@
|
|||||||
"PodcastGuid": "10fd7f79-c719-4e1d-9da7-10405dd4fd96",
|
"PodcastGuid": "10fd7f79-c719-4e1d-9da7-10405dd4fd96",
|
||||||
"FundingUrl": "https://example.com/support-us",
|
"FundingUrl": "https://example.com/support-us",
|
||||||
"FundingText": "Support Our Work",
|
"FundingText": "Support Our Work",
|
||||||
"Medium": "newsletter"
|
"Medium": "newsletter",
|
||||||
|
"People": [
|
||||||
|
{
|
||||||
|
"PersonId": "Q-6ElUj4cEyg5_wQeMpH_A",
|
||||||
|
"Role": "Host"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PersonId": "a8M4gN58R0evXnJEt9j95w",
|
||||||
|
"Role": "Host"
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
@@ -200,6 +210,19 @@
|
|||||||
]
|
]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
|
"People": [
|
||||||
|
{
|
||||||
|
"Id": "Q-6ElUj4cEyg5_wQeMpH_A",
|
||||||
|
"WebLogId": "uSitJEuD3UyzWC9jgOHc8g",
|
||||||
|
"Name": "Crackpot",
|
||||||
|
"Url": "bio.html"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"Id": "a8M4gN58R0evXnJEt9j95w",
|
||||||
|
"WebLogId": "uSitJEuD3UyzWC9jgOHc8g",
|
||||||
|
"Name": "Buzzkill"
|
||||||
|
}
|
||||||
|
],
|
||||||
"Posts": [
|
"Posts": [
|
||||||
{
|
{
|
||||||
"Id": "RCsCU2puYEmkpzotoi8p4g",
|
"Id": "RCsCU2puYEmkpzotoi8p4g",
|
||||||
@@ -260,7 +283,17 @@
|
|||||||
"SeasonNumber": 1,
|
"SeasonNumber": 1,
|
||||||
"SeasonDescription": "The First Season",
|
"SeasonDescription": "The First Season",
|
||||||
"EpisodeNumber": 1.0,
|
"EpisodeNumber": 1.0,
|
||||||
"EpisodeDescription": "The first episode ever!"
|
"EpisodeDescription": "The first episode ever!",
|
||||||
|
"People": [
|
||||||
|
{
|
||||||
|
"PersonId": "Q-6ElUj4cEyg5_wQeMpH_A",
|
||||||
|
"Role": "Co-Host"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"PersonId": "a8M4gN58R0evXnJEt9j95w",
|
||||||
|
"Role": "Voice Actor"
|
||||||
|
}
|
||||||
|
]
|
||||||
},
|
},
|
||||||
"Metadata": [
|
"Metadata": [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -229,6 +229,9 @@ module Backup =
|
|||||||
/// The pages for this web log (containing only the most recent revision)
|
/// The pages for this web log (containing only the most recent revision)
|
||||||
Pages: Page list
|
Pages: Page list
|
||||||
|
|
||||||
|
/// The person entries for this web log
|
||||||
|
People: Person list
|
||||||
|
|
||||||
/// The posts for this web log (containing only the most recent revision)
|
/// The posts for this web log (containing only the most recent revision)
|
||||||
Posts: Post list
|
Posts: Post list
|
||||||
|
|
||||||
@@ -249,6 +252,7 @@ module Backup =
|
|||||||
let categoryCount = List.length archive.Categories
|
let categoryCount = List.length archive.Categories
|
||||||
let tagMapCount = List.length archive.TagMappings
|
let tagMapCount = List.length archive.TagMappings
|
||||||
let pageCount = List.length archive.Pages
|
let pageCount = List.length archive.Pages
|
||||||
|
let personCount = List.length archive.People
|
||||||
let postCount = List.length archive.Posts
|
let postCount = List.length archive.Posts
|
||||||
let uploadCount = List.length archive.Uploads
|
let uploadCount = List.length archive.Uploads
|
||||||
|
|
||||||
@@ -263,6 +267,7 @@ module Backup =
|
|||||||
printfn $""" - {categoryCount} categor{plural categoryCount "y" "ies"}"""
|
printfn $""" - {categoryCount} categor{plural categoryCount "y" "ies"}"""
|
||||||
printfn $""" - {tagMapCount} tag mapping{plural tagMapCount "" "s"}"""
|
printfn $""" - {tagMapCount} tag mapping{plural tagMapCount "" "s"}"""
|
||||||
printfn $""" - {pageCount} page{plural pageCount "" "s"}"""
|
printfn $""" - {pageCount} page{plural pageCount "" "s"}"""
|
||||||
|
printfn $""" - {personCount} people"""
|
||||||
printfn $""" - {postCount} post{plural postCount "" "s"}"""
|
printfn $""" - {postCount} post{plural postCount "" "s"}"""
|
||||||
printfn $""" - {uploadCount} uploaded file{plural uploadCount "" "s"}"""
|
printfn $""" - {uploadCount} uploaded file{plural uploadCount "" "s"}"""
|
||||||
|
|
||||||
@@ -283,6 +288,9 @@ module Backup =
|
|||||||
printfn "- Exporting pages..."
|
printfn "- Exporting pages..."
|
||||||
let! pages = data.Page.FindFullByWebLog webLog.Id
|
let! pages = data.Page.FindFullByWebLog webLog.Id
|
||||||
|
|
||||||
|
printfn "- Exporting people..."
|
||||||
|
let! people = data.Person.FindByWebLog webLog.Id
|
||||||
|
|
||||||
printfn "- Exporting posts..."
|
printfn "- Exporting posts..."
|
||||||
let! posts = data.Post.FindFullByWebLog webLog.Id
|
let! posts = data.Post.FindFullByWebLog webLog.Id
|
||||||
|
|
||||||
@@ -298,6 +306,7 @@ module Backup =
|
|||||||
Categories = categories
|
Categories = categories
|
||||||
TagMappings = tagMaps
|
TagMappings = tagMaps
|
||||||
Pages = pages |> List.map (fun p -> { p with Revisions = List.truncate 1 p.Revisions })
|
Pages = pages |> List.map (fun p -> { p with Revisions = List.truncate 1 p.Revisions })
|
||||||
|
People = people
|
||||||
Posts = posts |> List.map (fun p -> { p with Revisions = List.truncate 1 p.Revisions })
|
Posts = posts |> List.map (fun p -> { p with Revisions = List.truncate 1 p.Revisions })
|
||||||
Uploads = uploads |> List.map EncodedUpload.fromUpload }
|
Uploads = uploads |> List.map EncodedUpload.fromUpload }
|
||||||
|
|
||||||
@@ -319,15 +328,17 @@ module Backup =
|
|||||||
return { archive with Archive.WebLog.UrlBase = defaultArg newUrlBase webLog.UrlBase }
|
return { archive with Archive.WebLog.UrlBase = defaultArg newUrlBase webLog.UrlBase }
|
||||||
| Some _ ->
|
| Some _ ->
|
||||||
// Err'body gets new IDs...
|
// Err'body gets new IDs...
|
||||||
let newWebLogId = WebLogId.Create()
|
let newWebLogId = WebLogId.Create()
|
||||||
let newCatIds = archive.Categories |> List.map (fun cat -> cat.Id, CategoryId.Create() ) |> dict
|
let newCatIds = archive.Categories |> List.map (fun cat -> cat.Id, CategoryId.Create() ) |> dict
|
||||||
let newMapIds = archive.TagMappings |> List.map (fun tm -> tm.Id, TagMapId.Create() ) |> dict
|
let newMapIds = archive.TagMappings |> List.map (fun tm -> tm.Id, TagMapId.Create() ) |> dict
|
||||||
let newPageIds = archive.Pages |> List.map (fun page -> page.Id, PageId.Create() ) |> dict
|
let newPageIds = archive.Pages |> List.map (fun page -> page.Id, PageId.Create() ) |> dict
|
||||||
let newPostIds = archive.Posts |> List.map (fun post -> post.Id, PostId.Create() ) |> dict
|
let newPersonIds = archive.People |> List.map (fun psn -> psn.Id, PersonId.Create() ) |> dict
|
||||||
let newUserIds = archive.Users |> List.map (fun user -> user.Id, WebLogUserId.Create()) |> dict
|
let newPostIds = archive.Posts |> List.map (fun post -> post.Id, PostId.Create() ) |> dict
|
||||||
let newUpIds = archive.Uploads |> List.map (fun up -> up.Id, UploadId.Create() ) |> dict
|
let newUserIds = archive.Users |> List.map (fun user -> user.Id, WebLogUserId.Create()) |> dict
|
||||||
|
let newUpIds = archive.Uploads |> List.map (fun up -> up.Id, UploadId.Create() ) |> dict
|
||||||
return
|
return
|
||||||
{ archive with
|
{ archive with
|
||||||
|
// TODO: person IDs should be updated in podcasts and episodes
|
||||||
WebLog = { archive.WebLog with Id = newWebLogId; UrlBase = Option.get newUrlBase }
|
WebLog = { archive.WebLog with Id = newWebLogId; UrlBase = Option.get newUrlBase }
|
||||||
Users = archive.Users
|
Users = archive.Users
|
||||||
|> List.map (fun u -> { u with Id = newUserIds[u.Id]; WebLogId = newWebLogId })
|
|> List.map (fun u -> { u with Id = newUserIds[u.Id]; WebLogId = newWebLogId })
|
||||||
@@ -341,6 +352,9 @@ module Backup =
|
|||||||
Id = newPageIds[page.Id]
|
Id = newPageIds[page.Id]
|
||||||
WebLogId = newWebLogId
|
WebLogId = newWebLogId
|
||||||
AuthorId = newUserIds[page.AuthorId] })
|
AuthorId = newUserIds[page.AuthorId] })
|
||||||
|
People = archive.People
|
||||||
|
|> List.map (fun person ->
|
||||||
|
{ person with Id = newPersonIds[person.Id]; WebLogId = newWebLogId })
|
||||||
Posts = archive.Posts
|
Posts = archive.Posts
|
||||||
|> List.map (fun post ->
|
|> List.map (fun post ->
|
||||||
{ post with
|
{ post with
|
||||||
@@ -381,6 +395,9 @@ module Backup =
|
|||||||
if isInteractive then printfn "- Restoring pages..."
|
if isInteractive then printfn "- Restoring pages..."
|
||||||
if not (List.isEmpty restore.Pages) then do! data.Page.Restore restore.Pages
|
if not (List.isEmpty restore.Pages) then do! data.Page.Restore restore.Pages
|
||||||
|
|
||||||
|
if isInteractive then printfn "- Restoring people..."
|
||||||
|
if not (List.isEmpty restore.People) then do! data.Person.Restore restore.People
|
||||||
|
|
||||||
if isInteractive then printfn "- Restoring posts..."
|
if isInteractive then printfn "- Restoring posts..."
|
||||||
if not (List.isEmpty restore.Posts) then do! data.Post.Restore restore.Posts
|
if not (List.isEmpty restore.Posts) then do! data.Post.Restore restore.Posts
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user