Finish user tests

This commit is contained in:
Daniel J. Summers 2024-02-03 19:45:29 -05:00
parent ac9df44904
commit 16f7396664
7 changed files with 292 additions and 17 deletions

View File

@ -10,6 +10,11 @@ open Npgsql.FSharp
/// PostgreSQL myWebLog user data implementation /// PostgreSQL myWebLog user data implementation
type PostgresWebLogUserData(log: ILogger) = 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 /// Find a user by their ID for the given web log
let findById userId webLogId = let findById userId webLogId =
log.LogTrace "WebLogUser.findById" 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 /// Find the names of users by their IDs for the given web log
let findNames webLogId (userIds: WebLogUserId list) = backgroundTask { let findNames webLogId (userIds: WebLogUserId list) = backgroundTask {
log.LogTrace "WebLogUser.findNames" 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 = let! users =
Custom.list Custom.list
$"{selectWithCriteria Table.WebLogUser} {idSql}" $"{selectWithCriteria Table.WebLogUser} {idSql}"
@ -80,13 +85,13 @@ type PostgresWebLogUserData(log: ILogger) =
| false -> () | false -> ()
} }
/// Save a user /// Update a user
let save (user: WebLogUser) = let update (user: WebLogUser) =
log.LogTrace "WebLogUser.save" log.LogTrace "WebLogUser.update"
save Table.WebLogUser user Update.byId Table.WebLogUser user.Id user
interface IWebLogUserData with interface IWebLogUserData with
member _.Add user = save user member _.Add user = add user
member _.Delete userId webLogId = delete userId webLogId member _.Delete userId webLogId = delete userId webLogId
member _.FindByEmail email webLogId = findByEmail email webLogId member _.FindByEmail email webLogId = findByEmail email webLogId
member _.FindById userId webLogId = findById userId webLogId member _.FindById userId webLogId = findById userId webLogId
@ -94,4 +99,4 @@ type PostgresWebLogUserData(log: ILogger) =
member _.FindNames webLogId userIds = findNames webLogId userIds member _.FindNames webLogId userIds = findNames webLogId userIds
member _.Restore users = restore users member _.Restore users = restore users
member _.SetLastSeen userId webLogId = setLastSeen userId webLogId member _.SetLastSeen userId webLogId = setLastSeen userId webLogId
member _.Update user = save user member _.Update user = update user

View File

@ -10,6 +10,11 @@ open MyWebLog.Data
/// SQLite myWebLog user data implementation /// SQLite myWebLog user data implementation
type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) = 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 /// Find a user by their ID for the given web log
let findById userId webLogId = let findById userId webLogId =
log.LogTrace "WebLogUser.findById" log.LogTrace "WebLogUser.findById"
@ -58,15 +63,10 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
let user = fromData<WebLogUser> rdr let user = fromData<WebLogUser> rdr
{ Name = string user.Id; Value = user.DisplayName }) { 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 /// Restore users from a backup
let restore users = backgroundTask { let restore users = backgroundTask {
log.LogTrace "WebLogUser.restore" 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 /// Set a user's last seen date/time to now
@ -77,8 +77,13 @@ type SQLiteWebLogUserData(conn: SqliteConnection, log: ILogger) =
| None -> () | None -> ()
} }
/// Update a user
let update (user: WebLogUser) =
log.LogTrace "WebLogUser.update"
conn.updateById Table.WebLogUser user.Id user
interface IWebLogUserData with interface IWebLogUserData with
member _.Add user = save user member _.Add user = add user
member _.Delete userId webLogId = delete userId webLogId member _.Delete userId webLogId = delete userId webLogId
member _.FindByEmail email webLogId = findByEmail email webLogId member _.FindByEmail email webLogId = findByEmail email webLogId
member _.FindById userId webLogId = findById userId 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 _.FindNames webLogId userIds = findNames webLogId userIds
member _.Restore users = restore users member _.Restore users = restore users
member _.SetLastSeen userId webLogId = setLastSeen userId webLogId member _.SetLastSeen userId webLogId = setLastSeen userId webLogId
member _.Update user = save user member _.Update user = update user

View File

@ -551,6 +551,8 @@ let private uploadTests = testList "Upload" [
let private webLogUserTests = testList "WebLogUser" [ let private webLogUserTests = testList "WebLogUser" [
testTask "Add succeeds" { testTask "Add succeeds" {
// This restore ensures all the posts and pages exist
do! freshEnvironment ()
do! WebLogUserDataTests.``Add succeeds`` (mkData ()) do! WebLogUserDataTests.``Add succeeds`` (mkData ())
} }
testList "FindByEmail" [ testList "FindByEmail" [
@ -575,6 +577,52 @@ let private webLogUserTests = testList "WebLogUser" [
do! WebLogUserDataTests.``FindById succeeds when a user is not found (bad ID)`` (mkData ()) 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 /// Drop the throwaway PostgreSQL database

View File

@ -551,6 +551,8 @@ let private uploadTests = testList "Upload" [
let private webLogUserTests = testList "WebLogUser" [ let private webLogUserTests = testList "WebLogUser" [
testTask "Add succeeds" { testTask "Add succeeds" {
// This restore ensures all the posts and pages exist
do! freshEnvironment ()
do! WebLogUserDataTests.``Add succeeds`` data.Value do! WebLogUserDataTests.``Add succeeds`` data.Value
} }
testList "FindByEmail" [ 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 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 /// Drop the throwaway RethinkDB database

View File

@ -814,7 +814,8 @@ let private uploadTests = testList "Upload" [
let private webLogUserTests = testList "WebLogUser" [ let private webLogUserTests = testList "WebLogUser" [
testTask "Add succeeds" { 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 try do! WebLogUserDataTests.``Add succeeds`` data
finally dispose data finally dispose data
} }
@ -852,6 +853,76 @@ let private webLogUserTests = testList "WebLogUser" [
finally dispose data 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 /// Delete the SQLite database

View File

@ -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 let! user = data.WebLogUser.FindById (WebLogUserId "tom") rootId
Expect.isNone user "There should not have been a user returned" Expect.isNone user "There should not have been a user returned"
} }
let ``FindByWebLog succeeds when users exist`` (data: IData) = task {
let! users = data.WebLogUser.FindByWebLog rootId
Expect.hasLength users 4 "There should have been 4 users returned"
for user in users do
Expect.contains [ adminId; editorId; authorId; newId ] user.Id $"Unexpected user returned ({user.Id})"
}
let ``FindByWebLog succeeds when no users exist`` (data: IData) = task {
let! users = data.WebLogUser.FindByWebLog (WebLogId "no-users")
Expect.isEmpty users "There should have been no users returned"
}
let ``FindNames succeeds when users exist`` (data: IData) = task {
let! names = data.WebLogUser.FindNames rootId [ editorId; authorId ]
let expected =
[ { Name = string editorId; Value = "Edits It-Or" }; { Name = string authorId; Value = "Mister Dude" } ]
Expect.hasLength names 2 "There should have been 2 names returned"
for name in names do Expect.contains expected name $"Unexpected name returned ({name.Name}|{name.Value})"
}
let ``FindNames succeeds when users do not exist`` (data: IData) = task {
let! names = data.WebLogUser.FindNames rootId [ WebLogUserId "nope"; WebLogUserId "no" ]
Expect.isEmpty names "There should have been no names returned"
}
let ``SetLastSeen succeeds when the user exists`` (data: IData) = task {
let now = Noda.now ()
do! data.WebLogUser.SetLastSeen newId rootId
let! user = data.WebLogUser.FindById newId rootId
Expect.isSome user "The user should have been returned"
let it = user.Value
Expect.isSome it.LastSeenOn "Last seen on should have been set"
Expect.isGreaterThanOrEqual it.LastSeenOn.Value now "The last seen on date/time was not set correctly"
}
let ``SetLastSeen succeeds when the user does not exist`` (data: IData) = task {
do! data.WebLogUser.SetLastSeen (WebLogUserId "matt") rootId
Expect.isTrue true "This not raising an exception is the test"
}
let ``Update succeeds when the user exists`` (data: IData) = task {
let! currentUser = data.WebLogUser.FindById newId rootId
Expect.isSome currentUser "The current user should have been found"
do! data.WebLogUser.Update
{ currentUser.Value with
Email = "newish@example.com"
FirstName = "New-ish"
LastName = "User-ish"
PreferredName = "n00b-ish"
PasswordHash = "hashed-ish-password"
Url = None
AccessLevel = Editor }
let! updated = data.WebLogUser.FindById newId rootId
Expect.isSome updated "The updated user should have been returned"
let it = updated.Value
Expect.equal it.Id newId "ID is incorrect"
Expect.equal it.WebLogId rootId "Web log ID is incorrect"
Expect.equal it.Email "newish@example.com" "E-mail address is incorrect"
Expect.equal it.FirstName "New-ish" "First name is incorrect"
Expect.equal it.LastName "User-ish" "Last name is incorrect"
Expect.equal it.PreferredName "n00b-ish" "Preferred name is incorrect"
Expect.equal it.PasswordHash "hashed-ish-password" "Password hash is incorrect"
Expect.isNone it.Url "URL is incorrect"
Expect.equal it.AccessLevel Editor "Access level is incorrect"
Expect.equal it.CreatedOn (Noda.epoch + Duration.FromDays 365) "Created on is incorrect"
Expect.isSome it.LastSeenOn "Last seen on should have had a value"
}
let ``Update succeeds when the user does not exist`` (data: IData) = task {
do! data.WebLogUser.Update { WebLogUser.Empty with Id = WebLogUserId "nothing"; WebLogId = rootId }
let! updated = data.WebLogUser.FindById (WebLogUserId "nothing") rootId
Expect.isNone updated "The update of a missing user should not have created the user"
}
let ``Delete fails when the user is the author of a page`` (data: IData) = task {
match! data.WebLogUser.Delete adminId rootId with
| Ok _ -> Expect.isTrue false "Deletion should have failed because the user is a page author"
| Error msg -> Expect.equal msg "User has pages or posts; cannot delete" "Error message is incorrect"
}
let ``Delete fails when the user is the author of a post`` (data: IData) = task {
match! data.WebLogUser.Delete authorId rootId with
| Ok _ -> Expect.isTrue false "Deletion should have failed because the user is a post author"
| Error msg -> Expect.equal msg "User has pages or posts; cannot delete" "Error message is incorrect"
}
let ``Delete succeeds when the user is not an author`` (data: IData) = task {
match! data.WebLogUser.Delete newId rootId with
| Ok _ -> Expect.isTrue true "This is the expected outcome"
| Error msg -> Expect.isTrue false $"Deletion unexpectedly failed (message {msg})"
}
let ``Delete succeeds when the user does not exist`` (data: IData) = task {
match! data.WebLogUser.Delete newId rootId with // already deleted above
| Ok _ -> Expect.isTrue false "Deletion should have failed because the user does not exist"
| Error msg -> Expect.equal msg "User does not exist" "Error message is incorrect"
}

View File

@ -285,7 +285,7 @@
{ {
"Id": "l4_Eh4aFO06SqqJjOymNzA", "Id": "l4_Eh4aFO06SqqJjOymNzA",
"WebLogId": "uSitJEuD3UyzWC9jgOHc8g", "WebLogId": "uSitJEuD3UyzWC9jgOHc8g",
"AuthorId": "5EM2rimH9kONpmd2zQkiVA", "AuthorId": "iIRNLSeY0EanxRPyqGuwVg",
"Status": "Published", "Status": "Published",
"Title": "Episode 2", "Title": "Episode 2",
"Permalink": "2024/episode-2.html", "Permalink": "2024/episode-2.html",