Version 2.1 #41
|
@ -10,6 +10,11 @@ open Npgsql.FSharp
|
|||
/// PostgreSQL myWebLog user data implementation
|
||||
type PostgresWebLogUserData(log: ILogger) =
|
||||
|
||||
/// Add a user
|
||||
let add (user: WebLogUser) =
|
||||
log.LogTrace "WebLogUser.add"
|
||||
insert Table.WebLogUser user
|
||||
|
||||
/// Find a user by their ID for the given web log
|
||||
let findById userId webLogId =
|
||||
log.LogTrace "WebLogUser.findById"
|
||||
|
@ -52,7 +57,7 @@ type PostgresWebLogUserData(log: ILogger) =
|
|||
/// Find the names of users by their IDs for the given web log
|
||||
let findNames webLogId (userIds: WebLogUserId list) = backgroundTask {
|
||||
log.LogTrace "WebLogUser.findNames"
|
||||
let idSql, idParams = inClause "AND id" "id" userIds
|
||||
let idSql, idParams = inClause $"AND data ->> '{nameof WebLogUser.Empty.Id}'" "id" userIds
|
||||
let! users =
|
||||
Custom.list
|
||||
$"{selectWithCriteria Table.WebLogUser} {idSql}"
|
||||
|
@ -80,13 +85,13 @@ type PostgresWebLogUserData(log: ILogger) =
|
|||
| false -> ()
|
||||
}
|
||||
|
||||
/// Save a user
|
||||
let save (user: WebLogUser) =
|
||||
log.LogTrace "WebLogUser.save"
|
||||
save Table.WebLogUser user
|
||||
/// Update a user
|
||||
let update (user: WebLogUser) =
|
||||
log.LogTrace "WebLogUser.update"
|
||||
Update.byId Table.WebLogUser user.Id user
|
||||
|
||||
interface IWebLogUserData with
|
||||
member _.Add user = save user
|
||||
member _.Add user = add user
|
||||
member _.Delete userId webLogId = delete userId webLogId
|
||||
member _.FindByEmail email webLogId = findByEmail email webLogId
|
||||
member _.FindById userId webLogId = findById userId webLogId
|
||||
|
@ -94,4 +99,4 @@ type PostgresWebLogUserData(log: ILogger) =
|
|||
member _.FindNames webLogId userIds = findNames webLogId userIds
|
||||
member _.Restore users = restore users
|
||||
member _.SetLastSeen userId webLogId = setLastSeen userId webLogId
|
||||
member _.Update user = save user
|
||||
member _.Update user = update user
|
||||
|
|
|
@ -10,6 +10,11 @@ open MyWebLog.Data
|
|||
/// SQLite myWebLog user data implementation
|
||||
type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
|
||||
|
||||
/// Add a user
|
||||
let add user =
|
||||
log.LogTrace "WebLogUser.add"
|
||||
conn.insert<WebLogUser> Table.WebLogUser user
|
||||
|
||||
/// Find a user by their ID for the given web log
|
||||
let findById userId webLogId =
|
||||
log.LogTrace "WebLogUser.findById"
|
||||
|
@ -58,15 +63,10 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
|
|||
let user = fromData<WebLogUser> rdr
|
||||
{ Name = string user.Id; Value = user.DisplayName })
|
||||
|
||||
/// Save a user
|
||||
let save user =
|
||||
log.LogTrace "WebLogUser.update"
|
||||
conn.save<WebLogUser> Table.WebLogUser user
|
||||
|
||||
/// Restore users from a backup
|
||||
let restore users = backgroundTask {
|
||||
log.LogTrace "WebLogUser.restore"
|
||||
for user in users do do! save user
|
||||
for user in users do do! add user
|
||||
}
|
||||
|
||||
/// Set a user's last seen date/time to now
|
||||
|
@ -77,8 +77,13 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
|
|||
| None -> ()
|
||||
}
|
||||
|
||||
/// Update a user
|
||||
let update (user: WebLogUser) =
|
||||
log.LogTrace "WebLogUser.update"
|
||||
conn.updateById Table.WebLogUser user.Id user
|
||||
|
||||
interface IWebLogUserData with
|
||||
member _.Add user = save user
|
||||
member _.Add user = add user
|
||||
member _.Delete userId webLogId = delete userId webLogId
|
||||
member _.FindByEmail email webLogId = findByEmail email webLogId
|
||||
member _.FindById userId webLogId = findById userId webLogId
|
||||
|
@ -86,4 +91,4 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
|
|||
member _.FindNames webLogId userIds = findNames webLogId userIds
|
||||
member _.Restore users = restore users
|
||||
member _.SetLastSeen userId webLogId = setLastSeen userId webLogId
|
||||
member _.Update user = save user
|
||||
member _.Update user = update user
|
||||
|
|
|
@ -551,6 +551,8 @@ let private uploadTests = testList "Upload" [
|
|||
|
||||
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" [
|
||||
|
@ -575,6 +577,52 @@ let private webLogUserTests = testList "WebLogUser" [
|
|||
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 ())
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
/// Drop the throwaway PostgreSQL database
|
||||
|
|
|
@ -551,6 +551,8 @@ let private uploadTests = testList "Upload" [
|
|||
|
||||
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" [
|
||||
|
@ -575,6 +577,52 @@ let private webLogUserTests = testList "WebLogUser" [
|
|||
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
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
/// Drop the throwaway RethinkDB database
|
||||
|
|
|
@ -814,7 +814,8 @@ let private uploadTests = testList "Upload" [
|
|||
|
||||
let private webLogUserTests = testList "WebLogUser" [
|
||||
testTask "Add succeeds" {
|
||||
let data = mkData ()
|
||||
// This restore ensures all the posts and pages exist
|
||||
let! data = freshEnvironment None
|
||||
try do! WebLogUserDataTests.``Add succeeds`` data
|
||||
finally dispose data
|
||||
}
|
||||
|
@ -852,6 +853,76 @@ let private webLogUserTests = testList "WebLogUser" [
|
|||
finally dispose data
|
||||
}
|
||||
]
|
||||
testList "FindByWebLog" [
|
||||
testTask "succeeds when users exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``FindByWebLog succeeds when users exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "succeeds when no users exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``FindByWebLog succeeds when no users exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
]
|
||||
testList "FindNames" [
|
||||
testTask "succeeds when users exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``FindNames succeeds when users exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "succeeds when users do not exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``FindNames succeeds when users do not exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
]
|
||||
testList "SetLastSeen" [
|
||||
testTask "succeeds when the user exists" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``SetLastSeen succeeds when the user exists`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "succeeds when the user does not exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``SetLastSeen succeeds when the user does not exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
]
|
||||
testList "Update" [
|
||||
testTask "succeeds when the user exists" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``Update succeeds when the user exists`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "succeeds when the user does not exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``Update succeeds when the user does not exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
]
|
||||
testList "Delete" [
|
||||
testTask "fails when the user is the author of a page" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``Delete fails when the user is the author of a page`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "fails when the user is the author of a post" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``Delete fails when the user is the author of a post`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "succeeds when the user is not an author" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``Delete succeeds when the user is not an author`` data
|
||||
finally dispose data
|
||||
}
|
||||
testTask "succeeds when the user does not exist" {
|
||||
let data = mkData ()
|
||||
try do! WebLogUserDataTests.``Delete succeeds when the user does not exist`` data
|
||||
finally dispose data
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
/// Delete the SQLite database
|
||||
|
|
|
@ -84,3 +84,101 @@ 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"
|
||||
}
|
||||
|
|
|
@ -285,7 +285,7 @@
|
|||
{
|
||||
"Id": "l4_Eh4aFO06SqqJjOymNzA",
|
||||
"WebLogId": "uSitJEuD3UyzWC9jgOHc8g",
|
||||
"AuthorId": "5EM2rimH9kONpmd2zQkiVA",
|
||||
"AuthorId": "iIRNLSeY0EanxRPyqGuwVg",
|
||||
"Status": "Published",
|
||||
"Title": "Episode 2",
|
||||
"Permalink": "2024/episode-2.html",
|
||||
|
|
Loading…
Reference in New Issue
Block a user