Version 4 #6
|
@ -46,17 +46,9 @@ module private Helpers =
|
||||||
()
|
()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Create a numerically-typed parameter, or use the given parameter derivation function if non-(numeric or string)
|
||||||
open BitBadger.Documents
|
let internal parameterFor<'T> (value: 'T) (catchAllFunc: 'T -> SqlValue) =
|
||||||
|
match box value with
|
||||||
/// Functions for creating parameters
|
|
||||||
[<AutoOpen>]
|
|
||||||
module Parameters =
|
|
||||||
|
|
||||||
/// Create an ID parameter (name "@id", key will be treated as a string)
|
|
||||||
[<CompiledName "Id">]
|
|
||||||
let idParam (key: 'TKey) =
|
|
||||||
match box key with
|
|
||||||
| :? int8 as it -> Sql.int8 it
|
| :? int8 as it -> Sql.int8 it
|
||||||
| :? uint8 as it -> Sql.int8 (int8 it)
|
| :? uint8 as it -> Sql.int8 (int8 it)
|
||||||
| :? int16 as it -> Sql.int16 it
|
| :? int16 as it -> Sql.int16 it
|
||||||
|
@ -69,8 +61,19 @@ module Parameters =
|
||||||
| :? single as it -> Sql.double (double it)
|
| :? single as it -> Sql.double (double it)
|
||||||
| :? double as it -> Sql.double it
|
| :? double as it -> Sql.double it
|
||||||
| :? string as it -> Sql.string it
|
| :? string as it -> Sql.string it
|
||||||
| _ -> Sql.string (string key)
|
| _ -> catchAllFunc value
|
||||||
|> function it -> "@id", it
|
|
||||||
|
|
||||||
|
open BitBadger.Documents
|
||||||
|
|
||||||
|
/// Functions for creating parameters
|
||||||
|
[<AutoOpen>]
|
||||||
|
module Parameters =
|
||||||
|
|
||||||
|
/// Create an ID parameter (name "@id", key will be treated as a string)
|
||||||
|
[<CompiledName "Id">]
|
||||||
|
let idParam (key: 'TKey) =
|
||||||
|
"@id", parameterFor key (fun it -> Sql.string (string it))
|
||||||
|
|
||||||
/// Create a parameter with a JSON value
|
/// Create a parameter with a JSON value
|
||||||
[<CompiledName "Json">]
|
[<CompiledName "Json">]
|
||||||
|
@ -89,11 +92,13 @@ module Parameters =
|
||||||
| BT ->
|
| BT ->
|
||||||
let p = name.Derive it.ParameterName
|
let p = name.Derive it.ParameterName
|
||||||
let values = it.Value :?> obj list
|
let values = it.Value :?> obj list
|
||||||
yield ($"{p}min", Sql.parameter (NpgsqlParameter($"{p}min", List.head values)))
|
yield ($"{p}min",
|
||||||
yield ($"{p}max", Sql.parameter (NpgsqlParameter($"{p}max", List.last values)))
|
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
|
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.collect id
|
||||||
|> Seq.append parameters
|
|> Seq.append parameters
|
||||||
|> Seq.toList
|
|> Seq.toList
|
||||||
|
@ -131,27 +136,30 @@ module Query =
|
||||||
[<CompiledName "WhereByFields">]
|
[<CompiledName "WhereByFields">]
|
||||||
let whereByFields howMatched fields =
|
let whereByFields howMatched fields =
|
||||||
let name = ParameterName()
|
let name = ParameterName()
|
||||||
|
let isNumeric (it: obj) =
|
||||||
|
match it with
|
||||||
|
| :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64
|
||||||
|
| :? decimal | :? single | :? double -> true
|
||||||
|
| _ -> false
|
||||||
fields
|
fields
|
||||||
|> Seq.map (fun it ->
|
|> Seq.map (fun it ->
|
||||||
match it.Op with
|
match it.Op with
|
||||||
| EX | NEX -> $"{it.Path PostgreSQL} {it.Op}"
|
| EX | NEX -> $"{it.Path PostgreSQL} {it.Op}"
|
||||||
| BT ->
|
| _ ->
|
||||||
let p = name.Derive it.ParameterName
|
let p = name.Derive it.ParameterName
|
||||||
let path, value =
|
let param, value =
|
||||||
match it.Op with
|
match it.Op with
|
||||||
| BT -> $"{p}min AND {p}max", (it.Value :?> obj list)[0]
|
| BT -> $"{p}min AND {p}max", (it.Value :?> obj list)[0]
|
||||||
| _ -> p, it.Value
|
| _ -> p, it.Value
|
||||||
match value with
|
if isNumeric value then
|
||||||
| :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64
|
$"({it.Path PostgreSQL})::numeric {it.Op} {param}"
|
||||||
| :? decimal | :? single | :? double -> $"({it.Path PostgreSQL})::numeric {it.Op} {path}"
|
else $"{it.Path PostgreSQL} {it.Op} {param}")
|
||||||
| _ -> $"{it.Path PostgreSQL} {it.Op} {path}"
|
|
||||||
| _ -> $"{it.Path PostgreSQL} {it.Op} {name.Derive it.ParameterName}")
|
|
||||||
|> 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 an ID-based query
|
/// Create a WHERE clause fragment to implement an ID-based query
|
||||||
[<CompiledName "WhereById">]
|
[<CompiledName "WhereById">]
|
||||||
let whereById paramName =
|
let whereById<'TKey> (docId: 'TKey) =
|
||||||
whereByFields Any [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ]
|
whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ]
|
||||||
|
|
||||||
/// Table and index definition queries
|
/// Table and index definition queries
|
||||||
module Definition =
|
module Definition =
|
||||||
|
@ -191,9 +199,7 @@ module Query =
|
||||||
/// Create a query by a document's ID
|
/// Create a query by a document's ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId<'TKey> statement (docId: 'TKey) =
|
let byId<'TKey> statement (docId: 'TKey) =
|
||||||
Query.statementWhere
|
Query.statementWhere statement (whereById docId)
|
||||||
statement
|
|
||||||
(whereByFields Any [ { Field.EQ (Configuration.idField ()) docId with ParameterName = Some "@id" } ])
|
|
||||||
|
|
||||||
/// Create a query on JSON fields
|
/// Create a query on JSON fields
|
||||||
[<CompiledName "ByFields">]
|
[<CompiledName "ByFields">]
|
||||||
|
@ -359,7 +365,7 @@ module WithProps =
|
||||||
/// Determine if a document exists for the given ID
|
/// Determine if a document exists for the given ID
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) sqlProps =
|
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 (->> =)
|
/// Determine if a document exists using JSON field comparisons (->> =)
|
||||||
[<CompiledName "ByFields">]
|
[<CompiledName "ByFields">]
|
||||||
|
@ -537,9 +543,7 @@ module WithProps =
|
||||||
[<CompiledName "ById">]
|
[<CompiledName "ById">]
|
||||||
let byId tableName (docId: 'TKey) (document: 'TDoc) sqlProps =
|
let byId tableName (docId: 'TKey) (document: 'TDoc) sqlProps =
|
||||||
Custom.nonQuery
|
Custom.nonQuery
|
||||||
(Query.statementWhere (Query.update tableName) (Query.whereById "@id"))
|
(Query.byId (Query.update tableName) docId) [ idParam docId; jsonParam "@data" document ] sqlProps
|
||||||
[ idParam docId; jsonParam "@data" document ]
|
|
||||||
sqlProps
|
|
||||||
|
|
||||||
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
|
/// Update an entire document by its ID, using the provided function to obtain the ID from the document
|
||||||
[<CompiledName "FSharpByFunc">]
|
[<CompiledName "FSharpByFunc">]
|
||||||
|
|
|
@ -20,12 +20,93 @@ public static class PostgresCSharpTests
|
||||||
[
|
[
|
||||||
TestList("Parameters",
|
TestList("Parameters",
|
||||||
[
|
[
|
||||||
TestCase("Id succeeds", () =>
|
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);
|
var it = Parameters.Id(88);
|
||||||
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
|
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
|
||||||
Expect.equal(it.Item2, Sql.@string("88"), "ID parameter value incorrect");
|
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", () =>
|
TestCase("Json succeeds", () =>
|
||||||
{
|
{
|
||||||
var it = Parameters.Json("@test", new { Something = "good" });
|
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");
|
Expect.hasLength(paramList, 1, "There should have been a parameter added");
|
||||||
var (name, value) = paramList[0];
|
var (name, value) = paramList[0];
|
||||||
Expect.equal(name, "@field0", "Field parameter name not correct");
|
Expect.equal(name, "@field0", "Field parameter name not correct");
|
||||||
if (!value.IsParameter)
|
Expect.equal(value, Sql.@string("242"), "Field parameter value not correct");
|
||||||
{
|
|
||||||
Expect.isTrue(false, "The parameter was not a Parameter type");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when multiple independent parameters are added", () =>
|
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");
|
Expect.hasLength(paramList, 3, "There should have been 2 parameters added");
|
||||||
var (name, value) = paramList[0];
|
var (name, value) = paramList[0];
|
||||||
Expect.equal(name, "@id", "First field parameter name not correct");
|
Expect.equal(name, "@id", "First field parameter name not correct");
|
||||||
if (!value.IsString)
|
Expect.equal(value, Sql.@int(14), "First field parameter value not correct");
|
||||||
{
|
|
||||||
Expect.isTrue(false, "First parameter was not a String type");
|
|
||||||
}
|
|
||||||
(name, value) = paramList[1];
|
(name, value) = paramList[1];
|
||||||
Expect.equal(name, "@field0", "Second field parameter name not correct");
|
Expect.equal(name, "@field0", "Second field parameter name not correct");
|
||||||
if (!value.IsParameter)
|
Expect.equal(value, Sql.@string("you"), "Second field parameter value not correct");
|
||||||
{
|
|
||||||
Expect.isTrue(false, "Second parameter was not a Parameter type");
|
|
||||||
}
|
|
||||||
(name, value) = paramList[2];
|
(name, value) = paramList[2];
|
||||||
Expect.equal(name, "@field1", "Third parameter name not correct");
|
Expect.equal(name, "@field1", "Third parameter name not correct");
|
||||||
if (!value.IsParameter)
|
Expect.equal(value, Sql.@string("them"), "Third parameter value not correct");
|
||||||
{
|
|
||||||
Expect.isTrue(false, "Third parameter was not a Parameter type");
|
|
||||||
}
|
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a parameter is not added", () =>
|
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");
|
Expect.hasLength(paramList, 2, "There should have been 2 parameters added");
|
||||||
var (name, value) = paramList[0];
|
var (name, value) = paramList[0];
|
||||||
Expect.equal(name, "@testmin", "Minimum field name not correct");
|
Expect.equal(name, "@testmin", "Minimum field name not correct");
|
||||||
if (!value.IsParameter)
|
Expect.equal(value, Sql.@string("eh"), "Minimum field value not correct");
|
||||||
{
|
|
||||||
Expect.isTrue(false, "Minimum parameter was not a Parameter type");
|
|
||||||
}
|
|
||||||
(name, value) = paramList[1];
|
(name, value) = paramList[1];
|
||||||
Expect.equal(name, "@testmax", "Maximum field name not correct");
|
Expect.equal(name, "@testmax", "Maximum field name not correct");
|
||||||
if (!value.IsParameter)
|
Expect.equal(value, Sql.@string("zed"), "Maximum field value not correct");
|
||||||
{
|
|
||||||
Expect.isTrue(false, "Maximum parameter was not a Parameter type");
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
#pragma warning disable CS0618
|
#pragma warning disable CS0618
|
||||||
|
@ -101,7 +164,7 @@ public static class PostgresCSharpTests
|
||||||
var it = Parameters.AddField("@field", Field.EQ("it", "242"), []).ToList();
|
var it = Parameters.AddField("@field", Field.EQ("it", "242"), []).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.IsString, "Field parameter value incorrect");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds when a parameter is not added", () =>
|
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();
|
var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"), []).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.IsString, "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.IsString, "Maximum field parameter value incorrect");
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
#pragma warning restore CS0618
|
#pragma warning restore CS0618
|
||||||
|
@ -172,7 +235,7 @@ public static class PostgresCSharpTests
|
||||||
{
|
{
|
||||||
Expect.equal(
|
Expect.equal(
|
||||||
Postgres.Query.WhereByFields(FieldMatch.Any,
|
Postgres.Query.WhereByFields(FieldMatch.Any,
|
||||||
[Field.GT("theField", 0).WithParameterName("@test")]),
|
[Field.GT("theField", "0").WithParameterName("@test")]),
|
||||||
"data->>'theField' > @test", "WHERE clause not correct");
|
"data->>'theField' > @test", "WHERE clause not correct");
|
||||||
}),
|
}),
|
||||||
TestCase("succeeds for a single field when an existence operator is passed", () =>
|
TestCase("succeeds for a single field when an existence operator is passed", () =>
|
||||||
|
@ -206,7 +269,8 @@ public static class PostgresCSharpTests
|
||||||
Expect.equal(
|
Expect.equal(
|
||||||
Postgres.Query.WhereByFields(FieldMatch.Any,
|
Postgres.Query.WhereByFields(FieldMatch.Any,
|
||||||
[Field.NEX("thatField"), Field.GE("thisField", 18)]),
|
[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", () =>
|
TestCase("succeeds for all multiple fields with between operators", () =>
|
||||||
{
|
{
|
||||||
|
@ -217,10 +281,23 @@ public static class PostgresCSharpTests
|
||||||
"WHERE clause not correct");
|
"WHERE clause not correct");
|
||||||
})
|
})
|
||||||
]),
|
]),
|
||||||
TestCase("WhereById succeeds", () =>
|
TestList("WhereById",
|
||||||
|
[
|
||||||
|
TestCase("succeeds for numeric ID", () =>
|
||||||
{
|
{
|
||||||
Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
|
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",
|
TestList("Definition",
|
||||||
[
|
[
|
||||||
TestCase("EnsureTable succeeds", () =>
|
TestCase("EnsureTable succeeds", () =>
|
||||||
|
|
|
@ -11,9 +11,78 @@ open BitBadger.Documents.Tests
|
||||||
let unitTests =
|
let unitTests =
|
||||||
testList "Unit" [
|
testList "Unit" [
|
||||||
testList "Parameters" [
|
testList "Parameters" [
|
||||||
test "idParam succeeds" {
|
testList "idParam" [
|
||||||
Expect.equal (idParam 88) ("@id", Sql.string "88") "ID parameter not constructed correctly"
|
// 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" {
|
test "jsonParam succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(jsonParam "@test" {| Something = "good" |})
|
(jsonParam "@test" {| Something = "good" |})
|
||||||
|
@ -24,35 +93,20 @@ let unitTests =
|
||||||
test "succeeds when a parameter is added" {
|
test "succeeds when a parameter is added" {
|
||||||
let paramList = addFieldParams [ Field.EQ "it" "242" ] []
|
let paramList = addFieldParams [ Field.EQ "it" "242" ] []
|
||||||
Expect.hasLength paramList 1 "There should have been a parameter added"
|
Expect.hasLength paramList 1 "There should have been a parameter added"
|
||||||
let it = Seq.head paramList
|
let name, value = Seq.head paramList
|
||||||
Expect.equal (fst it) "@field0" "Field parameter name not correct"
|
Expect.equal name "@field0" "Field parameter name not correct"
|
||||||
match snd it with
|
Expect.equal value (Sql.string "242") "Parameter value not correct"
|
||||||
| 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"
|
|
||||||
}
|
}
|
||||||
test "succeeds when multiple independent parameters are added" {
|
test "succeeds when multiple independent parameters are added" {
|
||||||
let paramList = addFieldParams [ Field.EQ "me" "you"; Field.GT "us" "them" ] [ idParam 14 ]
|
let paramList = addFieldParams [ Field.EQ "me" "you"; Field.GT "us" "them" ] [ idParam 14 ]
|
||||||
Expect.hasLength paramList 3 "There should have been 2 parameters added"
|
Expect.hasLength paramList 3 "There should have been 2 parameters added"
|
||||||
let p = Array.ofSeq paramList
|
let p = Array.ofSeq paramList
|
||||||
Expect.equal (fst p[0]) "@id" "First field parameter name not correct"
|
Expect.equal (fst p[0]) "@id" "First field parameter name not correct"
|
||||||
match snd p[0] with
|
Expect.equal (snd p[0]) (Sql.int 14) "First parameter value not correct"
|
||||||
| SqlValue.String value ->
|
|
||||||
Expect.equal value "14" "First parameter value not correct"
|
|
||||||
| _ -> Expect.isTrue false "First parameter was not a String type"
|
|
||||||
Expect.equal (fst p[1]) "@field0" "Second field parameter name not correct"
|
Expect.equal (fst p[1]) "@field0" "Second field parameter name not correct"
|
||||||
match snd p[1] with
|
Expect.equal (snd p[1]) (Sql.string "you") "Second parameter value not correct"
|
||||||
| 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 (fst p[2]) "@field1" "Third parameter name not correct"
|
Expect.equal (fst p[2]) "@field1" "Third parameter name not correct"
|
||||||
match snd p[2] with
|
Expect.equal (snd p[2]) (Sql.string "them") "Third parameter value not correct"
|
||||||
| 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"
|
|
||||||
}
|
}
|
||||||
test "succeeds when a parameter is not added" {
|
test "succeeds when a parameter is not added" {
|
||||||
let paramList = addFieldParams [ Field.EX "tacos" ] []
|
let paramList = addFieldParams [ Field.EX "tacos" ] []
|
||||||
|
@ -62,33 +116,21 @@ let unitTests =
|
||||||
let paramList =
|
let paramList =
|
||||||
addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] []
|
addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] []
|
||||||
Expect.hasLength paramList 2 "There should have been 2 parameters added"
|
Expect.hasLength paramList 2 "There should have been 2 parameters added"
|
||||||
let min = Seq.head paramList
|
let name, value = Seq.head paramList
|
||||||
Expect.equal (fst min) "@testmin" "Minimum field name not correct"
|
Expect.equal name "@testmin" "Minimum field name not correct"
|
||||||
match snd min with
|
Expect.equal value (Sql.string "eh") "Minimum parameter value not correct"
|
||||||
| SqlValue.Parameter value ->
|
let name, value = paramList |> Seq.skip 1 |> Seq.head
|
||||||
Expect.equal value.ParameterName "@testmin" "Minimum parameter name not correct"
|
Expect.equal name "@testmax" "Maximum field name not correct"
|
||||||
Expect.equal value.Value "eh" "Minimum parameter value not correct"
|
Expect.equal value (Sql.string "zed") "Maximum 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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "addFieldParam" [
|
testList "addFieldParam" [
|
||||||
test "succeeds when a parameter is added" {
|
test "succeeds when a parameter is added" {
|
||||||
let paramList = addFieldParam "@field" (Field.EQ "it" "242") []
|
let paramList = addFieldParam "@field" (Field.EQ "it" "242") []
|
||||||
Expect.hasLength paramList 1 "There should have been a parameter added"
|
Expect.hasLength paramList 1 "There should have been a parameter added"
|
||||||
let it = Seq.head paramList
|
let name, value = Seq.head paramList
|
||||||
Expect.equal (fst it) "@field" "Field parameter name not correct"
|
Expect.equal name "@field" "Field parameter name not correct"
|
||||||
match snd it with
|
Expect.equal value (Sql.string "242") "Parameter value not correct"
|
||||||
| 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"
|
|
||||||
}
|
}
|
||||||
test "succeeds when a parameter is not added" {
|
test "succeeds when a parameter is not added" {
|
||||||
let paramList = addFieldParam "@field" (Field.EX "tacos") []
|
let paramList = addFieldParam "@field" (Field.EX "tacos") []
|
||||||
|
@ -97,54 +139,36 @@ let unitTests =
|
||||||
test "succeeds when two parameters are added" {
|
test "succeeds when two parameters are added" {
|
||||||
let paramList = addFieldParam "@field" (Field.BT "that" "eh" "zed") []
|
let paramList = addFieldParam "@field" (Field.BT "that" "eh" "zed") []
|
||||||
Expect.hasLength paramList 2 "There should have been 2 parameters added"
|
Expect.hasLength paramList 2 "There should have been 2 parameters added"
|
||||||
let min = Seq.head paramList
|
let name, value = Seq.head paramList
|
||||||
Expect.equal (fst min) "@fieldmin" "Minimum field name not correct"
|
Expect.equal name "@fieldmin" "Minimum field name not correct"
|
||||||
match snd min with
|
Expect.equal value (Sql.string "eh") "Minimum parameter value not correct"
|
||||||
| SqlValue.Parameter value ->
|
let name, value = paramList |> Seq.skip 1 |> Seq.head
|
||||||
Expect.equal value.ParameterName "@fieldmin" "Minimum parameter name not correct"
|
Expect.equal name "@fieldmax" "Maximum field name not correct"
|
||||||
Expect.equal value.Value "eh" "Minimum parameter value not correct"
|
Expect.equal value (Sql.string "zed") "Maximum 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"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "fieldNameParams" [
|
testList "fieldNameParams" [
|
||||||
test "succeeds for one name" {
|
test "succeeds for one name" {
|
||||||
let name, value = fieldNameParams [ "bob" ]
|
let name, value = fieldNameParams [ "bob" ]
|
||||||
Expect.equal name "@name" "The parameter name was incorrect"
|
Expect.equal name "@name" "The parameter name was incorrect"
|
||||||
match value with
|
Expect.equal value (Sql.string "bob") "The parameter value was incorrect"
|
||||||
| SqlValue.String it -> Expect.equal it "bob" "The parameter value was incorrect"
|
|
||||||
| _ -> Expect.isTrue false "The parameter was not a String type"
|
|
||||||
}
|
}
|
||||||
test "succeeds for multiple names" {
|
test "succeeds for multiple names" {
|
||||||
let name, value = fieldNameParams [ "bob"; "tom"; "mike" ]
|
let name, value = fieldNameParams [ "bob"; "tom"; "mike" ]
|
||||||
Expect.equal name "@name" "The parameter name was incorrect"
|
Expect.equal name "@name" "The parameter name was incorrect"
|
||||||
match value with
|
Expect.equal value (Sql.stringArray [| "bob"; "tom"; "mike" |]) "The parameter value was incorrect"
|
||||||
| SqlValue.StringArray it ->
|
|
||||||
Expect.equal it [| "bob"; "tom"; "mike" |] "The parameter value was incorrect"
|
|
||||||
| _ -> Expect.isTrue false "The parameter was not a StringArray type"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
testList "fieldNameParam" [
|
testList "fieldNameParam" [
|
||||||
test "succeeds for one name" {
|
test "succeeds for one name" {
|
||||||
let name, value = fieldNameParam [ "bob" ]
|
let name, value = fieldNameParam [ "bob" ]
|
||||||
Expect.equal name "@name" "The parameter name was incorrect"
|
Expect.equal name "@name" "The parameter name was incorrect"
|
||||||
match value with
|
Expect.equal value (Sql.string "bob") "The parameter value was incorrect"
|
||||||
| SqlValue.String it -> Expect.equal it "bob" "The parameter value was incorrect"
|
|
||||||
| _ -> Expect.isTrue false "The parameter was not a String type"
|
|
||||||
}
|
}
|
||||||
test "succeeds for multiple names" {
|
test "succeeds for multiple names" {
|
||||||
let name, value = fieldNameParam [ "bob"; "tom"; "mike" ]
|
let name, value = fieldNameParam [ "bob"; "tom"; "mike" ]
|
||||||
Expect.equal name "@name" "The parameter name was incorrect"
|
Expect.equal name "@name" "The parameter name was incorrect"
|
||||||
match value with
|
Expect.equal value (Sql.stringArray [| "bob"; "tom"; "mike" |]) "The parameter value was incorrect"
|
||||||
| SqlValue.StringArray it ->
|
|
||||||
Expect.equal it [| "bob"; "tom"; "mike" |] "The parameter value was incorrect"
|
|
||||||
| _ -> Expect.isTrue false "The parameter was not a StringArray type"
|
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
test "noParams succeeds" {
|
test "noParams succeeds" {
|
||||||
|
@ -155,7 +179,7 @@ let unitTests =
|
||||||
testList "whereByFields" [
|
testList "whereByFields" [
|
||||||
test "succeeds for a single field when a logical operator is passed" {
|
test "succeeds for a single field when a logical operator is passed" {
|
||||||
Expect.equal
|
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"
|
"data->>'theField' > @test"
|
||||||
"WHERE clause not correct"
|
"WHERE clause not correct"
|
||||||
}
|
}
|
||||||
|
@ -186,7 +210,7 @@ let unitTests =
|
||||||
test "succeeds for any multiple fields with an existence operator" {
|
test "succeeds for any multiple fields with an existence operator" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
(Query.whereByFields Any [ Field.NEX "thatField"; Field.GE "thisField" 18 ])
|
(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"
|
"WHERE clause not correct"
|
||||||
}
|
}
|
||||||
test "succeeds for all multiple fields with between operators" {
|
test "succeeds for all multiple fields with between operators" {
|
||||||
|
@ -196,9 +220,20 @@ let unitTests =
|
||||||
"WHERE clause not correct"
|
"WHERE clause not correct"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
test "whereById succeeds" {
|
testList "whereById" [
|
||||||
Expect.equal (Query.whereById "@id") "data->>'Id' = @id" "WHERE clause not correct"
|
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" [
|
testList "Definition" [
|
||||||
test "ensureTable succeeds" {
|
test "ensureTable succeeds" {
|
||||||
Expect.equal
|
Expect.equal
|
||||||
|
@ -505,7 +540,7 @@ let integrationTests =
|
||||||
Expect.isFalse exists "There should not have been an existing document"
|
Expect.isFalse exists "There should not have been an existing document"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
ftestList "byFields" [
|
testList "byFields" [
|
||||||
testTask "succeeds when documents exist" {
|
testTask "succeeds when documents exist" {
|
||||||
use db = PostgresDb.BuildDb()
|
use db = PostgresDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
@ -621,7 +656,7 @@ let integrationTests =
|
||||||
PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ]
|
PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ]
|
||||||
Expect.equal (List.length docs) 1 "There should have been one document returned"
|
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()
|
use db = PostgresDb.BuildDb()
|
||||||
do! loadDocs ()
|
do! loadDocs ()
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user