From d9d37f110d5d57319f07a04c96260c062e83f7a1 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Sat, 10 Aug 2024 19:51:13 -0400 Subject: [PATCH] Add tests for PG parameter types - Alter whereById to expect a parameter --- src/Postgres/Library.fs | 74 ++++----- src/Tests.CSharp/PostgresCSharpTests.cs | 155 ++++++++++++++----- src/Tests/PostgresTests.fs | 195 ++++++++++++++---------- 3 files changed, 270 insertions(+), 154 deletions(-) diff --git a/src/Postgres/Library.fs b/src/Postgres/Library.fs index ce48e74..365be45 100644 --- a/src/Postgres/Library.fs +++ b/src/Postgres/Library.fs @@ -45,6 +45,23 @@ module private Helpers = let! _ = it () } + + /// Create a numerically-typed 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 + | :? uint8 as it -> Sql.int8 (int8 it) + | :? int16 as it -> Sql.int16 it + | :? uint16 as it -> Sql.int16 (int16 it) + | :? int as it -> Sql.int it + | :? uint32 as it -> Sql.int (int it) + | :? int64 as it -> Sql.int64 it + | :? uint64 as it -> Sql.int64 (int64 it) + | :? decimal as it -> Sql.decimal it + | :? single as it -> Sql.double (double it) + | :? double as it -> Sql.double it + | :? string as it -> Sql.string it + | _ -> catchAllFunc value open BitBadger.Documents @@ -56,21 +73,7 @@ module Parameters = /// Create an ID parameter (name "@id", key will be treated as a string) [] let idParam (key: 'TKey) = - match box key with - | :? int8 as it -> Sql.int8 it - | :? uint8 as it -> Sql.int8 (int8 it) - | :? int16 as it -> Sql.int16 it - | :? uint16 as it -> Sql.int16 (int16 it) - | :? int as it -> Sql.int it - | :? uint32 as it -> Sql.int (int it) - | :? int64 as it -> Sql.int64 it - | :? uint64 as it -> Sql.int64 (int64 it) - | :? decimal as it -> Sql.decimal it - | :? single as it -> Sql.double (double it) - | :? double as it -> Sql.double it - | :? string as it -> Sql.string it - | _ -> Sql.string (string key) - |> function it -> "@id", it + "@id", parameterFor key (fun it -> Sql.string (string it)) /// Create a parameter with a JSON value [] @@ -89,11 +92,13 @@ module Parameters = | BT -> let p = name.Derive it.ParameterName let values = it.Value :?> obj list - yield ($"{p}min", Sql.parameter (NpgsqlParameter($"{p}min", List.head values))) - yield ($"{p}max", Sql.parameter (NpgsqlParameter($"{p}max", List.last values))) + yield ($"{p}min", + parameterFor (List.head values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}min", v)))) + yield ($"{p}max", + parameterFor (List.last values) (fun v -> Sql.parameter (NpgsqlParameter($"{p}max", v)))) | _ -> let p = name.Derive it.ParameterName - yield (p, Sql.parameter (NpgsqlParameter(p, it.Value))) }) + yield (p, parameterFor it.Value (fun v -> Sql.parameter (NpgsqlParameter(p, v)))) }) |> Seq.collect id |> Seq.append parameters |> Seq.toList @@ -131,27 +136,30 @@ module Query = [] let whereByFields howMatched fields = let name = ParameterName() + let isNumeric (it: obj) = + match it with + | :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64 + | :? decimal | :? single | :? double -> true + | _ -> false fields |> Seq.map (fun it -> match it.Op with | EX | NEX -> $"{it.Path PostgreSQL} {it.Op}" - | BT -> - let p = name.Derive it.ParameterName - let path, value = + | _ -> + let p = name.Derive it.ParameterName + let param, value = match it.Op with | BT -> $"{p}min AND {p}max", (it.Value :?> obj list)[0] | _ -> p, it.Value - match value with - | :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64 - | :? decimal | :? single | :? double -> $"({it.Path PostgreSQL})::numeric {it.Op} {path}" - | _ -> $"{it.Path PostgreSQL} {it.Op} {path}" - | _ -> $"{it.Path PostgreSQL} {it.Op} {name.Derive it.ParameterName}") + 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 ") /// Create a WHERE clause fragment to implement an ID-based query [] - let whereById paramName = - whereByFields Any [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ] + let whereById<'TKey> (docId: 'TKey) = + whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ] /// Table and index definition queries module Definition = @@ -191,9 +199,7 @@ module Query = /// Create a query by a document's ID [] let byId<'TKey> statement (docId: 'TKey) = - Query.statementWhere - statement - (whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ]) + Query.statementWhere statement (whereById docId) /// Create a query on JSON fields [] @@ -359,7 +365,7 @@ module WithProps = /// Determine if a document exists for the given ID [] let byId tableName (docId: 'TKey) sqlProps = - Custom.scalar (Query.exists tableName (Query.whereById "@id")) [ idParam docId ] toExists sqlProps + Custom.scalar (Query.exists tableName (Query.whereById docId)) [ idParam docId ] toExists sqlProps /// Determine if a document exists using JSON field comparisons (->> =) [] @@ -537,9 +543,7 @@ module WithProps = [] let byId tableName (docId: 'TKey) (document: 'TDoc) sqlProps = Custom.nonQuery - (Query.statementWhere (Query.update tableName) (Query.whereById "@id")) - [ idParam docId; jsonParam "@data" document ] - sqlProps + (Query.byId (Query.update tableName) docId) [ idParam docId; jsonParam "@data" document ] sqlProps /// Update an entire document by its ID, using the provided function to obtain the ID from the document [] diff --git a/src/Tests.CSharp/PostgresCSharpTests.cs b/src/Tests.CSharp/PostgresCSharpTests.cs index 6e6d4c3..ff5d08a 100644 --- a/src/Tests.CSharp/PostgresCSharpTests.cs +++ b/src/Tests.CSharp/PostgresCSharpTests.cs @@ -20,12 +20,93 @@ public static class PostgresCSharpTests [ TestList("Parameters", [ - TestCase("Id succeeds", () => - { - var it = Parameters.Id(88); - Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); - Expect.equal(it.Item2, Sql.@string("88"), "ID parameter value incorrect"); - }), + TestList("Id", + [ + // NOTE: these tests also exercise all branches of the internal parameterFor function + TestCase("succeeds for byte ID", () => + { + var it = Parameters.Id((sbyte)7); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.int8(7), "Byte ID parameter not constructed correctly"); + }), + TestCase("succeeds for unsigned byte ID", () => + { + var it = Parameters.Id((byte)7); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.int8(7), "Unsigned byte ID parameter not constructed correctly"); + }), + TestCase("succeeds for short ID", () => + { + var it = Parameters.Id((short)44); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.int16(44), "Short ID parameter not constructed correctly"); + }), + TestCase("succeeds for unsigned short ID", () => + { + var it = Parameters.Id((ushort)64); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.int16(64), "Unsigned short ID parameter not constructed correctly"); + }), + TestCase("succeeds for integer ID", () => + { + var it = Parameters.Id(88); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@int(88), "ID parameter value incorrect"); + }), + TestCase("succeeds for unsigned integer ID", () => + { + var it = Parameters.Id((uint)889); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@int(889), "Unsigned int ID parameter not constructed correctly"); + }), + TestCase("succeeds for long ID", () => + { + var it = Parameters.Id((long)123); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.int64(123), "Long ID parameter not constructed correctly"); + }), + TestCase("succeeds for unsigned long ID", () => + { + var it = Parameters.Id((ulong)6464); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.int64(6464), + "Unsigned long ID parameter not constructed correctly"); + }), + TestCase("succeeds for decimal ID", () => + { + var it = Parameters.Id((decimal)4.56); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@decimal((decimal)4.56), + "Decimal ID parameter not constructed correctly"); + }), + TestCase("succeeds for single ID", () => + { + var it = Parameters.Id((float)5.67); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@double((float)5.67), + "Single ID parameter not constructed correctly"); + }), + TestCase("succeeds for double ID", () => + { + var it = Parameters.Id(6.78); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@double(6.78), + "Double ID parameter not constructed correctly"); + }), + TestCase("succeeds for string ID", () => + { + var it = Parameters.Id("99"); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@string("99"), "ID parameter value incorrect"); + }), + TestCase("succeeds for non-numeric non-string ID", () => + { + var it = Parameters.Id(new Uri("https://example.com")); + Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); + Expect.equal(it.Item2, Sql.@string("https://example.com/"), + "Non-numeric, non-string parameter value incorrect"); + }) + ]), TestCase("Json succeeds", () => { var it = Parameters.Json("@test", new { Something = "good" }); @@ -40,10 +121,7 @@ public static class PostgresCSharpTests Expect.hasLength(paramList, 1, "There should have been a parameter added"); var (name, value) = paramList[0]; Expect.equal(name, "@field0", "Field parameter name not correct"); - if (!value.IsParameter) - { - Expect.isTrue(false, "The parameter was not a Parameter type"); - } + Expect.equal(value, Sql.@string("242"), "Field parameter value not correct"); }), TestCase("succeeds when multiple independent parameters are added", () => { @@ -52,22 +130,13 @@ public static class PostgresCSharpTests Expect.hasLength(paramList, 3, "There should have been 2 parameters added"); var (name, value) = paramList[0]; Expect.equal(name, "@id", "First field parameter name not correct"); - if (!value.IsString) - { - Expect.isTrue(false, "First parameter was not a String type"); - } + Expect.equal(value, Sql.@int(14), "First field parameter value not correct"); (name, value) = paramList[1]; Expect.equal(name, "@field0", "Second field parameter name not correct"); - if (!value.IsParameter) - { - Expect.isTrue(false, "Second parameter was not a Parameter type"); - } + Expect.equal(value, Sql.@string("you"), "Second field parameter value not correct"); (name, value) = paramList[2]; Expect.equal(name, "@field1", "Third parameter name not correct"); - if (!value.IsParameter) - { - Expect.isTrue(false, "Third parameter was not a Parameter type"); - } + Expect.equal(value, Sql.@string("them"), "Third parameter value not correct"); }), TestCase("succeeds when a parameter is not added", () => { @@ -81,16 +150,10 @@ public static class PostgresCSharpTests Expect.hasLength(paramList, 2, "There should have been 2 parameters added"); var (name, value) = paramList[0]; Expect.equal(name, "@testmin", "Minimum field name not correct"); - if (!value.IsParameter) - { - Expect.isTrue(false, "Minimum parameter was not a Parameter type"); - } + Expect.equal(value, Sql.@string("eh"), "Minimum field value not correct"); (name, value) = paramList[1]; Expect.equal(name, "@testmax", "Maximum field name not correct"); - if (!value.IsParameter) - { - Expect.isTrue(false, "Maximum parameter was not a Parameter type"); - } + Expect.equal(value, Sql.@string("zed"), "Maximum field value not correct"); }) ]), #pragma warning disable CS0618 @@ -101,7 +164,7 @@ public static class PostgresCSharpTests 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.IsParameter, "Field parameter value incorrect"); + Expect.isTrue(it[0].Item2.IsString, "Field parameter value incorrect"); }), TestCase("succeeds when a parameter is not added", () => { @@ -113,9 +176,9 @@ public static class PostgresCSharpTests 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.IsParameter, "Minimum field parameter value incorrect"); + 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.IsParameter, "Maximum field parameter value incorrect"); + Expect.isTrue(it[1].Item2.IsString, "Maximum field parameter value incorrect"); }) ]), #pragma warning restore CS0618 @@ -172,7 +235,7 @@ public static class PostgresCSharpTests { Expect.equal( Postgres.Query.WhereByFields(FieldMatch.Any, - [Field.GT("theField", 0).WithParameterName("@test")]), + [Field.GT("theField", "0").WithParameterName("@test")]), "data->>'theField' > @test", "WHERE clause not correct"); }), TestCase("succeeds for a single field when an existence operator is passed", () => @@ -206,7 +269,8 @@ public static class PostgresCSharpTests Expect.equal( Postgres.Query.WhereByFields(FieldMatch.Any, [Field.NEX("thatField"), Field.GE("thisField", 18)]), - "data->>'thatField' IS NULL OR data->>'thisField' >= @field0", "WHERE clause not correct"); + "data->>'thatField' IS NULL OR (data->>'thisField')::numeric >= @field0", + "WHERE clause not correct"); }), TestCase("succeeds for all multiple fields with between operators", () => { @@ -217,10 +281,23 @@ public static class PostgresCSharpTests "WHERE clause not correct"); }) ]), - TestCase("WhereById succeeds", () => - { - Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct"); - }), + TestList("WhereById", + [ + TestCase("succeeds for numeric ID", () => + { + Expect.equal(Postgres.Query.WhereById(18), "(data->>'Id')::numeric = @id", + "WHERE clause not correct"); + }), + TestCase("succeeds for string ID", () => + { + Expect.equal(Postgres.Query.WhereById("18"), "data->>'Id' = @id", "WHERE clause not correct"); + }), + TestCase("succeeds for non-numeric non-string ID", () => + { + Expect.equal(Postgres.Query.WhereById(new Uri("https://example.com")), "data->>'Id' = @id", + "WHERE clause not correct"); + }), + ]), TestList("Definition", [ TestCase("EnsureTable succeeds", () => diff --git a/src/Tests/PostgresTests.fs b/src/Tests/PostgresTests.fs index fdd9068..ee34cf2 100644 --- a/src/Tests/PostgresTests.fs +++ b/src/Tests/PostgresTests.fs @@ -11,9 +11,78 @@ open BitBadger.Documents.Tests let unitTests = testList "Unit" [ testList "Parameters" [ - test "idParam succeeds" { - Expect.equal (idParam 88) ("@id", Sql.string "88") "ID parameter not constructed correctly" - } + testList "idParam" [ + // NOTE: these tests also exercise all branches of the internal parameterFor function + test "succeeds for byte ID" { + Expect.equal + (idParam (sbyte 7)) ("@id", Sql.int8 (sbyte 7)) "Byte ID parameter not constructed correctly" + } + test "succeeds for unsigned byte ID" { + Expect.equal + (idParam (byte 7)) + ("@id", Sql.int8 (int8 (byte 7))) + "Unsigned byte ID parameter not constructed correctly" + } + test "succeeds for short ID" { + Expect.equal + (idParam (int16 44)) + ("@id", Sql.int16 (int16 44)) + "Short ID parameter not constructed correctly" + } + test "succeeds for unsigned short ID" { + Expect.equal + (idParam (uint16 64)) + ("@id", Sql.int16 (int16 64)) + "Unsigned short ID parameter not constructed correctly" + } + test "succeeds for integer ID" { + Expect.equal (idParam 88) ("@id", Sql.int 88) "Int ID parameter not constructed correctly" + } + test "succeeds for unsigned integer ID" { + Expect.equal + (idParam (uint 889)) ("@id", Sql.int 889) "Unsigned int ID parameter not constructed correctly" + } + test "succeeds for long ID" { + Expect.equal + (idParam (int64 123)) + ("@id", Sql.int64 (int64 123)) + "Long ID parameter not constructed correctly" + } + test "succeeds for unsigned long ID" { + Expect.equal + (idParam (uint64 6464)) + ("@id", Sql.int64 (int64 6464)) + "Unsigned long ID parameter not constructed correctly" + } + test "succeeds for decimal ID" { + Expect.equal + (idParam (decimal 4.56)) + ("@id", Sql.decimal (decimal 4.56)) + "Decimal ID parameter not constructed correctly" + } + test "succeeds for single ID" { + Expect.equal + (idParam (single 5.67)) + ("@id", Sql.double (double (single 5.67))) + "Single ID parameter not constructed correctly" + } + test "succeeds for double ID" { + Expect.equal + (idParam (double 6.78)) + ("@id", Sql.double (double 6.78)) + "Double ID parameter not constructed correctly" + } + test "succeeds for string ID" { + Expect.equal (idParam "99") ("@id", Sql.string "99") "String ID parameter not constructed correctly" + } + test "succeeds for non-numeric non-string ID" { + let target = { new obj() with override _.ToString() = "ToString was called" } + Expect.equal + (idParam target) + ("@id", Sql.string "ToString was called") + "Non-numeric, non-string parameter not constructed correctly" + } + ] test "jsonParam succeeds" { Expect.equal (jsonParam "@test" {| Something = "good" |}) @@ -24,35 +93,20 @@ let unitTests = test "succeeds when a parameter is added" { let paramList = addFieldParams [ Field.EQ "it" "242" ] [] Expect.hasLength paramList 1 "There should have been a parameter added" - let it = Seq.head paramList - Expect.equal (fst it) "@field0" "Field parameter name not correct" - match snd it with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@field0" "Parameter name not correct" - Expect.equal value.Value "242" "Parameter value not correct" - | _ -> Expect.isTrue false "The parameter was not a Parameter type" + let name, value = Seq.head paramList + Expect.equal name "@field0" "Field parameter name not correct" + Expect.equal value (Sql.string "242") "Parameter value not correct" } test "succeeds when multiple independent parameters are added" { let paramList = addFieldParams [ Field.EQ "me" "you"; Field.GT "us" "them" ] [ idParam 14 ] Expect.hasLength paramList 3 "There should have been 2 parameters added" let p = Array.ofSeq paramList Expect.equal (fst p[0]) "@id" "First field parameter name not correct" - match snd p[0] with - | SqlValue.String value -> - Expect.equal value "14" "First parameter value not correct" - | _ -> Expect.isTrue false "First parameter was not a String type" + Expect.equal (snd p[0]) (Sql.int 14) "First parameter value not correct" Expect.equal (fst p[1]) "@field0" "Second field parameter name not correct" - match snd p[1] with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@field0" "Second parameter name not correct" - Expect.equal value.Value "you" "Second parameter value not correct" - | _ -> Expect.isTrue false "Second parameter was not a Parameter type" + Expect.equal (snd p[1]) (Sql.string "you") "Second parameter value not correct" Expect.equal (fst p[2]) "@field1" "Third parameter name not correct" - match snd p[2] with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@field1" "Third parameter name not correct" - Expect.equal value.Value "them" "Third parameter value not correct" - | _ -> Expect.isTrue false "Third parameter was not a Parameter type" + Expect.equal (snd p[2]) (Sql.string "them") "Third parameter value not correct" } test "succeeds when a parameter is not added" { let paramList = addFieldParams [ Field.EX "tacos" ] [] @@ -62,33 +116,21 @@ let unitTests = let paramList = addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] [] Expect.hasLength paramList 2 "There should have been 2 parameters added" - let min = Seq.head paramList - Expect.equal (fst min) "@testmin" "Minimum field name not correct" - match snd min with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@testmin" "Minimum parameter name not correct" - Expect.equal value.Value "eh" "Minimum parameter value not correct" - | _ -> Expect.isTrue false "Minimum parameter was not a Parameter type" - let max = paramList |> Seq.skip 1 |> Seq.head - Expect.equal (fst max) "@testmax" "Maximum field name not correct" - match snd max with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@testmax" "Maximum parameter name not correct" - Expect.equal value.Value "zed" "Maximum parameter value not correct" - | _ -> Expect.isTrue false "Maximum parameter was not a Parameter type" + let name, value = Seq.head paramList + Expect.equal name "@testmin" "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 "@testmax" "Maximum field name not correct" + 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 it = Seq.head paramList - Expect.equal (fst it) "@field" "Field parameter name not correct" - match snd it with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@field" "Parameter name not correct" - Expect.equal value.Value "242" "Parameter value not correct" - | _ -> Expect.isTrue false "The parameter was not a Parameter type" + 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") [] @@ -97,54 +139,36 @@ let unitTests = 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 min = Seq.head paramList - Expect.equal (fst min) "@fieldmin" "Minimum field name not correct" - match snd min with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@fieldmin" "Minimum parameter name not correct" - Expect.equal value.Value "eh" "Minimum parameter value not correct" - | _ -> Expect.isTrue false "Minimum parameter was not a Parameter type" - let max = paramList |> Seq.skip 1 |> Seq.head - Expect.equal (fst max) "@fieldmax" "Maximum field name not correct" - match snd max with - | SqlValue.Parameter value -> - Expect.equal value.ParameterName "@fieldmax" "Maximum parameter name not correct" - Expect.equal value.Value "zed" "Maximum parameter value not correct" - | _ -> Expect.isTrue false "Maximum parameter was not a Parameter type" + 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" ] Expect.equal name "@name" "The parameter name was incorrect" - match value with - | SqlValue.String it -> Expect.equal it "bob" "The parameter value was incorrect" - | _ -> Expect.isTrue false "The parameter was not a String type" + Expect.equal value (Sql.string "bob") "The parameter value was incorrect" } test "succeeds for multiple names" { let name, value = fieldNameParams [ "bob"; "tom"; "mike" ] Expect.equal name "@name" "The parameter name was incorrect" - match value with - | SqlValue.StringArray it -> - Expect.equal it [| "bob"; "tom"; "mike" |] "The parameter value was incorrect" - | _ -> Expect.isTrue false "The parameter was not a StringArray type" + Expect.equal value (Sql.stringArray [| "bob"; "tom"; "mike" |]) "The parameter value was incorrect" } ] testList "fieldNameParam" [ test "succeeds for one name" { let name, value = fieldNameParam [ "bob" ] Expect.equal name "@name" "The parameter name was incorrect" - match value with - | SqlValue.String it -> Expect.equal it "bob" "The parameter value was incorrect" - | _ -> Expect.isTrue false "The parameter was not a String type" + Expect.equal value (Sql.string "bob") "The parameter value was incorrect" } test "succeeds for multiple names" { let name, value = fieldNameParam [ "bob"; "tom"; "mike" ] Expect.equal name "@name" "The parameter name was incorrect" - match value with - | SqlValue.StringArray it -> - Expect.equal it [| "bob"; "tom"; "mike" |] "The parameter value was incorrect" - | _ -> Expect.isTrue false "The parameter was not a StringArray type" + Expect.equal value (Sql.stringArray [| "bob"; "tom"; "mike" |]) "The parameter value was incorrect" } ] test "noParams succeeds" { @@ -155,7 +179,7 @@ let unitTests = testList "whereByFields" [ test "succeeds for a single field when a logical operator is passed" { Expect.equal - (Query.whereByFields Any [ { Field.GT "theField" 0 with ParameterName = Some "@test" } ]) + (Query.whereByFields Any [ { Field.GT "theField" "0" with ParameterName = Some "@test" } ]) "data->>'theField' > @test" "WHERE clause not correct" } @@ -186,7 +210,7 @@ let unitTests = test "succeeds for any multiple fields with an existence operator" { Expect.equal (Query.whereByFields Any [ Field.NEX "thatField"; Field.GE "thisField" 18 ]) - "data->>'thatField' IS NULL OR data->>'thisField' >= @field0" + "data->>'thatField' IS NULL OR (data->>'thisField')::numeric >= @field0" "WHERE clause not correct" } test "succeeds for all multiple fields with between operators" { @@ -196,9 +220,20 @@ let unitTests = "WHERE clause not correct" } ] - test "whereById succeeds" { - Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct" - } + testList "whereById" [ + test "succeeds for numeric ID" { + Expect.equal (Query.whereById 18) "(data->>'Id')::numeric = @id" "WHERE clause not correct" + } + test "succeeds for string ID" { + Expect.equal (Query.whereById "18") "data->>'Id' = @id" "WHERE clause not correct" + } + test "succeeds for non-numeric non-string ID" { + Expect.equal + (Query.whereById (System.Uri "https://example.com")) + "data->>'Id' = @id" + "WHERE clause not correct" + } + ] testList "Definition" [ test "ensureTable succeeds" { Expect.equal @@ -505,7 +540,7 @@ let integrationTests = Expect.isFalse exists "There should not have been an existing document" } ] - ftestList "byFields" [ + testList "byFields" [ testTask "succeeds when documents exist" { use db = PostgresDb.BuildDb() do! loadDocs () @@ -621,7 +656,7 @@ let integrationTests = PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ] Expect.equal (List.length docs) 1 "There should have been one document returned" } - ftestTask "succeeds when documents are not found" { + testTask "succeeds when documents are not found" { use db = PostgresDb.BuildDb() do! loadDocs ()