Version 4 #6
@ -97,6 +97,18 @@ type Field = {
|
||||
member this.WithQualifier 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
|
||||
[<Struct>]
|
||||
@ -107,6 +119,20 @@ type FieldMatch =
|
||||
| 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
|
||||
type IDocumentSerializer =
|
||||
|
||||
|
@ -110,34 +110,27 @@ module Parameters =
|
||||
module Query =
|
||||
|
||||
/// Create a WHERE clause fragment to implement a comparison on fields in a JSON document
|
||||
[<CompiledName "WhereByFields">]
|
||||
[<CompiledName "FSharpWhereByFields">]
|
||||
let whereByFields fields howMatched =
|
||||
let mutable idx = 0
|
||||
let nameField () =
|
||||
let name = $"field{idx}"
|
||||
idx <- idx + 1
|
||||
name
|
||||
let name = ParameterName()
|
||||
fields
|
||||
|> 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
|
||||
| EX | NEX -> $"{column} {it.Op}"
|
||||
| EX | NEX -> $"{it.PgSqlPath} {it.Op}"
|
||||
| BT ->
|
||||
let p = defaultArg it.ParameterName (nameField ())
|
||||
let p = name.Derive it.ParameterName
|
||||
let names = $"{p}min AND {p}max"
|
||||
let values = it.Value :?> obj list
|
||||
match values[0] with
|
||||
| :? int8 | :? uint8 | :? int16 | :? uint16 | :? int | :? uint32 | :? int64 | :? uint64
|
||||
| :? decimal | :? single | :? double -> $"({column})::numeric {it.Op} {names}"
|
||||
| _ -> $"{column} {it.Op} {names}"
|
||||
| _ ->
|
||||
let p = defaultArg it.ParameterName (nameField ())
|
||||
$"{column} {it.Op} {p}")
|
||||
| :? decimal | :? single | :? double -> $"({it.PgSqlPath})::numeric {it.Op} {names}"
|
||||
| _ -> $"{it.PgSqlPath} {it.Op} {names}"
|
||||
| _ -> $"{it.PgSqlPath} {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
|
||||
[<CompiledName "WhereByField">]
|
||||
|
@ -31,20 +31,33 @@ module Configuration =
|
||||
[<RequireQualifiedAccess>]
|
||||
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
|
||||
[<CompiledName "WhereByField">]
|
||||
let whereByField field paramName =
|
||||
let theRest =
|
||||
match field.Op with
|
||||
| EX | NEX -> ""
|
||||
| BT -> $" {paramName}min AND {paramName}max"
|
||||
| _ -> $" %s{paramName}"
|
||||
$"data->>'{field.Name}' {field.Op}{theRest}"
|
||||
whereByFields [ { field with ParameterName = Some paramName } ] Any
|
||||
|
||||
/// Create a WHERE clause fragment to implement an ID-based query
|
||||
[<CompiledName "WhereById">]
|
||||
let whereById paramName =
|
||||
whereByField (Field.EQ (Configuration.idField ()) 0) paramName
|
||||
whereByFields [ { Field.EQ (Configuration.idField ()) 0 with ParameterName = Some paramName } ] Any
|
||||
|
||||
/// Data definition
|
||||
module Definition =
|
||||
|
@ -3,6 +3,7 @@
|
||||
<PropertyGroup>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<LangVersion>latest</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
@ -24,11 +24,11 @@ public static class CommonCSharpTests
|
||||
/// Unit tests
|
||||
/// </summary>
|
||||
[Tests]
|
||||
public static readonly Test Unit = TestList("Common.C# Unit", new[]
|
||||
{
|
||||
public static readonly Test Unit = TestList("Common.C# Unit",
|
||||
[
|
||||
TestSequenced(
|
||||
TestList("Configuration", new[]
|
||||
{
|
||||
TestList("Configuration",
|
||||
[
|
||||
TestCase("UseSerializer succeeds", () =>
|
||||
{
|
||||
try
|
||||
@ -70,9 +70,9 @@ public static class CommonCSharpTests
|
||||
Configuration.UseIdField("Id");
|
||||
}
|
||||
})
|
||||
})),
|
||||
TestList("Op", new[]
|
||||
{
|
||||
])),
|
||||
TestList("Op",
|
||||
[
|
||||
TestCase("EQ succeeds", () =>
|
||||
{
|
||||
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");
|
||||
})
|
||||
}),
|
||||
TestList("Field", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Field",
|
||||
[
|
||||
TestCase("EQ succeeds", () =>
|
||||
{
|
||||
var field = Field.EQ("Test", 14);
|
||||
@ -159,7 +159,7 @@ public static class CommonCSharpTests
|
||||
var field = Field.BT("Age", 18, 49);
|
||||
Expect.equal(field.Name, "Age", "Field name 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", () =>
|
||||
{
|
||||
@ -172,25 +172,83 @@ public static class CommonCSharpTests
|
||||
var field = Field.NEX("Rad");
|
||||
Expect.equal(field.Name, "Rad", "Field name incorrect");
|
||||
Expect.equal(field.Op, Op.NEX, "Operator incorrect");
|
||||
})
|
||||
}),
|
||||
TestList("Query", new[]
|
||||
{
|
||||
}),
|
||||
TestCase("WithParameterName succeeds", () =>
|
||||
{
|
||||
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", () =>
|
||||
{
|
||||
Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table",
|
||||
"SELECT statement not correct");
|
||||
}),
|
||||
TestList("Definition", new[]
|
||||
{
|
||||
TestList("Definition",
|
||||
[
|
||||
TestCase("EnsureTableFor succeeds", () =>
|
||||
{
|
||||
Expect.equal(Query.Definition.EnsureTableFor("my.table", "JSONB"),
|
||||
"CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)",
|
||||
"CREATE TABLE statement not constructed correctly");
|
||||
}),
|
||||
TestList("EnsureKey", new[]
|
||||
{
|
||||
TestList("EnsureKey",
|
||||
[
|
||||
TestCase("succeeds when a schema is present", () =>
|
||||
{
|
||||
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 INDEX for key statement without schema not constructed correctly");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("EnsureIndexOn succeeds for multiple fields and directions", () =>
|
||||
{
|
||||
Expect.equal(
|
||||
@ -213,7 +271,7 @@ public static class CommonCSharpTests
|
||||
+ "((data ->> 'taco'), (data ->> 'guac') DESC, (data ->> 'salsa') ASC)",
|
||||
"CREATE INDEX for multiple field statement incorrect");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("Insert succeeds", () =>
|
||||
{
|
||||
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 ON CONFLICT UPDATE statement not correct");
|
||||
})
|
||||
})
|
||||
});
|
||||
])
|
||||
]);
|
||||
}
|
||||
|
@ -16,10 +16,10 @@ public static class PostgresCSharpTests
|
||||
/// <summary>
|
||||
/// Tests which do not hit the database
|
||||
/// </summary>
|
||||
private static readonly Test Unit = TestList("Unit", new[]
|
||||
{
|
||||
TestList("Parameters", new[]
|
||||
{
|
||||
private static readonly Test Unit = TestList("Unit",
|
||||
[
|
||||
TestList("Parameters",
|
||||
[
|
||||
TestCase("Id succeeds", () =>
|
||||
{
|
||||
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.Item2, Sql.jsonb("{\"Something\":\"good\"}"), "JSON parameter value incorrect");
|
||||
}),
|
||||
TestList("AddField", new []
|
||||
{
|
||||
TestList("AddField",
|
||||
[
|
||||
TestCase("succeeds when a parameter is added", () =>
|
||||
{
|
||||
var it = Parameters
|
||||
.AddField("@field", Field.EQ("it", "242"), Enumerable.Empty<Tuple<string, SqlValue>>())
|
||||
.ToList();
|
||||
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");
|
||||
}),
|
||||
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");
|
||||
}),
|
||||
TestCase("succeeds when two parameters are added", () =>
|
||||
{
|
||||
var it = Parameters.AddField("@field", Field.BT("that", "eh", "zed"),
|
||||
Enumerable.Empty<Tuple<string, SqlValue>>()).ToList();
|
||||
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.equal(it[1].Item1, "@fieldmax", "Maximum field name not correct");
|
||||
Expect.isTrue(it[1].Item2.IsParameter, "Maximum field parameter value incorrect");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Query", new[]
|
||||
{
|
||||
TestList("WhereByField", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Query",
|
||||
[
|
||||
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", () =>
|
||||
{
|
||||
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"),
|
||||
"data->>'field0' BETWEEN @alphamin AND @alphamax", "WHERE clause not correct");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("WhereById succeeds", () =>
|
||||
{
|
||||
Expect.equal(Postgres.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct");
|
||||
}),
|
||||
TestList("Definition", new[]
|
||||
{
|
||||
TestList("Definition",
|
||||
[
|
||||
TestCase("EnsureTable succeeds", () =>
|
||||
{
|
||||
Expect.equal(Postgres.Query.Definition.EnsureTable(PostgresDb.TableName),
|
||||
@ -112,7 +160,7 @@ public static class PostgresCSharpTests
|
||||
PostgresDb.TableName),
|
||||
"CREATE INDEX statement not constructed correctly");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("Update succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"WHERE clause not correct");
|
||||
}),
|
||||
TestList("Count", new[]
|
||||
{
|
||||
TestList("Count",
|
||||
[
|
||||
TestCase("All succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"JSON Path match count query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Exists", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Exists",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"JSON Path match existence query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Find", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Find",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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 by JSON Path match query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Patch", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Patch",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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 partial by JSON Path statement not correct");
|
||||
})
|
||||
}),
|
||||
TestList("RemoveFields", new[]
|
||||
{
|
||||
]),
|
||||
TestList("RemoveFields",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"Remove field by JSON path query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Delete", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Delete",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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 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 = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } },
|
||||
new() { Id = "three", Value = "", NumValue = 4 },
|
||||
new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } },
|
||||
new() { Id = "five", Value = "purple", NumValue = 18 }
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// Add the test documents to the database
|
||||
@ -312,10 +360,10 @@ public static class PostgresCSharpTests
|
||||
/// <summary>
|
||||
/// Integration tests for the PostgreSQL library
|
||||
/// </summary>
|
||||
private static readonly Test Integration = TestList("Integration", new[]
|
||||
{
|
||||
TestList("Configuration", new[]
|
||||
{
|
||||
private static readonly Test Integration = TestList("Integration",
|
||||
[
|
||||
TestList("Configuration",
|
||||
[
|
||||
TestCase("UseDataSource disposes existing source", () =>
|
||||
{
|
||||
using var db1 = ThrowawayDatabase.Create(PostgresDb.ConnStr.Value);
|
||||
@ -343,11 +391,11 @@ public static class PostgresCSharpTests
|
||||
Expect.isTrue(ReferenceEquals(source, Postgres.Configuration.DataSource()),
|
||||
"Data source should have been the same");
|
||||
})
|
||||
}),
|
||||
TestList("Custom", new[]
|
||||
{
|
||||
TestList("List", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Custom",
|
||||
[
|
||||
TestList("List",
|
||||
[
|
||||
TestCase("succeeds when data is found", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -368,9 +416,9 @@ public static class PostgresCSharpTests
|
||||
Results.FromData<JsonDocument>);
|
||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("Single", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Single",
|
||||
[
|
||||
TestCase("succeeds when a row is found", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -390,9 +438,9 @@ public static class PostgresCSharpTests
|
||||
new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData<JsonDocument>);
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
}),
|
||||
TestList("NonQuery", new[]
|
||||
{
|
||||
]),
|
||||
TestList("NonQuery",
|
||||
[
|
||||
TestCase("succeeds when operating on data", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -414,7 +462,7 @@ public static class PostgresCSharpTests
|
||||
var remaining = await Count.All(PostgresDb.TableName);
|
||||
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("Scalar succeeds", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.equal(nbr, 5, "The query should have returned the number 5");
|
||||
})
|
||||
}),
|
||||
TestList("Definition", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Definition",
|
||||
[
|
||||
TestCase("EnsureTable succeeds", async () =>
|
||||
{
|
||||
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 alsoExists = await keyExists();
|
||||
var exists = await TableExists();
|
||||
var alsoExists = await KeyExists();
|
||||
Expect.isFalse(exists, "The table should not exist already");
|
||||
Expect.isFalse(alsoExists, "The key index should not exist already");
|
||||
|
||||
await Definition.EnsureTable("ensured");
|
||||
exists = await tableExists();
|
||||
alsoExists = await keyExists();
|
||||
exists = await TableExists();
|
||||
alsoExists = await KeyExists();
|
||||
Expect.isTrue(exists, "The table 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 () =>
|
||||
{
|
||||
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");
|
||||
|
||||
await Definition.EnsureTable("ensured");
|
||||
await Definition.EnsureDocumentIndex("ensured", DocumentIndex.Optimized);
|
||||
exists = await indexExists();
|
||||
exists = await IndexExists();
|
||||
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 () =>
|
||||
{
|
||||
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");
|
||||
|
||||
await Definition.EnsureTable("ensured");
|
||||
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
|
||||
exists = await indexExists();
|
||||
exists = await IndexExists();
|
||||
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("Insert", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Document",
|
||||
[
|
||||
TestList("Insert",
|
||||
[
|
||||
TestCase("succeeds", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -506,9 +560,9 @@ public static class PostgresCSharpTests
|
||||
// This is what should have happened
|
||||
}
|
||||
})
|
||||
}),
|
||||
TestList("Save", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Save",
|
||||
[
|
||||
TestCase("succeeds when a document is inserted", async () =>
|
||||
{
|
||||
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.Sub!.Foo, "c", "The updated document is not correct");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Count", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Count",
|
||||
[
|
||||
TestCase("All succeeds", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -581,11 +635,11 @@ public static class PostgresCSharpTests
|
||||
var theCount = await Count.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)");
|
||||
Expect.equal(theCount, 3, "There should have been 3 matching documents");
|
||||
})
|
||||
}),
|
||||
TestList("Exists", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Exists",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document exists", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -602,9 +656,9 @@ public static class PostgresCSharpTests
|
||||
var exists = await Exists.ById(PostgresDb.TableName, "seven");
|
||||
Expect.isFalse(exists, "There should not have been an existing document");
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when documents exist", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.isFalse(exists, "There should not have been existing documents");
|
||||
})
|
||||
}),
|
||||
TestList("ByContains", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByContains",
|
||||
[
|
||||
TestCase("succeeds when documents exist", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -640,8 +694,9 @@ public static class PostgresCSharpTests
|
||||
var exists = await Exists.ByContains(PostgresDb.TableName, new { Nothing = "none" });
|
||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||
})
|
||||
}),
|
||||
TestList("ByJsonPath", new[] {
|
||||
]),
|
||||
TestList("ByJsonPath",
|
||||
[
|
||||
TestCase("succeeds when documents exist", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -658,12 +713,12 @@ public static class PostgresCSharpTests
|
||||
var exists = await Exists.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 1000)");
|
||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Find", new[]
|
||||
{
|
||||
TestList("All", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Find",
|
||||
[
|
||||
TestList("All",
|
||||
[
|
||||
TestCase("succeeds when there is data", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -681,9 +736,9 @@ public static class PostgresCSharpTests
|
||||
var results = await Find.All<SubDocument>(PostgresDb.TableName);
|
||||
Expect.isEmpty(results, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("ById", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is found", async () =>
|
||||
{
|
||||
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");
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when documents are found", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("ByContains", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByContains",
|
||||
[
|
||||
TestCase("succeeds when documents are found", async () =>
|
||||
{
|
||||
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" });
|
||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("ByJsonPath", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByJsonPath",
|
||||
[
|
||||
TestCase("succeeds when documents are found", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -759,9 +814,9 @@ public static class PostgresCSharpTests
|
||||
var docs = await Find.ByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 0)");
|
||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("FirstByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("FirstByField",
|
||||
[
|
||||
TestCase("succeeds when a document is found", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
}),
|
||||
TestList("FirstByContains", new[]
|
||||
{
|
||||
]),
|
||||
TestList("FirstByContains",
|
||||
[
|
||||
TestCase("succeeds when a document is found", async () =>
|
||||
{
|
||||
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" });
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
}),
|
||||
TestList("FirstByJsonPath", new[]
|
||||
{
|
||||
]),
|
||||
TestList("FirstByJsonPath",
|
||||
[
|
||||
TestCase("succeeds when a document is found", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -849,12 +904,12 @@ public static class PostgresCSharpTests
|
||||
var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")");
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Update", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Update",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -882,9 +937,9 @@ public static class PostgresCSharpTests
|
||||
await Update.ById(PostgresDb.TableName, "test",
|
||||
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
||||
})
|
||||
}),
|
||||
TestList("ByFunc", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByFunc",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -910,12 +965,12 @@ public static class PostgresCSharpTests
|
||||
await Update.ByFunc(PostgresDb.TableName, doc => doc.Id,
|
||||
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Patch", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Patch",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -936,9 +991,9 @@ public static class PostgresCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await Patch.ById(PostgresDb.TableName, "test", new { Foo = "green" });
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -958,9 +1013,9 @@ public static class PostgresCSharpTests
|
||||
// This not raising an exception is the test
|
||||
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 () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -980,9 +1035,9 @@ public static class PostgresCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await Patch.ByContains(PostgresDb.TableName, new { Value = "burgundy" }, new { Foo = "green" });
|
||||
})
|
||||
}),
|
||||
TestList("ByJsonPath", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByJsonPath",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1002,12 +1057,12 @@ public static class PostgresCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await Patch.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" });
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("RemoveFields", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("RemoveFields",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when multiple fields are removed", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1045,9 +1100,9 @@ public static class PostgresCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Value" });
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when multiple fields are removed", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1087,9 +1142,9 @@ public static class PostgresCSharpTests
|
||||
await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"),
|
||||
new[] { "Value" });
|
||||
})
|
||||
}),
|
||||
TestList("ByContains", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByContains",
|
||||
[
|
||||
TestCase("succeeds when multiple fields are removed", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1129,9 +1184,9 @@ public static class PostgresCSharpTests
|
||||
await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" },
|
||||
new[] { "Value" });
|
||||
})
|
||||
}),
|
||||
TestList("ByJsonPath", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByJsonPath",
|
||||
[
|
||||
TestCase("succeeds when multiple fields are removed", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1171,12 +1226,12 @@ public static class PostgresCSharpTests
|
||||
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")",
|
||||
new[] { "Value" });
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Delete", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Delete",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is deleted", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1195,9 +1250,9 @@ public static class PostgresCSharpTests
|
||||
var remaining = await Count.All(PostgresDb.TableName);
|
||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when documents are deleted", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1216,9 +1271,9 @@ public static class PostgresCSharpTests
|
||||
var remaining = await Count.All(PostgresDb.TableName);
|
||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||
})
|
||||
}),
|
||||
TestList("ByContains", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByContains",
|
||||
[
|
||||
TestCase("succeeds when documents are deleted", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1237,9 +1292,9 @@ public static class PostgresCSharpTests
|
||||
var remaining = await Count.All(PostgresDb.TableName);
|
||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||
})
|
||||
}),
|
||||
TestList("ByJsonPath", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByJsonPath",
|
||||
[
|
||||
TestCase("succeeds when documents are deleted", async () =>
|
||||
{
|
||||
await using var db = PostgresDb.BuildDb();
|
||||
@ -1258,13 +1313,13 @@ public static class PostgresCSharpTests
|
||||
var remaining = await Count.All(PostgresDb.TableName);
|
||||
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
|
||||
})
|
||||
})
|
||||
})
|
||||
});
|
||||
])
|
||||
])
|
||||
]);
|
||||
|
||||
/// <summary>
|
||||
/// All Postgres C# tests
|
||||
/// </summary>
|
||||
[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 Microsoft.Data.Sqlite;
|
||||
using Microsoft.FSharp.Core;
|
||||
@ -17,12 +16,56 @@ public static class SqliteCSharpTests
|
||||
/// <summary>
|
||||
/// Unit tests for the SQLite library
|
||||
/// </summary>
|
||||
private static readonly Test Unit = TestList("Unit", new[]
|
||||
{
|
||||
TestList("Query", new[]
|
||||
{
|
||||
TestList("WhereByField", new[]
|
||||
{
|
||||
private static readonly Test Unit = TestList("Unit",
|
||||
[
|
||||
TestList("Query",
|
||||
[
|
||||
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", () =>
|
||||
{
|
||||
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"),
|
||||
"data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("WhereById succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"UPDATE full statement not correct");
|
||||
}),
|
||||
TestList("Count", new[]
|
||||
{
|
||||
TestList("Count",
|
||||
[
|
||||
TestCase("All succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"JSON field text comparison count query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Exists", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Exists",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"JSON field text comparison exists query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Find", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Find",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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 by JSON comparison query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Patch", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Patch",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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 partial by JSON comparison query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("RemoveFields", new[]
|
||||
{
|
||||
]),
|
||||
TestList("RemoveFields",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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",
|
||||
"Remove field by field query not correct");
|
||||
})
|
||||
}),
|
||||
TestList("Delete", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Delete",
|
||||
[
|
||||
TestCase("ById succeeds", () =>
|
||||
{
|
||||
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")),
|
||||
"DELETE FROM tbl WHERE data->>'gone' IS NULL", "DELETE by JSON comparison query not correct");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Parameters", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Parameters",
|
||||
[
|
||||
TestCase("Id succeeds", () =>
|
||||
{
|
||||
var theParam = Parameters.Id(7);
|
||||
@ -157,8 +200,7 @@ public static class SqliteCSharpTests
|
||||
}),
|
||||
TestCase("AddField succeeds when adding a parameter", () =>
|
||||
{
|
||||
var paramList = Parameters.AddField("@field", Field.EQ("it", 99), Enumerable.Empty<SqliteParameter>())
|
||||
.ToList();
|
||||
var paramList = Parameters.AddField("@field", Field.EQ("it", 99), []).ToList();
|
||||
Expect.hasLength(paramList, 1, "There should have been a parameter added");
|
||||
var theParam = paramList[0];
|
||||
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", () =>
|
||||
{
|
||||
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");
|
||||
}),
|
||||
TestCase("None succeeds", () =>
|
||||
{
|
||||
Expect.isEmpty(Parameters.None, "The parameter list should have been empty");
|
||||
})
|
||||
})
|
||||
])
|
||||
// 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 = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } },
|
||||
new() { Id = "three", Value = "", NumValue = 4 },
|
||||
new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } },
|
||||
new() { Id = "five", Value = "purple", NumValue = 18 }
|
||||
};
|
||||
];
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
|
||||
private static readonly Test Integration = TestList("Integration", new[]
|
||||
{
|
||||
private static readonly Test Integration = TestList("Integration",
|
||||
[
|
||||
TestCase("Configuration.UseConnectionString succeeds", () =>
|
||||
{
|
||||
try
|
||||
@ -209,10 +251,10 @@ public static class SqliteCSharpTests
|
||||
Sqlite.Configuration.UseConnectionString("Data Source=:memory:");
|
||||
}
|
||||
}),
|
||||
TestList("Custom", new[]
|
||||
{
|
||||
TestList("Single", new[]
|
||||
{
|
||||
TestList("Custom",
|
||||
[
|
||||
TestList("Single",
|
||||
[
|
||||
TestCase("succeeds when a row is found", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -232,9 +274,9 @@ public static class SqliteCSharpTests
|
||||
new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>);
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
}),
|
||||
TestList("List", new[]
|
||||
{
|
||||
]),
|
||||
TestList("List",
|
||||
[
|
||||
TestCase("succeeds when data is found", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -254,9 +296,9 @@ public static class SqliteCSharpTests
|
||||
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>);
|
||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("NonQuery", new[]
|
||||
{
|
||||
]),
|
||||
TestList("NonQuery",
|
||||
[
|
||||
TestCase("succeeds when operating on data", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -278,7 +320,7 @@ public static class SqliteCSharpTests
|
||||
var remaining = await Count.All(SqliteDb.TableName);
|
||||
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
|
||||
})
|
||||
}),
|
||||
]),
|
||||
TestCase("Scalar succeeds", async () =>
|
||||
{
|
||||
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));
|
||||
Expect.equal(nbr, 5, "The query should have returned the number 5");
|
||||
})
|
||||
}),
|
||||
TestList("Definition", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Definition",
|
||||
[
|
||||
TestCase("EnsureTable succeeds", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -316,21 +358,23 @@ public static class SqliteCSharpTests
|
||||
TestCase("EnsureFieldIndex succeeds", async () =>
|
||||
{
|
||||
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");
|
||||
|
||||
await Definition.EnsureTable("ensured");
|
||||
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
|
||||
exists = await indexExists();
|
||||
exists = await IndexExists();
|
||||
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 () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -355,9 +399,9 @@ public static class SqliteCSharpTests
|
||||
// This is what is supposed to happen
|
||||
}
|
||||
})
|
||||
}),
|
||||
TestList("Document.Save", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Document.Save",
|
||||
[
|
||||
TestCase("succeeds when a document is inserted", async () =>
|
||||
{
|
||||
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.isNull(after.Sub, "There should not have been a sub-document in the updated document");
|
||||
})
|
||||
}),
|
||||
TestList("Count", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Count",
|
||||
[
|
||||
TestCase("All succeeds", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.equal(theCount, 1L, "There should have been 1 matching document");
|
||||
})
|
||||
}),
|
||||
TestList("Exists", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
]),
|
||||
TestList("Exists",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document exists", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -436,9 +480,9 @@ public static class SqliteCSharpTests
|
||||
var exists = await Exists.ById(SqliteDb.TableName, "seven");
|
||||
Expect.isFalse(exists, "There should not have been an existing document");
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when documents exist", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.isFalse(exists, "There should not have been any existing documents");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Find", new[]
|
||||
{
|
||||
TestList("All", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Find",
|
||||
[
|
||||
TestList("All",
|
||||
[
|
||||
TestCase("succeeds when there is data", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -478,9 +522,9 @@ public static class SqliteCSharpTests
|
||||
var results = await Find.All<SubDocument>(SqliteDb.TableName);
|
||||
Expect.isEmpty(results, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("ById", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is found", async () =>
|
||||
{
|
||||
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");
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when documents are found", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.isEmpty(docs, "There should have been no documents returned");
|
||||
})
|
||||
}),
|
||||
TestList("FirstByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("FirstByField",
|
||||
[
|
||||
TestCase("succeeds when a document is found", async () =>
|
||||
{
|
||||
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"));
|
||||
Expect.isNull(doc, "There should not have been a document returned");
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Update", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Update",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -577,9 +621,9 @@ public static class SqliteCSharpTests
|
||||
await Update.ById(SqliteDb.TableName, "test",
|
||||
new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } });
|
||||
})
|
||||
}),
|
||||
TestList("ByFunc", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByFunc",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -605,12 +649,12 @@ public static class SqliteCSharpTests
|
||||
await Update.ByFunc(SqliteDb.TableName, doc => doc.Id,
|
||||
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
|
||||
})
|
||||
}),
|
||||
}),
|
||||
TestList("Patch", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
]),
|
||||
]),
|
||||
TestList("Patch",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -632,9 +676,9 @@ public static class SqliteCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" });
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when a document is updated", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -654,12 +698,12 @@ public static class SqliteCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("RemoveFields", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("RemoveFields",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when fields are removed", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -686,9 +730,9 @@ public static class SqliteCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" });
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when a field is removed", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -714,12 +758,12 @@ public static class SqliteCSharpTests
|
||||
// This not raising an exception is the test
|
||||
await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
|
||||
})
|
||||
})
|
||||
}),
|
||||
TestList("Delete", new[]
|
||||
{
|
||||
TestList("ById", new[]
|
||||
{
|
||||
])
|
||||
]),
|
||||
TestList("Delete",
|
||||
[
|
||||
TestList("ById",
|
||||
[
|
||||
TestCase("succeeds when a document is deleted", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -738,9 +782,9 @@ public static class SqliteCSharpTests
|
||||
var remaining = await Count.All(SqliteDb.TableName);
|
||||
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
||||
})
|
||||
}),
|
||||
TestList("ByField", new[]
|
||||
{
|
||||
]),
|
||||
TestList("ByField",
|
||||
[
|
||||
TestCase("succeeds when documents are deleted", async () =>
|
||||
{
|
||||
await using var db = await SqliteDb.BuildDb();
|
||||
@ -759,14 +803,14 @@ public static class SqliteCSharpTests
|
||||
var remaining = await Count.All(SqliteDb.TableName);
|
||||
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
|
||||
})
|
||||
})
|
||||
}),
|
||||
])
|
||||
]),
|
||||
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
|
||||
});
|
||||
]);
|
||||
|
||||
/// <summary>
|
||||
/// All tests for SQLite C# functions and methods
|
||||
/// </summary>
|
||||
[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.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" [
|
||||
test "selectFromTable succeeds" {
|
||||
|
@ -58,6 +58,50 @@ let unitTests =
|
||||
}
|
||||
]
|
||||
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" [
|
||||
test "succeeds when a logical operator is passed" {
|
||||
Expect.equal
|
||||
|
@ -12,6 +12,44 @@ open Types
|
||||
let unitTests =
|
||||
testList "Unit" [
|
||||
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" [
|
||||
test "succeeds when a logical operator is passed" {
|
||||
Expect.equal
|
||||
|
Loading…
Reference in New Issue
Block a user