using Expecto.CSharp; using Expecto; using Microsoft.Data.Sqlite; using Microsoft.FSharp.Core; using BitBadger.Documents.Sqlite; namespace BitBadger.Documents.Tests.CSharp; using static Runner; /// /// C# tests for the SQLite implementation of BitBadger.Documents /// public static class SqliteCSharpTests { /// /// Unit tests for the SQLite library /// 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(FieldMatch.Any, [Field.GT("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")]), "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")]), "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")]), "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)]), "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")]), "data->>'aField' BETWEEN @field0min AND @field0max AND data->>'anotherField' BETWEEN @field1min AND @field1max", "WHERE clause not correct"); }) ]), #pragma warning disable CS0618 TestList("WhereByField", [ TestCase("succeeds when a logical operator is passed", () => { Expect.equal(Sqlite.Query.WhereByField(Field.GT("theField", 0), "@test"), "data->>'theField' > @test", "WHERE clause not correct"); }), TestCase("succeeds when an existence operator is passed", () => { Expect.equal(Sqlite.Query.WhereByField(Field.NEX("thatField"), ""), "data->>'thatField' IS NULL", "WHERE clause not correct"); }), TestCase("succeeds when the between operator is passed", () => { Expect.equal(Sqlite.Query.WhereByField(Field.BT("aField", 50, 99), "@range"), "data->>'aField' BETWEEN @rangemin AND @rangemax", "WHERE clause not correct"); }) ]), #pragma warning restore CS0618 TestCase("WhereById succeeds", () => { Expect.equal(Sqlite.Query.WhereById("@id"), "data->>'Id' = @id", "WHERE clause not correct"); }), TestCase("Definition.EnsureTable succeeds", () => { Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"), "CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)", "CREATE TABLE statement not correct"); }), TestCase("Update succeeds", () => { Expect.equal(Sqlite.Query.Update("tbl"), "UPDATE tbl SET data = @data WHERE data->>'Id' = @id", "UPDATE full statement not correct"); }), TestList("Count", [ TestCase("All succeeds", () => { Expect.equal(Sqlite.Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl", "Count query not correct"); }), #pragma warning disable CS0618 TestCase("ByField succeeds", () => { Expect.equal(Sqlite.Query.Count.ByField("tbl", Field.EQ("thatField", 0)), "SELECT COUNT(*) AS it FROM tbl WHERE data->>'thatField' = @field0", "JSON field text comparison count query not correct"); }) #pragma warning restore CS0618 ]), TestList("Exists", [ TestCase("ById succeeds", () => { Expect.equal(Sqlite.Query.Exists.ById("tbl"), "SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Id' = @id) AS it", "ID existence query not correct"); }), #pragma warning disable CS0618 TestCase("ByField succeeds", () => { Expect.equal(Sqlite.Query.Exists.ByField("tbl", Field.LT("Test", 0)), "SELECT EXISTS (SELECT 1 FROM tbl WHERE data->>'Test' < @field0) AS it", "JSON field text comparison exists query not correct"); }) #pragma warning restore CS0618 ]), TestList("Find", [ TestCase("ById succeeds", () => { Expect.equal(Sqlite.Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data->>'Id' = @id", "SELECT by ID query not correct"); }), #pragma warning disable CS0618 TestCase("ByField succeeds", () => { Expect.equal(Sqlite.Query.Find.ByField("tbl", Field.GE("Golf", 0)), "SELECT data FROM tbl WHERE data->>'Golf' >= @field0", "SELECT by JSON comparison query not correct"); }) #pragma warning restore CS0618 ]), TestList("Patch", [ TestCase("ById succeeds", () => { Expect.equal(Sqlite.Query.Patch.ById("tbl"), "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Id' = @id", "UPDATE partial by ID statement not correct"); }), #pragma warning disable CS0618 TestCase("ByField succeeds", () => { Expect.equal(Sqlite.Query.Patch.ByField("tbl", Field.NE("Part", 0)), "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data->>'Part' <> @field0", "UPDATE partial by JSON comparison query not correct"); }) #pragma warning restore CS0618 ]), TestList("RemoveFields", [ TestCase("ById succeeds", () => { Expect.equal(Sqlite.Query.RemoveFields.ById("tbl", new[] { new SqliteParameter("@name", "one") }), "UPDATE tbl SET data = json_remove(data, @name) WHERE data->>'Id' = @id", "Remove field by ID query not correct"); }), #pragma warning disable CS0618 TestCase("ByField succeeds", () => { Expect.equal(Sqlite.Query.RemoveFields.ByField("tbl", Field.LT("Fly", 0), new[] { new SqliteParameter("@name0", "one"), new SqliteParameter("@name1", "two") }), "UPDATE tbl SET data = json_remove(data, @name0, @name1) WHERE data->>'Fly' < @field0", "Remove field by field query not correct"); }) #pragma warning restore CS0618 ]), TestList("Delete", [ TestCase("ById succeeds", () => { Expect.equal(Sqlite.Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data->>'Id' = @id", "DELETE by ID query not correct"); }), #pragma warning disable CS0618 TestCase("ByField succeeds", () => { 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"); }) #pragma warning restore CS0618 ]) ]), TestList("Parameters", [ TestCase("Id succeeds", () => { var theParam = Parameters.Id(7); Expect.equal(theParam.ParameterName, "@id", "The parameter name is incorrect"); Expect.equal(theParam.Value, "7", "The parameter value is incorrect"); }), TestCase("Json succeeds", () => { var theParam = Parameters.Json("@test", new { Nice = "job" }); Expect.equal(theParam.ParameterName, "@test", "The parameter name is incorrect"); Expect.equal(theParam.Value, "{\"Nice\":\"job\"}", "The parameter value is incorrect"); }), #pragma warning disable CS0618 TestCase("AddField succeeds when adding a parameter", () => { 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"); Expect.equal(theParam.Value, 99, "The parameter value is incorrect"); }), TestCase("AddField succeeds when not adding a parameter", () => { var paramSeq = Parameters.AddField("@it", Field.EX("Coffee"), []); Expect.isEmpty(paramSeq, "There should not have been any parameters added"); }), #pragma warning restore CS0618 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 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 } ]; /// /// Add the test documents to the database /// internal static async Task LoadDocs() { foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc); } private static readonly Test Integration = TestList("Integration", [ TestCase("Configuration.UseConnectionString succeeds", () => { try { Sqlite.Configuration.UseConnectionString("Data Source=test.db"); Expect.equal(Sqlite.Configuration.connectionString, new FSharpOption("Data Source=test.db;Foreign Keys=True"), "Connection string incorrect"); } finally { Sqlite.Configuration.UseConnectionString("Data Source=:memory:"); } }), TestList("Custom", [ TestList("Single", [ TestCase("succeeds when a row is found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", new[] { Parameters.Id("one") }, Results.FromData); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc!.Id, "one", "The incorrect document was returned"); }), TestCase("succeeds when a row is not found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", new[] { Parameters.Id("eighty") }, Results.FromData); Expect.isNull(doc, "There should not have been a document returned"); }) ]), TestList("List", [ TestCase("succeeds when data is found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var docs = await Custom.List(Query.SelectFromTable(SqliteDb.TableName), Parameters.None, Results.FromData); Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); }), TestCase("succeeds when data is not found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var docs = await Custom.List( $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", new[] { new SqliteParameter("@value", 100) }, Results.FromData); Expect.isEmpty(docs, "There should have been no documents returned"); }) ]), TestList("NonQuery", [ TestCase("succeeds when operating on data", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Custom.NonQuery($"DELETE FROM {SqliteDb.TableName}", Parameters.None); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 0L, "There should be no documents remaining in the table"); }), TestCase("succeeds when no data matches where clause", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Custom.NonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", new[] { new SqliteParameter("@value", 100) }); 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(); 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", [ TestCase("EnsureTable succeeds", async () => { await using var db = await SqliteDb.BuildDb(); var exists = await ItExists("ensured"); var alsoExists = await ItExists("idx_ensured_key"); 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 ItExists("ensured"); alsoExists = await ItExists("idx_ensured_key"); Expect.isTrue(exists, "The table should now exist"); Expect.isTrue(alsoExists, "The key index should now exist"); return; async ValueTask ItExists(string name) { return await Custom.Scalar( $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it", new SqliteParameter[] { new("@name", name) }, Results.ToExists); } }), TestCase("EnsureFieldIndex succeeds", async () => { await using var db = await SqliteDb.BuildDb(); 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(); Expect.isTrue(exists, "The index should now exist"); return; Task IndexExists() => Custom.Scalar( $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it", Parameters.None, Results.ToExists); }) ]), TestList("Document.Insert", [ TestCase("succeeds", async () => { await using var db = await SqliteDb.BuildDb(); var before = await Find.All(SqliteDb.TableName); Expect.isEmpty(before, "There should be no documents in the table"); await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "turkey", Sub = new() { Foo = "gobble", Bar = "gobble" } }); var after = await Find.All(SqliteDb.TableName); Expect.equal(after.Count, 1, "There should have been one document inserted"); }), TestCase("fails for duplicate key", async () => { await using var db = await SqliteDb.BuildDb(); await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "test" }); try { await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "test" }); Expect.isTrue(false, "An exception should have been raised for duplicate document ID insert"); } catch (Exception) { // This is what is supposed to happen } }) ]), TestList("Document.Save", [ TestCase("succeeds when a document is inserted", async () => { await using var db = await SqliteDb.BuildDb(); var before = await Find.All(SqliteDb.TableName); Expect.isEmpty(before, "There should be no documents in the table"); await Document.Save(SqliteDb.TableName, new JsonDocument { Id = "test", Sub = new() { Foo = "a", Bar = "b" } }); var after = await Find.All(SqliteDb.TableName); Expect.equal(after.Count, 1, "There should have been one document inserted"); }), TestCase("succeeds when a document is updated", async () => { await using var db = await SqliteDb.BuildDb(); await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "test", Sub = new() { Foo = "a", Bar = "b" } }); var before = await Find.ById(SqliteDb.TableName, "test"); Expect.isNotNull(before, "There should have been a document returned"); Expect.equal(before!.Id, "test", "The document is not correct"); Expect.isNotNull(before.Sub, "There should have been a sub-document"); Expect.equal(before.Sub!.Foo, "a", "The document is not correct"); Expect.equal(before.Sub.Bar, "b", "The document is not correct"); await Document.Save(SqliteDb.TableName, new JsonDocument { Id = "test" }); var after = await Find.ById(SqliteDb.TableName, "test"); Expect.isNotNull(after, "There should have been a document returned post-update"); 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", [ TestCase("All succeeds", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var theCount = await Count.All(SqliteDb.TableName); Expect.equal(theCount, 5L, "There should have been 5 matching documents"); }), #pragma warning disable CS0618 TestCase("ByField succeeds for numeric range", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("NumValue", 10, 20)); Expect.equal(theCount, 3L, "There should have been 3 matching documents"); }), TestCase("ByField succeeds for non-numeric range", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple")); Expect.equal(theCount, 1L, "There should have been 1 matching document"); }) #pragma warning restore CS0618 ]), TestList("Exists", [ TestList("ById", [ TestCase("succeeds when a document exists", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var exists = await Exists.ById(SqliteDb.TableName, "three"); Expect.isTrue(exists, "There should have been an existing document"); }), TestCase("succeeds when a document does not exist", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var exists = await Exists.ById(SqliteDb.TableName, "seven"); Expect.isFalse(exists, "There should not have been an existing document"); }) ]), #pragma warning disable CS0618 TestList("ByField", [ TestCase("succeeds when documents exist", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var exists = await Exists.ByField(SqliteDb.TableName, Field.GE("NumValue", 10)); Expect.isTrue(exists, "There should have been existing documents"); }), TestCase("succeeds when no matching documents exist", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none")); Expect.isFalse(exists, "There should not have been any existing documents"); }) ]) #pragma warning restore CS0618 ]), TestList("Find", [ TestList("All", [ TestCase("succeeds when there is data", async () => { await using var db = await SqliteDb.BuildDb(); await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "one", Value = "two" }); await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "three", Value = "four" }); await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "five", Value = "six" }); var results = await Find.All(SqliteDb.TableName); Expect.equal(results.Count, 3, "There should have been 3 documents returned"); }), TestCase("succeeds when there is no data", async () => { await using var db = await SqliteDb.BuildDb(); var results = await Find.All(SqliteDb.TableName); Expect.isEmpty(results, "There should have been no documents returned"); }) ]), TestList("ById", [ TestCase("succeeds when a document is found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Find.ById(SqliteDb.TableName, "two"); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc!.Id, "two", "The incorrect document was returned"); }), TestCase("succeeds when a document is not found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Find.ById(SqliteDb.TableName, "twenty two"); Expect.isNull(doc, "There should not have been a document returned"); }) ]), #pragma warning disable CS0618 TestList("ByField", [ TestCase("succeeds when documents are found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var docs = await Find.ByField(SqliteDb.TableName, Field.GT("NumValue", 15)); Expect.equal(docs.Count, 2, "There should have been two documents returned"); }), TestCase("succeeds when documents are not found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var docs = await Find.ByField(SqliteDb.TableName, Field.EQ("Value", "mauve")); Expect.isEmpty(docs, "There should have been no documents returned"); }) ]), TestList("FirstByField", [ TestCase("succeeds when a document is found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Find.FirstByField(SqliteDb.TableName, Field.EQ("Value", "another")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.equal(doc!.Id, "two", "The incorrect document was returned"); }), TestCase("succeeds when multiple documents are found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Find.FirstByField(SqliteDb.TableName, Field.EQ("Sub.Foo", "green")); Expect.isNotNull(doc, "There should have been a document returned"); Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned"); }), TestCase("succeeds when a document is not found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var doc = await Find.FirstByField(SqliteDb.TableName, Field.EQ("Value", "absent")); Expect.isNull(doc, "There should not have been a document returned"); }) ]) #pragma warning restore CS0618 ]), TestList("Update", [ TestList("ById", [ TestCase("succeeds when a document is updated", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); var testDoc = new JsonDocument { Id = "one", Sub = new() { Foo = "blue", Bar = "red" } }; await Update.ById(SqliteDb.TableName, "one", testDoc); var after = await Find.ById(SqliteDb.TableName, "one"); Expect.isNotNull(after, "There should have been a document returned post-update"); Expect.equal(after!.Id, "one", "The updated document is not correct"); Expect.isNotNull(after.Sub, "The updated document should have had a sub-document"); Expect.equal(after.Sub!.Foo, "blue", "The updated sub-document is not correct"); Expect.equal(after.Sub.Bar, "red", "The updated sub-document is not correct"); }), TestCase("succeeds when no document is updated", async () => { await using var db = await SqliteDb.BuildDb(); var before = await Find.All(SqliteDb.TableName); Expect.isEmpty(before, "There should have been no documents returned"); // This not raising an exception is the test await Update.ById(SqliteDb.TableName, "test", new JsonDocument { Id = "x", Sub = new() { Foo = "blue", Bar = "red" } }); }) ]), TestList("ByFunc", [ TestCase("succeeds when a document is updated", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Update.ByFunc(SqliteDb.TableName, doc => doc.Id, new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); var after = await Find.ById(SqliteDb.TableName, "one"); Expect.isNotNull(after, "There should have been a document returned post-update"); Expect.equal(after!.Id, "one", "The updated document is incorrect"); Expect.equal(after.Value, "le un", "The updated document is incorrect"); Expect.equal(after.NumValue, 1, "The updated document is incorrect"); Expect.isNull(after.Sub, "The updated document should not have a sub-document"); }), TestCase("succeeds when no document is updated", async () => { await using var db = await SqliteDb.BuildDb(); var before = await Find.All(SqliteDb.TableName); Expect.isEmpty(before, "There should have been no documents returned"); // This not raising an exception is the test await Update.ByFunc(SqliteDb.TableName, doc => doc.Id, new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); }) ]), ]), TestList("Patch", [ TestList("ById", [ TestCase("succeeds when a document is updated", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Patch.ById(SqliteDb.TableName, "one", new { NumValue = 44 }); var after = await Find.ById(SqliteDb.TableName, "one"); Expect.isNotNull(after, "There should have been a document returned post-update"); Expect.equal(after!.Id, "one", "The updated document is not correct"); Expect.equal(after.NumValue, 44, "The updated document is not correct"); }), TestCase("succeeds when no document is updated", async () => { await using var db = await SqliteDb.BuildDb(); var before = await Find.All(SqliteDb.TableName); Expect.isEmpty(before, "There should have been no documents returned"); // This not raising an exception is the test await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" }); }) ]), #pragma warning disable CS0618 TestList("ByField", [ TestCase("succeeds when a document is updated", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); var after = await Count.ByField(SqliteDb.TableName, Field.EQ("NumValue", 77)); Expect.equal(after, 2L, "There should have been 2 documents returned"); }), TestCase("succeeds when no document is updated", async () => { await using var db = await SqliteDb.BuildDb(); var before = await Find.All(SqliteDb.TableName); Expect.isEmpty(before, "There should have been no documents returned"); // This not raising an exception is the test await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" }); }) ]) #pragma warning restore CS0618 ]), TestList("RemoveFields", [ TestList("ById", [ TestCase("succeeds when fields are removed", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Sub", "Value" }); var updated = await Find.ById(SqliteDb.TableName, "two"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.equal(updated.Value, "", "The string value should have been removed"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); }), TestCase("succeeds when a field is not removed", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); // This not raising an exception is the test await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" }); }), TestCase("succeeds when no document is matched", async () => { await using var db = await SqliteDb.BuildDb(); // This not raising an exception is the test await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" }); }) ]), #pragma warning disable CS0618 TestList("ByField", [ TestCase("succeeds when a field is removed", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" }); var updated = await Find.ById(SqliteDb.TableName, "four"); Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNull(updated.Sub, "The sub-document should have been removed"); }), TestCase("succeeds when a field is not removed", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); // This not raising an exception is the test await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" }); }), TestCase("succeeds when no document is matched", async () => { await using var db = await SqliteDb.BuildDb(); // This not raising an exception is the test await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" }); }) ]) #pragma warning restore CS0618 ]), TestList("Delete", [ TestList("ById", [ TestCase("succeeds when a document is deleted", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Delete.ById(SqliteDb.TableName, "four"); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 4L, "There should have been 4 documents remaining"); }), TestCase("succeeds when a document is not deleted", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Delete.ById(SqliteDb.TableName, "thirty"); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); }) ]), #pragma warning disable CS0618 TestList("ByField", [ TestCase("succeeds when documents are deleted", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Delete.ByField(SqliteDb.TableName, Field.NE("Value", "purple")); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 2L, "There should have been 2 documents remaining"); }), TestCase("succeeds when documents are not deleted", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); await Delete.ByField(SqliteDb.TableName, Field.EQ("Value", "crimson")); var remaining = await Count.All(SqliteDb.TableName); Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); }) ]) #pragma warning restore CS0618 ]), TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:")) ]); /// /// All tests for SQLite C# functions and methods /// [Tests] public static readonly Test All = TestList("Sqlite.C#", [Unit, TestSequenced(Integration)]); }