Add Postgres C# unit tests
This commit is contained in:
parent
a8b2927b2c
commit
a1559ad29e
|
@ -92,10 +92,12 @@ module Query =
|
||||||
$"CREATE INDEX IF NOT EXISTS idx_{tableName}_document ON {name} USING GIN (data{extraOps})"
|
$"CREATE INDEX IF NOT EXISTS idx_{tableName}_document ON {name} USING GIN (data{extraOps})"
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement a @> (JSON contains) condition
|
/// Create a WHERE clause fragment to implement a @> (JSON contains) condition
|
||||||
|
[<CompiledName "WhereDataContains">]
|
||||||
let whereDataContains paramName =
|
let whereDataContains paramName =
|
||||||
$"data @> %s{paramName}"
|
$"data @> %s{paramName}"
|
||||||
|
|
||||||
/// Create a WHERE clause fragment to implement a @? (JSON Path match) condition
|
/// Create a WHERE clause fragment to implement a @? (JSON Path match) condition
|
||||||
|
[<CompiledName "WhereJsonPathMatches">]
|
||||||
let whereJsonPathMatches paramName =
|
let whereJsonPathMatches paramName =
|
||||||
$"data @? %s{paramName}::jsonpath"
|
$"data @? %s{paramName}::jsonpath"
|
||||||
|
|
||||||
|
@ -103,10 +105,12 @@ module Query =
|
||||||
module Count =
|
module Count =
|
||||||
|
|
||||||
/// Query to count matching documents using a JSON containment query (@>)
|
/// Query to count matching documents using a JSON containment query (@>)
|
||||||
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
$"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereDataContains "@criteria"}"""
|
$"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereDataContains "@criteria"}"""
|
||||||
|
|
||||||
/// Query to count matching documents using a JSON Path match (@?)
|
/// Query to count matching documents using a JSON Path match (@?)
|
||||||
|
[<CompiledName "ByJsonPath">]
|
||||||
let byJsonPath tableName =
|
let byJsonPath tableName =
|
||||||
$"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereJsonPathMatches "@path"}"""
|
$"""SELECT COUNT(*) AS it FROM %s{tableName} WHERE {whereJsonPathMatches "@path"}"""
|
||||||
|
|
||||||
|
@ -114,10 +118,12 @@ module Query =
|
||||||
module Exists =
|
module Exists =
|
||||||
|
|
||||||
/// Query to determine if documents exist using a JSON containment query (@>)
|
/// Query to determine if documents exist using a JSON containment query (@>)
|
||||||
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereDataContains "@criteria"}) AS it"""
|
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereDataContains "@criteria"}) AS it"""
|
||||||
|
|
||||||
/// Query to determine if documents exist using a JSON Path match (@?)
|
/// Query to determine if documents exist using a JSON Path match (@?)
|
||||||
|
[<CompiledName "ByJsonPath">]
|
||||||
let byJsonPath tableName =
|
let byJsonPath tableName =
|
||||||
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereJsonPathMatches "@path"}) AS it"""
|
$"""SELECT EXISTS (SELECT 1 FROM %s{tableName} WHERE {whereJsonPathMatches "@path"}) AS it"""
|
||||||
|
|
||||||
|
@ -125,10 +131,12 @@ module Query =
|
||||||
module Find =
|
module Find =
|
||||||
|
|
||||||
/// Query to retrieve documents using a JSON containment query (@>)
|
/// Query to retrieve documents using a JSON containment query (@>)
|
||||||
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
$"""{Query.selectFromTable tableName} WHERE {whereDataContains "@criteria"}"""
|
$"""{Query.selectFromTable tableName} WHERE {whereDataContains "@criteria"}"""
|
||||||
|
|
||||||
/// Query to retrieve documents using a JSON Path match (@?)
|
/// Query to retrieve documents using a JSON Path match (@?)
|
||||||
|
[<CompiledName "ByJsonPath">]
|
||||||
let byJsonPath tableName =
|
let byJsonPath tableName =
|
||||||
$"""{Query.selectFromTable tableName} WHERE {whereJsonPathMatches "@path"}"""
|
$"""{Query.selectFromTable tableName} WHERE {whereJsonPathMatches "@path"}"""
|
||||||
|
|
||||||
|
@ -136,18 +144,22 @@ module Query =
|
||||||
module Update =
|
module Update =
|
||||||
|
|
||||||
/// Query to update a document
|
/// Query to update a document
|
||||||
|
[<CompiledName "PartialById">]
|
||||||
let partialById tableName =
|
let partialById tableName =
|
||||||
$"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereById "@id"}"""
|
$"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereById "@id"}"""
|
||||||
|
|
||||||
/// Query to update a document
|
/// Query to update a document
|
||||||
|
[<CompiledName "PartialByField">]
|
||||||
let partialByField tableName fieldName op =
|
let partialByField tableName fieldName op =
|
||||||
$"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereByField fieldName op "@field"}"""
|
$"""UPDATE %s{tableName} SET data = data || @data WHERE {Query.whereByField fieldName op "@field"}"""
|
||||||
|
|
||||||
/// Query to update partial documents matching a JSON containment query (@>)
|
/// Query to update partial documents matching a JSON containment query (@>)
|
||||||
|
[<CompiledName "PartialByContains">]
|
||||||
let partialByContains tableName =
|
let partialByContains tableName =
|
||||||
$"""UPDATE %s{tableName} SET data = data || @data WHERE {whereDataContains "@criteria"}"""
|
$"""UPDATE %s{tableName} SET data = data || @data WHERE {whereDataContains "@criteria"}"""
|
||||||
|
|
||||||
/// Query to update partial documents matching a JSON containment query (@>)
|
/// Query to update partial documents matching a JSON containment query (@>)
|
||||||
|
[<CompiledName "PartialByJsonPath">]
|
||||||
let partialByJsonPath tableName =
|
let partialByJsonPath tableName =
|
||||||
$"""UPDATE %s{tableName} SET data = data || @data WHERE {whereJsonPathMatches "@path"}"""
|
$"""UPDATE %s{tableName} SET data = data || @data WHERE {whereJsonPathMatches "@path"}"""
|
||||||
|
|
||||||
|
@ -155,10 +167,12 @@ module Query =
|
||||||
module Delete =
|
module Delete =
|
||||||
|
|
||||||
/// Query to delete documents using a JSON containment query (@>)
|
/// Query to delete documents using a JSON containment query (@>)
|
||||||
|
[<CompiledName "ByContains">]
|
||||||
let byContains tableName =
|
let byContains tableName =
|
||||||
$"""DELETE FROM %s{tableName} WHERE {whereDataContains "@criteria"}"""
|
$"""DELETE FROM %s{tableName} WHERE {whereDataContains "@criteria"}"""
|
||||||
|
|
||||||
/// Query to delete documents using a JSON Path match (@?)
|
/// Query to delete documents using a JSON Path match (@?)
|
||||||
|
[<CompiledName "ByJsonPath">]
|
||||||
let byJsonPath tableName =
|
let byJsonPath tableName =
|
||||||
$"""DELETE FROM %s{tableName} WHERE {whereJsonPathMatches "@path"}"""
|
$"""DELETE FROM %s{tableName} WHERE {whereJsonPathMatches "@path"}"""
|
||||||
|
|
||||||
|
@ -168,18 +182,22 @@ module Query =
|
||||||
module Results =
|
module Results =
|
||||||
|
|
||||||
/// Create a domain item from a document, specifying the field in which the document is found
|
/// Create a domain item from a document, specifying the field in which the document is found
|
||||||
|
[<CompiledName "FromDocument">]
|
||||||
let fromDocument<'T> field (row: RowReader) : 'T =
|
let fromDocument<'T> field (row: RowReader) : 'T =
|
||||||
Configuration.serializer().Deserialize<'T>(row.string field)
|
Configuration.serializer().Deserialize<'T>(row.string field)
|
||||||
|
|
||||||
/// Create a domain item from a document
|
/// Create a domain item from a document
|
||||||
|
[<CompiledName "FromData">]
|
||||||
let fromData<'T> row : 'T =
|
let fromData<'T> row : 'T =
|
||||||
fromDocument "data" row
|
fromDocument "data" row
|
||||||
|
|
||||||
/// Extract a count from the column "it"
|
/// Extract a count from the column "it"
|
||||||
|
[<CompiledName "ToCount">]
|
||||||
let toCount (row: RowReader) =
|
let toCount (row: RowReader) =
|
||||||
row.int "it"
|
row.int "it"
|
||||||
|
|
||||||
/// Extract a true/false value from the column "it"
|
/// Extract a true/false value from the column "it"
|
||||||
|
[<CompiledName "ToExists">]
|
||||||
let toExists (row: RowReader) =
|
let toExists (row: RowReader) =
|
||||||
row.bool "it"
|
row.bool "it"
|
||||||
|
|
||||||
|
|
180
src/Tests.CSharp/PostgresCSharpTests.cs
Normal file
180
src/Tests.CSharp/PostgresCSharpTests.cs
Normal file
|
@ -0,0 +1,180 @@
|
||||||
|
using Expecto.CSharp;
|
||||||
|
using Expecto;
|
||||||
|
using BitBadger.Documents.Postgres;
|
||||||
|
using Npgsql.FSharp;
|
||||||
|
|
||||||
|
namespace BitBadger.Documents.Tests.CSharp;
|
||||||
|
|
||||||
|
using static Runner;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// C# tests for the PostgreSQL implementation of <tt>BitBadger.Documents</tt>
|
||||||
|
/// </summary>
|
||||||
|
public class PostgresCSharpTests
|
||||||
|
{
|
||||||
|
public static Test Unit =
|
||||||
|
TestList("Unit", new[]
|
||||||
|
{
|
||||||
|
TestList("Parameters", new[]
|
||||||
|
{
|
||||||
|
TestCase("Id succeeds", () => {
|
||||||
|
Expect.equal(Parameters.Id(88).Item1, "@id", "ID parameter not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("Json succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Parameters.Json("@test", new { Something = "good" }).Item1, "@test",
|
||||||
|
"JSON parameter not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("Field succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Parameters.Field(242).Item1, "@field", "Field parameter not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("None succeeds", () => {
|
||||||
|
Expect.isEmpty(Parameters.None, "The no-params sequence should be empty");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestList("Query", new[]
|
||||||
|
{
|
||||||
|
TestList("Definition", new[]
|
||||||
|
{
|
||||||
|
TestCase("EnsureTable succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Definition.EnsureTable(PostgresDb.TableName),
|
||||||
|
$"CREATE TABLE IF NOT EXISTS {PostgresDb.TableName} (data JSONB NOT NULL)",
|
||||||
|
"CREATE TABLE statement not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("EnsureJsonIndex succeeds for full index", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Definition.EnsureJsonIndex("schema.tbl", DocumentIndex.Full),
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_tbl_document ON schema.tbl USING GIN (data)",
|
||||||
|
"CREATE INDEX statement not constructed correctly");
|
||||||
|
}),
|
||||||
|
TestCase("EnsureJsonIndex succeeds for JSONB Path Ops index", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(
|
||||||
|
Postgres.Query.Definition.EnsureJsonIndex(PostgresDb.TableName, DocumentIndex.Optimized),
|
||||||
|
string.Format(
|
||||||
|
"CREATE INDEX IF NOT EXISTS idx_{0}_document ON {0} USING GIN (data jsonb_path_ops)",
|
||||||
|
PostgresDb.TableName),
|
||||||
|
"CREATE INDEX statement not constructed correctly");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestCase("WhereDataContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test",
|
||||||
|
"WHERE clause not correct");
|
||||||
|
}),
|
||||||
|
TestCase("WhereJsonPathMatches succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.WhereJsonPathMatches("@path"), "data @? @path::jsonpath",
|
||||||
|
"WHERE clause not correct");
|
||||||
|
}),
|
||||||
|
TestList("Count", new[]
|
||||||
|
{
|
||||||
|
TestCase("ByContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Count.ByContains(PostgresDb.TableName),
|
||||||
|
$"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data @> @criteria",
|
||||||
|
"JSON containment count query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("ByJsonPath succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Count.ByJsonPath(PostgresDb.TableName),
|
||||||
|
$"SELECT COUNT(*) AS it FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
||||||
|
"JSON Path match count query not correct");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestList("Exists", new[]
|
||||||
|
{
|
||||||
|
TestCase("ByContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Exists.ByContains(PostgresDb.TableName),
|
||||||
|
$"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data @> @criteria) AS it",
|
||||||
|
"JSON containment exists query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("byJsonPath succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Exists.ByJsonPath(PostgresDb.TableName),
|
||||||
|
$"SELECT EXISTS (SELECT 1 FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath) AS it",
|
||||||
|
"JSON Path match existence query not correct");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestList("Find", new[]
|
||||||
|
{
|
||||||
|
TestCase("byContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Find.ByContains(PostgresDb.TableName),
|
||||||
|
$"SELECT data FROM {PostgresDb.TableName} WHERE data @> @criteria",
|
||||||
|
"SELECT by JSON containment query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("byJsonPath succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Find.ByJsonPath(PostgresDb.TableName),
|
||||||
|
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
||||||
|
"SELECT by JSON Path match query not correct");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestList("Update", new[]
|
||||||
|
{
|
||||||
|
TestCase("partialById succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Update.PartialById(PostgresDb.TableName),
|
||||||
|
$"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Id' = @id",
|
||||||
|
"UPDATE partial by ID statement not correct");
|
||||||
|
}),
|
||||||
|
TestCase("partialByField succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Update.PartialByField(PostgresDb.TableName, "Snail", Op.LT),
|
||||||
|
$"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data ->> 'Snail' < @field",
|
||||||
|
"UPDATE partial by ID statement not correct");
|
||||||
|
}),
|
||||||
|
TestCase("partialByContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Update.PartialByContains(PostgresDb.TableName),
|
||||||
|
$"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data @> @criteria",
|
||||||
|
"UPDATE partial by JSON containment statement not correct");
|
||||||
|
}),
|
||||||
|
TestCase("partialByJsonPath succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Update.PartialByJsonPath(PostgresDb.TableName),
|
||||||
|
$"UPDATE {PostgresDb.TableName} SET data = data || @data WHERE data @? @path::jsonpath",
|
||||||
|
"UPDATE partial by JSON Path statement not correct");
|
||||||
|
})
|
||||||
|
}),
|
||||||
|
TestList("Delete", new[]
|
||||||
|
{
|
||||||
|
TestCase("byContains succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Delete.ByContains(PostgresDb.TableName),
|
||||||
|
$"DELETE FROM {PostgresDb.TableName} WHERE data @> @criteria",
|
||||||
|
"DELETE by JSON containment query not correct");
|
||||||
|
}),
|
||||||
|
TestCase("byJsonPath succeeds", () =>
|
||||||
|
{
|
||||||
|
Expect.equal(Postgres.Query.Delete.ByJsonPath(PostgresDb.TableName),
|
||||||
|
$"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
|
||||||
|
"DELETE by JSON Path match query not correct");
|
||||||
|
})
|
||||||
|
})
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
private static readonly List<JsonDocument> TestDocuments = new()
|
||||||
|
{
|
||||||
|
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 }
|
||||||
|
};
|
||||||
|
|
||||||
|
internal static async Task LoadDocs()
|
||||||
|
{
|
||||||
|
foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All Postgres C# tests
|
||||||
|
/// </summary>
|
||||||
|
public static Test All = TestList("Postgres.C#", new[] { Unit });
|
||||||
|
}
|
|
@ -16,6 +16,7 @@ public static class SqliteCSharpTests
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Unit tests for the SQLite library
|
/// Unit tests for the SQLite library
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
[Tests]
|
||||||
public static Test Unit =
|
public static Test Unit =
|
||||||
TestList("Unit", new[]
|
TestList("Unit", new[]
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,6 +7,7 @@ let allTests =
|
||||||
[ CommonTests.all
|
[ CommonTests.all
|
||||||
CommonCSharpTests.Unit
|
CommonCSharpTests.Unit
|
||||||
PostgresTests.all
|
PostgresTests.all
|
||||||
|
PostgresCSharpTests.All
|
||||||
SqliteTests.all
|
SqliteTests.all
|
||||||
testSequenced SqliteExtensionTests.integrationTests
|
testSequenced SqliteExtensionTests.integrationTests
|
||||||
SqliteCSharpTests.All
|
SqliteCSharpTests.All
|
||||||
|
|
Loading…
Reference in New Issue
Block a user