Add whereByFields code / tests
- Add wrapper class for unnamed field parameters - Support table qualifiers by field - Support dot access to document fields/sub-fields
This commit is contained in:
		
							parent
							
								
									e96c449324
								
							
						
					
					
						commit
						e2232e91bb
					
				| @ -97,6 +97,18 @@ type Field = { | |||||||
|     member this.WithQualifier alias = |     member this.WithQualifier alias = | ||||||
|         { this with Qualifier = Some alias } |         { this with Qualifier = Some alias } | ||||||
| 
 | 
 | ||||||
|  |     /// Get the path for this field in PostgreSQL's format     | ||||||
|  |     member this.PgSqlPath = | ||||||
|  |         (this.Qualifier |> Option.map (fun q -> $"{q}.data") |> Option.defaultValue "data") | ||||||
|  |             + if this.Name.Contains '.' then "#>>'{" + String.concat "," (this.Name.Split '.') + "}'" | ||||||
|  |                                         else $"->>'{this.Name}'" | ||||||
|  | 
 | ||||||
|  |     /// Get the path for this field in SQLite's format     | ||||||
|  |     member this.SqlitePath = | ||||||
|  |         (this.Qualifier |> Option.map (fun q -> $"{q}.data") |> Option.defaultValue "data") | ||||||
|  |             + if this.Name.Contains '.' then "->>'" + String.concat "'->>'" (this.Name.Split '.') + "'" | ||||||
|  |                                         else $"->>'{this.Name}'" | ||||||
|  | 
 | ||||||
| 
 | 
 | ||||||
| /// How fields should be matched | /// How fields should be matched | ||||||
| [<Struct>] | [<Struct>] | ||||||
| @ -107,6 +119,20 @@ type FieldMatch = | |||||||
|     | All |     | All | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
|  | /// Derive parameter names (each instance wraps a counter to uniquely name anonymous fields) | ||||||
|  | type ParameterName() = | ||||||
|  |     /// The counter for the next field value | ||||||
|  |     let mutable currentIdx = -1 | ||||||
|  |      | ||||||
|  |     /// Return the specified name for the parameter, or an anonymous parameter name if none is specified | ||||||
|  |     member this.Derive paramName = | ||||||
|  |         match paramName with | ||||||
|  |         | Some it -> it | ||||||
|  |         | None -> | ||||||
|  |             currentIdx <- currentIdx + 1 | ||||||
|  |             $"@field{currentIdx}" | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| /// The required document serialization implementation | /// The required document serialization implementation | ||||||
| type IDocumentSerializer = | type IDocumentSerializer = | ||||||
|      |      | ||||||
|  | |||||||
| @ -110,34 +110,27 @@ module Parameters = | |||||||
| module Query = | module Query = | ||||||
|      |      | ||||||
|     /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document |     /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document | ||||||
|     [<CompiledName "WhereByFields">] |     [<CompiledName "FSharpWhereByFields">] | ||||||
|     let whereByFields fields howMatched = |     let whereByFields fields howMatched = | ||||||
|         let mutable idx = 0 |         let name = ParameterName() | ||||||
|         let nameField () = |  | ||||||
|             let name = $"field{idx}" |  | ||||||
|             idx <- idx + 1 |  | ||||||
|             name |  | ||||||
|         fields |         fields | ||||||
|         |> List.map (fun it -> |         |> List.map (fun it -> | ||||||
|             let fieldName = it.Qualifier |> Option.map (fun q -> $"{q}.data") |> Option.defaultValue "data" |  | ||||||
|             let jsonPath  = |  | ||||||
|                 if it.Name.Contains '.' then "#>>'{" + String.concat "," (it.Name.Split '.') + "}'" |  | ||||||
|                 else $"->>'{it.Name}'" |  | ||||||
|             let column = fieldName + jsonPath |  | ||||||
|             match it.Op with |             match it.Op with | ||||||
|             | EX | NEX -> $"{column} {it.Op}" |             | EX | NEX -> $"{it.PgSqlPath} {it.Op}" | ||||||
|             | BT -> |             | BT -> | ||||||
|                 let p      = defaultArg it.ParameterName (nameField ()) |                 let p      = name.Derive it.ParameterName | ||||||
|                 let names  = $"{p}min AND {p}max" |                 let names  = $"{p}min AND {p}max" | ||||||
|                 let values = it.Value :?> obj list |                 let values = it.Value :?> obj list | ||||||
|                 match values[0] with |                 match values[0] with | ||||||
|                 | :? int8    | :? uint8  | :? int16  | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64 |                 | :? int8    | :? uint8  | :? int16  | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64 | ||||||
|                 | :? decimal | :? single | :? double -> $"({column})::numeric {it.Op} {names}" |                 | :? decimal | :? single | :? double -> $"({it.PgSqlPath})::numeric {it.Op} {names}" | ||||||
|                 | _ -> $"{column} {it.Op} {names}" |                 | _ -> $"{it.PgSqlPath} {it.Op} {names}" | ||||||
|             | _ -> |             | _ -> $"{it.PgSqlPath} {it.Op} {name.Derive it.ParameterName}") | ||||||
|                 let p = defaultArg it.ParameterName (nameField ()) |  | ||||||
|                 $"{column} {it.Op} {p}") |  | ||||||
|         |> String.concat (match howMatched with Any -> " OR " | All -> " AND ") |         |> String.concat (match howMatched with Any -> " OR " | All -> " AND ") | ||||||
|  | 
 | ||||||
|  |     /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document | ||||||
|  |     let WhereByFields(fields: Field seq, howMatched) = | ||||||
|  |         whereByFields (List.ofSeq fields) howMatched | ||||||
|      |      | ||||||
|     /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document |     /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document | ||||||
|     [<CompiledName "WhereByField">] |     [<CompiledName "WhereByField">] | ||||||
|  | |||||||
| @ -31,20 +31,33 @@ module Configuration = | |||||||
| [<RequireQualifiedAccess>] | [<RequireQualifiedAccess>] | ||||||
| module Query = | module Query = | ||||||
|      |      | ||||||
|  |     /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document | ||||||
|  |     [<CompiledName "FSharpWhereByFields">] | ||||||
|  |     let whereByFields fields howMatched = | ||||||
|  |         let name = ParameterName() | ||||||
|  |         fields | ||||||
|  |         |> List.map (fun it -> | ||||||
|  |             match it.Op with | ||||||
|  |             | EX | NEX -> $"{it.SqlitePath} {it.Op}" | ||||||
|  |             | BT -> | ||||||
|  |                 let p = name.Derive it.ParameterName | ||||||
|  |                 $"{it.SqlitePath} {it.Op} {p}min AND {p}max" | ||||||
|  |             | _ -> $"{it.SqlitePath} {it.Op} {name.Derive it.ParameterName}") | ||||||
|  |         |> String.concat (match howMatched with Any -> " OR " | All -> " AND ") | ||||||
|  | 
 | ||||||
|  |     /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document | ||||||
|  |     let WhereByFields(fields: Field seq, howMatched) = | ||||||
|  |         whereByFields (List.ofSeq fields) howMatched | ||||||
|  | 
 | ||||||
|     /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document |     /// Create a WHERE clause fragment to implement a comparison on a field in a JSON document | ||||||
|     [<CompiledName "WhereByField">] |     [<CompiledName "WhereByField">] | ||||||
|     let whereByField field paramName = |     let whereByField field paramName = | ||||||
|         let theRest = |         whereByFields [ { field with ParameterName = Some paramName } ] Any | ||||||
|             match field.Op with |  | ||||||
|             | EX | NEX -> "" |  | ||||||
|             | BT -> $" {paramName}min AND {paramName}max" |  | ||||||
|             | _ -> $" %s{paramName}" |  | ||||||
|         $"data->>'{field.Name}' {field.Op}{theRest}" |  | ||||||
|      |      | ||||||
|     /// Create a WHERE clause fragment to implement an ID-based query |     /// Create a WHERE clause fragment to implement an ID-based query | ||||||
|     [<CompiledName "WhereById">] |     [<CompiledName "WhereById">] | ||||||
|     let whereById paramName = |     let whereById paramName = | ||||||
|         whereByField (Field.EQ (Configuration.idField ()) 0) paramName |         whereByFields [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ] Any | ||||||
|      |      | ||||||
|     /// Data definition |     /// Data definition | ||||||
|     module Definition = |     module Definition = | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ | |||||||
|   <PropertyGroup> |   <PropertyGroup> | ||||||
|     <ImplicitUsings>enable</ImplicitUsings> |     <ImplicitUsings>enable</ImplicitUsings> | ||||||
|     <Nullable>enable</Nullable> |     <Nullable>enable</Nullable> | ||||||
|  |     <LangVersion>latest</LangVersion> | ||||||
|   </PropertyGroup> |   </PropertyGroup> | ||||||
| 
 | 
 | ||||||
|   <ItemGroup> |   <ItemGroup> | ||||||
|  | |||||||
| @ -24,11 +24,11 @@ public static class CommonCSharpTests | |||||||
|     /// Unit tests |     /// Unit tests | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     [Tests] |     [Tests] | ||||||
|     public static readonly Test Unit = TestList("Common.C# Unit", new[] |     public static readonly Test Unit = TestList("Common.C# Unit", | ||||||
|     { |     [ | ||||||
|         TestSequenced( |         TestSequenced( | ||||||
|             TestList("Configuration", new[] |             TestList("Configuration", | ||||||
|             { |             [ | ||||||
|                 TestCase("UseSerializer succeeds", () => |                 TestCase("UseSerializer succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     try |                     try | ||||||
| @ -70,9 +70,9 @@ public static class CommonCSharpTests | |||||||
|                         Configuration.UseIdField("Id"); |                         Configuration.UseIdField("Id"); | ||||||
|                     } |                     } | ||||||
|                 }) |                 }) | ||||||
|             })), |             ])), | ||||||
|         TestList("Op", new[] |         TestList("Op", | ||||||
|         { |         [ | ||||||
|             TestCase("EQ succeeds", () => |             TestCase("EQ succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.equal(Op.EQ.ToString(), "=", "The equals operator was not correct"); |                 Expect.equal(Op.EQ.ToString(), "=", "The equals operator was not correct"); | ||||||
| @ -109,9 +109,9 @@ public static class CommonCSharpTests | |||||||
|             { |             { | ||||||
|                 Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct"); |                 Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Field", new[] |         TestList("Field", | ||||||
|         { |         [ | ||||||
|             TestCase("EQ succeeds", () => |             TestCase("EQ succeeds", () => | ||||||
|             { |             { | ||||||
|                 var field = Field.EQ("Test", 14); |                 var field = Field.EQ("Test", 14); | ||||||
| @ -159,7 +159,7 @@ public static class CommonCSharpTests | |||||||
|                 var field = Field.BT("Age", 18, 49); |                 var 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, Op.BT, "Operator incorrect"); |                 Expect.equal(field.Op, Op.BT, "Operator incorrect"); | ||||||
|                 Expect.equal(((FSharpList<object>)field.Value).ToArray(), new object[] { 18, 49 }, "Value incorrect"); |                 Expect.equal(((FSharpList<object>)field.Value).ToArray(), [18, 49], "Value incorrect"); | ||||||
|             }), |             }), | ||||||
|             TestCase("EX succeeds", () => |             TestCase("EX succeeds", () => | ||||||
|             { |             { | ||||||
| @ -172,25 +172,83 @@ public static class CommonCSharpTests | |||||||
|                 var field = Field.NEX("Rad"); |                 var field = Field.NEX("Rad"); | ||||||
|                 Expect.equal(field.Name, "Rad", "Field name incorrect"); |                 Expect.equal(field.Name, "Rad", "Field name incorrect"); | ||||||
|                 Expect.equal(field.Op, Op.NEX, "Operator incorrect"); |                 Expect.equal(field.Op, Op.NEX, "Operator incorrect"); | ||||||
|             }) |             }), | ||||||
|         }), |             TestCase("WithParameterName succeeds", () => | ||||||
|         TestList("Query", new[] |             { | ||||||
|         { |                 var 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"); | ||||||
|  |             }), | ||||||
|  |             TestCase("WithQualifier succeeds", () => | ||||||
|  |             { | ||||||
|  |                 var 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("PgSqlPath", | ||||||
|  |             [ | ||||||
|  |                 TestCase("succeeds for a single field with no qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.GE("SomethingCool", 18); | ||||||
|  |                     Expect.equal("data->>'SomethingCool'", field.PgSqlPath, "The PostgreSQL path is incorrect"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field with a qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.LT("SomethingElse", 9).WithQualifier("this"); | ||||||
|  |                     Expect.equal("this.data->>'SomethingElse'", field.PgSqlPath, "The PostgreSQL path is incorrect"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a nested field with no qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.EQ("My.Nested.Field", "howdy"); | ||||||
|  |                     Expect.equal("data#>>'{My,Nested,Field}'", field.PgSqlPath, "The PostgreSQL path is incorrect"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a nested field with a qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.EQ("Nest.Away", "doc").WithQualifier("bird"); | ||||||
|  |                     Expect.equal("bird.data#>>'{Nest,Away}'", field.PgSqlPath, "The PostgreSQL path is incorrect"); | ||||||
|  |                 }) | ||||||
|  |             ]), | ||||||
|  |             TestList("SqlitePath", | ||||||
|  |             [ | ||||||
|  |                 TestCase("succeeds for a single field with no qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.GE("SomethingCool", 18); | ||||||
|  |                     Expect.equal("data->>'SomethingCool'", field.SqlitePath, "The SQLite path is incorrect"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field with a qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.LT("SomethingElse", 9).WithQualifier("this"); | ||||||
|  |                     Expect.equal("this.data->>'SomethingElse'", field.SqlitePath, "The SQLite path is incorrect"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a nested field with no qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.EQ("My.Nested.Field", "howdy"); | ||||||
|  |                     Expect.equal("data->>'My'->>'Nested'->>'Field'", field.SqlitePath, "The SQLite path is incorrect"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a nested field with a qualifier", () => | ||||||
|  |                 { | ||||||
|  |                     var field = Field.EQ("Nest.Away", "doc").WithQualifier("bird"); | ||||||
|  |                     Expect.equal("bird.data->>'Nest'->>'Away'", field.SqlitePath, "The SQLite path is incorrect"); | ||||||
|  |                 }) | ||||||
|  |             ]) | ||||||
|  |         ]), | ||||||
|  |         TestList("Query", | ||||||
|  |         [ | ||||||
|             TestCase("SelectFromTable succeeds", () => |             TestCase("SelectFromTable succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table", |                 Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table", | ||||||
|                     "SELECT statement not correct"); |                     "SELECT statement not correct"); | ||||||
|             }), |             }), | ||||||
|             TestList("Definition", new[] |             TestList("Definition", | ||||||
|             { |             [ | ||||||
|                 TestCase("EnsureTableFor succeeds", () => |                 TestCase("EnsureTableFor succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Query.Definition.EnsureTableFor("my.table", "JSONB"), |                     Expect.equal(Query.Definition.EnsureTableFor("my.table", "JSONB"), | ||||||
|                         "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)", |                         "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)", | ||||||
|                         "CREATE TABLE statement not constructed correctly"); |                         "CREATE TABLE statement not constructed correctly"); | ||||||
|                 }), |                 }), | ||||||
|                 TestList("EnsureKey", new[] |                 TestList("EnsureKey", | ||||||
|                 { |                 [ | ||||||
|                     TestCase("succeeds when a schema is present", () => |                     TestCase("succeeds when a schema is present", () => | ||||||
|                     { |                     { | ||||||
|                         Expect.equal(Query.Definition.EnsureKey("test.table"), |                         Expect.equal(Query.Definition.EnsureKey("test.table"), | ||||||
| @ -203,7 +261,7 @@ public static class CommonCSharpTests | |||||||
|                             "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data ->> 'Id'))", |                             "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data ->> 'Id'))", | ||||||
|                             "CREATE INDEX for key statement without schema not constructed correctly"); |                             "CREATE INDEX for key statement without schema not constructed correctly"); | ||||||
|                     }) |                     }) | ||||||
|                 }), |                 ]), | ||||||
|                 TestCase("EnsureIndexOn succeeds for multiple fields and directions", () => |                 TestCase("EnsureIndexOn succeeds for multiple fields and directions", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal( |                     Expect.equal( | ||||||
| @ -213,7 +271,7 @@ public static class CommonCSharpTests | |||||||
|                         + "((data ->> 'taco'), (data ->> 'guac') DESC, (data ->> 'salsa') ASC)", |                         + "((data ->> 'taco'), (data ->> 'guac') DESC, (data ->> 'salsa') ASC)", | ||||||
|                         "CREATE INDEX for multiple field statement incorrect"); |                         "CREATE INDEX for multiple field statement incorrect"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestCase("Insert succeeds", () => |             TestCase("Insert succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.equal(Query.Insert("tbl"), "INSERT INTO tbl VALUES (@data)", "INSERT statement not correct"); |                 Expect.equal(Query.Insert("tbl"), "INSERT INTO tbl VALUES (@data)", "INSERT statement not correct"); | ||||||
| @ -224,6 +282,6 @@ public static class CommonCSharpTests | |||||||
|                     "INSERT INTO tbl VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data", |                     "INSERT INTO tbl VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data", | ||||||
|                     "INSERT ON CONFLICT UPDATE statement not correct"); |                     "INSERT ON CONFLICT UPDATE statement not correct"); | ||||||
|             }) |             }) | ||||||
|         }) |         ]) | ||||||
|     }); |     ]); | ||||||
| } | } | ||||||
|  | |||||||
| @ -16,10 +16,10 @@ public static class PostgresCSharpTests | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Tests which do not hit the database |     /// Tests which do not hit the database | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly Test Unit = TestList("Unit", new[] |     private static readonly Test Unit = TestList("Unit", | ||||||
|     { |     [ | ||||||
|         TestList("Parameters", new[] |         TestList("Parameters", | ||||||
|         { |         [ | ||||||
|             TestCase("Id succeeds", () => |             TestCase("Id succeeds", () => | ||||||
|             { |             { | ||||||
|                 var it = Parameters.Id(88); |                 var it = Parameters.Id(88); | ||||||
| @ -32,38 +32,86 @@ public static class PostgresCSharpTests | |||||||
|                 Expect.equal(it.Item1, "@test", "JSON parameter not constructed correctly"); |                 Expect.equal(it.Item1, "@test", "JSON parameter not constructed correctly"); | ||||||
|                 Expect.equal(it.Item2, Sql.jsonb("{\"Something\":\"good\"}"), "JSON parameter value incorrect"); |                 Expect.equal(it.Item2, Sql.jsonb("{\"Something\":\"good\"}"), "JSON parameter value incorrect"); | ||||||
|             }), |             }), | ||||||
|             TestList("AddField", new [] |             TestList("AddField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a parameter is added", () => |                 TestCase("succeeds when a parameter is added", () => | ||||||
|                 { |                 { | ||||||
|                     var it = Parameters |                     var it = Parameters.AddField("@field", Field.EQ("it", "242"), []).ToList(); | ||||||
|                         .AddField("@field", Field.EQ("it", "242"), Enumerable.Empty<Tuple<string, SqlValue>>()) |  | ||||||
|                         .ToList(); |  | ||||||
|                     Expect.hasLength(it, 1, "There should have been a parameter added"); |                     Expect.hasLength(it, 1, "There should have been a parameter added"); | ||||||
|                     Expect.equal(it[0].Item1, "@field", "Field parameter not constructed correctly"); |                     Expect.equal(it[0].Item1, "@field", "Field parameter not constructed correctly"); | ||||||
|                     Expect.isTrue(it[0].Item2.IsParameter, "Field parameter value incorrect"); |                     Expect.isTrue(it[0].Item2.IsParameter, "Field parameter value incorrect"); | ||||||
|                 }), |                 }), | ||||||
|                 TestCase("succeeds when a parameter is not added", () => |                 TestCase("succeeds when a parameter is not added", () => | ||||||
|                 { |                 { | ||||||
|                     var it = Parameters.AddField("@it", Field.EX("It"), Enumerable.Empty<Tuple<string, SqlValue>>()); |                     var it = Parameters.AddField("@it", Field.EX("It"), []); | ||||||
|                     Expect.isEmpty(it, "There should not have been any parameters added"); |                     Expect.isEmpty(it, "There should not have been any parameters added"); | ||||||
|                 }), |                 }), | ||||||
|                 TestCase("succeeds when two parameters are added", () => |                 TestCase("succeeds when two parameters are added", () => | ||||||
|                 { |                 { | ||||||
|                     var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"), |                     var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"), []).ToList(); | ||||||
|                         Enumerable.Empty<Tuple<string, SqlValue>>()).ToList(); |  | ||||||
|                     Expect.hasLength(it, 2, "There should have been 2 parameters added"); |                     Expect.hasLength(it, 2, "There should have been 2 parameters added"); | ||||||
|                     Expect.equal(it[0].Item1, "@fieldmin", "Minimum field name not correct"); |                     Expect.equal(it[0].Item1, "@fieldmin", "Minimum field name not correct"); | ||||||
|                     Expect.isTrue(it[0].Item2.IsParameter, "Minimum field parameter value incorrect"); |                     Expect.isTrue(it[0].Item2.IsParameter, "Minimum field parameter value incorrect"); | ||||||
|                     Expect.equal(it[1].Item1, "@fieldmax", "Maximum field name not correct"); |                     Expect.equal(it[1].Item1, "@fieldmax", "Maximum field name not correct"); | ||||||
|                     Expect.isTrue(it[1].Item2.IsParameter, "Maximum field parameter value incorrect"); |                     Expect.isTrue(it[1].Item2.IsParameter, "Maximum field parameter value incorrect"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Query", new[] |         TestList("Query", | ||||||
|         { |         [ | ||||||
|             TestList("WhereByField", new[] |             TestList("WhereByFields", | ||||||
|             { |             [ | ||||||
|  |                 TestCase("succeeds for a single field when a logical operator is passed", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Postgres.Query.WhereByFields([Field.GT("theField", 0).WithParameterName("@test")], | ||||||
|  |                             FieldMatch.Any), | ||||||
|  |                         "data->>'theField' > @test", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field when an existence operator is passed", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal(Postgres.Query.WhereByFields([Field.NEX("thatField")], FieldMatch.Any), | ||||||
|  |                         "data->>'thatField' IS NULL", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field when a between operator is passed with numeric values", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Postgres.Query.WhereByFields([Field.BT("aField", 50, 99).WithParameterName("@range")], | ||||||
|  |                             FieldMatch.All), | ||||||
|  |                         "(data->>'aField')::numeric BETWEEN @rangemin AND @rangemax", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field when a between operator is passed with non-numeric values", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Postgres.Query.WhereByFields([Field.BT("field0", "a", "b").WithParameterName("@alpha")], | ||||||
|  |                             FieldMatch.Any), | ||||||
|  |                         "data->>'field0' BETWEEN @alphamin AND @alphamax", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for all multiple fields with logical operators", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Postgres.Query.WhereByFields([Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")], | ||||||
|  |                             FieldMatch.All), | ||||||
|  |                         "data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for any multiple fields with an existence operator", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Postgres.Query.WhereByFields([Field.NEX("thatField"), Field.GE("thisField", 18)], | ||||||
|  |                             FieldMatch.Any), | ||||||
|  |                         "data->>'thatField' IS NULL OR data->>'thisField' >= @field0", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for all multiple fields with between operators", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Postgres.Query.WhereByFields([Field.BT("aField", 50, 99), Field.BT("anotherField", "a", "b")], | ||||||
|  |                             FieldMatch.All), | ||||||
|  |                         "(data->>'aField')::numeric BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max", | ||||||
|  |                         "WHERE clause not correct"); | ||||||
|  |                 }) | ||||||
|  |             ]), | ||||||
|  |             TestList("WhereByField", | ||||||
|  |             [ | ||||||
|                 TestCase("succeeds when a logical operator is passed", () => |                 TestCase("succeeds when a logical operator is passed", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.WhereByField(Field.GT("theField", 0), "@test"), |                     Expect.equal(Postgres.Query.WhereByField(Field.GT("theField", 0), "@test"), | ||||||
| @ -84,13 +132,13 @@ public static class PostgresCSharpTests | |||||||
|                     Expect.equal(Postgres.Query.WhereByField(Field.BT("field0", "a", "b"), "@alpha"), |                     Expect.equal(Postgres.Query.WhereByField(Field.BT("field0", "a", "b"), "@alpha"), | ||||||
|                         "data->>'field0' BETWEEN @alphamin AND @alphamax", "WHERE clause not correct"); |                         "data->>'field0' BETWEEN @alphamin AND @alphamax", "WHERE clause not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestCase("WhereById succeeds", () => |             TestCase("WhereById succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct"); |                 Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct"); | ||||||
|             }), |             }), | ||||||
|             TestList("Definition", new[] |             TestList("Definition", | ||||||
|             { |             [ | ||||||
|                 TestCase("EnsureTable succeeds", () => |                 TestCase("EnsureTable succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.Definition.EnsureTable(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.Definition.EnsureTable(PostgresDb.TableName), | ||||||
| @ -112,7 +160,7 @@ public static class PostgresCSharpTests | |||||||
|                             PostgresDb.TableName), |                             PostgresDb.TableName), | ||||||
|                         "CREATE INDEX statement not constructed correctly"); |                         "CREATE INDEX statement not constructed correctly"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestCase("Update succeeds", () => |             TestCase("Update succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.equal(Postgres.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id", |                 Expect.equal(Postgres.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id", | ||||||
| @ -128,8 +176,8 @@ public static class PostgresCSharpTests | |||||||
|                 Expect.equal(Postgres.Query.WhereJsonPathMatches("@path"), "data @? @path::jsonpath", |                 Expect.equal(Postgres.Query.WhereJsonPathMatches("@path"), "data @? @path::jsonpath", | ||||||
|                     "WHERE clause not correct"); |                     "WHERE clause not correct"); | ||||||
|             }), |             }), | ||||||
|             TestList("Count", new[] |             TestList("Count", | ||||||
|             { |             [ | ||||||
|                 TestCase("All succeeds", () => |                 TestCase("All succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.Count.All(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.Count.All(PostgresDb.TableName), | ||||||
| @ -153,9 +201,9 @@ public static class PostgresCSharpTests | |||||||
|                         $"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", |                         $"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", | ||||||
|                         "JSON Path match count query not correct"); |                         "JSON Path match count query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Exists", new[] |             TestList("Exists", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.Exists.ById(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.Exists.ById(PostgresDb.TableName), | ||||||
| @ -180,9 +228,9 @@ public static class PostgresCSharpTests | |||||||
|                         $"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath) AS it", |                         $"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath) AS it", | ||||||
|                         "JSON Path match existence query not correct"); |                         "JSON Path match existence query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Find", new[] |             TestList("Find", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.Find.ById(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.Find.ById(PostgresDb.TableName), | ||||||
| @ -207,9 +255,9 @@ public static class PostgresCSharpTests | |||||||
|                         $"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", |                         $"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", | ||||||
|                         "SELECT by JSON Path match query not correct"); |                         "SELECT by JSON Path match query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Patch", new[] |             TestList("Patch", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.Patch.ById(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.Patch.ById(PostgresDb.TableName), | ||||||
| @ -234,9 +282,9 @@ public static class PostgresCSharpTests | |||||||
|                         $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data @? @path::jsonpath", |                         $"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data @? @path::jsonpath", | ||||||
|                         "UPDATE partial by JSON Path statement not correct"); |                         "UPDATE partial by JSON Path statement not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("RemoveFields", new[] |             TestList("RemoveFields", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.RemoveFields.ById(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.RemoveFields.ById(PostgresDb.TableName), | ||||||
| @ -261,9 +309,9 @@ public static class PostgresCSharpTests | |||||||
|                         $"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data @? @path::jsonpath", |                         $"UPDATE {PostgresDb.TableName} SET data = data - @name WHERE data @? @path::jsonpath", | ||||||
|                         "Remove field by JSON path query not correct"); |                         "Remove field by JSON path query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Delete", new[] |             TestList("Delete", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Postgres.Query.Delete.ById(PostgresDb.TableName), |                     Expect.equal(Postgres.Query.Delete.ById(PostgresDb.TableName), | ||||||
| @ -288,18 +336,18 @@ public static class PostgresCSharpTests | |||||||
|                         $"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", |                         $"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", | ||||||
|                         "DELETE by JSON Path match query not correct"); |                         "DELETE by JSON Path match query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }) |         ]) | ||||||
|     }); |     ]); | ||||||
| 
 | 
 | ||||||
|     private static readonly List<JsonDocument> TestDocuments = new() |     private static readonly List<JsonDocument> TestDocuments = | ||||||
|     { |     [ | ||||||
|         new() { Id = "one", Value = "FIRST!", NumValue = 0 }, |         new() { Id = "one", Value = "FIRST!", NumValue = 0 }, | ||||||
|         new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, |         new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, | ||||||
|         new() { Id = "three", Value = "", NumValue = 4 }, |         new() { Id = "three", Value = "", NumValue = 4 }, | ||||||
|         new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, |         new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, | ||||||
|         new() { Id = "five", Value = "purple", NumValue = 18 } |         new() { Id = "five", Value = "purple", NumValue = 18 } | ||||||
|     }; |     ]; | ||||||
| 
 | 
 | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Add the test documents to the database |     /// Add the test documents to the database | ||||||
| @ -312,10 +360,10 @@ public static class PostgresCSharpTests | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Integration tests for the PostgreSQL library |     /// Integration tests for the PostgreSQL library | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly Test Integration = TestList("Integration", new[] |     private static readonly Test Integration = TestList("Integration", | ||||||
|     { |     [ | ||||||
|         TestList("Configuration", new[] |         TestList("Configuration", | ||||||
|         { |         [ | ||||||
|             TestCase("UseDataSource disposes existing source", () => |             TestCase("UseDataSource disposes existing source", () => | ||||||
|             { |             { | ||||||
|                 using var db1 = ThrowawayDatabase.Create(PostgresDb.ConnStr.Value); |                 using var db1 = ThrowawayDatabase.Create(PostgresDb.ConnStr.Value); | ||||||
| @ -343,11 +391,11 @@ public static class PostgresCSharpTests | |||||||
|                 Expect.isTrue(ReferenceEquals(source, Postgres.Configuration.DataSource()), |                 Expect.isTrue(ReferenceEquals(source, Postgres.Configuration.DataSource()), | ||||||
|                     "Data source should have been the same"); |                     "Data source should have been the same"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Custom", new[] |         TestList("Custom", | ||||||
|         { |         [ | ||||||
|             TestList("List", new[] |             TestList("List", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when data is found", async () => |                 TestCase("succeeds when data is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -368,9 +416,9 @@ public static class PostgresCSharpTests | |||||||
|                         Results.FromData<JsonDocument>); |                         Results.FromData<JsonDocument>); | ||||||
|                     Expect.isEmpty(docs, "There should have been no documents returned"); |                     Expect.isEmpty(docs, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Single", new[] |             TestList("Single", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a row is found", async () => |                 TestCase("succeeds when a row is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -390,9 +438,9 @@ public static class PostgresCSharpTests | |||||||
|                         new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData<JsonDocument>); |                         new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData<JsonDocument>); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("NonQuery", new[] |             TestList("NonQuery", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when operating on data", async () => |                 TestCase("succeeds when operating on data", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -414,7 +462,7 @@ public static class PostgresCSharpTests | |||||||
|                     var remaining = await Count.All(PostgresDb.TableName); |                     var remaining = await Count.All(PostgresDb.TableName); | ||||||
|                     Expect.equal(remaining, 5, "There should be 5 documents remaining in the table"); |                     Expect.equal(remaining, 5, "There should be 5 documents remaining in the table"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestCase("Scalar succeeds", async () => |             TestCase("Scalar succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = PostgresDb.BuildDb(); |                 await using var db = PostgresDb.BuildDb(); | ||||||
| @ -422,65 +470,71 @@ public static class PostgresCSharpTests | |||||||
|                 var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, row => row.@int("test_value")); |                 var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, row => row.@int("test_value")); | ||||||
|                 Expect.equal(nbr, 5, "The query should have returned the number 5"); |                 Expect.equal(nbr, 5, "The query should have returned the number 5"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Definition", new[] |         TestList("Definition", | ||||||
|         { |         [ | ||||||
|             TestCase("EnsureTable succeeds", async () => |             TestCase("EnsureTable succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = PostgresDb.BuildDb(); |                 await using var db = PostgresDb.BuildDb(); | ||||||
|                 var tableExists = () => Custom.Scalar( |  | ||||||
|                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None, |  | ||||||
|                     Results.ToExists); |  | ||||||
|                 var keyExists = () => Custom.Scalar( |  | ||||||
|                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None, |  | ||||||
|                     Results.ToExists); |  | ||||||
| 
 | 
 | ||||||
|                 var exists = await tableExists(); |                 var exists = await TableExists(); | ||||||
|                 var alsoExists = await keyExists(); |                 var alsoExists = await KeyExists(); | ||||||
|                 Expect.isFalse(exists, "The table should not exist already"); |                 Expect.isFalse(exists, "The table should not exist already"); | ||||||
|                 Expect.isFalse(alsoExists, "The key index should not exist already"); |                 Expect.isFalse(alsoExists, "The key index should not exist already"); | ||||||
| 
 | 
 | ||||||
|                 await Definition.EnsureTable("ensured"); |                 await Definition.EnsureTable("ensured"); | ||||||
|                 exists = await tableExists(); |                 exists = await TableExists(); | ||||||
|                 alsoExists = await keyExists(); |                 alsoExists = await KeyExists(); | ||||||
|                 Expect.isTrue(exists, "The table should now exist"); |                 Expect.isTrue(exists, "The table should now exist"); | ||||||
|                 Expect.isTrue(alsoExists, "The key index should now exist"); |                 Expect.isTrue(alsoExists, "The key index should now exist"); | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |                 Task<bool> TableExists() => Custom.Scalar( | ||||||
|  |                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None, | ||||||
|  |                     Results.ToExists); | ||||||
|  |                 Task<bool> KeyExists() => Custom.Scalar( | ||||||
|  |                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None, | ||||||
|  |                     Results.ToExists); | ||||||
|             }), |             }), | ||||||
|             TestCase("EnsureDocumentIndex succeeds", async () => |             TestCase("EnsureDocumentIndex succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = PostgresDb.BuildDb(); |                 await using var db = PostgresDb.BuildDb(); | ||||||
|                 var indexExists = () => Custom.Scalar( |  | ||||||
|                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it", |  | ||||||
|                     Parameters.None, Results.ToExists); |  | ||||||
| 
 | 
 | ||||||
|                 var exists = await indexExists(); |                 var exists = await IndexExists(); | ||||||
|                 Expect.isFalse(exists, "The index should not exist already"); |                 Expect.isFalse(exists, "The index should not exist already"); | ||||||
| 
 | 
 | ||||||
|                 await Definition.EnsureTable("ensured"); |                 await Definition.EnsureTable("ensured"); | ||||||
|                 await Definition.EnsureDocumentIndex("ensured", DocumentIndex.Optimized); |                 await Definition.EnsureDocumentIndex("ensured", DocumentIndex.Optimized); | ||||||
|                 exists = await indexExists(); |                 exists = await IndexExists(); | ||||||
|                 Expect.isTrue(exists, "The index should now exist"); |                 Expect.isTrue(exists, "The index should now exist"); | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |                 Task<bool> IndexExists() => Custom.Scalar( | ||||||
|  |                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it", | ||||||
|  |                     Parameters.None, Results.ToExists); | ||||||
|             }), |             }), | ||||||
|             TestCase("EnsureFieldIndex succeeds", async () => |             TestCase("EnsureFieldIndex succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = PostgresDb.BuildDb(); |                 await using var db = PostgresDb.BuildDb(); | ||||||
|                 var indexExists = () => Custom.Scalar( |  | ||||||
|                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None, |  | ||||||
|                     Results.ToExists); |  | ||||||
| 
 | 
 | ||||||
|                 var exists = await indexExists(); |                 var exists = await IndexExists(); | ||||||
|                 Expect.isFalse(exists, "The index should not exist already"); |                 Expect.isFalse(exists, "The index should not exist already"); | ||||||
| 
 | 
 | ||||||
|                 await Definition.EnsureTable("ensured"); |                 await Definition.EnsureTable("ensured"); | ||||||
|                 await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); |                 await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); | ||||||
|                 exists = await indexExists(); |                 exists = await IndexExists(); | ||||||
|                 Expect.isTrue(exists, "The index should now exist"); |                 Expect.isTrue(exists, "The index should now exist"); | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |                 Task<bool> IndexExists() => Custom.Scalar( | ||||||
|  |                     "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None, | ||||||
|  |                     Results.ToExists); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Document", new[] |         TestList("Document", | ||||||
|         { |         [ | ||||||
|             TestList("Insert", new[] |             TestList("Insert", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds", async () => |                 TestCase("succeeds", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -506,9 +560,9 @@ public static class PostgresCSharpTests | |||||||
|                         // This is what should have happened |                         // This is what should have happened | ||||||
|                     } |                     } | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Save", new[] |             TestList("Save", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is inserted", async () => |                 TestCase("succeeds when a document is inserted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -537,10 +591,10 @@ public static class PostgresCSharpTests | |||||||
|                     Expect.equal(after.Id, "test", "The document is not correct"); |                     Expect.equal(after.Id, "test", "The document is not correct"); | ||||||
|                     Expect.equal(after.Sub!.Foo, "c", "The updated document is not correct"); |                     Expect.equal(after.Sub!.Foo, "c", "The updated document is not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Count", new[] |         TestList("Count", | ||||||
|         { |         [ | ||||||
|             TestCase("All succeeds", async () => |             TestCase("All succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = PostgresDb.BuildDb(); |                 await using var db = PostgresDb.BuildDb(); | ||||||
| @ -581,11 +635,11 @@ public static class PostgresCSharpTests | |||||||
|                 var theCount = await Count.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)"); |                 var theCount = await Count.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)"); | ||||||
|                 Expect.equal(theCount, 3, "There should have been 3 matching documents"); |                 Expect.equal(theCount, 3, "There should have been 3 matching documents"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Exists", new[] |         TestList("Exists", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document exists", async () => |                 TestCase("succeeds when a document exists", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -602,9 +656,9 @@ public static class PostgresCSharpTests | |||||||
|                     var exists = await Exists.ById(PostgresDb.TableName, "seven"); |                     var exists = await Exists.ById(PostgresDb.TableName, "seven"); | ||||||
|                     Expect.isFalse(exists, "There should not have been an existing document"); |                     Expect.isFalse(exists, "There should not have been an existing document"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents exist", async () => |                 TestCase("succeeds when documents exist", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -621,9 +675,9 @@ public static class PostgresCSharpTests | |||||||
|                     var exists = await Exists.ByField(PostgresDb.TableName, Field.EQ("NumValue", "six")); |                     var exists = await Exists.ByField(PostgresDb.TableName, Field.EQ("NumValue", "six")); | ||||||
|                     Expect.isFalse(exists, "There should not have been existing documents"); |                     Expect.isFalse(exists, "There should not have been existing documents"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByContains", new[] |             TestList("ByContains", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents exist", async () => |                 TestCase("succeeds when documents exist", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -640,8 +694,9 @@ public static class PostgresCSharpTests | |||||||
|                     var exists = await Exists.ByContains(PostgresDb.TableName, new { Nothing = "none" }); |                     var exists = await Exists.ByContains(PostgresDb.TableName, new { Nothing = "none" }); | ||||||
|                     Expect.isFalse(exists, "There should not have been any existing documents"); |                     Expect.isFalse(exists, "There should not have been any existing documents"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByJsonPath", new[] { |             TestList("ByJsonPath", | ||||||
|  |             [ | ||||||
|                 TestCase("succeeds when documents exist", async () => |                 TestCase("succeeds when documents exist", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -658,12 +713,12 @@ public static class PostgresCSharpTests | |||||||
|                     var exists = await Exists.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 1000)"); |                     var exists = await Exists.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 1000)"); | ||||||
|                     Expect.isFalse(exists, "There should not have been any existing documents"); |                     Expect.isFalse(exists, "There should not have been any existing documents"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Find", new[] |         TestList("Find", | ||||||
|         { |         [ | ||||||
|             TestList("All", new[] |             TestList("All", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when there is data", async () => |                 TestCase("succeeds when there is data", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -681,9 +736,9 @@ public static class PostgresCSharpTests | |||||||
|                     var results = await Find.All<SubDocument>(PostgresDb.TableName); |                     var results = await Find.All<SubDocument>(PostgresDb.TableName); | ||||||
|                     Expect.isEmpty(results, "There should have been no documents returned"); |                     Expect.isEmpty(results, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is found", async () => |                 TestCase("succeeds when a document is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -701,9 +756,9 @@ public static class PostgresCSharpTests | |||||||
|                     var doc = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "three hundred eighty-seven"); |                     var doc = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "three hundred eighty-seven"); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are found", async () => |                 TestCase("succeeds when documents are found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -720,9 +775,9 @@ public static class PostgresCSharpTests | |||||||
|                     var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve")); |                     var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve")); | ||||||
|                     Expect.isEmpty(docs, "There should have been no documents returned"); |                     Expect.isEmpty(docs, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByContains", new[] |             TestList("ByContains", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are found", async () => |                 TestCase("succeeds when documents are found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -740,9 +795,9 @@ public static class PostgresCSharpTests | |||||||
|                     var docs = await Find.ByContains<JsonDocument>(PostgresDb.TableName, new { Value = "mauve" }); |                     var docs = await Find.ByContains<JsonDocument>(PostgresDb.TableName, new { Value = "mauve" }); | ||||||
|                     Expect.isEmpty(docs, "There should have been no documents returned"); |                     Expect.isEmpty(docs, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByJsonPath", new[] |             TestList("ByJsonPath", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are found", async () => |                 TestCase("succeeds when documents are found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -759,9 +814,9 @@ public static class PostgresCSharpTests | |||||||
|                     var docs = await Find.ByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 0)"); |                     var docs = await Find.ByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 0)"); | ||||||
|                     Expect.isEmpty(docs, "There should have been no documents returned"); |                     Expect.isEmpty(docs, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("FirstByField", new[] |             TestList("FirstByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is found", async () => |                 TestCase("succeeds when a document is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -788,9 +843,9 @@ public static class PostgresCSharpTests | |||||||
|                     var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent")); |                     var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent")); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("FirstByContains", new[] |             TestList("FirstByContains", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is found", async () => |                 TestCase("succeeds when a document is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -818,9 +873,9 @@ public static class PostgresCSharpTests | |||||||
|                     var doc = await Find.FirstByContains<JsonDocument>(PostgresDb.TableName, new { Value = "absent" }); |                     var doc = await Find.FirstByContains<JsonDocument>(PostgresDb.TableName, new { Value = "absent" }); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("FirstByJsonPath", new[] |             TestList("FirstByJsonPath", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is found", async () => |                 TestCase("succeeds when a document is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -849,12 +904,12 @@ public static class PostgresCSharpTests | |||||||
|                     var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")"); |                     var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")"); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Update", new[] |         TestList("Update", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -882,9 +937,9 @@ public static class PostgresCSharpTests | |||||||
|                     await Update.ById(PostgresDb.TableName, "test", |                     await Update.ById(PostgresDb.TableName, "test", | ||||||
|                         new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } }); |                         new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByFunc", new[] |             TestList("ByFunc", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -910,12 +965,12 @@ public static class PostgresCSharpTests | |||||||
|                     await Update.ByFunc(PostgresDb.TableName, doc => doc.Id, |                     await Update.ByFunc(PostgresDb.TableName, doc => doc.Id, | ||||||
|                         new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); |                         new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Patch", new[] |         TestList("Patch", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -936,9 +991,9 @@ public static class PostgresCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await Patch.ById(PostgresDb.TableName, "test", new { Foo = "green" }); |                     await Patch.ById(PostgresDb.TableName, "test", new { Foo = "green" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -958,9 +1013,9 @@ public static class PostgresCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); |                     await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByContains", new[] |             TestList("ByContains", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -980,9 +1035,9 @@ public static class PostgresCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await Patch.ByContains(PostgresDb.TableName, new { Value = "burgundy" }, new { Foo = "green" }); |                     await Patch.ByContains(PostgresDb.TableName, new { Value = "burgundy" }, new { Foo = "green" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByJsonPath", new[] |             TestList("ByJsonPath", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1002,12 +1057,12 @@ public static class PostgresCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await Patch.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" }); |                     await Patch.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" }); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("RemoveFields", new[] |         TestList("RemoveFields", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when multiple fields are removed", async () => |                 TestCase("succeeds when multiple fields are removed", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1045,9 +1100,9 @@ public static class PostgresCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Value" }); |                     await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Value" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when multiple fields are removed", async () => |                 TestCase("succeeds when multiple fields are removed", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1087,9 +1142,9 @@ public static class PostgresCSharpTests | |||||||
|                     await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), |                     await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), | ||||||
|                         new[] { "Value" }); |                         new[] { "Value" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByContains", new[] |             TestList("ByContains", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when multiple fields are removed", async () => |                 TestCase("succeeds when multiple fields are removed", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1129,9 +1184,9 @@ public static class PostgresCSharpTests | |||||||
|                     await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, |                     await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, | ||||||
|                         new[] { "Value" }); |                         new[] { "Value" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByJsonPath", new[] |             TestList("ByJsonPath", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when multiple fields are removed", async () => |                 TestCase("succeeds when multiple fields are removed", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1171,12 +1226,12 @@ public static class PostgresCSharpTests | |||||||
|                     await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", |                     await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", | ||||||
|                         new[] { "Value" }); |                         new[] { "Value" }); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Delete", new[] |         TestList("Delete", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is deleted", async () => |                 TestCase("succeeds when a document is deleted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1195,9 +1250,9 @@ public static class PostgresCSharpTests | |||||||
|                     var remaining = await Count.All(PostgresDb.TableName); |                     var remaining = await Count.All(PostgresDb.TableName); | ||||||
|                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); |                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are deleted", async () => |                 TestCase("succeeds when documents are deleted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1216,9 +1271,9 @@ public static class PostgresCSharpTests | |||||||
|                     var remaining = await Count.All(PostgresDb.TableName); |                     var remaining = await Count.All(PostgresDb.TableName); | ||||||
|                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); |                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByContains", new[] |             TestList("ByContains", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are deleted", async () => |                 TestCase("succeeds when documents are deleted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1237,9 +1292,9 @@ public static class PostgresCSharpTests | |||||||
|                     var remaining = await Count.All(PostgresDb.TableName); |                     var remaining = await Count.All(PostgresDb.TableName); | ||||||
|                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); |                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByJsonPath", new[] |             TestList("ByJsonPath", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are deleted", async () => |                 TestCase("succeeds when documents are deleted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = PostgresDb.BuildDb(); |                     await using var db = PostgresDb.BuildDb(); | ||||||
| @ -1258,13 +1313,13 @@ public static class PostgresCSharpTests | |||||||
|                     var remaining = await Count.All(PostgresDb.TableName); |                     var remaining = await Count.All(PostgresDb.TableName); | ||||||
|                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); |                     Expect.equal(remaining, 5, "There should have been 5 documents remaining"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }) |         ]) | ||||||
|     }); |     ]); | ||||||
|      |      | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// All Postgres C# tests |     /// All Postgres C# tests | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     [Tests] |     [Tests] | ||||||
|     public static readonly Test All = TestList("Postgres.C#", new[] { Unit, TestSequenced(Integration) }); |     public static readonly Test All = TestList("Postgres.C#", [Unit, TestSequenced(Integration)]); | ||||||
| } | } | ||||||
|  | |||||||
| @ -1,5 +1,4 @@ | |||||||
| using System.Text.Json; | using Expecto.CSharp; | ||||||
| using Expecto.CSharp; |  | ||||||
| using Expecto; | using Expecto; | ||||||
| using Microsoft.Data.Sqlite; | using Microsoft.Data.Sqlite; | ||||||
| using Microsoft.FSharp.Core; | using Microsoft.FSharp.Core; | ||||||
| @ -17,12 +16,56 @@ public static class SqliteCSharpTests | |||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Unit tests for the SQLite library |     /// Unit tests for the SQLite library | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     private static readonly Test Unit = TestList("Unit", new[] |     private static readonly Test Unit = TestList("Unit", | ||||||
|     { |     [ | ||||||
|         TestList("Query", new[] |         TestList("Query", | ||||||
|         { |         [ | ||||||
|             TestList("WhereByField", new[] |             TestList("WhereByFields", | ||||||
|             { |             [ | ||||||
|  |                 TestCase("succeeds for a single field when a logical operator is passed", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Sqlite.Query.WhereByFields([Field.GT("theField", 0).WithParameterName("@test")], | ||||||
|  |                             FieldMatch.Any), | ||||||
|  |                         "data->>'theField' > @test", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field when an existence operator is passed", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal(Sqlite.Query.WhereByFields([Field.NEX("thatField")], FieldMatch.Any), | ||||||
|  |                         "data->>'thatField' IS NULL", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for a single field when a between operator is passed", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Sqlite.Query.WhereByFields([Field.BT("aField", 50, 99).WithParameterName("@range")], | ||||||
|  |                             FieldMatch.All), | ||||||
|  |                         "data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for all multiple fields with logical operators", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Sqlite.Query.WhereByFields([Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")], | ||||||
|  |                             FieldMatch.All), | ||||||
|  |                         "data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for any multiple fields with an existence operator", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Sqlite.Query.WhereByFields([Field.NEX("thatField"), Field.GE("thisField", 18)], | ||||||
|  |                             FieldMatch.Any), | ||||||
|  |                         "data->>'thatField' IS NULL OR data->>'thisField' >= @field0", "WHERE clause not correct"); | ||||||
|  |                 }), | ||||||
|  |                 TestCase("succeeds for all multiple fields with between operators", () => | ||||||
|  |                 { | ||||||
|  |                     Expect.equal( | ||||||
|  |                         Sqlite.Query.WhereByFields([Field.BT("aField", 50, 99), Field.BT("anotherField", "a", "b")], | ||||||
|  |                             FieldMatch.All), | ||||||
|  |                         "data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max", | ||||||
|  |                         "WHERE clause not correct"); | ||||||
|  |                 }) | ||||||
|  |             ]), | ||||||
|  |             TestList("WhereByField", | ||||||
|  |             [ | ||||||
|                 TestCase("succeeds when a logical operator is passed", () => |                 TestCase("succeeds when a logical operator is passed", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.WhereByField(Field.GT("theField", 0), "@test"), |                     Expect.equal(Sqlite.Query.WhereByField(Field.GT("theField", 0), "@test"), | ||||||
| @ -38,7 +81,7 @@ public static class SqliteCSharpTests | |||||||
|                     Expect.equal(Sqlite.Query.WhereByField(Field.BT("aField", 50, 99), "@range"), |                     Expect.equal(Sqlite.Query.WhereByField(Field.BT("aField", 50, 99), "@range"), | ||||||
|                         "data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct"); |                         "data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestCase("WhereById succeeds", () => |             TestCase("WhereById succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct"); |                 Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct"); | ||||||
| @ -53,8 +96,8 @@ public static class SqliteCSharpTests | |||||||
|                 Expect.equal(Sqlite.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id", |                 Expect.equal(Sqlite.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id", | ||||||
|                     "UPDATE full statement not correct"); |                     "UPDATE full statement not correct"); | ||||||
|             }), |             }), | ||||||
|             TestList("Count", new[] |             TestList("Count", | ||||||
|             { |             [ | ||||||
|                 TestCase("All succeeds", () => |                 TestCase("All succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl", |                     Expect.equal(Sqlite.Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl", | ||||||
| @ -66,9 +109,9 @@ public static class SqliteCSharpTests | |||||||
|                         "SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field", |                         "SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field", | ||||||
|                         "JSON field text comparison count query not correct"); |                         "JSON field text comparison count query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Exists", new[] |             TestList("Exists", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.Exists.ById("tbl"), |                     Expect.equal(Sqlite.Query.Exists.ById("tbl"), | ||||||
| @ -81,9 +124,9 @@ public static class SqliteCSharpTests | |||||||
|                         "SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field) AS it", |                         "SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field) AS it", | ||||||
|                         "JSON field text comparison exists query not correct"); |                         "JSON field text comparison exists query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Find", new[] |             TestList("Find", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data->>'Id' = @id", |                     Expect.equal(Sqlite.Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data->>'Id' = @id", | ||||||
| @ -95,9 +138,9 @@ public static class SqliteCSharpTests | |||||||
|                         "SELECT data FROM tbl WHERE data->>'Golf' >= @field", |                         "SELECT data FROM tbl WHERE data->>'Golf' >= @field", | ||||||
|                         "SELECT by JSON comparison query not correct"); |                         "SELECT by JSON comparison query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Patch", new[] |             TestList("Patch", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.Patch.ById("tbl"), |                     Expect.equal(Sqlite.Query.Patch.ById("tbl"), | ||||||
| @ -110,9 +153,9 @@ public static class SqliteCSharpTests | |||||||
|                         "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field", |                         "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field", | ||||||
|                         "UPDATE partial by JSON comparison query not correct"); |                         "UPDATE partial by JSON comparison query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("RemoveFields", new[] |             TestList("RemoveFields", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", new[] { new SqliteParameter("@name", "one") }), |                     Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", new[] { new SqliteParameter("@name", "one") }), | ||||||
| @ -126,9 +169,9 @@ public static class SqliteCSharpTests | |||||||
|                         "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' < @field", |                         "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' < @field", | ||||||
|                         "Remove field by field query not correct"); |                         "Remove field by field query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("Delete", new[] |             TestList("Delete", | ||||||
|             { |             [ | ||||||
|                 TestCase("ById succeeds", () => |                 TestCase("ById succeeds", () => | ||||||
|                 { |                 { | ||||||
|                     Expect.equal(Sqlite.Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data->>'Id' = @id", |                     Expect.equal(Sqlite.Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data->>'Id' = @id", | ||||||
| @ -139,10 +182,10 @@ public static class SqliteCSharpTests | |||||||
|                     Expect.equal(Sqlite.Query.Delete.ByField("tbl", Field.NEX("gone")), |                     Expect.equal(Sqlite.Query.Delete.ByField("tbl", Field.NEX("gone")), | ||||||
|                         "DELETE FROM tbl WHERE data->>'gone' IS NULL", "DELETE by JSON comparison query not correct"); |                         "DELETE FROM tbl WHERE data->>'gone' IS NULL", "DELETE by JSON comparison query not correct"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Parameters", new[] |         TestList("Parameters", | ||||||
|         { |         [ | ||||||
|             TestCase("Id succeeds", () => |             TestCase("Id succeeds", () => | ||||||
|             { |             { | ||||||
|                 var theParam = Parameters.Id(7); |                 var theParam = Parameters.Id(7); | ||||||
| @ -157,8 +200,7 @@ public static class SqliteCSharpTests | |||||||
|             }), |             }), | ||||||
|             TestCase("AddField succeeds when adding a parameter", () => |             TestCase("AddField succeeds when adding a parameter", () => | ||||||
|             { |             { | ||||||
|                 var paramList = Parameters.AddField("@field", Field.EQ("it", 99), Enumerable.Empty<SqliteParameter>()) |                 var paramList = Parameters.AddField("@field", Field.EQ("it", 99), []).ToList(); | ||||||
|                     .ToList(); |  | ||||||
|                 Expect.hasLength(paramList, 1, "There should have been a parameter added"); |                 Expect.hasLength(paramList, 1, "There should have been a parameter added"); | ||||||
|                 var theParam = paramList[0]; |                 var theParam = paramList[0]; | ||||||
|                 Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect"); |                 Expect.equal(theParam.ParameterName, "@field", "The parameter name is incorrect"); | ||||||
| @ -166,25 +208,25 @@ public static class SqliteCSharpTests | |||||||
|             }), |             }), | ||||||
|             TestCase("AddField succeeds when not adding a parameter", () => |             TestCase("AddField succeeds when not adding a parameter", () => | ||||||
|             { |             { | ||||||
|                 var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), Enumerable.Empty<SqliteParameter>()); |                 var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), []); | ||||||
|                 Expect.isEmpty(paramSeq, "There should not have been any parameters added"); |                 Expect.isEmpty(paramSeq, "There should not have been any parameters added"); | ||||||
|             }), |             }), | ||||||
|             TestCase("None succeeds", () => |             TestCase("None succeeds", () => | ||||||
|             { |             { | ||||||
|                 Expect.isEmpty(Parameters.None, "The parameter list should have been empty"); |                 Expect.isEmpty(Parameters.None, "The parameter list should have been empty"); | ||||||
|             }) |             }) | ||||||
|         }) |         ]) | ||||||
|         // Results are exhaustively executed in the context of other tests |         // Results are exhaustively executed in the context of other tests | ||||||
|     }); |     ]); | ||||||
| 
 | 
 | ||||||
|     private static readonly List<JsonDocument> TestDocuments = new() |     private static readonly List<JsonDocument> TestDocuments = | ||||||
|     { |     [ | ||||||
|         new() { Id = "one", Value = "FIRST!", NumValue = 0 }, |         new() { Id = "one", Value = "FIRST!", NumValue = 0 }, | ||||||
|         new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, |         new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, | ||||||
|         new() { Id = "three", Value = "", NumValue = 4 }, |         new() { Id = "three", Value = "", NumValue = 4 }, | ||||||
|         new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, |         new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, | ||||||
|         new() { Id = "five", Value = "purple", NumValue = 18 } |         new() { Id = "five", Value = "purple", NumValue = 18 } | ||||||
|     }; |     ]; | ||||||
| 
 | 
 | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// Add the test documents to the database |     /// Add the test documents to the database | ||||||
| @ -194,8 +236,8 @@ public static class SqliteCSharpTests | |||||||
|         foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc); |         foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     private static readonly Test Integration = TestList("Integration", new[] |     private static readonly Test Integration = TestList("Integration", | ||||||
|     { |     [ | ||||||
|         TestCase("Configuration.UseConnectionString succeeds", () => |         TestCase("Configuration.UseConnectionString succeeds", () => | ||||||
|         { |         { | ||||||
|             try |             try | ||||||
| @ -209,10 +251,10 @@ public static class SqliteCSharpTests | |||||||
|                 Sqlite.Configuration.UseConnectionString("Data Source=:memory:"); |                 Sqlite.Configuration.UseConnectionString("Data Source=:memory:"); | ||||||
|             } |             } | ||||||
|         }), |         }), | ||||||
|         TestList("Custom", new[] |         TestList("Custom", | ||||||
|         { |         [ | ||||||
|             TestList("Single", new[] |             TestList("Single", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a row is found", async () => |                 TestCase("succeeds when a row is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -232,9 +274,9 @@ public static class SqliteCSharpTests | |||||||
|                         new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>); |                         new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("List", new[] |             TestList("List", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when data is found", async () => |                 TestCase("succeeds when data is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -254,9 +296,9 @@ public static class SqliteCSharpTests | |||||||
|                         new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>); |                         new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>); | ||||||
|                     Expect.isEmpty(docs, "There should have been no documents returned"); |                     Expect.isEmpty(docs, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("NonQuery", new[] |             TestList("NonQuery", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when operating on data", async () => |                 TestCase("succeeds when operating on data", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -278,7 +320,7 @@ public static class SqliteCSharpTests | |||||||
|                     var remaining = await Count.All(SqliteDb.TableName); |                     var remaining = await Count.All(SqliteDb.TableName); | ||||||
|                     Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table"); |                     Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestCase("Scalar succeeds", async () => |             TestCase("Scalar succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = await SqliteDb.BuildDb(); |                 await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -286,9 +328,9 @@ public static class SqliteCSharpTests | |||||||
|                 var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, rdr => rdr.GetInt32(0)); |                 var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, rdr => rdr.GetInt32(0)); | ||||||
|                 Expect.equal(nbr, 5, "The query should have returned the number 5"); |                 Expect.equal(nbr, 5, "The query should have returned the number 5"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Definition", new[] |         TestList("Definition", | ||||||
|         { |         [ | ||||||
|             TestCase("EnsureTable succeeds", async () => |             TestCase("EnsureTable succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = await SqliteDb.BuildDb(); |                 await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -316,21 +358,23 @@ public static class SqliteCSharpTests | |||||||
|             TestCase("EnsureFieldIndex succeeds", async () => |             TestCase("EnsureFieldIndex succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = await SqliteDb.BuildDb(); |                 await using var db = await SqliteDb.BuildDb(); | ||||||
|                 var indexExists = () => Custom.Scalar( |  | ||||||
|                     $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it", |  | ||||||
|                     Parameters.None, Results.ToExists); |  | ||||||
| 
 | 
 | ||||||
|                 var exists = await indexExists(); |                 var exists = await IndexExists(); | ||||||
|                 Expect.isFalse(exists, "The index should not exist already"); |                 Expect.isFalse(exists, "The index should not exist already"); | ||||||
| 
 | 
 | ||||||
|                 await Definition.EnsureTable("ensured"); |                 await Definition.EnsureTable("ensured"); | ||||||
|                 await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); |                 await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); | ||||||
|                 exists = await indexExists(); |                 exists = await IndexExists(); | ||||||
|                 Expect.isTrue(exists, "The index should now exist"); |                 Expect.isTrue(exists, "The index should now exist"); | ||||||
|  |                 return; | ||||||
|  | 
 | ||||||
|  |                 Task<bool> IndexExists() => Custom.Scalar( | ||||||
|  |                     $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it", | ||||||
|  |                     Parameters.None, Results.ToExists); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Document.Insert", new[] |         TestList("Document.Insert", | ||||||
|         { |         [ | ||||||
|             TestCase("succeeds", async () => |             TestCase("succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = await SqliteDb.BuildDb(); |                 await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -355,9 +399,9 @@ public static class SqliteCSharpTests | |||||||
|                     // This is what is supposed to happen |                     // This is what is supposed to happen | ||||||
|                 } |                 } | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Document.Save", new[] |         TestList("Document.Save", | ||||||
|         { |         [ | ||||||
|             TestCase("succeeds when a document is inserted", async () => |             TestCase("succeeds when a document is inserted", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = await SqliteDb.BuildDb(); |                 await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -388,9 +432,9 @@ public static class SqliteCSharpTests | |||||||
|                 Expect.equal(after!.Id, "test", "The updated document is not correct"); |                 Expect.equal(after!.Id, "test", "The updated document is not correct"); | ||||||
|                 Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document"); |                 Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Count", new[] |         TestList("Count", | ||||||
|         { |         [ | ||||||
|             TestCase("All succeeds", async () => |             TestCase("All succeeds", async () => | ||||||
|             { |             { | ||||||
|                 await using var db = await SqliteDb.BuildDb(); |                 await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -415,11 +459,11 @@ public static class SqliteCSharpTests | |||||||
|                 var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple")); |                 var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple")); | ||||||
|                 Expect.equal(theCount, 1L, "There should have been 1 matching document"); |                 Expect.equal(theCount, 1L, "There should have been 1 matching document"); | ||||||
|             }) |             }) | ||||||
|         }), |         ]), | ||||||
|         TestList("Exists", new[] |         TestList("Exists", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document exists", async () => |                 TestCase("succeeds when a document exists", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -436,9 +480,9 @@ public static class SqliteCSharpTests | |||||||
|                     var exists = await Exists.ById(SqliteDb.TableName, "seven"); |                     var exists = await Exists.ById(SqliteDb.TableName, "seven"); | ||||||
|                     Expect.isFalse(exists, "There should not have been an existing document"); |                     Expect.isFalse(exists, "There should not have been an existing document"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents exist", async () => |                 TestCase("succeeds when documents exist", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -455,12 +499,12 @@ public static class SqliteCSharpTests | |||||||
|                     var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none")); |                     var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none")); | ||||||
|                     Expect.isFalse(exists, "There should not have been any existing documents"); |                     Expect.isFalse(exists, "There should not have been any existing documents"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Find", new[] |         TestList("Find", | ||||||
|         { |         [ | ||||||
|             TestList("All", new[] |             TestList("All", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when there is data", async () => |                 TestCase("succeeds when there is data", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -478,9 +522,9 @@ public static class SqliteCSharpTests | |||||||
|                     var results = await Find.All<SubDocument>(SqliteDb.TableName); |                     var results = await Find.All<SubDocument>(SqliteDb.TableName); | ||||||
|                     Expect.isEmpty(results, "There should have been no documents returned"); |                     Expect.isEmpty(results, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is found", async () => |                 TestCase("succeeds when a document is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -498,9 +542,9 @@ public static class SqliteCSharpTests | |||||||
|                     var doc = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "twenty two"); |                     var doc = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "twenty two"); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are found", async () => |                 TestCase("succeeds when documents are found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -517,9 +561,9 @@ public static class SqliteCSharpTests | |||||||
|                     var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve")); |                     var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve")); | ||||||
|                     Expect.isEmpty(docs, "There should have been no documents returned"); |                     Expect.isEmpty(docs, "There should have been no documents returned"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("FirstByField", new[] |             TestList("FirstByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is found", async () => |                 TestCase("succeeds when a document is found", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -546,12 +590,12 @@ public static class SqliteCSharpTests | |||||||
|                     var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent")); |                     var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent")); | ||||||
|                     Expect.isNull(doc, "There should not have been a document returned"); |                     Expect.isNull(doc, "There should not have been a document returned"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Update", new[] |         TestList("Update", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -577,9 +621,9 @@ public static class SqliteCSharpTests | |||||||
|                     await Update.ById(SqliteDb.TableName, "test", |                     await Update.ById(SqliteDb.TableName, "test", | ||||||
|                         new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } }); |                         new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByFunc", new[] |             TestList("ByFunc", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -605,12 +649,12 @@ public static class SqliteCSharpTests | |||||||
|                     await Update.ByFunc(SqliteDb.TableName, doc => doc.Id, |                     await Update.ByFunc(SqliteDb.TableName, doc => doc.Id, | ||||||
|                         new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); |                         new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|         }), |         ]), | ||||||
|         TestList("Patch", new[] |         TestList("Patch", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -632,9 +676,9 @@ public static class SqliteCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" }); |                     await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is updated", async () => |                 TestCase("succeeds when a document is updated", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -654,12 +698,12 @@ public static class SqliteCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); |                     await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("RemoveFields", new[] |         TestList("RemoveFields", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when fields are removed", async () => |                 TestCase("succeeds when fields are removed", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -686,9 +730,9 @@ public static class SqliteCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" }); |                     await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" }); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a field is removed", async () => |                 TestCase("succeeds when a field is removed", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -714,12 +758,12 @@ public static class SqliteCSharpTests | |||||||
|                     // This not raising an exception is the test |                     // This not raising an exception is the test | ||||||
|                     await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" }); |                     await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" }); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestList("Delete", new[] |         TestList("Delete", | ||||||
|         { |         [ | ||||||
|             TestList("ById", new[] |             TestList("ById", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when a document is deleted", async () => |                 TestCase("succeeds when a document is deleted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -738,9 +782,9 @@ public static class SqliteCSharpTests | |||||||
|                     var remaining = await Count.All(SqliteDb.TableName); |                     var remaining = await Count.All(SqliteDb.TableName); | ||||||
|                     Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); |                     Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); | ||||||
|                 }) |                 }) | ||||||
|             }), |             ]), | ||||||
|             TestList("ByField", new[] |             TestList("ByField", | ||||||
|             { |             [ | ||||||
|                 TestCase("succeeds when documents are deleted", async () => |                 TestCase("succeeds when documents are deleted", async () => | ||||||
|                 { |                 { | ||||||
|                     await using var db = await SqliteDb.BuildDb(); |                     await using var db = await SqliteDb.BuildDb(); | ||||||
| @ -759,14 +803,14 @@ public static class SqliteCSharpTests | |||||||
|                     var remaining = await Count.All(SqliteDb.TableName); |                     var remaining = await Count.All(SqliteDb.TableName); | ||||||
|                     Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); |                     Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); | ||||||
|                 }) |                 }) | ||||||
|             }) |             ]) | ||||||
|         }), |         ]), | ||||||
|         TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:")) |         TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:")) | ||||||
|     }); |     ]); | ||||||
| 
 | 
 | ||||||
|     /// <summary> |     /// <summary> | ||||||
|     /// All tests for SQLite C# functions and methods |     /// All tests for SQLite C# functions and methods | ||||||
|     /// </summary> |     /// </summary> | ||||||
|     [Tests] |     [Tests] | ||||||
|     public static readonly Test All = TestList("Sqlite.C#", new[] { Unit, TestSequenced(Integration) }); |     public static readonly Test All = TestList("Sqlite.C#", [Unit, TestSequenced(Integration)]); | ||||||
| } | } | ||||||
|  | |||||||
| @ -119,6 +119,56 @@ let all = | |||||||
|                 Expect.isSome field.Qualifier "The table qualifier should have been filled" |                 Expect.isSome field.Qualifier "The table qualifier should have been filled" | ||||||
|                 Expect.equal "joe" field.Qualifier.Value "The table qualifier is incorrect" |                 Expect.equal "joe" field.Qualifier.Value "The table qualifier is incorrect" | ||||||
|             } |             } | ||||||
|  |             testList "PgSqlPath" [ | ||||||
|  |                 test "succeeds for a single field with no qualifier" { | ||||||
|  |                     let field = Field.GE "SomethingCool" 18 | ||||||
|  |                     Expect.equal "data->>'SomethingCool'" field.PgSqlPath "The PostgreSQL path is incorrect" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field with a qualifier" { | ||||||
|  |                     let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" } | ||||||
|  |                     Expect.equal "this.data->>'SomethingElse'" field.PgSqlPath "The PostgreSQL path is incorrect" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a nested field with no qualifier" { | ||||||
|  |                     let field = Field.EQ "My.Nested.Field" "howdy" | ||||||
|  |                     Expect.equal "data#>>'{My,Nested,Field}'" field.PgSqlPath "The PostgreSQL path is incorrect" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a nested field with a qualifier" { | ||||||
|  |                     let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" }  | ||||||
|  |                     Expect.equal "bird.data#>>'{Nest,Away}'" field.PgSqlPath "The PostgreSQL path is incorrect" | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |             testList "SqlitePath" [ | ||||||
|  |                 test "succeeds for a single field with no qualifier" { | ||||||
|  |                     let field = Field.GE "SomethingCool" 18 | ||||||
|  |                     Expect.equal "data->>'SomethingCool'" field.SqlitePath "The SQLite path is incorrect" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field with a qualifier" { | ||||||
|  |                     let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" } | ||||||
|  |                     Expect.equal "this.data->>'SomethingElse'" field.SqlitePath "The SQLite path is incorrect" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a nested field with no qualifier" { | ||||||
|  |                     let field = Field.EQ "My.Nested.Field" "howdy" | ||||||
|  |                     Expect.equal "data->>'My'->>'Nested'->>'Field'" field.SqlitePath "The SQLite path is incorrect" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a nested field with a qualifier" { | ||||||
|  |                     let field = { Field.EQ "Nest.Away" "doc" with Qualifier = Some "bird" }  | ||||||
|  |                     Expect.equal "bird.data->>'Nest'->>'Away'" field.SqlitePath "The SQLite path is incorrect" | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|  |         ] | ||||||
|  |         testList "ParameterName" [ | ||||||
|  |             test "Derive 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 "Derive 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" [ |         testList "Query" [ | ||||||
|             test "selectFromTable succeeds" { |             test "selectFromTable succeeds" { | ||||||
|  | |||||||
| @ -58,6 +58,50 @@ let unitTests = | |||||||
|             } |             } | ||||||
|         ] |         ] | ||||||
|         testList "Query" [ |         testList "Query" [ | ||||||
|  |             testList "whereByFields" [ | ||||||
|  |                 test "succeeds for a single field when a logical operator is passed" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ { Field.GT "theField" 0 with ParameterName = Some "@test" } ] Any) | ||||||
|  |                         "data->>'theField' > @test" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field when an existence operator is passed" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.NEX "thatField" ] Any) | ||||||
|  |                         "data->>'thatField' IS NULL" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field when a between operator is passed with numeric values" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ { Field.BT "aField" 50 99 with ParameterName = Some "@range" } ] All) | ||||||
|  |                         "(data->>'aField')::numeric BETWEEN @rangemin AND @rangemax" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field when a between operator is passed with non-numeric values" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ { Field.BT "field0" "a" "b" with ParameterName = Some "@alpha" } ] Any) | ||||||
|  |                         "data->>'field0' BETWEEN @alphamin AND @alphamax" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for all multiple fields with logical operators" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.EQ "theFirst" "1"; Field.EQ "numberTwo" "2" ] All) | ||||||
|  |                         "data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for any multiple fields with an existence operator" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.NEX "thatField"; Field.GE "thisField" 18 ] Any) | ||||||
|  |                         "data->>'thatField' IS NULL OR data->>'thisField' >= @field0" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for all multiple fields with between operators" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.BT "aField" 50 99; Field.BT "anotherField" "a" "b" ] All) | ||||||
|  |                         "(data->>'aField')::numeric BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|             testList "whereByField" [ |             testList "whereByField" [ | ||||||
|                 test "succeeds when a logical operator is passed" { |                 test "succeeds when a logical operator is passed" { | ||||||
|                     Expect.equal |                     Expect.equal | ||||||
|  | |||||||
| @ -12,6 +12,44 @@ open Types | |||||||
| let unitTests = | let unitTests = | ||||||
|     testList "Unit" [ |     testList "Unit" [ | ||||||
|         testList "Query" [ |         testList "Query" [ | ||||||
|  |             testList "whereByFields" [ | ||||||
|  |                 test "succeeds for a single field when a logical operator is passed" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ { Field.GT "theField" 0 with ParameterName = Some "@test" } ] Any) | ||||||
|  |                         "data->>'theField' > @test" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field when an existence operator is passed" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.NEX "thatField" ] Any) | ||||||
|  |                         "data->>'thatField' IS NULL" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for a single field when a between operator is passed" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ { Field.BT "aField" 50 99 with ParameterName = Some "@range" } ] All) | ||||||
|  |                         "data->>'aField' BETWEEN @rangemin AND @rangemax" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for all multiple fields with logical operators" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.EQ "theFirst" "1"; Field.EQ "numberTwo" "2" ] All) | ||||||
|  |                         "data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for any multiple fields with an existence operator" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.NEX "thatField"; Field.GE "thisField" 18 ] Any) | ||||||
|  |                         "data->>'thatField' IS NULL OR data->>'thisField' >= @field0" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |                 test "succeeds for all multiple fields with between operators" { | ||||||
|  |                     Expect.equal | ||||||
|  |                         (Query.whereByFields [ Field.BT "aField" 50 99; Field.BT "anotherField" "a" "b" ] All) | ||||||
|  |                         "data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max" | ||||||
|  |                         "WHERE clause not correct" | ||||||
|  |                 } | ||||||
|  |             ] | ||||||
|             testList "whereByField" [ |             testList "whereByField" [ | ||||||
|                 test "succeeds when a logical operator is passed" { |                 test "succeeds when a logical operator is passed" { | ||||||
|                     Expect.equal |                     Expect.equal | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user