666 lines
29 KiB
C#
666 lines
29 KiB
C#
using Expecto.CSharp;
|
|
using Expecto;
|
|
using Microsoft.FSharp.Core;
|
|
|
|
namespace BitBadger.Documents.Tests.CSharp;
|
|
|
|
using static Runner;
|
|
|
|
/// <summary>
|
|
/// A test serializer that returns known values
|
|
/// </summary>
|
|
internal class TestSerializer : IDocumentSerializer
|
|
{
|
|
public string Serialize<T>(T it) => "{\"Overridden\":true}";
|
|
public T Deserialize<T>(string it) => default!;
|
|
}
|
|
|
|
/// <summary>
|
|
/// C# Tests for common functionality in <c>BitBadger.Documents</c>
|
|
/// </summary>
|
|
public static class CommonCSharpTests
|
|
{
|
|
/// <summary>
|
|
/// Unit tests for the OpSql property of the Comparison discriminated union
|
|
/// </summary>
|
|
private static readonly Test OpTests = TestList("Comparison.OpSql",
|
|
[
|
|
TestCase("Equal succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewEqual("").OpSql, "=", "The Equals SQL was not correct");
|
|
}),
|
|
TestCase("Greater succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewGreater("").OpSql, ">", "The Greater SQL was not correct");
|
|
}),
|
|
TestCase("GreaterOrEqual succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewGreaterOrEqual("").OpSql, ">=", "The GreaterOrEqual SQL was not correct");
|
|
}),
|
|
TestCase("Less succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewLess("").OpSql, "<", "The Less SQL was not correct");
|
|
}),
|
|
TestCase("LessOrEqual succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewLessOrEqual("").OpSql, "<=", "The LessOrEqual SQL was not correct");
|
|
}),
|
|
TestCase("NotEqual succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewNotEqual("").OpSql, "<>", "The NotEqual SQL was not correct");
|
|
}),
|
|
TestCase("Between succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewBetween("", "").OpSql, "BETWEEN", "The Between SQL was not correct");
|
|
}),
|
|
TestCase("In succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewIn([]).OpSql, "IN", "The In SQL was not correct");
|
|
}),
|
|
TestCase("InArray succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NewInArray("", []).OpSql, "?|", "The InArray SQL was not correct");
|
|
}),
|
|
TestCase("Exists succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.Exists.OpSql, "IS NOT NULL", "The Exists SQL was not correct");
|
|
}),
|
|
TestCase("NotExists succeeds", () =>
|
|
{
|
|
Expect.equal(Comparison.NotExists.OpSql, "IS NULL", "The NotExists SQL was not correct");
|
|
})
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests for the Field class
|
|
/// </summary>
|
|
private static readonly Test FieldTests = TestList("Field",
|
|
[
|
|
TestCase("Equal succeeds", () =>
|
|
{
|
|
var field = Field.Equal("Test", 14);
|
|
Expect.equal(field.Name, "Test", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewEqual(14), "Comparison incorrect");
|
|
}),
|
|
TestCase("Greater succeeds", () =>
|
|
{
|
|
var field = Field.Greater("Great", "night");
|
|
Expect.equal(field.Name, "Great", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewGreater("night"), "Comparison incorrect");
|
|
}),
|
|
TestCase("GreaterOrEqual succeeds", () =>
|
|
{
|
|
var field = Field.GreaterOrEqual("Nice", 88L);
|
|
Expect.equal(field.Name, "Nice", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewGreaterOrEqual(88L), "Comparison incorrect");
|
|
}),
|
|
TestCase("Less succeeds", () =>
|
|
{
|
|
var field = Field.Less("Lesser", "seven");
|
|
Expect.equal(field.Name, "Lesser", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewLess("seven"), "Comparison incorrect");
|
|
}),
|
|
TestCase("LessOrEqual succeeds", () =>
|
|
{
|
|
var field = Field.LessOrEqual("Nobody", "KNOWS");
|
|
Expect.equal(field.Name, "Nobody", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewLessOrEqual("KNOWS"), "Comparison incorrect");
|
|
}),
|
|
TestCase("NotEqual succeeds", () =>
|
|
{
|
|
var field = Field.NotEqual("Park", "here");
|
|
Expect.equal(field.Name, "Park", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewNotEqual("here"), "Comparison incorrect");
|
|
}),
|
|
TestCase("Between succeeds", () =>
|
|
{
|
|
var field = Field.Between("Age", 18, 49);
|
|
Expect.equal(field.Name, "Age", "Field name incorrect");
|
|
Expect.equal(field.Comparison, Comparison.NewBetween(18, 49), "Comparison incorrect");
|
|
}),
|
|
TestCase("In succeeds", () =>
|
|
{
|
|
var field = Field.In("Here", [8, 16, 32]);
|
|
Expect.equal(field.Name, "Here", "Field name incorrect");
|
|
Expect.isTrue(field.Comparison.IsIn, "Comparison incorrect");
|
|
Expect.sequenceEqual(((Comparison.In)field.Comparison).Values, [8, 16, 32], "Value incorrect");
|
|
}),
|
|
TestCase("InArray succeeds", () =>
|
|
{
|
|
var field = Field.InArray("ArrayField", "table", ["x", "y", "z"]);
|
|
Expect.equal(field.Name, "ArrayField", "Field name incorrect");
|
|
Expect.isTrue(field.Comparison.IsInArray, "Comparison incorrect");
|
|
var it = (Comparison.InArray)field.Comparison;
|
|
Expect.equal(it.Table, "table", "Table name incorrect");
|
|
Expect.sequenceEqual(it.Values, ["x", "y", "z"], "Value incorrect");
|
|
}),
|
|
TestCase("Exists succeeds", () =>
|
|
{
|
|
var field = Field.Exists("Groovy");
|
|
Expect.equal(field.Name, "Groovy", "Field name incorrect");
|
|
Expect.isTrue(field.Comparison.IsExists, "Comparison incorrect");
|
|
}),
|
|
TestCase("NotExists succeeds", () =>
|
|
{
|
|
var field = Field.NotExists("Rad");
|
|
Expect.equal(field.Name, "Rad", "Field name incorrect");
|
|
Expect.isTrue(field.Comparison.IsNotExists, "Comparison incorrect");
|
|
}),
|
|
TestList("NameToPath",
|
|
[
|
|
TestCase("succeeds for PostgreSQL and a simple name", () =>
|
|
{
|
|
Expect.equal("data->>'Simple'", Field.NameToPath("Simple", Dialect.PostgreSQL, FieldFormat.AsSql),
|
|
"Path not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for SQLite and a simple name", () =>
|
|
{
|
|
Expect.equal("data->>'Simple'", Field.NameToPath("Simple", Dialect.SQLite, FieldFormat.AsSql),
|
|
"Path not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for PostgreSQL and a nested name", () =>
|
|
{
|
|
Expect.equal("data#>>'{A,Long,Path,to,the,Property}'",
|
|
Field.NameToPath("A.Long.Path.to.the.Property", Dialect.PostgreSQL, FieldFormat.AsSql),
|
|
"Path not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for SQLite and a nested name", () =>
|
|
{
|
|
Expect.equal("data->'A'->'Long'->'Path'->'to'->'the'->>'Property'",
|
|
Field.NameToPath("A.Long.Path.to.the.Property", Dialect.SQLite, FieldFormat.AsSql),
|
|
"Path not constructed correctly");
|
|
})
|
|
]),
|
|
TestCase("WithParameterName succeeds", () =>
|
|
{
|
|
var field = Field.Equal("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.Equal("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("Path",
|
|
[
|
|
TestCase("succeeds for a PostgreSQL single field with no qualifier", () =>
|
|
{
|
|
var field = Field.GreaterOrEqual("SomethingCool", 18);
|
|
Expect.equal("data->>'SomethingCool'", field.Path(Dialect.PostgreSQL, FieldFormat.AsSql),
|
|
"The PostgreSQL path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a PostgreSQL single field with a qualifier", () =>
|
|
{
|
|
var field = Field.Less("SomethingElse", 9).WithQualifier("this");
|
|
Expect.equal("this.data->>'SomethingElse'", field.Path(Dialect.PostgreSQL, FieldFormat.AsSql),
|
|
"The PostgreSQL path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a PostgreSQL nested field with no qualifier", () =>
|
|
{
|
|
var field = Field.Equal("My.Nested.Field", "howdy");
|
|
Expect.equal("data#>>'{My,Nested,Field}'", field.Path(Dialect.PostgreSQL, FieldFormat.AsSql),
|
|
"The PostgreSQL path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a PostgreSQL nested field with a qualifier", () =>
|
|
{
|
|
var field = Field.Equal("Nest.Away", "doc").WithQualifier("bird");
|
|
Expect.equal("bird.data#>>'{Nest,Away}'", field.Path(Dialect.PostgreSQL, FieldFormat.AsSql),
|
|
"The PostgreSQL path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a SQLite single field with no qualifier", () =>
|
|
{
|
|
var field = Field.GreaterOrEqual("SomethingCool", 18);
|
|
Expect.equal("data->>'SomethingCool'", field.Path(Dialect.SQLite, FieldFormat.AsSql),
|
|
"The SQLite path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a SQLite single field with a qualifier", () =>
|
|
{
|
|
var field = Field.Less("SomethingElse", 9).WithQualifier("this");
|
|
Expect.equal("this.data->>'SomethingElse'", field.Path(Dialect.SQLite, FieldFormat.AsSql),
|
|
"The SQLite path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a SQLite nested field with no qualifier", () =>
|
|
{
|
|
var field = Field.Equal("My.Nested.Field", "howdy");
|
|
Expect.equal("data->'My'->'Nested'->>'Field'", field.Path(Dialect.SQLite, FieldFormat.AsSql),
|
|
"The SQLite path is incorrect");
|
|
}),
|
|
TestCase("succeeds for a SQLite nested field with a qualifier", () =>
|
|
{
|
|
var field = Field.Equal("Nest.Away", "doc").WithQualifier("bird");
|
|
Expect.equal("bird.data->'Nest'->>'Away'", field.Path(Dialect.SQLite, FieldFormat.AsSql),
|
|
"The SQLite path is incorrect");
|
|
})
|
|
])
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests for the FieldMatch enum
|
|
/// </summary>
|
|
private static readonly Test FieldMatchTests = TestList("FieldMatch.ToString",
|
|
[
|
|
TestCase("succeeds for Any", () =>
|
|
{
|
|
Expect.equal(FieldMatch.Any.ToString(), "OR", "SQL for Any is incorrect");
|
|
}),
|
|
TestCase("succeeds for All", () =>
|
|
{
|
|
Expect.equal(FieldMatch.All.ToString(), "AND", "SQL for All is incorrect");
|
|
})
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests for the ParameterName class
|
|
/// </summary>
|
|
private static readonly Test ParameterNameTests = TestList("ParameterName.Derive",
|
|
[
|
|
TestCase("succeeds with existing name", () =>
|
|
{
|
|
ParameterName name = new();
|
|
Expect.equal(name.Derive(FSharpOption<string>.Some("@taco")), "@taco", "Name should have been @taco");
|
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field0",
|
|
"Counter should not have advanced for named field");
|
|
}),
|
|
TestCase("Derive succeeds with non-existent name", () =>
|
|
{
|
|
ParameterName name = new();
|
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field0",
|
|
"Anonymous field name should have been returned");
|
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field1",
|
|
"Counter should have advanced from previous call");
|
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field2",
|
|
"Counter should have advanced from previous call");
|
|
Expect.equal(name.Derive(FSharpOption<string>.None), "@field3",
|
|
"Counter should have advanced from previous call");
|
|
})
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests for the AutoId enum
|
|
/// </summary>
|
|
private static readonly Test AutoIdTests = TestList("AutoId",
|
|
[
|
|
TestCase("GenerateGuid succeeds", () =>
|
|
{
|
|
var autoId = AutoId.GenerateGuid();
|
|
Expect.isNotNull(autoId, "The GUID auto-ID should not have been null");
|
|
Expect.stringHasLength(autoId, 32, "The GUID auto-ID should have been 32 characters long");
|
|
Expect.equal(autoId, autoId.ToLowerInvariant(), "The GUID auto-ID should have been lowercase");
|
|
}),
|
|
TestCase("GenerateRandomString succeeds", () =>
|
|
{
|
|
foreach (var length in (int[]) [6, 8, 12, 20, 32, 57, 64])
|
|
{
|
|
var autoId = AutoId.GenerateRandomString(length);
|
|
Expect.isNotNull(autoId, $"Random string ({length}) should not have been null");
|
|
Expect.stringHasLength(autoId, length, $"Random string should have been {length} characters long");
|
|
Expect.equal(autoId, autoId.ToLowerInvariant(),
|
|
$"Random string ({length}) should have been lowercase");
|
|
}
|
|
}),
|
|
TestList("NeedsAutoId",
|
|
[
|
|
TestCase("succeeds when no auto ID is configured", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Disabled, new object(), "id"),
|
|
"Disabled auto-ID never needs an automatic ID");
|
|
}),
|
|
TestCase("fails for any when the ID property is not found", () =>
|
|
{
|
|
try
|
|
{
|
|
_ = AutoId.NeedsAutoId(AutoId.Number, new { Key = "" }, "Id");
|
|
Expect.isTrue(false, "Non-existent ID property should have thrown an exception");
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
// pass
|
|
}
|
|
}),
|
|
TestCase("succeeds for byte when the ID is zero", () =>
|
|
{
|
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = (sbyte)0 }, "Id"),
|
|
"Zero ID should have returned true");
|
|
}),
|
|
TestCase("succeeds for byte when the ID is non-zero", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = (sbyte)4 }, "Id"),
|
|
"Non-zero ID should have returned false");
|
|
}),
|
|
TestCase("succeeds for short when the ID is zero", () =>
|
|
{
|
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = (short)0 }, "Id"),
|
|
"Zero ID should have returned true");
|
|
}),
|
|
TestCase("succeeds for short when the ID is non-zero", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = (short)7 }, "Id"),
|
|
"Non-zero ID should have returned false");
|
|
}),
|
|
TestCase("succeeds for int when the ID is zero", () =>
|
|
{
|
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = 0 }, "Id"),
|
|
"Zero ID should have returned true");
|
|
}),
|
|
TestCase("succeeds for int when the ID is non-zero", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = 32 }, "Id"),
|
|
"Non-zero ID should have returned false");
|
|
}),
|
|
TestCase("succeeds for long when the ID is zero", () =>
|
|
{
|
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Number, new { Id = 0L }, "Id"),
|
|
"Zero ID should have returned true");
|
|
}),
|
|
TestCase("succeeds for long when the ID is non-zero", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Number, new { Id = 80L }, "Id"),
|
|
"Non-zero ID should have returned false");
|
|
}),
|
|
TestCase("fails for number when the ID is not a number", () =>
|
|
{
|
|
try
|
|
{
|
|
_ = AutoId.NeedsAutoId(AutoId.Number, new { Id = "" }, "Id");
|
|
Expect.isTrue(false, "Numeric ID against a string should have thrown an exception");
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
// pass
|
|
}
|
|
}),
|
|
TestCase("succeeds for GUID when the ID is blank", () =>
|
|
{
|
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.Guid, new { Id = "" }, "Id"),
|
|
"Blank ID should have returned true");
|
|
}),
|
|
TestCase("succeeds for GUID when the ID is filled", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.Guid, new { Id = "abc" }, "Id"),
|
|
"Filled ID should have returned false");
|
|
}),
|
|
TestCase("fails for GUID when the ID is not a string", () =>
|
|
{
|
|
try
|
|
{
|
|
_ = AutoId.NeedsAutoId(AutoId.Guid, new { Id = 8 }, "Id");
|
|
Expect.isTrue(false, "String ID against a number should have thrown an exception");
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
// pass
|
|
}
|
|
}),
|
|
TestCase("succeeds for RandomString when the ID is blank", () =>
|
|
{
|
|
Expect.isTrue(AutoId.NeedsAutoId(AutoId.RandomString, new { Id = "" }, "Id"),
|
|
"Blank ID should have returned true");
|
|
}),
|
|
TestCase("succeeds for RandomString when the ID is filled", () =>
|
|
{
|
|
Expect.isFalse(AutoId.NeedsAutoId(AutoId.RandomString, new { Id = "x" }, "Id"),
|
|
"Filled ID should have returned false");
|
|
}),
|
|
TestCase("fails for RandomString when the ID is not a string", () =>
|
|
{
|
|
try
|
|
{
|
|
_ = AutoId.NeedsAutoId(AutoId.RandomString, new { Id = 33 }, "Id");
|
|
Expect.isTrue(false, "String ID against a number should have thrown an exception");
|
|
}
|
|
catch (InvalidOperationException)
|
|
{
|
|
// pass
|
|
}
|
|
})
|
|
])
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests for the Configuration static class
|
|
/// </summary>
|
|
private static readonly Test ConfigurationTests = TestList("Configuration",
|
|
[
|
|
TestCase("UseSerializer succeeds", () =>
|
|
{
|
|
try
|
|
{
|
|
Configuration.UseSerializer(new TestSerializer());
|
|
|
|
var serialized = Configuration.Serializer().Serialize(new SubDocument
|
|
{
|
|
Foo = "howdy",
|
|
Bar = "bye"
|
|
});
|
|
Expect.equal(serialized, "{\"Overridden\":true}", "Specified serializer was not used");
|
|
|
|
var deserialized = Configuration.Serializer()
|
|
.Deserialize<object>("{\"Something\":\"here\"}");
|
|
Expect.isNull(deserialized, "Specified serializer should have returned null");
|
|
}
|
|
finally
|
|
{
|
|
Configuration.UseSerializer(DocumentSerializer.Default);
|
|
}
|
|
}),
|
|
TestCase("Serializer returns configured serializer", () =>
|
|
{
|
|
Expect.isTrue(ReferenceEquals(DocumentSerializer.Default, Configuration.Serializer()),
|
|
"Serializer should have been the same");
|
|
}),
|
|
TestCase("UseIdField / IdField succeeds", () =>
|
|
{
|
|
try
|
|
{
|
|
Expect.equal(Configuration.IdField(), "Id",
|
|
"The default configured ID field was incorrect");
|
|
Configuration.UseIdField("id");
|
|
Expect.equal(Configuration.IdField(), "id", "UseIdField did not set the ID field");
|
|
}
|
|
finally
|
|
{
|
|
Configuration.UseIdField("Id");
|
|
}
|
|
}),
|
|
TestCase("UseAutoIdStrategy / AutoIdStrategy succeeds", () =>
|
|
{
|
|
try
|
|
{
|
|
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Disabled,
|
|
"The default auto-ID strategy was incorrect");
|
|
Configuration.UseAutoIdStrategy(AutoId.Guid);
|
|
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Guid,
|
|
"The auto-ID strategy was not set correctly");
|
|
}
|
|
finally
|
|
{
|
|
Configuration.UseAutoIdStrategy(AutoId.Disabled);
|
|
}
|
|
}),
|
|
TestCase("UseIdStringLength / IdStringLength succeeds", () =>
|
|
{
|
|
try
|
|
{
|
|
Expect.equal(Configuration.IdStringLength(), 16, "The default ID string length was incorrect");
|
|
Configuration.UseIdStringLength(33);
|
|
Expect.equal(Configuration.IdStringLength(), 33, "The ID string length was not set correctly");
|
|
}
|
|
finally
|
|
{
|
|
Configuration.UseIdStringLength(16);
|
|
}
|
|
})
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests for the Query static class
|
|
/// </summary>
|
|
private static readonly Test QueryTests = TestList("Query",
|
|
[
|
|
TestCase("StatementWhere succeeds", () =>
|
|
{
|
|
Expect.equal(Query.StatementWhere("q", "r"), "q WHERE r", "Statements not combined correctly");
|
|
}),
|
|
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",
|
|
[
|
|
TestCase("succeeds when a schema is present", () =>
|
|
{
|
|
Expect.equal(Query.Definition.EnsureKey("test.table", Dialect.SQLite),
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data->>'Id'))",
|
|
"CREATE INDEX for key statement with schema not constructed correctly");
|
|
}),
|
|
TestCase("succeeds when a schema is not present", () =>
|
|
{
|
|
Expect.equal(Query.Definition.EnsureKey("table", Dialect.PostgreSQL),
|
|
"CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data->>'Id'))",
|
|
"CREATE INDEX for key statement without schema not constructed correctly");
|
|
})
|
|
]),
|
|
TestList("EnsureIndexOn",
|
|
[
|
|
TestCase("succeeds for multiple fields and directions", () =>
|
|
{
|
|
Expect.equal(
|
|
Query.Definition.EnsureIndexOn("test.table", "gibberish",
|
|
["taco", "guac DESC", "salsa ASC"], Dialect.SQLite),
|
|
"CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
|
|
+ "((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
|
|
"CREATE INDEX for multiple field statement incorrect");
|
|
}),
|
|
TestCase("succeeds for nested PostgreSQL field", () =>
|
|
{
|
|
Expect.equal(
|
|
Query.Definition.EnsureIndexOn("tbl", "nest", ["a.b.c"], Dialect.PostgreSQL),
|
|
"CREATE INDEX IF NOT EXISTS idx_tbl_nest ON tbl ((data#>>'{a,b,c}'))",
|
|
"CREATE INDEX for nested PostgreSQL field incorrect");
|
|
}),
|
|
TestCase("succeeds for nested SQLite field", () =>
|
|
{
|
|
Expect.equal(
|
|
Query.Definition.EnsureIndexOn("tbl", "nest", ["a.b.c"], Dialect.SQLite),
|
|
"CREATE INDEX IF NOT EXISTS idx_tbl_nest ON tbl ((data->'a'->'b'->>'c'))",
|
|
"CREATE INDEX for nested SQLite field incorrect");
|
|
})
|
|
])
|
|
]),
|
|
TestCase("Insert succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Insert("tbl"), "INSERT INTO tbl VALUES (@data)", "INSERT statement not correct");
|
|
}),
|
|
TestCase("Save succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Save("tbl"),
|
|
"INSERT INTO tbl VALUES (@data) ON CONFLICT ((data->>'Id')) DO UPDATE SET data = EXCLUDED.data",
|
|
"INSERT ON CONFLICT UPDATE statement not correct");
|
|
}),
|
|
TestCase("Count succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Count("tbl"), "SELECT COUNT(*) AS it FROM tbl", "Count query not correct");
|
|
}),
|
|
TestCase("Exists succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Exists("tbl", "chicken"), "SELECT EXISTS (SELECT 1 FROM tbl WHERE chicken) AS it",
|
|
"Exists query not correct");
|
|
}),
|
|
TestCase("Find succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Find("test.table"), "SELECT data FROM test.table", "Find query not correct");
|
|
}),
|
|
TestCase("Update succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Update("tbl"), "UPDATE tbl SET data = @data", "Update query not correct");
|
|
}),
|
|
TestCase("Delete succeeds", () =>
|
|
{
|
|
Expect.equal(Query.Delete("tbl"), "DELETE FROM tbl", "Delete query not correct");
|
|
}),
|
|
TestList("OrderBy",
|
|
[
|
|
TestCase("succeeds for no fields", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([], Dialect.PostgreSQL), "", "Order By should have been blank (PostgreSQL)");
|
|
Expect.equal(Query.OrderBy([], Dialect.SQLite), "", "Order By should have been blank (SQLite)");
|
|
}),
|
|
TestCase("succeeds for PostgreSQL with one field and no direction", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([Field.Named("TestField")], Dialect.PostgreSQL),
|
|
" ORDER BY data->>'TestField'", "Order By not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for SQLite with one field and no direction", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([Field.Named("TestField")], Dialect.SQLite),
|
|
" ORDER BY data->>'TestField'", "Order By not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for PostgreSQL with multiple fields and direction", () =>
|
|
{
|
|
Expect.equal(
|
|
Query.OrderBy(
|
|
[
|
|
Field.Named("Nested.Test.Field DESC"), Field.Named("AnotherField"),
|
|
Field.Named("It DESC")
|
|
], Dialect.PostgreSQL),
|
|
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
|
|
"Order By not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for SQLite with multiple fields and direction", () =>
|
|
{
|
|
Expect.equal(
|
|
Query.OrderBy(
|
|
[
|
|
Field.Named("Nested.Test.Field DESC"), Field.Named("AnotherField"),
|
|
Field.Named("It DESC")
|
|
], Dialect.SQLite),
|
|
" ORDER BY data->'Nested'->'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
|
|
"Order By not constructed correctly");
|
|
}),
|
|
TestCase("succeeds for PostgreSQL numeric fields", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([Field.Named("n:Test")], Dialect.PostgreSQL),
|
|
" ORDER BY (data->>'Test')::numeric", "Order By not constructed correctly for numeric field");
|
|
}),
|
|
TestCase("succeeds for SQLite numeric fields", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([Field.Named("n:Test")], Dialect.SQLite), " ORDER BY data->>'Test'",
|
|
"Order By not constructed correctly for numeric field");
|
|
}),
|
|
TestCase("succeeds for PostgreSQL case-insensitive ordering", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([Field.Named("i:Test.Field DESC NULLS FIRST")], Dialect.PostgreSQL),
|
|
" ORDER BY LOWER(data#>>'{Test,Field}') DESC NULLS FIRST",
|
|
"Order By not constructed correctly for case-insensitive field");
|
|
}),
|
|
TestCase("succeeds for SQLite case-insensitive ordering", () =>
|
|
{
|
|
Expect.equal(Query.OrderBy([Field.Named("i:Test.Field ASC NULLS LAST")], Dialect.SQLite),
|
|
" ORDER BY data->'Test'->>'Field' COLLATE NOCASE ASC NULLS LAST",
|
|
"Order By not constructed correctly for case-insensitive field");
|
|
})
|
|
])
|
|
]);
|
|
|
|
/// <summary>
|
|
/// Unit tests
|
|
/// </summary>
|
|
[Tests]
|
|
public static readonly Test Unit = TestList("Common.C# Unit",
|
|
[
|
|
OpTests,
|
|
FieldTests,
|
|
FieldMatchTests,
|
|
ParameterNameTests,
|
|
AutoIdTests,
|
|
QueryTests,
|
|
TestSequenced(ConfigurationTests)
|
|
]);
|
|
}
|