Version 4 #6

Merged
danieljsummers merged 30 commits from version-four into main 2024-08-19 23:30:39 +00:00
4 changed files with 495 additions and 335 deletions
Showing only changes of commit b357b5a01f - Show all commits

View File

@ -1,5 +1,7 @@
namespace BitBadger.Documents namespace BitBadger.Documents
open System.Security.Cryptography
/// The types of logical operations available for JSON fields /// The types of logical operations available for JSON fields
[<Struct>] [<Struct>]
type Op = type Op =
@ -148,6 +150,36 @@ type ParameterName() =
currentIdx <- currentIdx + 1 currentIdx <- currentIdx + 1
$"@field{currentIdx}" $"@field{currentIdx}"
#if NET6_0
open System.Text
#endif
/// Automatically-generated document ID options
[<Struct>]
type AutoId =
/// No automatic IDs will be generated
| Disabled
/// Generate a MAX-plus-1 numeric value for documents
| Number
/// Generate a GUID for each document (as a lowercase, no-dashes, 32-character string)
| Guid
/// Generate a random string of hexadecimal characters for each document
| RandomString
with
/// Generate a GUID string
static member GenerateGuid() =
System.Guid.NewGuid().ToString "N"
/// Generate a string of random hexadecimal characters
static member GenerateRandomString(length: int) =
#if NET8_0_OR_GREATER
RandomNumberGenerator.GetHexString(length, lowercase = true)
#else
RandomNumberGenerator.GetBytes((length / 2) + 1)
|> Array.fold (fun (str: StringBuilder) byt -> str.Append(byt.ToString "x2")) (StringBuilder length)
|> function it -> it.Length <- length; it.ToString()
#endif
/// The required document serialization implementation /// The required document serialization implementation
type IDocumentSerializer = type IDocumentSerializer =
@ -200,7 +232,7 @@ module Configuration =
serializerValue serializerValue
/// The serialized name of the ID field for documents /// The serialized name of the ID field for documents
let mutable idFieldValue = "Id" let mutable private idFieldValue = "Id"
/// Specify the name of the ID field for documents /// Specify the name of the ID field for documents
[<CompiledName "UseIdField">] [<CompiledName "UseIdField">]
@ -211,6 +243,32 @@ module Configuration =
[<CompiledName "IdField">] [<CompiledName "IdField">]
let idField () = let idField () =
idFieldValue idFieldValue
/// The automatic ID strategy used by the library
let mutable private autoIdValue = Disabled
/// Specify the automatic ID generation strategy used by the library
[<CompiledName "UseAutoIdStrategy">]
let useAutoIdStrategy it =
autoIdValue <- it
/// Retrieve the currently configured automatic ID generation strategy
[<CompiledName "AutoIdStrategy">]
let autoIdStrategy () =
autoIdValue
/// The length of automatically generated random strings
let mutable private idStringLengthValue = 16
/// Specify the length of automatically generated random strings
[<CompiledName "UseIdStringLength">]
let useIdStringLength length =
idStringLengthValue <- length
/// Retrieve the currently configured length of automatically generated random strings
[<CompiledName "IdStringLength">]
let idStringLength () =
idStringLengthValue
/// Query construction functions /// Query construction functions

View File

@ -70,6 +70,34 @@ public static class CommonCSharpTests
{ {
Configuration.UseIdField("Id"); Configuration.UseIdField("Id");
} }
}),
TestCase("UseAutoIdStrategy / AutoIdStrategy succeeds", () =>
{
try
{
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Disabled,
"The default auto-ID strategy was incorrect");
Configuration.UseAutoIdStrategy(AutoId.Guid);
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Guid,
"The auto-ID strategy was not set correctly");
}
finally
{
Configuration.UseAutoIdStrategy(AutoId.Disabled);
}
}),
TestCase("UseIdStringLength / IdStringLength succeeds", () =>
{
try
{
Expect.equal(Configuration.IdStringLength(), 16, "The default ID string length was incorrect");
Configuration.UseIdStringLength(33);
Expect.equal(Configuration.IdStringLength(), 33, "The ID string length was not set correctly");
}
finally
{
Configuration.UseIdStringLength(16);
}
}) })
])), ])),
TestList("Op", TestList("Op",
@ -295,6 +323,27 @@ public static class CommonCSharpTests
"Counter should have advanced from previous call"); "Counter should have advanced from previous call");
}) })
]), ]),
TestList("AutoId",
[
TestCase("GenerateGuid succeeds", () =>
{
var autoId = AutoId.GenerateGuid();
Expect.isNotNull(autoId, "The GUID auto-ID should not have been null");
Expect.stringHasLength(autoId, 32, "The GUID auto-ID should have been 32 characters long");
Expect.equal(autoId, autoId.ToLowerInvariant(), "The GUID auto-ID should have been lowercase");
}),
TestCase("GenerateRandomString succeeds", () =>
{
foreach (var length in (int[])[6, 8, 12, 20, 32, 57, 64])
{
var autoId = AutoId.GenerateRandomString(length);
Expect.isNotNull(autoId, $"Random string ({length}) should not have been null");
Expect.stringHasLength(autoId, length, $"Random string should have been {length} characters long");
Expect.equal(autoId, autoId.ToLowerInvariant(),
$"Random string ({length}) should have been lowercase");
}
})
]),
TestList("Query", TestList("Query",
[ [
TestCase("StatementWhere succeeds", () => TestCase("StatementWhere succeeds", () =>

View File

@ -6,320 +6,399 @@ open Expecto
/// Test table name /// Test table name
let tbl = "test_table" let tbl = "test_table"
/// Tests which do not hit the database /// Unit tests for the Op DU
let all = let opTests = testList "Op" [
testList "Common" [ test "EQ succeeds" {
testList "Op" [ Expect.equal (string EQ) "=" "The equals operator was not correct"
test "EQ succeeds" { }
Expect.equal (string EQ) "=" "The equals operator was not correct" test "GT succeeds" {
} Expect.equal (string GT) ">" "The greater than operator was not correct"
test "GT succeeds" { }
Expect.equal (string GT) ">" "The greater than operator was not correct" test "GE succeeds" {
} Expect.equal (string GE) ">=" "The greater than or equal to operator was not correct"
test "GE succeeds" { }
Expect.equal (string GE) ">=" "The greater than or equal to operator was not correct" test "LT succeeds" {
} Expect.equal (string LT) "<" "The less than operator was not correct"
test "LT succeeds" { }
Expect.equal (string LT) "<" "The less than operator was not correct" test "LE succeeds" {
} Expect.equal (string LE) "<=" "The less than or equal to operator was not correct"
test "LE succeeds" { }
Expect.equal (string LE) "<=" "The less than or equal to operator was not correct" test "NE succeeds" {
} Expect.equal (string NE) "<>" "The not equal to operator was not correct"
test "NE succeeds" { }
Expect.equal (string NE) "<>" "The not equal to operator was not correct" test "BT succeeds" {
} Expect.equal (string BT) "BETWEEN" """The "between" operator was not correct"""
test "BT succeeds" { }
Expect.equal (string BT) "BETWEEN" """The "between" operator was not correct""" test "EX succeeds" {
} Expect.equal (string EX) "IS NOT NULL" """The "exists" operator was not correct"""
test "EX succeeds" { }
Expect.equal (string EX) "IS NOT NULL" """The "exists" operator was not correct""" test "NEX succeeds" {
} Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
test "NEX succeeds" { }
Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct""" ]
}
] /// Unit tests for the Field class
testList "Field" [ let fieldTests = testList "Field" [
test "EQ succeeds" { test "EQ succeeds" {
let field = Field.EQ "Test" 14 let field = Field.EQ "Test" 14
Expect.equal field.Name "Test" "Field name incorrect" Expect.equal field.Name "Test" "Field name incorrect"
Expect.equal field.Op EQ "Operator incorrect" Expect.equal field.Op EQ "Operator incorrect"
Expect.equal field.Value 14 "Value incorrect" Expect.equal field.Value 14 "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "GT succeeds" { test "GT succeeds" {
let field = Field.GT "Great" "night" let field = Field.GT "Great" "night"
Expect.equal field.Name "Great" "Field name incorrect" Expect.equal field.Name "Great" "Field name incorrect"
Expect.equal field.Op GT "Operator incorrect" Expect.equal field.Op GT "Operator incorrect"
Expect.equal field.Value "night" "Value incorrect" Expect.equal field.Value "night" "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "GE succeeds" { test "GE succeeds" {
let field = Field.GE "Nice" 88L let field = Field.GE "Nice" 88L
Expect.equal field.Name "Nice" "Field name incorrect" Expect.equal field.Name "Nice" "Field name incorrect"
Expect.equal field.Op GE "Operator incorrect" Expect.equal field.Op GE "Operator incorrect"
Expect.equal field.Value 88L "Value incorrect" Expect.equal field.Value 88L "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "LT succeeds" { test "LT succeeds" {
let field = Field.LT "Lesser" "seven" let field = Field.LT "Lesser" "seven"
Expect.equal field.Name "Lesser" "Field name incorrect" Expect.equal field.Name "Lesser" "Field name incorrect"
Expect.equal field.Op LT "Operator incorrect" Expect.equal field.Op LT "Operator incorrect"
Expect.equal field.Value "seven" "Value incorrect" Expect.equal field.Value "seven" "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "LE succeeds" { test "LE succeeds" {
let field = Field.LE "Nobody" "KNOWS"; let field = Field.LE "Nobody" "KNOWS";
Expect.equal field.Name "Nobody" "Field name incorrect" Expect.equal field.Name "Nobody" "Field name incorrect"
Expect.equal field.Op LE "Operator incorrect" Expect.equal field.Op LE "Operator incorrect"
Expect.equal field.Value "KNOWS" "Value incorrect" Expect.equal field.Value "KNOWS" "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "NE succeeds" { test "NE succeeds" {
let field = Field.NE "Park" "here" let field = Field.NE "Park" "here"
Expect.equal field.Name "Park" "Field name incorrect" Expect.equal field.Name "Park" "Field name incorrect"
Expect.equal field.Op NE "Operator incorrect" Expect.equal field.Op NE "Operator incorrect"
Expect.equal field.Value "here" "Value incorrect" Expect.equal field.Value "here" "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "BT succeeds" { test "BT succeeds" {
let field = Field.BT "Age" 18 49 let field = Field.BT "Age" 18 49
Expect.equal field.Name "Age" "Field name incorrect" Expect.equal field.Name "Age" "Field name incorrect"
Expect.equal field.Op BT "Operator incorrect" Expect.equal field.Op BT "Operator incorrect"
Expect.sequenceEqual (field.Value :?> obj list) [ 18; 49 ] "Value incorrect" Expect.sequenceEqual (field.Value :?> obj list) [ 18; 49 ] "Value incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "EX succeeds" { test "EX succeeds" {
let field = Field.EX "Groovy" let field = Field.EX "Groovy"
Expect.equal field.Name "Groovy" "Field name incorrect" Expect.equal field.Name "Groovy" "Field name incorrect"
Expect.equal field.Op EX "Operator incorrect" Expect.equal field.Op EX "Operator incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
test "NEX succeeds" { test "NEX succeeds" {
let field = Field.NEX "Rad" let field = Field.NEX "Rad"
Expect.equal field.Name "Rad" "Field name incorrect" Expect.equal field.Name "Rad" "Field name incorrect"
Expect.equal field.Op NEX "Operator incorrect" Expect.equal field.Op NEX "Operator incorrect"
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None" Expect.isNone field.Qualifier "The default table qualifier should be None"
} }
testList "NameToPath" [ testList "NameToPath" [
test "succeeds for PostgreSQL and a simple name" { test "succeeds for PostgreSQL and a simple name" {
Expect.equal Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly"
"data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly" }
} test "succeeds for SQLite and a simple name" {
test "succeeds for SQLite and a simple name" { Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly"
Expect.equal }
"data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly" test "succeeds for PostgreSQL and a nested name" {
} Expect.equal
test "succeeds for PostgreSQL and a nested name" { "data#>>'{A,Long,Path,to,the,Property}'"
Expect.equal (Field.NameToPath "A.Long.Path.to.the.Property" PostgreSQL)
"data#>>'{A,Long,Path,to,the,Property}'" "Path not constructed correctly"
(Field.NameToPath "A.Long.Path.to.the.Property" PostgreSQL) }
"Path not constructed correctly" test "succeeds for SQLite and a nested name" {
} Expect.equal
test "succeeds for SQLite and a nested name" { "data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'"
Expect.equal (Field.NameToPath "A.Long.Path.to.the.Property" SQLite)
"data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'" "Path not constructed correctly"
(Field.NameToPath "A.Long.Path.to.the.Property" SQLite) }
"Path not constructed correctly" ]
} test "WithParameterName succeeds" {
] let field = (Field.EQ "Bob" "Tom").WithParameterName "@name"
test "WithParameterName succeeds" { Expect.isSome field.ParameterName "The parameter name should have been filled"
let field = (Field.EQ "Bob" "Tom").WithParameterName "@name" Expect.equal "@name" field.ParameterName.Value "The parameter name is incorrect"
Expect.isSome field.ParameterName "The parameter name should have been filled" }
Expect.equal "@name" field.ParameterName.Value "The parameter name is incorrect" test "WithQualifier succeeds" {
} let field = (Field.EQ "Bill" "Matt").WithQualifier "joe"
test "WithQualifier succeeds" { Expect.isSome field.Qualifier "The table qualifier should have been filled"
let field = (Field.EQ "Bill" "Matt").WithQualifier "joe" Expect.equal "joe" field.Qualifier.Value "The table qualifier is incorrect"
Expect.isSome field.Qualifier "The table qualifier should have been filled" }
Expect.equal "joe" field.Qualifier.Value "The table qualifier is incorrect" testList "Path" [
} test "succeeds for a PostgreSQL single field with no qualifier" {
testList "Path" [ let field = Field.GE "SomethingCool" 18
test "succeeds for a PostgreSQL single field with no qualifier" { Expect.equal "data->>'SomethingCool'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
let field = Field.GE "SomethingCool" 18 }
Expect.equal "data->>'SomethingCool'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect" test "succeeds for a PostgreSQL single field with a qualifier" {
} let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" }
test "succeeds for a PostgreSQL single field with a qualifier" { Expect.equal "this.data->>'SomethingElse'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" } }
Expect.equal test "succeeds for a PostgreSQL nested field with no qualifier" {
"this.data->>'SomethingElse'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect" let field = Field.EQ "My.Nested.Field" "howdy"
} Expect.equal "data#>>'{My,Nested,Field}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
test "succeeds for a PostgreSQL nested field with no qualifier" { }
let field = Field.EQ "My.Nested.Field" "howdy" test "succeeds for a PostgreSQL nested field with a qualifier" {
Expect.equal "data#>>'{My,Nested,Field}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect" let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" }
} Expect.equal "bird.data#>>'{Nest,Away}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
test "succeeds for a PostgreSQL nested field with a qualifier" { }
let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" } test "succeeds for a SQLite single field with no qualifier" {
Expect.equal "bird.data#>>'{Nest,Away}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect" let field = Field.GE "SomethingCool" 18
} Expect.equal "data->>'SomethingCool'" (field.Path SQLite) "The SQLite path is incorrect"
test "succeeds for a SQLite single field with no qualifier" { }
let field = Field.GE "SomethingCool" 18 test "succeeds for a SQLite single field with a qualifier" {
Expect.equal "data->>'SomethingCool'" (field.Path SQLite) "The SQLite path is incorrect" let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" }
} Expect.equal "this.data->>'SomethingElse'" (field.Path SQLite) "The SQLite path is incorrect"
test "succeeds for a SQLite single field with a qualifier" { }
let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" } test "succeeds for a SQLite nested field with no qualifier" {
Expect.equal "this.data->>'SomethingElse'" (field.Path SQLite) "The SQLite path is incorrect" let field = Field.EQ "My.Nested.Field" "howdy"
} Expect.equal "data->>'My'->>'Nested'->>'Field'" (field.Path SQLite) "The SQLite path is incorrect"
test "succeeds for a SQLite nested field with no qualifier" { }
let field = Field.EQ "My.Nested.Field" "howdy" test "succeeds for a SQLite nested field with a qualifier" {
Expect.equal "data->>'My'->>'Nested'->>'Field'" (field.Path SQLite) "The SQLite path is incorrect" let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" }
} Expect.equal "bird.data->>'Nest'->>'Away'" (field.Path SQLite) "The SQLite path is incorrect"
test "succeeds for a SQLite nested field with a qualifier" { }
let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" } ]
Expect.equal "bird.data->>'Nest'->>'Away'" (field.Path SQLite) "The SQLite path is incorrect" ]
}
] /// Unit tests for the FieldMatch DU
] let fieldMatchTests = testList "FieldMatch.ToString" [
testList "FieldMatch.ToString" [ test "succeeds for Any" {
test "succeeds for Any" { Expect.equal (string Any) "OR" "SQL for Any is incorrect"
Expect.equal (string Any) "OR" "SQL for Any is incorrect" }
} test "succeeds for All" {
test "succeeds for All" { Expect.equal (string All) "AND" "SQL for All is incorrect"
Expect.equal (string All) "AND" "SQL for All is incorrect" }
} ]
]
testList "ParameterName.Derive" [ /// Unit tests for the ParameterName class
test "succeeds with existing name" { let parameterNameTests = testList "ParameterName.Derive" [
let name = ParameterName() test "succeeds with existing name" {
Expect.equal (name.Derive(Some "@taco")) "@taco" "Name should have been @taco" let name = ParameterName()
Expect.equal (name.Derive None) "@field0" "Counter should not have advanced for named field" Expect.equal (name.Derive(Some "@taco")) "@taco" "Name should have been @taco"
} Expect.equal (name.Derive None) "@field0" "Counter should not have advanced for named field"
test "succeeds with non-existent name" { }
let name = ParameterName() test "succeeds with non-existent name" {
Expect.equal (name.Derive None) "@field0" "Anonymous field name should have been returned" let name = ParameterName()
Expect.equal (name.Derive None) "@field1" "Counter should have advanced from previous call" Expect.equal (name.Derive None) "@field0" "Anonymous field name should have been returned"
Expect.equal (name.Derive None) "@field2" "Counter should have advanced from previous call" Expect.equal (name.Derive None) "@field1" "Counter should have advanced from previous call"
Expect.equal (name.Derive None) "@field3" "Counter should have advanced from previous call" Expect.equal (name.Derive None) "@field2" "Counter should have advanced from previous call"
} Expect.equal (name.Derive None) "@field3" "Counter should have advanced from previous call"
] }
testList "Query" [ ]
test "statementWhere succeeds" {
Expect.equal (Query.statementWhere "x" "y") "x WHERE y" "Statements not combined correctly" /// Unit tests for the AutoId DU
} let autoIdTests = testList "AutoId" [
testList "Definition" [ test "GenerateGuid succeeds" {
test "ensureTableFor succeeds" { let autoId = AutoId.GenerateGuid()
Expect.equal Expect.isNotNull autoId "The GUID auto-ID should not have been null"
(Query.Definition.ensureTableFor "my.table" "JSONB") Expect.stringHasLength autoId 32 "The GUID auto-ID should have been 32 characters long"
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)" Expect.equal autoId (autoId.ToLowerInvariant ()) "The GUID auto-ID should have been lowercase"
"CREATE TABLE statement not constructed correctly" }
} test "GenerateRandomString succeeds" {
testList "ensureKey" [ [ 6; 8; 12; 20; 32; 57; 64 ]
test "succeeds when a schema is present" { |> List.iter (fun length ->
Expect.equal let autoId = AutoId.GenerateRandomString length
(Query.Definition.ensureKey "test.table" PostgreSQL) Expect.isNotNull autoId $"Random string ({length}) should not have been null"
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))" Expect.stringHasLength autoId length $"Random string should have been {length} characters long"
"CREATE INDEX for key statement with schema not constructed correctly" Expect.equal autoId (autoId.ToLowerInvariant ()) $"Random string ({length}) should have been lowercase")
} }
test "succeeds when a schema is not present" { ]
Expect.equal
(Query.Definition.ensureKey "table" SQLite) /// Unit tests for the Query module
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))" let queryTests = testList "Query" [
"CREATE INDEX for key statement without schema not constructed correctly" test "statementWhere succeeds" {
} Expect.equal (Query.statementWhere "x" "y") "x WHERE y" "Statements not combined correctly"
] }
testList "ensureIndexOn" [ testList "Definition" [
test "succeeds for multiple fields and directions" { test "ensureTableFor succeeds" {
Expect.equal Expect.equal
(Query.Definition.ensureIndexOn (Query.Definition.ensureTableFor "my.table" "JSONB")
"test.table" "gibberish" [ "taco"; "guac DESC"; "salsa ASC" ] PostgreSQL) "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)"
([ "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table " "CREATE TABLE statement not constructed correctly"
"((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)" ] }
|> String.concat "") testList "ensureKey" [
"CREATE INDEX for multiple field statement incorrect" test "succeeds when a schema is present" {
}
test "succeeds for nested PostgreSQL field" {
Expect.equal
(Query.Definition.ensureIndexOn tbl "nest" [ "a.b.c" ] PostgreSQL)
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_nest ON {tbl} ((data#>>'{{a,b,c}}'))"
"CREATE INDEX for nested PostgreSQL field incorrect"
}
test "succeeds for nested SQLite field" {
Expect.equal
(Query.Definition.ensureIndexOn tbl "nest" [ "a.b.c" ] SQLite)
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_nest ON {tbl} ((data->>'a'->>'b'->>'c'))"
"CREATE INDEX for nested SQLite field incorrect"
}
]
]
test "insert succeeds" {
Expect.equal (Query.insert tbl) $"INSERT INTO {tbl} VALUES (@data)" "INSERT statement not correct"
}
test "save succeeds" {
Expect.equal Expect.equal
(Query.save tbl) (Query.Definition.ensureKey "test.table" PostgreSQL)
$"INSERT INTO {tbl} VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data" "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))"
"INSERT ON CONFLICT UPDATE statement not correct" "CREATE INDEX for key statement with schema not constructed correctly"
} }
test "count succeeds" { test "succeeds when a schema is not present" {
Expect.equal (Query.count tbl) $"SELECT COUNT(*) AS it FROM {tbl}" "Count query not correct"
}
test "exists succeeds" {
Expect.equal Expect.equal
(Query.exists tbl "turkey") (Query.Definition.ensureKey "table" SQLite)
$"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE turkey) AS it" "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))"
"Exists query not correct" "CREATE INDEX for key statement without schema not constructed correctly"
} }
test "find succeeds" { ]
Expect.equal (Query.find tbl) $"SELECT data FROM {tbl}" "Find query not correct" testList "ensureIndexOn" [
test "succeeds for multiple fields and directions" {
Expect.equal
(Query.Definition.ensureIndexOn
"test.table" "gibberish" [ "taco"; "guac DESC"; "salsa ASC" ] PostgreSQL)
([ "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
"((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)" ]
|> String.concat "")
"CREATE INDEX for multiple field statement incorrect"
} }
test "update succeeds" { test "succeeds for nested PostgreSQL field" {
Expect.equal (Query.update tbl) $"UPDATE {tbl} SET data = @data" "Update query not correct" Expect.equal
(Query.Definition.ensureIndexOn tbl "nest" [ "a.b.c" ] PostgreSQL)
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_nest ON {tbl} ((data#>>'{{a,b,c}}'))"
"CREATE INDEX for nested PostgreSQL field incorrect"
} }
test "delete succeeds" { test "succeeds for nested SQLite field" {
Expect.equal (Query.delete tbl) $"DELETE FROM {tbl}" "Delete query not correct" Expect.equal
(Query.Definition.ensureIndexOn tbl "nest" [ "a.b.c" ] SQLite)
$"CREATE INDEX IF NOT EXISTS idx_{tbl}_nest ON {tbl} ((data->>'a'->>'b'->>'c'))"
"CREATE INDEX for nested SQLite field incorrect"
} }
testList "orderBy" [
test "succeeds for no fields" {
Expect.equal (Query.orderBy [] PostgreSQL) "" "Order By should have been blank (PostgreSQL)"
Expect.equal (Query.orderBy [] SQLite) "" "Order By should have been blank (SQLite)"
}
test "succeeds for PostgreSQL with one field and no direction" {
Expect.equal
(Query.orderBy [ Field.Named "TestField" ] PostgreSQL)
" ORDER BY data->>'TestField'"
"Order By not constructed correctly"
}
test "succeeds for SQLite with one field and no direction" {
Expect.equal
(Query.orderBy [ Field.Named "TestField" ] SQLite)
" ORDER BY data->>'TestField'"
"Order By not constructed correctly"
}
test "succeeds for PostgreSQL with multiple fields and direction" {
Expect.equal
(Query.orderBy
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
PostgreSQL)
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC"
"Order By not constructed correctly"
}
test "succeeds for SQLite with multiple fields and direction" {
Expect.equal
(Query.orderBy
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
SQLite)
" ORDER BY data->>'Nested'->>'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC"
"Order By not constructed correctly"
}
test "succeeds for PostgreSQL numeric fields" {
Expect.equal
(Query.orderBy [ Field.Named "n:Test" ] PostgreSQL)
" ORDER BY (data->>'Test')::numeric"
"Order By not constructed correctly for numeric field"
}
test "succeeds for SQLite numeric fields" {
Expect.equal
(Query.orderBy [ Field.Named "n:Test" ] SQLite)
" ORDER BY data->>'Test'"
"Order By not constructed correctly for numeric field"
}
]
] ]
] ]
test "insert succeeds" {
Expect.equal (Query.insert tbl) $"INSERT INTO {tbl} VALUES (@data)" "INSERT statement not correct"
}
test "save succeeds" {
Expect.equal
(Query.save tbl)
$"INSERT INTO {tbl} VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data"
"INSERT ON CONFLICT UPDATE statement not correct"
}
test "count succeeds" {
Expect.equal (Query.count tbl) $"SELECT COUNT(*) AS it FROM {tbl}" "Count query not correct"
}
test "exists succeeds" {
Expect.equal
(Query.exists tbl "turkey")
$"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE turkey) AS it"
"Exists query not correct"
}
test "find succeeds" {
Expect.equal (Query.find tbl) $"SELECT data FROM {tbl}" "Find query not correct"
}
test "update succeeds" {
Expect.equal (Query.update tbl) $"UPDATE {tbl} SET data = @data" "Update query not correct"
}
test "delete succeeds" {
Expect.equal (Query.delete tbl) $"DELETE FROM {tbl}" "Delete query not correct"
}
testList "orderBy" [
test "succeeds for no fields" {
Expect.equal (Query.orderBy [] PostgreSQL) "" "Order By should have been blank (PostgreSQL)"
Expect.equal (Query.orderBy [] SQLite) "" "Order By should have been blank (SQLite)"
}
test "succeeds for PostgreSQL with one field and no direction" {
Expect.equal
(Query.orderBy [ Field.Named "TestField" ] PostgreSQL)
" ORDER BY data->>'TestField'"
"Order By not constructed correctly"
}
test "succeeds for SQLite with one field and no direction" {
Expect.equal
(Query.orderBy [ Field.Named "TestField" ] SQLite)
" ORDER BY data->>'TestField'"
"Order By not constructed correctly"
}
test "succeeds for PostgreSQL with multiple fields and direction" {
Expect.equal
(Query.orderBy
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
PostgreSQL)
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC"
"Order By not constructed correctly"
}
test "succeeds for SQLite with multiple fields and direction" {
Expect.equal
(Query.orderBy
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
SQLite)
" ORDER BY data->>'Nested'->>'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC"
"Order By not constructed correctly"
}
test "succeeds for PostgreSQL numeric fields" {
Expect.equal
(Query.orderBy [ Field.Named "n:Test" ] PostgreSQL)
" ORDER BY (data->>'Test')::numeric"
"Order By not constructed correctly for numeric field"
}
test "succeeds for SQLite numeric fields" {
Expect.equal
(Query.orderBy [ Field.Named "n:Test" ] SQLite)
" ORDER BY data->>'Test'"
"Order By not constructed correctly for numeric field"
}
]
]
/// Unit tests for the Configuration module
let configurationTests = testList "Configuration" [
test "useSerializer succeeds" {
try
Configuration.useSerializer
{ new IDocumentSerializer with
member _.Serialize<'T>(it: 'T) : string = """{"Overridden":true}"""
member _.Deserialize<'T>(it: string) : 'T = Unchecked.defaultof<'T>
}
let serialized = Configuration.serializer().Serialize {| Foo = "howdy"; Bar = "bye" |}
Expect.equal serialized """{"Overridden":true}""" "Specified serializer was not used"
let deserialized = Configuration.serializer().Deserialize<obj> """{"Something":"here"}"""
Expect.isNull deserialized "Specified serializer should have returned null"
finally
Configuration.useSerializer DocumentSerializer.``default``
}
test "serializer returns configured serializer" {
Expect.isTrue (obj.ReferenceEquals(DocumentSerializer.``default``, Configuration.serializer ()))
"Serializer should have been the same"
}
test "useIdField / idField succeeds" {
try
Expect.equal (Configuration.idField ()) "Id" "The default configured ID field was incorrect"
Configuration.useIdField "id"
Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field"
finally
Configuration.useIdField "Id"
}
test "useAutoIdStrategy / autoIdStrategy succeeds" {
try
Expect.equal (Configuration.autoIdStrategy ()) Disabled "The default auto-ID strategy was incorrect"
Configuration.useAutoIdStrategy Guid
Expect.equal (Configuration.autoIdStrategy ()) Guid "The auto-ID strategy was not set correctly"
finally
Configuration.useAutoIdStrategy Disabled
}
test "useIdStringLength / idStringLength succeeds" {
try
Expect.equal (Configuration.idStringLength ()) 16 "The default ID string length was incorrect"
Configuration.useIdStringLength 33
Expect.equal (Configuration.idStringLength ()) 33 "The ID string length was not set correctly"
finally
Configuration.useIdStringLength 16
}
]
/// Tests which do not hit the database
let all = testList "Common" [
opTests
fieldTests
fieldMatchTests
parameterNameTests
autoIdTests
queryTests
testSequenced configurationTests
]

View File

@ -144,32 +144,6 @@ let configurationTests = testList "Configuration" [
finally finally
Configuration.useConnectionString "Data Source=:memory:" Configuration.useConnectionString "Data Source=:memory:"
} }
test "useSerializer succeeds" {
try
Configuration.useSerializer
{ new IDocumentSerializer with
member _.Serialize<'T>(it: 'T) : string = """{"Overridden":true}"""
member _.Deserialize<'T>(it: string) : 'T = Unchecked.defaultof<'T>
}
let serialized = Configuration.serializer().Serialize { Foo = "howdy"; Bar = "bye"}
Expect.equal serialized """{"Overridden":true}""" "Specified serializer was not used"
let deserialized = Configuration.serializer().Deserialize<obj> """{"Something":"here"}"""
Expect.isNull deserialized "Specified serializer should have returned null"
finally
Configuration.useSerializer DocumentSerializer.``default``
}
test "serializer returns configured serializer" {
Expect.isTrue (obj.ReferenceEquals(DocumentSerializer.``default``, Configuration.serializer ()))
"Serializer should have been the same"
}
test "useIdField / idField succeeds" {
Expect.equal (Configuration.idField ()) "Id" "The default configured ID field was incorrect"
Configuration.useIdField "id"
Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field"
Configuration.useIdField "Id"
}
] ]
/// Integration tests for the Custom module of the SQLite library /// Integration tests for the Custom module of the SQLite library