Add RethinkDB tests
- Add env variables for all data tests - Standardize Page.All behavior - Add Page.Count* tests
This commit is contained in:
parent
f701fc9c93
commit
0e8044b948
src
MyWebLog.Data
MyWebLog.Tests
@ -54,7 +54,7 @@ type IPageData =
|
|||||||
/// Add a page
|
/// Add a page
|
||||||
abstract member Add : Page -> Task<unit>
|
abstract member Add : Page -> Task<unit>
|
||||||
|
|
||||||
/// Get all pages for the web log (excluding revisions)
|
/// Get all pages for the web log (excluding text, metadata, revisions, and prior permalinks)
|
||||||
abstract member All : WebLogId -> Task<Page list>
|
abstract member All : WebLogId -> Task<Page list>
|
||||||
|
|
||||||
/// Count all pages for the given web log
|
/// Count all pages for the given web log
|
||||||
|
@ -35,13 +35,13 @@ type PostgresPageData(log: ILogger) =
|
|||||||
|
|
||||||
// IMPLEMENTATION FUNCTIONS
|
// IMPLEMENTATION FUNCTIONS
|
||||||
|
|
||||||
/// Get all pages for a web log (without text or revisions)
|
/// Get all pages for a web log (without text, metadata, revisions, or prior permalinks)
|
||||||
let all webLogId =
|
let all webLogId =
|
||||||
log.LogTrace "Page.all"
|
log.LogTrace "Page.all"
|
||||||
Custom.list
|
Custom.list
|
||||||
$"{selectWithCriteria Table.Page} ORDER BY LOWER(data ->> '{nameof Page.Empty.Title}')"
|
$"{selectWithCriteria Table.Page} ORDER BY LOWER(data ->> '{nameof Page.Empty.Title}')"
|
||||||
[ webLogContains webLogId ]
|
[ webLogContains webLogId ]
|
||||||
fromData<Page>
|
(fun row -> { fromData<Page> row with Text = ""; Metadata = []; PriorPermalinks = [] })
|
||||||
|
|
||||||
/// Count all pages for the given web log
|
/// Count all pages for the given web log
|
||||||
let countAll webLogId =
|
let countAll webLogId =
|
||||||
|
@ -279,7 +279,7 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi
|
|||||||
member _.CountTopLevel webLogId = rethink<int> {
|
member _.CountTopLevel webLogId = rethink<int> {
|
||||||
withTable Table.Category
|
withTable Table.Category
|
||||||
getAll [ webLogId ] (nameof Category.Empty.WebLogId)
|
getAll [ webLogId ] (nameof Category.Empty.WebLogId)
|
||||||
filter (nameof Category.Empty.ParentId) None
|
filter (nameof Category.Empty.ParentId) None (Default FilterDefaultHandling.Return)
|
||||||
count
|
count
|
||||||
result; withRetryDefault conn
|
result; withRetryDefault conn
|
||||||
}
|
}
|
||||||
@ -361,7 +361,9 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi
|
|||||||
getAll [ webLogId ] (nameof Post.Empty.WebLogId)
|
getAll [ webLogId ] (nameof Post.Empty.WebLogId)
|
||||||
filter (fun row -> row[nameof Post.Empty.CategoryIds].Contains catId :> obj)
|
filter (fun row -> row[nameof Post.Empty.CategoryIds].Contains catId :> obj)
|
||||||
update (fun row ->
|
update (fun row ->
|
||||||
{| CategoryIds = r.Array(row[nameof Post.Empty.CategoryIds]).Remove catId |} :> obj)
|
{| CategoryIds =
|
||||||
|
row[nameof Post.Empty.CategoryIds].CoerceTo("array")
|
||||||
|
.SetDifference(r.Array(catId)) |} :> obj)
|
||||||
write; withRetryDefault; ignoreResult conn
|
write; withRetryDefault; ignoreResult conn
|
||||||
}
|
}
|
||||||
// Delete the category itself
|
// Delete the category itself
|
||||||
@ -408,10 +410,10 @@ type RethinkDbData(conn: Net.IConnection, config: DataConfig, log: ILogger<Rethi
|
|||||||
member _.All webLogId = rethink<Page list> {
|
member _.All webLogId = rethink<Page list> {
|
||||||
withTable Table.Page
|
withTable Table.Page
|
||||||
getAll [ webLogId ] (nameof Page.Empty.WebLogId)
|
getAll [ webLogId ] (nameof Page.Empty.WebLogId)
|
||||||
without [ nameof Page.Empty.Text
|
merge (r.HashMap(nameof Page.Empty.Text, "")
|
||||||
nameof Page.Empty.Metadata
|
.With(nameof Page.Empty.Metadata, [||])
|
||||||
nameof Page.Empty.Revisions
|
.With(nameof Page.Empty.Revisions, [||])
|
||||||
nameof Page.Empty.PriorPermalinks ]
|
.With(nameof Page.Empty.PriorPermalinks, [||]))
|
||||||
orderByFunc (fun row -> row[nameof Page.Empty.Title].Downcase() :> obj)
|
orderByFunc (fun row -> row[nameof Page.Empty.Title].Downcase() :> obj)
|
||||||
result; withRetryDefault conn
|
result; withRetryDefault conn
|
||||||
}
|
}
|
||||||
|
@ -36,13 +36,13 @@ type SQLitePageData(conn: SqliteConnection, log: ILogger) =
|
|||||||
|
|
||||||
// IMPLEMENTATION FUNCTIONS
|
// IMPLEMENTATION FUNCTIONS
|
||||||
|
|
||||||
/// Get all pages for a web log (without text or revisions)
|
/// Get all pages for a web log (without text, metadata, revisions, or prior permalinks)
|
||||||
let all webLogId =
|
let all webLogId =
|
||||||
log.LogTrace "Page.all"
|
log.LogTrace "Page.all"
|
||||||
conn.customList
|
conn.customList
|
||||||
$"{Query.selectFromTable Table.Page} WHERE {Document.Query.whereByWebLog} ORDER BY LOWER({titleField})"
|
$"{Query.selectFromTable Table.Page} WHERE {Document.Query.whereByWebLog} ORDER BY LOWER({titleField})"
|
||||||
[ webLogParam webLogId ]
|
[ webLogParam webLogId ]
|
||||||
(fun rdr -> { fromData<Page> rdr with Text = "" })
|
(fun rdr -> { fromData<Page> rdr with Text = ""; Metadata = []; PriorPermalinks = [] })
|
||||||
|
|
||||||
/// Count all pages for the given web log
|
/// Count all pages for the given web log
|
||||||
let countAll webLogId =
|
let countAll webLogId =
|
||||||
|
@ -49,7 +49,20 @@ let ``Add succeeds`` (data: IData) = task {
|
|||||||
|
|
||||||
let ``All succeeds`` (data: IData) = task {
|
let ``All succeeds`` (data: IData) = task {
|
||||||
let! pages = data.Page.All rootId
|
let! pages = data.Page.All rootId
|
||||||
Expect.hasLength pages 2 "There should have been 4 pages retrieved"
|
Expect.hasLength pages 2 "There should have been 2 pages retrieved"
|
||||||
Expect.isEmpty pages[0].Revisions "Page 0 should have had no revisions"
|
pages |> List.iteri (fun idx pg ->
|
||||||
Expect.isEmpty pages[1].Revisions "Page 1 should have had no revisions"
|
Expect.equal pg.Text "" $"Page {idx} should have had no text"
|
||||||
|
Expect.isEmpty pg.Metadata $"Page {idx} should have had no metadata"
|
||||||
|
Expect.isEmpty pg.Revisions $"Page {idx} should have had no revisions"
|
||||||
|
Expect.isEmpty pg.PriorPermalinks $"Page {idx} should have had no prior permalinks")
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``CountAll succeeds`` (data: IData) = task {
|
||||||
|
let! pages = data.Page.CountAll rootId
|
||||||
|
Expect.equal pages 2 "There should have been 2 pages counted"
|
||||||
|
}
|
||||||
|
|
||||||
|
let ``CountListed succeeds`` (data: IData) = task {
|
||||||
|
let! pages = data.Page.CountListed rootId
|
||||||
|
Expect.equal pages 1 "There should have been 1 page in the page list"
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
module PostgresDataTests
|
module PostgresDataTests
|
||||||
|
|
||||||
|
open System
|
||||||
open BitBadger.Documents
|
open BitBadger.Documents
|
||||||
open Expecto
|
open Expecto
|
||||||
open Microsoft.Extensions.Logging.Abstractions
|
open Microsoft.Extensions.Logging.Abstractions
|
||||||
@ -20,10 +21,26 @@ let mutable db: ThrowawayDatabase option = None
|
|||||||
let mkData () =
|
let mkData () =
|
||||||
PostgresData(NullLogger<PostgresData>(), ser) :> IData
|
PostgresData(NullLogger<PostgresData>(), ser) :> IData
|
||||||
|
|
||||||
|
/// The host for the PostgreSQL test database (defaults to localhost)
|
||||||
|
let testHost =
|
||||||
|
RethinkDbTests.env "PG_HOST" "localhost"
|
||||||
|
|
||||||
|
/// The database name for the PostgreSQL test database (defaults to postgres)
|
||||||
|
let testDb =
|
||||||
|
RethinkDbTests.env "PG_DB" "postgres"
|
||||||
|
|
||||||
|
/// The user ID for the PostgreSQL test database (defaults to postgres)
|
||||||
|
let testUser =
|
||||||
|
RethinkDbTests.env "PG_USER" "postgres"
|
||||||
|
|
||||||
|
/// The password for the PostgreSQL test database (defaults to postgres)
|
||||||
|
let testPw =
|
||||||
|
RethinkDbTests.env "PG_PW" "postgres"
|
||||||
|
|
||||||
/// Create a fresh environment from the root backup
|
/// Create a fresh environment from the root backup
|
||||||
let freshEnvironment () = task {
|
let freshEnvironment () = task {
|
||||||
if Option.isSome db then db.Value.Dispose()
|
if Option.isSome db then db.Value.Dispose()
|
||||||
db <- Some (ThrowawayDatabase.Create "Host=localhost;Database=postgres;User ID=postgres;Password=postgres")
|
db <- Some (ThrowawayDatabase.Create $"Host={testHost};Database={testDb};User ID={testUser};Password={testPw}")
|
||||||
let source = NpgsqlDataSourceBuilder db.Value.ConnectionString
|
let source = NpgsqlDataSourceBuilder db.Value.ConnectionString
|
||||||
let _ = source.UseNodaTime()
|
let _ = source.UseNodaTime()
|
||||||
Postgres.Configuration.useDataSource (source.Build())
|
Postgres.Configuration.useDataSource (source.Build())
|
||||||
@ -108,6 +125,12 @@ let pageTests = testList "Page" [
|
|||||||
testTask "All succeeds" {
|
testTask "All succeeds" {
|
||||||
do! PageDataTests.``All succeeds`` (mkData ())
|
do! PageDataTests.``All succeeds`` (mkData ())
|
||||||
}
|
}
|
||||||
|
testTask "CountAll succeeds" {
|
||||||
|
do! PageDataTests.``CountAll succeeds`` (mkData ())
|
||||||
|
}
|
||||||
|
testTask "CountListed succeeds" {
|
||||||
|
do! PageDataTests.``CountListed succeeds`` (mkData ())
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
/// Drop the throwaway PostgreSQL database
|
/// Drop the throwaway PostgreSQL database
|
||||||
|
147
src/MyWebLog.Tests/Data/RethinkDbTests.fs
Normal file
147
src/MyWebLog.Tests/Data/RethinkDbTests.fs
Normal file
@ -0,0 +1,147 @@
|
|||||||
|
module RethinkDbTests
|
||||||
|
|
||||||
|
open System
|
||||||
|
open Expecto
|
||||||
|
open Microsoft.Extensions.Logging.Abstractions
|
||||||
|
open MyWebLog
|
||||||
|
open MyWebLog.Converters
|
||||||
|
open MyWebLog.Data
|
||||||
|
open RethinkDb.Driver.FSharp
|
||||||
|
open RethinkDb.Driver.Net
|
||||||
|
|
||||||
|
/// Get an environment variable, using the given value as the default if it is not set
|
||||||
|
let env name value =
|
||||||
|
match Environment.GetEnvironmentVariable $"MWL_TEST_{name}" with
|
||||||
|
| null -> value
|
||||||
|
| it when it.Trim() = "" -> value
|
||||||
|
| it -> it
|
||||||
|
|
||||||
|
|
||||||
|
/// The data configuration for the test database
|
||||||
|
let dataCfg =
|
||||||
|
DataConfig.FromUri (env "RETHINK_URI" "rethinkdb://172.17.0.2/mwl_test")
|
||||||
|
|
||||||
|
/// The active data instance to use for testing
|
||||||
|
let mutable data: IData option = None
|
||||||
|
|
||||||
|
/// Dispose the existing data
|
||||||
|
let disposeData () = task {
|
||||||
|
if data.IsSome then
|
||||||
|
let conn = (data.Value :?> RethinkDbData).Conn
|
||||||
|
do! rethink { dbDrop dataCfg.Database; write; withRetryOnce; ignoreResult conn }
|
||||||
|
conn.Dispose()
|
||||||
|
data <- None
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Create a new data implementation instance
|
||||||
|
let newData () =
|
||||||
|
let log = NullLogger<RethinkDbData>()
|
||||||
|
let conn = dataCfg.CreateConnection log
|
||||||
|
RethinkDbData(conn, dataCfg, log)
|
||||||
|
|
||||||
|
/// Create a fresh environment from the root backup
|
||||||
|
let freshEnvironment () = task {
|
||||||
|
do! disposeData ()
|
||||||
|
data <- Some (newData ())
|
||||||
|
do! data.Value.StartUp()
|
||||||
|
// This exercises Restore for all implementations; all tests are dependent on it working as expected
|
||||||
|
do! Maintenance.Backup.restoreBackup "root-weblog.json" None false false data.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Set up the environment for the RethinkDB tests
|
||||||
|
let environmentSetUp = testTask "creating database" {
|
||||||
|
let _ = Json.configure Converter.Serializer
|
||||||
|
do! freshEnvironment ()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Integration tests for the Category implementation in RethinkDB
|
||||||
|
let categoryTests = testList "Category" [
|
||||||
|
testTask "Add succeeds" {
|
||||||
|
do! CategoryDataTests.``Add succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testList "CountAll" [
|
||||||
|
testTask "succeeds when categories exist" {
|
||||||
|
do! CategoryDataTests.``CountAll succeeds when categories exist`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when categories do not exist" {
|
||||||
|
do! CategoryDataTests.``CountAll succeeds when categories do not exist`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "CountTopLevel" [
|
||||||
|
testTask "succeeds when top-level categories exist" {
|
||||||
|
do! CategoryDataTests.``CountTopLevel succeeds when top-level categories exist`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when no top-level categories exist" {
|
||||||
|
do! CategoryDataTests.``CountTopLevel succeeds when no top-level categories exist`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testTask "FindAllForView succeeds" {
|
||||||
|
do! CategoryDataTests.``FindAllForView succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testList "FindById" [
|
||||||
|
testTask "succeeds when a category is found" {
|
||||||
|
do! CategoryDataTests.``FindById succeeds when a category is found`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when a category is not found" {
|
||||||
|
do! CategoryDataTests.``FindById succeeds when a category is not found`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testList "FindByWebLog" [
|
||||||
|
testTask "succeeds when categories exist" {
|
||||||
|
do! CategoryDataTests.``FindByWebLog succeeds when categories exist`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when no categories exist" {
|
||||||
|
do! CategoryDataTests.``FindByWebLog succeeds when no categories exist`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
testTask "Update succeeds" {
|
||||||
|
do! CategoryDataTests.``Update succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testList "Delete" [
|
||||||
|
testTask "succeeds when the category is deleted (no posts)" {
|
||||||
|
do! CategoryDataTests.``Delete succeeds when the category is deleted (no posts)`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when the category does not exist" {
|
||||||
|
do! CategoryDataTests.``Delete succeeds when the category does not exist`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when reassigning parent category to None" {
|
||||||
|
do! CategoryDataTests.``Delete succeeds when reassigning parent category to None`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds when reassigning parent category to Some" {
|
||||||
|
do! CategoryDataTests.``Delete succeeds when reassigning parent category to Some`` data.Value
|
||||||
|
}
|
||||||
|
testTask "succeeds and removes category from posts" {
|
||||||
|
do! CategoryDataTests.``Delete succeeds and removes category from posts`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Integration tests for the Page implementation in RethinkDB
|
||||||
|
let pageTests = testList "Page" [
|
||||||
|
testTask "Add succeeds" {
|
||||||
|
do! PageDataTests.``Add succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testTask "All succeeds" {
|
||||||
|
do! PageDataTests.``All succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testTask "CountAll succeeds" {
|
||||||
|
do! PageDataTests.``CountAll succeeds`` data.Value
|
||||||
|
}
|
||||||
|
testTask "CountListed succeeds" {
|
||||||
|
do! PageDataTests.``CountListed succeeds`` data.Value
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
/// Drop the throwaway RethinkDB database
|
||||||
|
let environmentCleanUp = testTask "Clean Up" {
|
||||||
|
do! disposeData ()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// All RethinkDB data tests
|
||||||
|
let all =
|
||||||
|
testList "RethinkDbData"
|
||||||
|
[ environmentSetUp
|
||||||
|
categoryTests
|
||||||
|
pageTests
|
||||||
|
environmentCleanUp ]
|
||||||
|
|> testSequenced
|
@ -13,7 +13,8 @@ open Newtonsoft.Json
|
|||||||
let ser = Json.configure (JsonSerializer.CreateDefault())
|
let ser = Json.configure (JsonSerializer.CreateDefault())
|
||||||
|
|
||||||
/// The test database name
|
/// The test database name
|
||||||
let dbName = "test-db.db"
|
let dbName =
|
||||||
|
RethinkDbTests.env "SQLITE_DB" "test-db.db"
|
||||||
|
|
||||||
/// Create a SQLiteData instance for testing
|
/// Create a SQLiteData instance for testing
|
||||||
let mkData () =
|
let mkData () =
|
||||||
@ -153,6 +154,16 @@ let pageTests = testList "Page" [
|
|||||||
try do! PageDataTests.``All succeeds`` data
|
try do! PageDataTests.``All succeeds`` data
|
||||||
finally dispose data
|
finally dispose data
|
||||||
}
|
}
|
||||||
|
testTask "CountAll succeeds" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PageDataTests.``CountAll succeeds`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
|
testTask "CountListed succeeds" {
|
||||||
|
let data = mkData ()
|
||||||
|
try do! PageDataTests.``CountListed succeeds`` data
|
||||||
|
finally dispose data
|
||||||
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
/// Delete the SQLite database
|
/// Delete the SQLite database
|
||||||
|
@ -12,6 +12,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\RethinkDbTests.fs" />
|
||||||
<Compile Include="Data\SQLiteDataTests.fs" />
|
<Compile Include="Data\SQLiteDataTests.fs" />
|
||||||
<Compile Include="Data\PostgresDataTests.fs" />
|
<Compile Include="Data\PostgresDataTests.fs" />
|
||||||
<Compile Include="Program.fs" />
|
<Compile Include="Program.fs" />
|
||||||
|
@ -2,7 +2,13 @@
|
|||||||
|
|
||||||
let allTests = testList "MyWebLog" [
|
let allTests = testList "MyWebLog" [
|
||||||
testList "Domain" [ SupportTypesTests.all; DataTypesTests.all; ViewModelsTests.all ]
|
testList "Domain" [ SupportTypesTests.all; DataTypesTests.all; ViewModelsTests.all ]
|
||||||
testList "Data" [ ConvertersTests.all; UtilsTests.all; SQLiteDataTests.all; PostgresDataTests.all ]
|
testList "Data" [
|
||||||
|
ConvertersTests.all
|
||||||
|
UtilsTests.all
|
||||||
|
RethinkDbTests.all
|
||||||
|
SQLiteDataTests.all
|
||||||
|
PostgresDataTests.all
|
||||||
|
]
|
||||||
]
|
]
|
||||||
|
|
||||||
[<EntryPoint>]
|
[<EntryPoint>]
|
||||||
|
Loading…
Reference in New Issue
Block a user