Add AutoId DU / tests
This commit is contained in:
parent
88f11a854f
commit
b357b5a01f
@ -1,5 +1,7 @@
|
||||
namespace BitBadger.Documents
|
||||
|
||||
open System.Security.Cryptography
|
||||
|
||||
/// The types of logical operations available for JSON fields
|
||||
[<Struct>]
|
||||
type Op =
|
||||
@ -148,6 +150,36 @@ type ParameterName() =
|
||||
currentIdx <- currentIdx + 1
|
||||
$"@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
|
||||
type IDocumentSerializer =
|
||||
@ -200,7 +232,7 @@ module Configuration =
|
||||
serializerValue
|
||||
|
||||
/// 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
|
||||
[<CompiledName "UseIdField">]
|
||||
@ -211,6 +243,32 @@ module Configuration =
|
||||
[<CompiledName "IdField">]
|
||||
let idField () =
|
||||
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
|
||||
|
@ -70,6 +70,34 @@ public static class CommonCSharpTests
|
||||
{
|
||||
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",
|
||||
@ -295,6 +323,27 @@ public static class CommonCSharpTests
|
||||
"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",
|
||||
[
|
||||
TestCase("StatementWhere succeeds", () =>
|
||||
|
@ -6,320 +6,399 @@ open Expecto
|
||||
/// Test table name
|
||||
let tbl = "test_table"
|
||||
|
||||
/// Tests which do not hit the database
|
||||
let all =
|
||||
testList "Common" [
|
||||
testList "Op" [
|
||||
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 "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 "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 "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 "NEX succeeds" {
|
||||
Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
|
||||
}
|
||||
]
|
||||
testList "Field" [
|
||||
test "EQ succeeds" {
|
||||
let field = Field.EQ "Test" 14
|
||||
Expect.equal field.Name "Test" "Field name incorrect"
|
||||
Expect.equal field.Op EQ "Operator incorrect"
|
||||
Expect.equal field.Value 14 "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "GT succeeds" {
|
||||
let field = Field.GT "Great" "night"
|
||||
Expect.equal field.Name "Great" "Field name incorrect"
|
||||
Expect.equal field.Op GT "Operator incorrect"
|
||||
Expect.equal field.Value "night" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "GE succeeds" {
|
||||
let field = Field.GE "Nice" 88L
|
||||
Expect.equal field.Name "Nice" "Field name incorrect"
|
||||
Expect.equal field.Op GE "Operator incorrect"
|
||||
Expect.equal field.Value 88L "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "LT succeeds" {
|
||||
let field = Field.LT "Lesser" "seven"
|
||||
Expect.equal field.Name "Lesser" "Field name incorrect"
|
||||
Expect.equal field.Op LT "Operator incorrect"
|
||||
Expect.equal field.Value "seven" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "LE succeeds" {
|
||||
let field = Field.LE "Nobody" "KNOWS";
|
||||
Expect.equal field.Name "Nobody" "Field name incorrect"
|
||||
Expect.equal field.Op LE "Operator incorrect"
|
||||
Expect.equal field.Value "KNOWS" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "NE succeeds" {
|
||||
let field = Field.NE "Park" "here"
|
||||
Expect.equal field.Name "Park" "Field name incorrect"
|
||||
Expect.equal field.Op NE "Operator incorrect"
|
||||
Expect.equal field.Value "here" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "BT succeeds" {
|
||||
let field = Field.BT "Age" 18 49
|
||||
Expect.equal field.Name "Age" "Field name incorrect"
|
||||
Expect.equal field.Op BT "Operator 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.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "EX succeeds" {
|
||||
let field = Field.EX "Groovy"
|
||||
Expect.equal field.Name "Groovy" "Field name incorrect"
|
||||
Expect.equal field.Op EX "Operator incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "NEX succeeds" {
|
||||
let field = Field.NEX "Rad"
|
||||
Expect.equal field.Name "Rad" "Field name incorrect"
|
||||
Expect.equal field.Op NEX "Operator incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
testList "NameToPath" [
|
||||
test "succeeds for PostgreSQL and a simple name" {
|
||||
Expect.equal
|
||||
"data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for SQLite and a simple name" {
|
||||
Expect.equal
|
||||
"data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for PostgreSQL and a nested name" {
|
||||
Expect.equal
|
||||
"data#>>'{A,Long,Path,to,the,Property}'"
|
||||
(Field.NameToPath "A.Long.Path.to.the.Property" PostgreSQL)
|
||||
"Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for SQLite and a nested name" {
|
||||
Expect.equal
|
||||
"data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'"
|
||||
(Field.NameToPath "A.Long.Path.to.the.Property" SQLite)
|
||||
"Path not constructed correctly"
|
||||
}
|
||||
]
|
||||
test "WithParameterName succeeds" {
|
||||
let field = (Field.EQ "Bob" "Tom").WithParameterName "@name"
|
||||
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"
|
||||
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" {
|
||||
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" }
|
||||
Expect.equal
|
||||
"this.data->>'SomethingElse'" (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"
|
||||
Expect.equal "data#>>'{My,Nested,Field}'" (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" }
|
||||
Expect.equal "bird.data#>>'{Nest,Away}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||
}
|
||||
test "succeeds for a SQLite single field with no qualifier" {
|
||||
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 a qualifier" {
|
||||
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 nested field with no qualifier" {
|
||||
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 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"
|
||||
}
|
||||
]
|
||||
]
|
||||
testList "FieldMatch.ToString" [
|
||||
test "succeeds for Any" {
|
||||
Expect.equal (string Any) "OR" "SQL for Any is incorrect"
|
||||
}
|
||||
test "succeeds for All" {
|
||||
Expect.equal (string All) "AND" "SQL for All is incorrect"
|
||||
}
|
||||
]
|
||||
testList "ParameterName.Derive" [
|
||||
test "succeeds with existing name" {
|
||||
let name = ParameterName()
|
||||
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()
|
||||
Expect.equal (name.Derive None) "@field0" "Anonymous field name should have been returned"
|
||||
Expect.equal (name.Derive None) "@field1" "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"
|
||||
}
|
||||
testList "Definition" [
|
||||
test "ensureTableFor succeeds" {
|
||||
Expect.equal
|
||||
(Query.Definition.ensureTableFor "my.table" "JSONB")
|
||||
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)"
|
||||
"CREATE TABLE statement not constructed correctly"
|
||||
}
|
||||
testList "ensureKey" [
|
||||
test "succeeds when a schema is present" {
|
||||
Expect.equal
|
||||
(Query.Definition.ensureKey "test.table" PostgreSQL)
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))"
|
||||
"CREATE INDEX for key statement with schema not constructed correctly"
|
||||
}
|
||||
test "succeeds when a schema is not present" {
|
||||
Expect.equal
|
||||
(Query.Definition.ensureKey "table" SQLite)
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))"
|
||||
"CREATE INDEX for key statement without schema not constructed correctly"
|
||||
}
|
||||
]
|
||||
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 "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" {
|
||||
/// Unit tests for the Op DU
|
||||
let opTests = testList "Op" [
|
||||
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 "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 "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 "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 "NEX succeeds" {
|
||||
Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
|
||||
}
|
||||
]
|
||||
|
||||
/// Unit tests for the Field class
|
||||
let fieldTests = testList "Field" [
|
||||
test "EQ succeeds" {
|
||||
let field = Field.EQ "Test" 14
|
||||
Expect.equal field.Name "Test" "Field name incorrect"
|
||||
Expect.equal field.Op EQ "Operator incorrect"
|
||||
Expect.equal field.Value 14 "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "GT succeeds" {
|
||||
let field = Field.GT "Great" "night"
|
||||
Expect.equal field.Name "Great" "Field name incorrect"
|
||||
Expect.equal field.Op GT "Operator incorrect"
|
||||
Expect.equal field.Value "night" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "GE succeeds" {
|
||||
let field = Field.GE "Nice" 88L
|
||||
Expect.equal field.Name "Nice" "Field name incorrect"
|
||||
Expect.equal field.Op GE "Operator incorrect"
|
||||
Expect.equal field.Value 88L "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "LT succeeds" {
|
||||
let field = Field.LT "Lesser" "seven"
|
||||
Expect.equal field.Name "Lesser" "Field name incorrect"
|
||||
Expect.equal field.Op LT "Operator incorrect"
|
||||
Expect.equal field.Value "seven" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "LE succeeds" {
|
||||
let field = Field.LE "Nobody" "KNOWS";
|
||||
Expect.equal field.Name "Nobody" "Field name incorrect"
|
||||
Expect.equal field.Op LE "Operator incorrect"
|
||||
Expect.equal field.Value "KNOWS" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "NE succeeds" {
|
||||
let field = Field.NE "Park" "here"
|
||||
Expect.equal field.Name "Park" "Field name incorrect"
|
||||
Expect.equal field.Op NE "Operator incorrect"
|
||||
Expect.equal field.Value "here" "Value incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "BT succeeds" {
|
||||
let field = Field.BT "Age" 18 49
|
||||
Expect.equal field.Name "Age" "Field name incorrect"
|
||||
Expect.equal field.Op BT "Operator 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.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "EX succeeds" {
|
||||
let field = Field.EX "Groovy"
|
||||
Expect.equal field.Name "Groovy" "Field name incorrect"
|
||||
Expect.equal field.Op EX "Operator incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
test "NEX succeeds" {
|
||||
let field = Field.NEX "Rad"
|
||||
Expect.equal field.Name "Rad" "Field name incorrect"
|
||||
Expect.equal field.Op NEX "Operator incorrect"
|
||||
Expect.isNone field.ParameterName "The default parameter name should be None"
|
||||
Expect.isNone field.Qualifier "The default table qualifier should be None"
|
||||
}
|
||||
testList "NameToPath" [
|
||||
test "succeeds for PostgreSQL and a simple name" {
|
||||
Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for SQLite and a simple name" {
|
||||
Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for PostgreSQL and a nested name" {
|
||||
Expect.equal
|
||||
"data#>>'{A,Long,Path,to,the,Property}'"
|
||||
(Field.NameToPath "A.Long.Path.to.the.Property" PostgreSQL)
|
||||
"Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for SQLite and a nested name" {
|
||||
Expect.equal
|
||||
"data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'"
|
||||
(Field.NameToPath "A.Long.Path.to.the.Property" SQLite)
|
||||
"Path not constructed correctly"
|
||||
}
|
||||
]
|
||||
test "WithParameterName succeeds" {
|
||||
let field = (Field.EQ "Bob" "Tom").WithParameterName "@name"
|
||||
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"
|
||||
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" {
|
||||
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" }
|
||||
Expect.equal "this.data->>'SomethingElse'" (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"
|
||||
Expect.equal "data#>>'{My,Nested,Field}'" (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" }
|
||||
Expect.equal "bird.data#>>'{Nest,Away}'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||
}
|
||||
test "succeeds for a SQLite single field with no qualifier" {
|
||||
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 a qualifier" {
|
||||
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 nested field with no qualifier" {
|
||||
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 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" [
|
||||
test "succeeds for Any" {
|
||||
Expect.equal (string Any) "OR" "SQL for Any is incorrect"
|
||||
}
|
||||
test "succeeds for All" {
|
||||
Expect.equal (string All) "AND" "SQL for All is incorrect"
|
||||
}
|
||||
]
|
||||
|
||||
/// Unit tests for the ParameterName class
|
||||
let parameterNameTests = testList "ParameterName.Derive" [
|
||||
test "succeeds with existing name" {
|
||||
let name = ParameterName()
|
||||
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()
|
||||
Expect.equal (name.Derive None) "@field0" "Anonymous field name should have been returned"
|
||||
Expect.equal (name.Derive None) "@field1" "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"
|
||||
}
|
||||
]
|
||||
|
||||
/// Unit tests for the AutoId DU
|
||||
let autoIdTests = testList "AutoId" [
|
||||
test "GenerateGuid succeeds" {
|
||||
let 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"
|
||||
}
|
||||
test "GenerateRandomString succeeds" {
|
||||
[ 6; 8; 12; 20; 32; 57; 64 ]
|
||||
|> List.iter (fun length ->
|
||||
let 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")
|
||||
}
|
||||
]
|
||||
|
||||
/// Unit tests for the Query module
|
||||
let queryTests = testList "Query" [
|
||||
test "statementWhere succeeds" {
|
||||
Expect.equal (Query.statementWhere "x" "y") "x WHERE y" "Statements not combined correctly"
|
||||
}
|
||||
testList "Definition" [
|
||||
test "ensureTableFor succeeds" {
|
||||
Expect.equal
|
||||
(Query.Definition.ensureTableFor "my.table" "JSONB")
|
||||
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)"
|
||||
"CREATE TABLE statement not constructed correctly"
|
||||
}
|
||||
testList "ensureKey" [
|
||||
test "succeeds when a schema is present" {
|
||||
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"
|
||||
(Query.Definition.ensureKey "test.table" PostgreSQL)
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))"
|
||||
"CREATE INDEX for key statement with schema not constructed correctly"
|
||||
}
|
||||
test "count succeeds" {
|
||||
Expect.equal (Query.count tbl) $"SELECT COUNT(*) AS it FROM {tbl}" "Count query not correct"
|
||||
}
|
||||
test "exists succeeds" {
|
||||
test "succeeds when a schema is not present" {
|
||||
Expect.equal
|
||||
(Query.exists tbl "turkey")
|
||||
$"SELECT EXISTS (SELECT 1 FROM {tbl} WHERE turkey) AS it"
|
||||
"Exists query not correct"
|
||||
(Query.Definition.ensureKey "table" SQLite)
|
||||
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))"
|
||||
"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" {
|
||||
Expect.equal (Query.update tbl) $"UPDATE {tbl} SET data = @data" "Update query not correct"
|
||||
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 "delete succeeds" {
|
||||
Expect.equal (Query.delete tbl) $"DELETE FROM {tbl}" "Delete query not correct"
|
||||
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"
|
||||
}
|
||||
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
|
||||
]
|
||||
|
@ -144,32 +144,6 @@ let configurationTests = testList "Configuration" [
|
||||
finally
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user