|
|
|
@ -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
|
|
|
|
|
]
|
|
|
|
|