RC4 changes (#7)

- Add `In` and `InArray` comparisons
- Replace `Op` with `Comparison` (internal API, but was public)
- Spell out comparisons in `Field` constructor functions

Reviewed-on: #7
This commit was merged in pull request #7.
This commit is contained in:
2024-09-17 02:33:57 +00:00
parent 3bc662c984
commit 168bf0cd14
19 changed files with 883 additions and 569 deletions
+81 -46
View File
@@ -22,40 +22,55 @@ public static class SqliteCSharpTests
TestCase("succeeds for a single field when a logical operator is passed", () =>
{
Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.GT("theField", 0).WithParameterName("@test")]),
Sqlite.Query.WhereByFields(FieldMatch.Any,
[Field.Greater("theField", 0).WithParameterName("@test")]),
"data->>'theField' > @test", "WHERE clause not correct");
}),
TestCase("succeeds for a single field when an existence operator is passed", () =>
{
Expect.equal(Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.NEX("thatField")]),
Expect.equal(Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.NotExists("thatField")]),
"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(FieldMatch.All,
[Field.BT("aField", 50, 99).WithParameterName("@range")]),
[Field.Between("aField", 50, 99).WithParameterName("@range")]),
"data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct");
}),
TestCase("succeeds for all multiple fields with logical operators", () =>
{
Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.All, [Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")]),
Sqlite.Query.WhereByFields(FieldMatch.All,
[Field.Equal("theFirst", "1"), Field.Equal("numberTwo", "2")]),
"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(FieldMatch.Any, [Field.NEX("thatField"), Field.GE("thisField", 18)]),
Sqlite.Query.WhereByFields(FieldMatch.Any,
[Field.NotExists("thatField"), Field.GreaterOrEqual("thisField", 18)]),
"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(FieldMatch.All,
[Field.BT("aField", 50, 99), Field.BT("anotherField", "a", "b")]),
[Field.Between("aField", 50, 99), Field.Between("anotherField", "a", "b")]),
"data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max",
"WHERE clause not correct");
}),
TestCase("succeeds for a field with an In comparison", () =>
{
Expect.equal(Sqlite.Query.WhereByFields(FieldMatch.All, [Field.In("this", ["a", "b", "c"])]),
"data->>'this' IN (@field0_0, @field0_1, @field0_2)", "WHERE clause not correct");
}),
TestCase("succeeds for a field with an InArray comparison", () =>
{
Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.All, [Field.InArray("this", "the_table", ["a", "b"])]),
"EXISTS (SELECT 1 FROM json_each(the_table.data, '$.this') WHERE value IN (@field0_0, @field0_1))",
"WHERE clause not correct");
})
]),
TestCase("WhereById succeeds", () =>
@@ -79,7 +94,7 @@ public static class SqliteCSharpTests
}),
TestCase("ByFields succeeds", () =>
{
Expect.equal(Sqlite.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]),
Expect.equal(Sqlite.Query.ByFields("unit", FieldMatch.Any, [Field.Greater("That", 14)]),
"unit WHERE data->>'That' > @field0", "By-Field query not correct");
}),
TestCase("Definition.EnsureTable succeeds", () =>
@@ -109,7 +124,7 @@ public static class SqliteCSharpTests
#pragma warning disable CS0618
TestCase("AddField succeeds when adding a parameter", () =>
{
var paramList = Parameters.AddField("@field", Field.EQ("it", 99), []).ToList();
var paramList = Parameters.AddField("@field", Field.Equal("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");
@@ -117,7 +132,7 @@ public static class SqliteCSharpTests
}),
TestCase("AddField succeeds when not adding a parameter", () =>
{
var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), []);
var paramSeq = Parameters.AddField("@it", Field.Exists("Coffee"), []);
Expect.isEmpty(paramSeq, "There should not have been any parameters added");
}),
#pragma warning restore CS0618
@@ -129,24 +144,12 @@ public static class SqliteCSharpTests
// Results are exhaustively executed in the context of other tests
/// <summary>
/// Documents used for integration tests
/// </summary>
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
/// </summary>
internal static async Task LoadDocs()
{
foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
foreach (var doc in JsonDocument.TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
}
/// <summary>
@@ -459,7 +462,8 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.BT("NumValue", 10, 20)]);
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any,
[Field.Between("NumValue", 10, 20)]);
Expect.equal(theCount, 3L, "There should have been 3 matching documents");
}),
TestCase("succeeds for non-numeric range", async () =>
@@ -468,7 +472,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any,
[Field.BT("Value", "aardvark", "apple")]);
[Field.Between("Value", "aardvark", "apple")]);
Expect.equal(theCount, 1L, "There should have been 1 matching document");
})
])
@@ -505,7 +509,8 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.GE("NumValue", 10)]);
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any,
[Field.GreaterOrEqual("NumValue", 10)]);
Expect.isTrue(exists, "There should have been existing documents");
}),
TestCase("succeeds when no matching documents exist", async () =>
@@ -513,7 +518,8 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Nothing", "none")]);
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any,
[Field.Equal("Nothing", "none")]);
Expect.isFalse(exists, "There should not have been any existing documents");
})
])
@@ -605,16 +611,45 @@ public static class SqliteCSharpTests
await LoadDocs();
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)]);
[Field.Greater("NumValue", 15)]);
Expect.equal(docs.Count, 2, "There should have been two documents returned");
}),
TestCase("succeeds when documents are found using IN with numeric field", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.All,
[Field.In("NumValue", [2, 4, 6, 8])]);
Expect.hasLength(docs, 1, "There should have been one document returned");
}),
TestCase("succeeds when documents are not found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "mauve")]);
[Field.Equal("Value", "mauve")]);
Expect.isEmpty(docs, "There should have been no documents returned");
}),
TestCase("succeeds for InArray when matching documents exist", async () =>
{
await using var db = await SqliteDb.BuildDb();
await Definition.EnsureTable(SqliteDb.TableName);
foreach (var doc in ArrayDocument.TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
var docs = await Find.ByFields<ArrayDocument>(SqliteDb.TableName, FieldMatch.All,
[Field.InArray("Values", SqliteDb.TableName, ["c"])]);
Expect.hasLength(docs, 2, "There should have been two document returned");
}),
TestCase("succeeds for InArray when no matching documents exist", async () =>
{
await using var db = await SqliteDb.BuildDb();
await Definition.EnsureTable(SqliteDb.TableName);
foreach (var doc in ArrayDocument.TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
var docs = await Find.ByFields<ArrayDocument>(SqliteDb.TableName, FieldMatch.All,
[Field.InArray("Values", SqliteDb.TableName, ["j"])]);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
@@ -626,7 +661,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)], [Field.Named("Id")]);
[Field.Greater("NumValue", 15)], [Field.Named("Id")]);
Expect.hasLength(docs, 2, "There should have been two documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
"The documents were not sorted correctly");
@@ -637,7 +672,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)], [Field.Named("Id DESC")]);
[Field.Greater("NumValue", 15)], [Field.Named("Id DESC")]);
Expect.hasLength(docs, 2, "There should have been two documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
"The documents were not sorted correctly");
@@ -648,7 +683,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.LE("NumValue", 10)], [Field.Named("Value")]);
[Field.LessOrEqual("NumValue", 10)], [Field.Named("Value")]);
Expect.hasLength(docs, 3, "There should have been three documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "three|one|two",
"The documents were not sorted correctly");
@@ -659,7 +694,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.LE("NumValue", 10)], [Field.Named("i:Value")]);
[Field.LessOrEqual("NumValue", 10)], [Field.Named("i:Value")]);
Expect.hasLength(docs, 3, "There should have been three documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "three|two|one",
"The documents were not sorted correctly");
@@ -673,7 +708,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
[Field.Equal("Value", "another")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
}),
@@ -683,7 +718,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")]);
[Field.Equal("Sub.Foo", "green")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(["two", "four"], doc!.Id, "An incorrect document was returned");
}),
@@ -693,7 +728,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "absent")]);
[Field.Equal("Value", "absent")]);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
@@ -705,7 +740,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar")]);
[Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc!.Id, "An incorrect document was returned");
}),
@@ -715,7 +750,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]);
[Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc!.Id, "An incorrect document was returned");
})
@@ -821,9 +856,9 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "purple")],
new { NumValue = 77 });
var after = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 77)]);
var after = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("NumValue", 77)]);
Expect.equal(after, 2L, "There should have been 2 documents returned");
}),
TestCase("succeeds when no document is updated", async () =>
@@ -834,7 +869,7 @@ public static class SqliteCSharpTests
Expect.isEmpty(before, "There should have been no documents returned");
// This not raising an exception is the test
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "burgundy")],
new { Foo = "green" });
})
])
@@ -881,7 +916,7 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)], ["Sub"]);
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("NumValue", 17)], ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.isNull(updated.Sub, "The sub-document should have been removed");
@@ -892,7 +927,7 @@ public static class SqliteCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("NumValue", 17)],
["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
@@ -900,8 +935,8 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
["Value"]);
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any,
[Field.NotEqual("Abracadabra", "apple")], ["Value"]);
})
])
]);
@@ -939,7 +974,7 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await Delete.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
await Delete.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NotEqual("Value", "purple")]);
var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
}),
@@ -948,7 +983,7 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await Delete.ByFields(SqliteDb.TableName, FieldMatch.All, [Field.EQ("Value", "crimson")]);
await Delete.ByFields(SqliteDb.TableName, FieldMatch.All, [Field.Equal("Value", "crimson")]);
var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})