Add ToString for FieldMatch; add tests
This commit is contained in:
		
							parent
							
								
									d9d37f110d
								
							
						
					
					
						commit
						0c308c5f33
					
				@ -125,6 +125,10 @@ type FieldMatch =
 | 
			
		||||
    | Any
 | 
			
		||||
    /// All fields match (AND)
 | 
			
		||||
    | All
 | 
			
		||||
    
 | 
			
		||||
    /// The SQL value implementing each matching strategy
 | 
			
		||||
    override this.ToString() =
 | 
			
		||||
        match this with Any -> "OR" | All -> "AND"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Derive parameter names (each instance wraps a counter to uniquely name anonymous fields)
 | 
			
		||||
 | 
			
		||||
@ -46,7 +46,7 @@ module private Helpers =
 | 
			
		||||
        ()
 | 
			
		||||
    }
 | 
			
		||||
    
 | 
			
		||||
    /// Create a numerically-typed parameter, or use the given parameter derivation function if non-(numeric or string)
 | 
			
		||||
    /// Create a number or string parameter, or use the given parameter derivation function if non-(numeric or string)
 | 
			
		||||
    let internal parameterFor<'T> (value: 'T) (catchAllFunc: 'T -> SqlValue) =
 | 
			
		||||
        match box value with
 | 
			
		||||
        | :? int8    as it -> Sql.int8 it
 | 
			
		||||
@ -70,7 +70,7 @@ open BitBadger.Documents
 | 
			
		||||
[<AutoOpen>]
 | 
			
		||||
module Parameters =
 | 
			
		||||
    
 | 
			
		||||
    /// Create an ID parameter (name "@id", key will be treated as a string)
 | 
			
		||||
    /// Create an ID parameter (name "@id")
 | 
			
		||||
    [<CompiledName "Id">]
 | 
			
		||||
    let idParam (key: 'TKey) =
 | 
			
		||||
        "@id", parameterFor key (fun it -> Sql.string (string it))
 | 
			
		||||
@ -134,7 +134,7 @@ module Query =
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
 | 
			
		||||
    [<CompiledName "WhereByFields">]
 | 
			
		||||
    let whereByFields howMatched fields =
 | 
			
		||||
    let whereByFields (howMatched: FieldMatch) fields =
 | 
			
		||||
        let name = ParameterName()
 | 
			
		||||
        let isNumeric (it: obj) =
 | 
			
		||||
            match it with
 | 
			
		||||
@ -154,7 +154,7 @@ module Query =
 | 
			
		||||
                if isNumeric value then
 | 
			
		||||
                    $"({it.Path PostgreSQL})::numeric {it.Op} {param}"
 | 
			
		||||
                else $"{it.Path PostgreSQL} {it.Op} {param}")
 | 
			
		||||
        |> String.concat (match howMatched with Any -> " OR " | All -> " AND ")
 | 
			
		||||
        |> String.concat $" {howMatched} "
 | 
			
		||||
 | 
			
		||||
    /// Create a WHERE clause fragment to implement an ID-based query
 | 
			
		||||
    [<CompiledName "WhereById">]
 | 
			
		||||
 | 
			
		||||
@ -33,7 +33,7 @@ module Query =
 | 
			
		||||
    
 | 
			
		||||
    /// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
 | 
			
		||||
    [<CompiledName "WhereByFields">]
 | 
			
		||||
    let whereByFields howMatched fields =
 | 
			
		||||
    let whereByFields (howMatched: FieldMatch) fields =
 | 
			
		||||
        let name = ParameterName()
 | 
			
		||||
        fields
 | 
			
		||||
        |> Seq.map (fun it ->
 | 
			
		||||
@ -43,7 +43,7 @@ module Query =
 | 
			
		||||
                let p = name.Derive it.ParameterName
 | 
			
		||||
                $"{it.Path SQLite} {it.Op} {p}min AND {p}max"
 | 
			
		||||
            | _ -> $"{it.Path SQLite} {it.Op} {name.Derive it.ParameterName}")
 | 
			
		||||
        |> String.concat (match howMatched with Any -> " OR " | All -> " AND ")
 | 
			
		||||
        |> String.concat $" {howMatched} "
 | 
			
		||||
 | 
			
		||||
    /// Create a WHERE clause fragment to implement an ID-based query
 | 
			
		||||
    [<CompiledName "WhereById">]
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,7 @@
 | 
			
		||||
using Expecto.CSharp;
 | 
			
		||||
using Expecto;
 | 
			
		||||
using Microsoft.FSharp.Collections;
 | 
			
		||||
using Microsoft.FSharp.Core;
 | 
			
		||||
 | 
			
		||||
namespace BitBadger.Documents.Tests.CSharp;
 | 
			
		||||
 | 
			
		||||
@ -236,6 +237,39 @@ public static class CommonCSharpTests
 | 
			
		||||
                })
 | 
			
		||||
            ])
 | 
			
		||||
        ]),
 | 
			
		||||
        TestList("FieldMatch.ToString",
 | 
			
		||||
        [
 | 
			
		||||
            TestCase("succeeds for Any", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(FieldMatch.Any.ToString(), "OR", "SQL for Any is incorrect");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds for All", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(FieldMatch.All.ToString(), "AND", "SQL for All is incorrect");
 | 
			
		||||
            })
 | 
			
		||||
        ]),
 | 
			
		||||
        TestList("ParameterName.Derive",
 | 
			
		||||
        [
 | 
			
		||||
            TestCase("succeeds with existing name", () =>
 | 
			
		||||
            {
 | 
			
		||||
                ParameterName name = new();
 | 
			
		||||
                Expect.equal(name.Derive(FSharpOption<string>.Some("@taco")), "@taco", "Name should have been @taco");
 | 
			
		||||
                Expect.equal(name.Derive(FSharpOption<string>.None), "@field0",
 | 
			
		||||
                    "Counter should not have advanced for named field");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Derive succeeds with non-existent name", () =>
 | 
			
		||||
            {
 | 
			
		||||
                ParameterName name = new();
 | 
			
		||||
                Expect.equal(name.Derive(FSharpOption<string>.None), "@field0",
 | 
			
		||||
                    "Anonymous field name should have been returned");
 | 
			
		||||
                Expect.equal(name.Derive(FSharpOption<string>.None), "@field1",
 | 
			
		||||
                    "Counter should have advanced from previous call");
 | 
			
		||||
                Expect.equal(name.Derive(FSharpOption<string>.None), "@field2",
 | 
			
		||||
                    "Counter should have advanced from previous call");
 | 
			
		||||
                Expect.equal(name.Derive(FSharpOption<string>.None), "@field3",
 | 
			
		||||
                    "Counter should have advanced from previous call");
 | 
			
		||||
            })
 | 
			
		||||
        ]),
 | 
			
		||||
        TestList("Query",
 | 
			
		||||
        [
 | 
			
		||||
            TestCase("StatementWhere succeeds", () =>
 | 
			
		||||
 | 
			
		||||
@ -156,32 +156,6 @@ public static class PostgresCSharpTests
 | 
			
		||||
                    Expect.equal(value, Sql.@string("zed"), "Maximum field value not correct");
 | 
			
		||||
                })
 | 
			
		||||
            ]),
 | 
			
		||||
#pragma warning disable CS0618
 | 
			
		||||
            TestList("AddField",
 | 
			
		||||
            [
 | 
			
		||||
                TestCase("succeeds when a parameter is added", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters.AddField("@field", Field.EQ("it", "242"), []).ToList();
 | 
			
		||||
                    Expect.hasLength(it, 1, "There should have been a parameter added");
 | 
			
		||||
                    Expect.equal(it[0].Item1, "@field", "Field parameter not constructed correctly");
 | 
			
		||||
                    Expect.isTrue(it[0].Item2.IsString, "Field parameter value incorrect");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when a parameter is not added", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters.AddField("@it", Field.EX("It"), []);
 | 
			
		||||
                    Expect.isEmpty(it, "There should not have been any parameters added");
 | 
			
		||||
                }),
 | 
			
		||||
                TestCase("succeeds when two parameters are added", () =>
 | 
			
		||||
                {
 | 
			
		||||
                    var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"), []).ToList();
 | 
			
		||||
                    Expect.hasLength(it, 2, "There should have been 2 parameters added");
 | 
			
		||||
                    Expect.equal(it[0].Item1, "@fieldmin", "Minimum field name not correct");
 | 
			
		||||
                    Expect.isTrue(it[0].Item2.IsString, "Minimum field parameter value incorrect");
 | 
			
		||||
                    Expect.equal(it[1].Item1, "@fieldmax", "Maximum field name not correct");
 | 
			
		||||
                    Expect.isTrue(it[1].Item2.IsString, "Maximum field parameter value incorrect");
 | 
			
		||||
                })
 | 
			
		||||
            ]),
 | 
			
		||||
#pragma warning restore CS0618
 | 
			
		||||
            TestList("FieldNames",
 | 
			
		||||
            [
 | 
			
		||||
                TestCase("succeeds for one name", () =>
 | 
			
		||||
@ -331,6 +305,36 @@ public static class PostgresCSharpTests
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.WhereJsonPathMatches("@path"), "data @? @path::jsonpath",
 | 
			
		||||
                    "WHERE clause not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Patch succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.Patch(PostgresDb.TableName),
 | 
			
		||||
                    $"UPDATE {PostgresDb.TableName} SET data = data || @data", "Patch query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("RemoveFields succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.RemoveFields(PostgresDb.TableName),
 | 
			
		||||
                    $"UPDATE {PostgresDb.TableName} SET data = data - @name", "Field removal query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ById succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.ById("test", "14"), "test WHERE data->>'Id' = @id",
 | 
			
		||||
                    "By-ID query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ByFields succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]),
 | 
			
		||||
                    "unit WHERE (data->>'That')::numeric > @field0", "By-Field query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase ("ByContains succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.ByContains("exam"), "exam WHERE data @> @criteria",
 | 
			
		||||
                    "By-Contains query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ByPathMach succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Postgres.Query.ByPathMatch("verify"), "verify WHERE data @? @path::jsonpath",
 | 
			
		||||
                    "By-JSON Path query not correct");
 | 
			
		||||
            })
 | 
			
		||||
        ])
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
@ -67,6 +67,27 @@ public static class SqliteCSharpTests
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Patch succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.Patch(SqliteDb.TableName),
 | 
			
		||||
                    $"UPDATE {SqliteDb.TableName} SET data = json_patch(data, json(@data))", "Patch query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("RemoveFields succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.RemoveFields(SqliteDb.TableName, [new("@a", "a"), new("@b", "b")]),
 | 
			
		||||
                    $"UPDATE {SqliteDb.TableName} SET data = json_remove(data, @a, @b)",
 | 
			
		||||
                    "Field removal query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ById succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.ById("test", "14"), "test WHERE data->>'Id' = @id",
 | 
			
		||||
                    "By-ID query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("ByFields succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]),
 | 
			
		||||
                    "unit WHERE data->>'That' > @field0", "By-Field query not correct");
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("Definition.EnsureTable succeeds", () =>
 | 
			
		||||
            {
 | 
			
		||||
                Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"),
 | 
			
		||||
 | 
			
		||||
@ -155,13 +155,21 @@ let all =
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
        ]
 | 
			
		||||
        testList "ParameterName" [
 | 
			
		||||
            test "Derive succeeds with existing name" {
 | 
			
		||||
        testList "FieldMatch.ToString" [
 | 
			
		||||
            test "succeeds for Any" {
 | 
			
		||||
                Expect.equal (string Any) "OR" "SQL for Any is incorrect"
 | 
			
		||||
            }
 | 
			
		||||
            test "succeeds for All" {
 | 
			
		||||
                Expect.equal (string All) "AND" "SQL for All is incorrect"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
        testList "ParameterName.Derive" [
 | 
			
		||||
            test "succeeds with existing name" {
 | 
			
		||||
                let name = ParameterName()
 | 
			
		||||
                Expect.equal (name.Derive(Some "@taco")) "@taco" "Name should have been @taco"
 | 
			
		||||
                Expect.equal (name.Derive None) "@field0" "Counter should not have advanced for named field"
 | 
			
		||||
            }
 | 
			
		||||
            test "Derive succeeds with non-existent name" {
 | 
			
		||||
            test "succeeds with non-existent name" {
 | 
			
		||||
                let name = ParameterName()
 | 
			
		||||
                Expect.equal (name.Derive None) "@field0" "Anonymous field name should have been returned"
 | 
			
		||||
                Expect.equal (name.Derive None) "@field1" "Counter should have advanced from previous call"
 | 
			
		||||
 | 
			
		||||
@ -124,29 +124,6 @@ let unitTests =
 | 
			
		||||
                    Expect.equal value (Sql.string "zed") "Maximum parameter value not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "addFieldParam" [
 | 
			
		||||
                test "succeeds when a parameter is added" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.EQ "it" "242") []
 | 
			
		||||
                    Expect.hasLength paramList 1 "There should have been a parameter added"
 | 
			
		||||
                    let name, value = Seq.head paramList
 | 
			
		||||
                    Expect.equal name "@field" "Field parameter name not correct"
 | 
			
		||||
                    Expect.equal value (Sql.string "242") "Parameter value not correct"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when a parameter is not added" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.EX "tacos") []
 | 
			
		||||
                    Expect.isEmpty paramList "There should not have been any parameters added"
 | 
			
		||||
                }
 | 
			
		||||
                test "succeeds when two parameters are added" {
 | 
			
		||||
                    let paramList = addFieldParam "@field" (Field.BT "that" "eh" "zed") []
 | 
			
		||||
                    Expect.hasLength paramList 2 "There should have been 2 parameters added"
 | 
			
		||||
                    let name, value = Seq.head paramList
 | 
			
		||||
                    Expect.equal name "@fieldmin" "Minimum field name not correct"
 | 
			
		||||
                    Expect.equal value (Sql.string "eh") "Minimum parameter value not correct"
 | 
			
		||||
                    let name, value = paramList |> Seq.skip 1 |> Seq.head
 | 
			
		||||
                    Expect.equal name "@fieldmax" "Maximum field name not correct"
 | 
			
		||||
                    Expect.equal value (Sql.string "zed") "Maximum parameter value not correct"
 | 
			
		||||
                }
 | 
			
		||||
            ]
 | 
			
		||||
            testList "fieldNameParams" [
 | 
			
		||||
                test "succeeds for one name" {
 | 
			
		||||
                    let name, value = fieldNameParams [ "bob" ]
 | 
			
		||||
@ -261,6 +238,34 @@ let unitTests =
 | 
			
		||||
            test "whereJsonPathMatches succeeds" {
 | 
			
		||||
                Expect.equal (Query.whereJsonPathMatches "@path") "data @? @path::jsonpath" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "patch succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.patch PostgresDb.TableName)
 | 
			
		||||
                    $"UPDATE {PostgresDb.TableName} SET data = data || @data"
 | 
			
		||||
                    "Patch query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "removeFields succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.removeFields PostgresDb.TableName)
 | 
			
		||||
                    $"UPDATE {PostgresDb.TableName} SET data = data - @name"
 | 
			
		||||
                    "Field removal query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "byId succeeds" {
 | 
			
		||||
                Expect.equal (Query.byId "test" "14") "test WHERE data->>'Id' = @id" "By-ID query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "byFields succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.byFields "unit" Any [ Field.GT "That" 14 ])
 | 
			
		||||
                    "unit WHERE (data->>'That')::numeric > @field0"
 | 
			
		||||
                    "By-Field query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "byContains succeeds" {
 | 
			
		||||
                Expect.equal (Query.byContains "exam") "exam WHERE data @> @criteria" "By-Contains query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "byPathMach succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.byPathMatch "verify") "verify WHERE data @? @path::jsonpath" "By-JSON Path query not correct"
 | 
			
		||||
            }
 | 
			
		||||
        ]
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -55,6 +55,27 @@ let unitTests =
 | 
			
		||||
            test "whereById succeeds" {
 | 
			
		||||
                Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "patch succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.patch SqliteDb.TableName)
 | 
			
		||||
                    $"UPDATE {SqliteDb.TableName} SET data = json_patch(data, json(@data))"
 | 
			
		||||
                    "Patch query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "removeFields succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.removeFields SqliteDb.TableName [ SqliteParameter("@a", "a"); SqliteParameter("@b", "b") ])
 | 
			
		||||
                    $"UPDATE {SqliteDb.TableName} SET data = json_remove(data, @a, @b)"
 | 
			
		||||
                    "Field removal query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "byId succeeds" {
 | 
			
		||||
                Expect.equal (Query.byId "test" "14") "test WHERE data->>'Id' = @id" "By-ID query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "byFields succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.byFields "unit" Any [ Field.GT "That" 14 ])
 | 
			
		||||
                    "unit WHERE data->>'That' > @field0"
 | 
			
		||||
                    "By-Field query not correct"
 | 
			
		||||
            }
 | 
			
		||||
            test "Definition.ensureTable succeeds" {
 | 
			
		||||
                Expect.equal
 | 
			
		||||
                    (Query.Definition.ensureTable "tbl")
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user