From 7a18ec53e54b033a1ddc23a20599a7020525b79c Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 7 Apr 2025 21:37:11 -0400 Subject: [PATCH] Add C# SQLite Json tests --- .../SqliteCSharpExtensionTests.cs | 761 ++++++++++++++++-- src/Tests.CSharp/SqliteCSharpTests.cs | 625 +++++++++++++- src/Tests.CSharp/Types.cs | 18 +- src/Tests/SqliteExtensionTests.fs | 110 +-- src/Tests/SqliteTests.fs | 2 +- 5 files changed, 1394 insertions(+), 122 deletions(-) diff --git a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs index 5d23375..6842b2b 100644 --- a/src/Tests.CSharp/SqliteCSharpExtensionTests.cs +++ b/src/Tests.CSharp/SqliteCSharpExtensionTests.cs @@ -1,17 +1,65 @@ using Expecto.CSharp; using Expecto; using BitBadger.Documents.Sqlite; +using Microsoft.Data.Sqlite; namespace BitBadger.Documents.Tests.CSharp; using static Runner; /// -/// C# tests for the extensions on the SqliteConnection class +/// C# tests for the extensions on the SqliteConnection class /// public static class SqliteCSharpExtensionTests { - private static Task LoadDocs() => SqliteCSharpTests.LoadDocs(); + private static async Task LoadDocs(SqliteConnection conn) + { + foreach (var doc in JsonDocument.TestDocuments) await conn.Insert(SqliteDb.TableName, doc); + } + + /// Verify a JSON array begins with "[" and ends with "]" + private static void VerifyBeginEnd(string json) + { + Expect.stringStarts(json, "[", "The array should have started with `[`"); + Expect.stringEnds(json, "]", "The array should have ended with `]`"); + } + + /// Verify an empty JSON array + private static void VerifyEmpty(string json) + { + Expect.equal(json, "[]", "There should be no documents returned"); + } + + /// Verify an empty JSON document + private static void VerifyNoDoc(string json) + { + Expect.equal(json, "{}", "There should be no document returned"); + } + + /// Set up a stream writer for a test + private static StreamWriter WriteStream(Stream stream) + { + StreamWriter writer = new(stream); + writer.AutoFlush = true; + return writer; + } + + /// Get the text of the given stream + private static string StreamText(Stream stream) + { + stream.Position = 0L; + using StreamReader reader = new(stream); + return reader.ReadToEnd(); + } + + /// Verify the presence of any of the given documents in the given JSON + private static void VerifyAny(string json, IEnumerable docs) + { + var theDocs = docs.ToList(); + if (theDocs.Any(json.Contains)) return; + var anyDocs = string.Join(" | ", theDocs); + Expect.isTrue(false, $"Could not find any of |{anyDocs}| in {json}"); + } /// /// Integration tests for the SQLite extension methods @@ -19,13 +67,99 @@ public static class SqliteCSharpExtensionTests [Tests] public static readonly Test Integration = TestList("Sqlite.C#.Extensions", [ + TestList("CustomList", + [ + TestCase("succeeds when data is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + var docs = await conn.CustomList(Query.Find(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 using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + var docs = await conn.CustomList( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)], + Results.FromData); + Expect.isEmpty(docs, "There should have been no documents returned"); + }) + ]), + TestList("CustomJsonArray", + [ + TestCase("succeeds when data is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + var json = await conn.CustomJsonArray(Query.Find(SqliteDb.TableName), [], Results.JsonFromData); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.One, "Document ID `one` should have been found"); + Expect.stringContains(json, JsonDocument.Two,"Document ID `two` should have been found"); + Expect.stringContains(json, JsonDocument.Three, "Document ID `three` should have been found"); + Expect.stringContains(json, JsonDocument.Four, "Document ID `four` should have been found"); + Expect.stringContains(json, JsonDocument.Five, "Document ID `five` should have been found"); + }), + TestCase("succeeds when data is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + VerifyEmpty(await conn.CustomJsonArray( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", + [new SqliteParameter("@value", 100)], Results.JsonFromData)); + }) + ]), + TestList("WriteCustomJsonArray", + [ + TestCase("succeeds when data is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteCustomJsonArray(Query.Find(SqliteDb.TableName), [], writer, Results.JsonFromData); + + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.One, "Document ID `one` should have been found"); + Expect.stringContains(json, JsonDocument.Two,"Document ID `two` should have been found"); + Expect.stringContains(json, JsonDocument.Three, "Document ID `three` should have been found"); + Expect.stringContains(json, JsonDocument.Four, "Document ID `four` should have been found"); + Expect.stringContains(json, JsonDocument.Five, "Document ID `five` should have been found"); + }), + TestCase("succeeds when data is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteCustomJsonArray( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", + [new SqliteParameter("@value", 100)], writer, Results.JsonFromData); + + VerifyEmpty(StreamText(stream)); + }) + ]), TestList("CustomSingle", [ TestCase("succeeds when a row is found", async () => { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", [Parameters.Id("one")], Results.FromData); @@ -36,35 +170,35 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", [Parameters.Id("eighty")], Results.FromData); Expect.isNull(doc, "There should not have been a document returned"); }) ]), - TestList("CustomList", + TestList("CustomJsonSingle", [ - TestCase("succeeds when data is found", async () => + TestCase("succeeds when a row is found", async () => { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); - var docs = await conn.CustomList(Query.Find(SqliteDb.TableName), Parameters.None, - Results.FromData); - Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); + var json = await conn.CustomJsonSingle( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", + [new SqliteParameter("@id", "one")], Results.JsonFromData); + Expect.equal(json, JsonDocument.One, "The JSON document is incorrect"); }), - TestCase("succeeds when data is not found", async () => + TestCase("succeeds when a row is not found", async () => { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); - var docs = await conn.CustomList( - $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)], - Results.FromData); - Expect.isEmpty(docs, "There should have been no documents returned"); + VerifyNoDoc(await conn.CustomJsonSingle( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", + [new SqliteParameter("@id", "eighty")], Results.JsonFromData)); }) ]), TestList("CustomNonQuery", @@ -73,7 +207,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.CustomNonQuery($"DELETE FROM {SqliteDb.TableName}", Parameters.None); @@ -84,7 +218,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.CustomNonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)]); @@ -210,7 +344,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var theCount = await conn.CountAll(SqliteDb.TableName); Expect.equal(theCount, 5L, "There should have been 5 matching documents"); @@ -219,7 +353,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var theCount = await conn.CountByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "purple")]); @@ -231,7 +365,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var exists = await conn.ExistsById(SqliteDb.TableName, "three"); Expect.isTrue(exists, "There should have been an existing document"); @@ -240,7 +374,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var exists = await conn.ExistsById(SqliteDb.TableName, "seven"); Expect.isFalse(exists, "There should not have been an existing document"); @@ -252,7 +386,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var exists = await conn.ExistsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.GreaterOrEqual("NumValue", 10)]); @@ -262,7 +396,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var exists = await conn.ExistsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Nothing", "none")]); @@ -297,7 +431,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var results = await conn.FindAllOrdered(SqliteDb.TableName, [Field.Named("n:NumValue")]); Expect.hasLength(results, 5, "There should have been 5 documents returned"); @@ -308,7 +442,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var results = await conn.FindAllOrdered(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]); @@ -320,7 +454,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var results = await conn.FindAllOrdered(SqliteDb.TableName, [Field.Named("Id DESC")]); Expect.hasLength(results, 5, "There should have been 5 documents returned"); @@ -334,7 +468,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindById(SqliteDb.TableName, "two"); Expect.isNotNull(doc, "There should have been a document returned"); @@ -344,7 +478,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindById(SqliteDb.TableName, "eighty-seven"); Expect.isNull(doc, "There should not have been a document returned"); @@ -356,7 +490,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var docs = await conn.FindByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)]); @@ -366,7 +500,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var docs = await conn.FindByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "mauve")]); @@ -379,7 +513,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var docs = await conn.FindByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)], [Field.Named("Id")]); @@ -390,7 +524,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var docs = await conn.FindByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)], [Field.Named("Id DESC")]); @@ -404,7 +538,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindFirstByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "another")]); @@ -415,7 +549,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindFirstByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Sub.Foo", "green")]); @@ -426,7 +560,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindFirstByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "absent")]); @@ -439,7 +573,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindFirstByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar")]); @@ -450,7 +584,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var doc = await conn.FindFirstByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]); @@ -458,13 +592,532 @@ public static class SqliteCSharpExtensionTests Expect.equal("four", doc!.Id, "An incorrect document was returned"); }) ]), + TestList("JsonAll", + [ + TestCase("succeeds when there is data", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + + await conn.Insert(SqliteDb.TableName, new SubDocument { Foo = "one", Bar = "two" }); + await conn.Insert(SqliteDb.TableName, new SubDocument { Foo = "three", Bar = "four" }); + await conn.Insert(SqliteDb.TableName, new SubDocument { Foo = "five", Bar = "six" }); + + var json = await conn.JsonAll(SqliteDb.TableName); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Foo":"one","Bar":"two"}""", "The first document was not found"); + Expect.stringContains(json, """{"Foo":"three","Bar":"four"}""", "The second document was not found"); + Expect.stringContains(json, """{"Foo":"five","Bar":"six"}""", "The third document was not found"); + }), + TestCase("succeeds when there is no data", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + VerifyEmpty(await conn.JsonAll(SqliteDb.TableName)); + }) + ]), + TestList("JsonAllOrdered", + [ + TestCase("succeeds when ordering numerically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal(await conn.JsonAllOrdered(SqliteDb.TableName, [Field.Named("n:NumValue")]), + $"[{JsonDocument.One},{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering numerically descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal(await conn.JsonAllOrdered(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]), + $"[{JsonDocument.Five},{JsonDocument.Four},{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering alphabetically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal(await conn.JsonAllOrdered(SqliteDb.TableName, [Field.Named("Id DESC")]), + $"[{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }) + ]), + TestList("JsonById", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal(await conn.JsonById(SqliteDb.TableName, "two"), JsonDocument.Two, + "The incorrect document was returned"); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + VerifyNoDoc(await conn.JsonById(SqliteDb.TableName, "three hundred eighty-seven")); + }) + ]), + TestList("JsonByFields", + [ + TestCase("succeeds when documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + var json = await conn.JsonByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)]); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.Four, "Document `four` should have been returned"); + Expect.stringContains(json, JsonDocument.Five, "Document `five` should have been returned"); + }), + TestCase("succeeds when documents are found using IN with numeric field", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonByFields(SqliteDb.TableName, FieldMatch.All, [Field.In("NumValue", [2, 4, 6, 8])]), + $"[{JsonDocument.Three}]", "There should have been one document returned"); + }), + TestCase("succeeds when documents are not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + VerifyEmpty(await conn.JsonByFields(SqliteDb.TableName, FieldMatch.Any, + [Field.Greater("NumValue", 100)])); + }), + TestCase("succeeds for InArray when matching documents exist", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await conn.EnsureTable(SqliteDb.TableName); + foreach (var doc in ArrayDocument.TestDocuments) await conn.Insert(SqliteDb.TableName, doc); + + var json = await conn.JsonByFields(SqliteDb.TableName, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["c"])]); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Id":"first","Values":["a","b","c"]}""", + "Document `first` should have been returned"); + Expect.stringContains(json, """{"Id":"second","Values":["c","d","e"]}""", + "Document `second` should have been returned"); + }), + TestCase("succeeds for InArray when no matching documents exist", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await conn.EnsureTable(SqliteDb.TableName); + foreach (var doc in ArrayDocument.TestDocuments) await conn.Insert(SqliteDb.TableName, doc); + VerifyEmpty(await conn.JsonByFields(SqliteDb.TableName, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["j"])])); + }) + ]), + TestList("JsonByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)], + [Field.Named("Id")]), $"[{JsonDocument.Five},{JsonDocument.Four}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)], + [Field.Named("Id DESC")]), $"[{JsonDocument.Four},{JsonDocument.Five}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting case-sensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonByFieldsOrdered(SqliteDb.TableName, FieldMatch.All, + [Field.LessOrEqual("NumValue", 10)], [Field.Named("Value")]), + $"[{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Two}]", "Documents not ordered correctly"); + }), + TestCase("succeeds when sorting case-insensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonByFieldsOrdered(SqliteDb.TableName, FieldMatch.All, + [Field.LessOrEqual("NumValue", 10)], [Field.Named("i:Value")]), + $"[{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.One}]", "Documents not ordered correctly"); + }) + ]), + TestList("JsonFirstByFields", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonFirstByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "another")]), + JsonDocument.Two, "The incorrect document was returned"); + }), + TestCase("succeeds when multiple documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + var json = await conn.JsonFirstByFields(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")]); + Expect.notEqual(json, "{}", "There should have been a document returned"); + VerifyAny(json, [JsonDocument.Two, JsonDocument.Four]); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + VerifyNoDoc(await conn.JsonFirstByFields(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Value", "absent")])); + }) + ]), + TestList("JsonFirstByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonFirstByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar")]), JsonDocument.Two, + "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + Expect.equal( + await conn.JsonFirstByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]), JsonDocument.Four, + "An incorrect document was returned"); + }) + ]), + TestList("WriteJsonAll", + [ + TestCase("succeeds when there is data", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + + await conn.Insert(SqliteDb.TableName, new SubDocument { Foo = "one", Bar = "two" }); + await conn.Insert(SqliteDb.TableName, new SubDocument { Foo = "three", Bar = "four" }); + await conn.Insert(SqliteDb.TableName, new SubDocument { Foo = "five", Bar = "six" }); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonAll(SqliteDb.TableName, writer); + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Foo":"one","Bar":"two"}""", "The first document was not found"); + Expect.stringContains(json, """{"Foo":"three","Bar":"four"}""", "The second document was not found"); + Expect.stringContains(json, """{"Foo":"five","Bar":"six"}""", "The third document was not found"); + }), + TestCase("succeeds when there is no data", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonAll(SqliteDb.TableName, writer); + VerifyEmpty(StreamText(stream)); + }) + ]), + TestList("WriteJsonAllOrdered", + [ + TestCase("succeeds when ordering numerically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonAllOrdered(SqliteDb.TableName, writer, [Field.Named("n:NumValue")]); + Expect.equal(StreamText(stream), + $"[{JsonDocument.One},{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering numerically descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonAllOrdered(SqliteDb.TableName, writer, [Field.Named("n:NumValue DESC")]); + Expect.equal(StreamText(stream), + $"[{JsonDocument.Five},{JsonDocument.Four},{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering alphabetically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonAllOrdered(SqliteDb.TableName, writer, [Field.Named("Id DESC")]); + Expect.equal(StreamText(stream), + $"[{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }) + ]), + TestList("WriteJsonById", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonById(SqliteDb.TableName, writer, "two"); + Expect.equal(StreamText(stream), JsonDocument.Two, "The incorrect document was returned"); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonById(SqliteDb.TableName, writer, "three hundred eighty-seven"); + VerifyNoDoc(StreamText(stream)); + }) + ]), + TestList("WriteJsonByFields", + [ + TestCase("succeeds when documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Greater("NumValue", 15)]); + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.Four, "Document `four` should have been returned"); + Expect.stringContains(json, JsonDocument.Five, "Document `five` should have been returned"); + }), + TestCase("succeeds when documents are found using IN with numeric field", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFields(SqliteDb.TableName, writer, FieldMatch.All, + [Field.In("NumValue", [2, 4, 6, 8])]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Three}]", + "There should have been one document returned"); + }), + TestCase("succeeds when documents are not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Greater("NumValue", 100)]); + VerifyEmpty(StreamText(stream)); + }), + TestCase("succeeds for InArray when matching documents exist", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await conn.EnsureTable(SqliteDb.TableName); + foreach (var doc in ArrayDocument.TestDocuments) await conn.Insert(SqliteDb.TableName, doc); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFields(SqliteDb.TableName, writer, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["c"])]); + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Id":"first","Values":["a","b","c"]}""", + "Document `first` should have been returned"); + Expect.stringContains(json, """{"Id":"second","Values":["c","d","e"]}""", + "Document `second` should have been returned"); + }), + TestCase("succeeds for InArray when no matching documents exist", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await conn.EnsureTable(SqliteDb.TableName); + foreach (var doc in ArrayDocument.TestDocuments) await conn.Insert(SqliteDb.TableName, doc); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFields(SqliteDb.TableName, writer, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["j"])]); + VerifyEmpty(StreamText(stream)); + }) + ]), + TestList("WriteJsonByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Greater("NumValue", 15)], [Field.Named("Id")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Five},{JsonDocument.Four}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Greater("NumValue", 15)], [Field.Named("Id DESC")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Four},{JsonDocument.Five}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting case-sensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.All, + [Field.LessOrEqual("NumValue", 10)], [Field.Named("Value")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Two}]", + "Documents not ordered correctly"); + }), + TestCase("succeeds when sorting case-insensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.All, + [Field.LessOrEqual("NumValue", 10)], [Field.Named("i:Value")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.One}]", + "Documents not ordered correctly"); + }) + ]), + TestList("WriteJsonFirstByFields", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonFirstByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Value", "another")]); + Expect.equal(StreamText(stream), JsonDocument.Two, "The incorrect document was returned"); + }), + TestCase("succeeds when multiple documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonFirstByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")]); + var json = StreamText(stream); + Expect.notEqual(json, "{}", "There should have been a document returned"); + VerifyAny(json, [JsonDocument.Two, JsonDocument.Four]); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonFirstByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Value", "absent")]); + VerifyNoDoc(StreamText(stream)); + }) + ]), + TestList("WriteJsonFirstByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonFirstByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar")]); + Expect.equal(StreamText(stream), JsonDocument.Two, "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await using var conn = Sqlite.Configuration.DbConn(); + await LoadDocs(conn); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await conn.WriteJsonFirstByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]); + Expect.equal(StreamText(stream), JsonDocument.Four, "An incorrect document was returned"); + }) + ]), TestList("UpdateById", [ TestCase("succeeds when a document is updated", async () => { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); var testDoc = new JsonDocument { Id = "one", Sub = new() { Foo = "blue", Bar = "red" } }; await conn.UpdateById(SqliteDb.TableName, "one", testDoc); @@ -493,7 +1146,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.UpdateByFunc(SqliteDb.TableName, doc => doc.Id, new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); @@ -522,7 +1175,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.PatchById(SqliteDb.TableName, "one", new { NumValue = 44 }); var after = await conn.FindById(SqliteDb.TableName, "one"); @@ -547,7 +1200,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.PatchByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "purple")], new { NumValue = 77 }); @@ -572,7 +1225,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["Sub", "Value"]); var updated = await Find.ById(SqliteDb.TableName, "two"); @@ -584,8 +1237,8 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); - + await LoadDocs(conn); + // This not raising an exception is the test await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["AFieldThatIsNotThere"]); }), @@ -593,7 +1246,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - + // This not raising an exception is the test await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["Value"]); }) @@ -604,7 +1257,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("NumValue", 17)], ["Sub"]); @@ -616,8 +1269,8 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); - + await LoadDocs(conn); + // This not raising an exception is the test await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("NumValue", 17)], ["Nothing"]); @@ -626,7 +1279,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - + // This not raising an exception is the test await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NotEqual("Abracadabra", "apple")], ["Value"]); @@ -638,7 +1291,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.DeleteById(SqliteDb.TableName, "four"); var remaining = await conn.CountAll(SqliteDb.TableName); @@ -648,7 +1301,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.DeleteById(SqliteDb.TableName, "thirty"); var remaining = await conn.CountAll(SqliteDb.TableName); @@ -661,7 +1314,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.DeleteByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NotEqual("Value", "purple")]); var remaining = await conn.CountAll(SqliteDb.TableName); @@ -671,7 +1324,7 @@ public static class SqliteCSharpExtensionTests { await using var db = await SqliteDb.BuildDb(); await using var conn = Sqlite.Configuration.DbConn(); - await LoadDocs(); + await LoadDocs(conn); await conn.DeleteByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "crimson")]); var remaining = await conn.CountAll(SqliteDb.TableName); diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs index 14aa617..5c89aab 100644 --- a/src/Tests.CSharp/SqliteCSharpTests.cs +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -2,6 +2,7 @@ using Expecto; using Microsoft.FSharp.Core; using BitBadger.Documents.Sqlite; +using Microsoft.Data.Sqlite; namespace BitBadger.Documents.Tests.CSharp; @@ -147,7 +148,7 @@ public static class SqliteCSharpTests /// /// Add the test documents to the database /// - internal static async Task LoadDocs() + private static async Task LoadDocs() { foreach (var doc in JsonDocument.TestDocuments) await Document.Insert(SqliteDb.TableName, doc); } @@ -169,11 +170,135 @@ public static class SqliteCSharpTests } }); + /// Verify a JSON array begins with "[" and ends with "]" + private static void VerifyBeginEnd(string json) + { + Expect.stringStarts(json, "[", "The array should have started with `[`"); + Expect.stringEnds(json, "]", "The array should have ended with `]`"); + } + + /// Verify an empty JSON array + private static void VerifyEmpty(string json) + { + Expect.equal(json, "[]", "There should be no documents returned"); + } + + /// Verify an empty JSON document + private static void VerifyNoDoc(string json) + { + Expect.equal(json, "{}", "There should be no document returned"); + } + + /// Set up a stream writer for a test + private static StreamWriter WriteStream(Stream stream) + { + StreamWriter writer = new(stream); + writer.AutoFlush = true; + return writer; + } + + /// Get the text of the given stream + private static string StreamText(Stream stream) + { + stream.Position = 0L; + using StreamReader reader = new(stream); + return reader.ReadToEnd(); + } + + /// Verify the presence of any of the given documents in the given JSON + private static void VerifyAny(string json, IEnumerable docs) + { + var theDocs = docs.ToList(); + if (theDocs.Any(json.Contains)) return; + var anyDocs = string.Join(" | ", theDocs); + Expect.isTrue(false, $"Could not find any of |{anyDocs}| in {json}"); + } + /// /// Integration tests for the Custom module of the SQLite library /// private static readonly Test CustomTests = TestList("Custom", [ + TestList("List", + [ + TestCase("succeeds when data is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var docs = await Custom.List(Query.Find(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("@value", 100)], + Results.FromData); + Expect.isEmpty(docs, "There should have been no documents returned"); + }) + ]), + TestList("JsonArray", + [ + TestCase("succeeds when data is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var json = await Custom.JsonArray(Query.Find(SqliteDb.TableName), [], Results.JsonFromData); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.One, "Document ID `one` should have been found"); + Expect.stringContains(json, JsonDocument.Two,"Document ID `two` should have been found"); + Expect.stringContains(json, JsonDocument.Three, "Document ID `three` should have been found"); + Expect.stringContains(json, JsonDocument.Four, "Document ID `four` should have been found"); + Expect.stringContains(json, JsonDocument.Five, "Document ID `five` should have been found"); + }), + TestCase("succeeds when data is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + VerifyEmpty(await Custom.JsonArray( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", + [new SqliteParameter("@value", 100)], Results.JsonFromData)); + }) + ]), + TestList("WriteJsonArray", + [ + TestCase("succeeds when data is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Custom.WriteJsonArray(Query.Find(SqliteDb.TableName), [], writer, Results.JsonFromData); + + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.One, "Document ID `one` should have been found"); + Expect.stringContains(json, JsonDocument.Two,"Document ID `two` should have been found"); + Expect.stringContains(json, JsonDocument.Three, "Document ID `three` should have been found"); + Expect.stringContains(json, JsonDocument.Four, "Document ID `four` should have been found"); + Expect.stringContains(json, JsonDocument.Five, "Document ID `five` should have been found"); + }), + TestCase("succeeds when data is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Custom.WriteJsonArray( + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", + [new SqliteParameter("@value", 100)], writer, Results.JsonFromData); + + VerifyEmpty(StreamText(stream)); + }) + ]), TestList("Single", [ TestCase("succeeds when a row is found", async () => @@ -196,26 +321,24 @@ public static class SqliteCSharpTests Expect.isNull(doc, "There should not have been a document returned"); }) ]), - TestList("List", + TestList("JsonSingle", [ - TestCase("succeeds when data is found", async () => + TestCase("succeeds when a row is found", async () => { await using var db = await SqliteDb.BuildDb(); await LoadDocs(); - var docs = await Custom.List(Query.Find(SqliteDb.TableName), Parameters.None, - Results.FromData); - Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); + var json = await Custom.JsonSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", + [new SqliteParameter("@id", "one")], Results.JsonFromData); + Expect.equal(json, JsonDocument.One, "The JSON document is incorrect"); }), - TestCase("succeeds when data is not found", async () => + TestCase("succeeds when a row 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("@value", 100)], - Results.FromData); - Expect.isEmpty(docs, "There should have been no documents returned"); + VerifyNoDoc(await Custom.JsonSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", + [new SqliteParameter("@id", "eighty")], Results.JsonFromData)); }) ]), TestList("NonQuery", @@ -757,6 +880,485 @@ public static class SqliteCSharpTests ]) ]); + /// Integration tests for the Json module of the SQLite library + private static readonly Test JsonTests = TestList("Json", + [ + TestList("All", + [ + TestCase("succeeds when there is data", async () => + { + await using var db = await SqliteDb.BuildDb(); + + await Document.Insert(SqliteDb.TableName, new SubDocument { Foo = "one", Bar = "two" }); + await Document.Insert(SqliteDb.TableName, new SubDocument { Foo = "three", Bar = "four" }); + await Document.Insert(SqliteDb.TableName, new SubDocument { Foo = "five", Bar = "six" }); + + var json = await Json.All(SqliteDb.TableName); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Foo":"one","Bar":"two"}""", "The first document was not found"); + Expect.stringContains(json, """{"Foo":"three","Bar":"four"}""", "The second document was not found"); + Expect.stringContains(json, """{"Foo":"five","Bar":"six"}""", "The third document was not found"); + }), + TestCase("succeeds when there is no data", async () => + { + await using var db = await SqliteDb.BuildDb(); + VerifyEmpty(await Json.All(SqliteDb.TableName)); + }) + ]), + TestList("AllOrdered", + [ + TestCase("succeeds when ordering numerically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal(await Json.AllOrdered(SqliteDb.TableName, [Field.Named("n:NumValue")]), + $"[{JsonDocument.One},{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering numerically descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal(await Json.AllOrdered(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]), + $"[{JsonDocument.Five},{JsonDocument.Four},{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering alphabetically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal(await Json.AllOrdered(SqliteDb.TableName, [Field.Named("Id DESC")]), + $"[{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }) + ]), + TestList("ById", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal(await Json.ById(SqliteDb.TableName, "two"), JsonDocument.Two, + "The incorrect document was returned"); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + VerifyNoDoc(await Json.ById(SqliteDb.TableName, "three hundred eighty-seven")); + }) + ]), + TestList("ByFields", + [ + TestCase("succeeds when documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var json = await Json.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)]); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.Four, "Document `four` should have been returned"); + Expect.stringContains(json, JsonDocument.Five, "Document `five` should have been returned"); + }), + TestCase("succeeds when documents are found using IN with numeric field", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.ByFields(SqliteDb.TableName, FieldMatch.All, [Field.In("NumValue", [2, 4, 6, 8])]), + $"[{JsonDocument.Three}]", "There should have been one document returned"); + }), + TestCase("succeeds when documents are not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + VerifyEmpty(await Json.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 100)])); + }), + 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 json = await Json.ByFields(SqliteDb.TableName, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["c"])]); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Id":"first","Values":["a","b","c"]}""", + "Document `first` should have been returned"); + Expect.stringContains(json, """{"Id":"second","Values":["c","d","e"]}""", + "Document `second` should have been 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); + VerifyEmpty(await Json.ByFields(SqliteDb.TableName, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["j"])])); + }) + ]), + TestList("ByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.ByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)], + [Field.Named("Id")]), $"[{JsonDocument.Five},{JsonDocument.Four}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.ByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, [Field.Greater("NumValue", 15)], + [Field.Named("Id DESC")]), $"[{JsonDocument.Four},{JsonDocument.Five}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting case-sensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.ByFieldsOrdered(SqliteDb.TableName, FieldMatch.All, [Field.LessOrEqual("NumValue", 10)], + [Field.Named("Value")]), + $"[{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Two}]", "Documents not ordered correctly"); + }), + TestCase("succeeds when sorting case-insensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.ByFieldsOrdered(SqliteDb.TableName, FieldMatch.All, [Field.LessOrEqual("NumValue", 10)], + [Field.Named("i:Value")]), + $"[{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.One}]", "Documents not ordered correctly"); + }) + ]), + TestList("FirstByFields", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.FirstByFields(SqliteDb.TableName, FieldMatch.Any, [Field.Equal("Value", "another")]), + JsonDocument.Two, "The incorrect document was returned"); + }), + TestCase("succeeds when multiple documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var json = await Json.FirstByFields(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")]); + Expect.notEqual(json, "{}", "There should have been a document returned"); + VerifyAny(json, [JsonDocument.Two, JsonDocument.Four]); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + VerifyNoDoc(await Json.FirstByFields(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Value", "absent")])); + }) + ]), + TestList("FirstByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.FirstByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar")]), JsonDocument.Two, + "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + Expect.equal( + await Json.FirstByFieldsOrdered(SqliteDb.TableName, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]), JsonDocument.Four, + "An incorrect document was returned"); + }) + ]), + TestList("WriteAll", + [ + TestCase("succeeds when there is data", async () => + { + await using var db = await SqliteDb.BuildDb(); + + await Document.Insert(SqliteDb.TableName, new SubDocument { Foo = "one", Bar = "two" }); + await Document.Insert(SqliteDb.TableName, new SubDocument { Foo = "three", Bar = "four" }); + await Document.Insert(SqliteDb.TableName, new SubDocument { Foo = "five", Bar = "six" }); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteAll(SqliteDb.TableName, writer); + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Foo":"one","Bar":"two"}""", "The first document was not found"); + Expect.stringContains(json, """{"Foo":"three","Bar":"four"}""", "The second document was not found"); + Expect.stringContains(json, """{"Foo":"five","Bar":"six"}""", "The third document was not found"); + }), + TestCase("succeeds when there is no data", async () => + { + await using var db = await SqliteDb.BuildDb(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteAll(SqliteDb.TableName, writer); + VerifyEmpty(StreamText(stream)); + }) + ]), + TestList("WriteAllOrdered", + [ + TestCase("succeeds when ordering numerically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteAllOrdered(SqliteDb.TableName, writer, [Field.Named("n:NumValue")]); + Expect.equal(StreamText(stream), + $"[{JsonDocument.One},{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering numerically descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteAllOrdered(SqliteDb.TableName, writer, [Field.Named("n:NumValue DESC")]); + Expect.equal(StreamText(stream), + $"[{JsonDocument.Five},{JsonDocument.Four},{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One}]", + "The documents were not ordered correctly"); + }), + TestCase("succeeds when ordering alphabetically", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteAllOrdered(SqliteDb.TableName, writer, [Field.Named("Id DESC")]); + Expect.equal(StreamText(stream), + $"[{JsonDocument.Two},{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Four},{JsonDocument.Five}]", + "The documents were not ordered correctly"); + }) + ]), + TestList("WriteById", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteById(SqliteDb.TableName, writer, "two"); + Expect.equal(StreamText(stream), JsonDocument.Two, "The incorrect document was returned"); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteById(SqliteDb.TableName, writer, "three hundred eighty-seven"); + VerifyNoDoc(StreamText(stream)); + }) + ]), + TestList("WriteByFields", + [ + TestCase("succeeds when documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFields(SqliteDb.TableName, writer, FieldMatch.Any, [Field.Greater("NumValue", 15)]); + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, JsonDocument.Four, "Document `four` should have been returned"); + Expect.stringContains(json, JsonDocument.Five, "Document `five` should have been returned"); + }), + TestCase("succeeds when documents are found using IN with numeric field", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFields(SqliteDb.TableName, writer, FieldMatch.All, + [Field.In("NumValue", [2, 4, 6, 8])]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Three}]", + "There should have been one document returned"); + }), + TestCase("succeeds when documents are not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFields(SqliteDb.TableName, writer, FieldMatch.Any, [Field.Greater("NumValue", 100)]); + VerifyEmpty(StreamText(stream)); + }), + 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); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFields(SqliteDb.TableName, writer, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["c"])]); + var json = StreamText(stream); + VerifyBeginEnd(json); + Expect.stringContains(json, """{"Id":"first","Values":["a","b","c"]}""", + "Document `first` should have been returned"); + Expect.stringContains(json, """{"Id":"second","Values":["c","d","e"]}""", + "Document `second` should have been 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); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFields(SqliteDb.TableName, writer, FieldMatch.All, + [Field.InArray("Values", SqliteDb.TableName, ["j"])]); + VerifyEmpty(StreamText(stream)); + }) + ]), + TestList("WriteByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Greater("NumValue", 15)], [Field.Named("Id")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Five},{JsonDocument.Four}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Greater("NumValue", 15)], [Field.Named("Id DESC")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Four},{JsonDocument.Five}]", + "Incorrect documents were returned"); + }), + TestCase("succeeds when sorting case-sensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.All, + [Field.LessOrEqual("NumValue", 10)], [Field.Named("Value")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Three},{JsonDocument.One},{JsonDocument.Two}]", + "Documents not ordered correctly"); + }), + TestCase("succeeds when sorting case-insensitively", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.All, + [Field.LessOrEqual("NumValue", 10)], [Field.Named("i:Value")]); + Expect.equal(StreamText(stream), $"[{JsonDocument.Three},{JsonDocument.Two},{JsonDocument.One}]", + "Documents not ordered correctly"); + }) + ]), + TestList("WriteFirstByFields", + [ + TestCase("succeeds when a document is found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteFirstByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Value", "another")]); + Expect.equal(StreamText(stream), JsonDocument.Two, "The incorrect document was returned"); + }), + TestCase("succeeds when multiple documents are found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteFirstByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")]); + var json = StreamText(stream); + Expect.notEqual(json, "{}", "There should have been a document returned"); + VerifyAny(json, [JsonDocument.Two, JsonDocument.Four]); + }), + TestCase("succeeds when a document is not found", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteFirstByFields(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Value", "absent")]); + VerifyNoDoc(StreamText(stream)); + }) + ]), + TestList("WriteFirstByFieldsOrdered", + [ + TestCase("succeeds when sorting ascending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteFirstByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar")]); + Expect.equal(StreamText(stream), JsonDocument.Two, "An incorrect document was returned"); + }), + TestCase("succeeds when sorting descending", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + using MemoryStream stream = new(); + await using var writer = WriteStream(stream); + await Json.WriteFirstByFieldsOrdered(SqliteDb.TableName, writer, FieldMatch.Any, + [Field.Equal("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]); + Expect.equal(StreamText(stream), JsonDocument.Four, "An incorrect document was returned"); + }) + ]) + ]); + /// /// Integration tests for the Update module of the SQLite library /// @@ -1006,6 +1608,7 @@ public static class SqliteCSharpTests CountTests, ExistsTests, FindTests, + JsonTests, UpdateTests, PatchTests, RemoveFieldsTests, diff --git a/src/Tests.CSharp/Types.cs b/src/Tests.CSharp/Types.cs index 3acb0d0..e388084 100644 --- a/src/Tests.CSharp/Types.cs +++ b/src/Tests.CSharp/Types.cs @@ -18,7 +18,7 @@ public class JsonDocument public string Value { get; set; } = ""; public int NumValue { get; set; } = 0; public SubDocument? Sub { get; set; } = null; - + /// /// A set of documents used for integration tests /// @@ -30,6 +30,22 @@ public class JsonDocument new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, new() { Id = "five", Value = "purple", NumValue = 18 } ]; + + /// The JSON for document ID `one` + public static string One = """{"Id":"one","Value":"FIRST!","NumValue":0,"Sub":null}"""; + + /// The JSON for document ID `two` + public static string Two = """{"Id":"two","Value":"another","NumValue":10,"Sub":{"Foo":"green","Bar":"blue"}}"""; + + /// The JSON for document ID `three` + public static string Three = """{"Id":"three","Value":"","NumValue":4,"Sub":null}"""; + + /// The JSON for document ID `four` + public static string Four = """{"Id":"four","Value":"purple","NumValue":17,"Sub":{"Foo":"green","Bar":"red"}}"""; + + /// The JSON for document ID `five` + public static string Five = """{"Id":"five","Value":"purple","NumValue":18,"Sub":null}"""; + } public class ArrayDocument diff --git a/src/Tests/SqliteExtensionTests.fs b/src/Tests/SqliteExtensionTests.fs index b17c19e..44dca44 100644 --- a/src/Tests/SqliteExtensionTests.fs +++ b/src/Tests/SqliteExtensionTests.fs @@ -14,7 +14,7 @@ let integrationTests = let loadDocs (conn: SqliteConnection) = backgroundTask { for doc in testDocuments do do! conn.insert SqliteDb.TableName doc } - + /// Set up a stream writer for a test let writeStream (stream: Stream) = let writer = new StreamWriter(stream) @@ -48,7 +48,7 @@ let integrationTests = let theDocs = docs |> String.concat " | " Expect.isTrue false $"Could not find any of |{theDocs}| in {json}" - ftestList "Sqlite.Extensions" [ + testList "Sqlite.Extensions" [ testTask "ensureTable succeeds" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () @@ -57,12 +57,12 @@ let integrationTests = $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it" [ SqliteParameter("@name", name) ] toExists - + let! exists = itExists "ensured" let! alsoExists = itExists "idx_ensured_key" Expect.isFalse exists "The table should not exist already" Expect.isFalse alsoExists "The key index should not exist already" - + do! conn.ensureTable "ensured" let! exists' = itExists "ensured" let! alsoExists' = itExists "idx_ensured_key" @@ -77,10 +77,10 @@ let integrationTests = $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it" [] toExists - + let! exists = indexExists () Expect.isFalse exists "The index should not exist already" - + do! conn.ensureTable "ensured" do! conn.ensureFieldIndex "ensured" "test" [ "Name"; "Age" ] let! exists' = indexExists () @@ -92,7 +92,7 @@ let integrationTests = use conn = Configuration.dbConn () let! before = conn.findAll SqliteDb.TableName Expect.equal before [] "There should be no documents in the table" - + let testDoc = { emptyDoc with Id = "turkey"; Sub = Some { Foo = "gobble"; Bar = "gobble" } } do! conn.insert SqliteDb.TableName testDoc let! after = conn.findAll SqliteDb.TableName @@ -116,7 +116,7 @@ let integrationTests = use conn = Configuration.dbConn () let! before = conn.findAll SqliteDb.TableName Expect.equal before [] "There should be no documents in the table" - + let testDoc = { emptyDoc with Id = "test"; Sub = Some { Foo = "a"; Bar = "b" } } do! conn.save SqliteDb.TableName testDoc let! after = conn.findAll SqliteDb.TableName @@ -127,11 +127,11 @@ let integrationTests = use conn = Configuration.dbConn () let testDoc = { emptyDoc with Id = "test"; Sub = Some { Foo = "a"; Bar = "b" } } do! conn.insert SqliteDb.TableName testDoc - + let! before = conn.findById SqliteDb.TableName "test" if Option.isNone before then Expect.isTrue false "There should have been a document returned" Expect.equal before.Value testDoc "The document is not correct" - + let upd8Doc = { testDoc with Sub = Some { Foo = "c"; Bar = "d" } } do! conn.save SqliteDb.TableName upd8Doc let! after = conn.findById SqliteDb.TableName "test" @@ -144,7 +144,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! theCount = conn.countAll SqliteDb.TableName Expect.equal theCount 5L "There should have been 5 matching documents" } @@ -152,7 +152,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! theCount = conn.countByFields SqliteDb.TableName Any [ Field.Equal "Value" "purple" ] Expect.equal theCount 2L "There should have been 2 matching documents" } @@ -161,7 +161,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! exists = conn.existsById SqliteDb.TableName "three" Expect.isTrue exists "There should have been an existing document" } @@ -169,7 +169,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! exists = conn.existsById SqliteDb.TableName "seven" Expect.isFalse exists "There should not have been an existing document" } @@ -179,7 +179,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 10 ] Expect.isTrue exists "There should have been existing documents" } @@ -187,7 +187,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.Equal "Nothing" "none" ] Expect.isFalse exists "There should not have been any existing documents" } @@ -196,11 +196,11 @@ let integrationTests = testTask "succeeds when there is data" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + do! insert SqliteDb.TableName { Foo = "one"; Bar = "two" } do! insert SqliteDb.TableName { Foo = "three"; Bar = "four" } do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" } - + let! results = conn.findAll SqliteDb.TableName let expected = [ { Foo = "one"; Bar = "two" } @@ -221,7 +221,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! results = conn.findAllOrdered SqliteDb.TableName [ Field.Named "n:NumValue" ] Expect.hasLength results 5 "There should have been 5 documents returned" Expect.equal @@ -233,7 +233,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! results = conn.findAllOrdered SqliteDb.TableName [ Field.Named "n:NumValue DESC" ] Expect.hasLength results 5 "There should have been 5 documents returned" Expect.equal @@ -245,7 +245,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! results = conn.findAllOrdered SqliteDb.TableName [ Field.Named "Id DESC" ] Expect.hasLength results 5 "There should have been 5 documents returned" Expect.equal @@ -259,7 +259,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.findById SqliteDb.TableName "two" Expect.isSome doc "There should have been a document returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned" @@ -268,7 +268,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.findById SqliteDb.TableName "three hundred eighty-seven" Expect.isNone doc "There should not have been a document returned" } @@ -278,7 +278,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! docs = conn.findByFields SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] Expect.hasLength docs 2 "There should have been two documents returned" } @@ -286,7 +286,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! docs = conn.findByFields SqliteDb.TableName Any [ Field.Equal "Value" "mauve" ] Expect.isEmpty docs "There should have been no documents returned" } @@ -320,7 +320,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.findFirstByFields SqliteDb.TableName Any [ Field.Equal "Value" "another" ] Expect.isSome doc "There should have been a document returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned" @@ -329,7 +329,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.findFirstByFields SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] Expect.isSome doc "There should have been a document returned" Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned" @@ -338,7 +338,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.findFirstByFields SqliteDb.TableName Any [ Field.Equal "Value" "absent" ] Expect.isNone doc "There should not have been a document returned" } @@ -861,7 +861,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let testDoc = { emptyDoc with Id = "one"; Sub = Some { Foo = "blue"; Bar = "red" } } do! conn.updateById SqliteDb.TableName "one" testDoc let! after = conn.findById SqliteDb.TableName "one" @@ -872,10 +872,10 @@ let integrationTests = testTask "succeeds when no document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + let! before = conn.findAll SqliteDb.TableName Expect.isEmpty before "There should have been no documents returned" - + // This not raising an exception is the test do! conn.updateById SqliteDb.TableName @@ -888,7 +888,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.updateByFunc SqliteDb.TableName _.Id { Id = "one"; Value = "le un"; NumValue = 1; Sub = None } let! after = conn.findById SqliteDb.TableName "one" @@ -902,10 +902,10 @@ let integrationTests = testTask "succeeds when no document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + let! before = conn.findAll SqliteDb.TableName Expect.isEmpty before "There should have been no documents returned" - + // This not raising an exception is the test do! conn.updateByFunc SqliteDb.TableName _.Id { Id = "one"; Value = "le un"; NumValue = 1; Sub = None } @@ -916,7 +916,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.patchById SqliteDb.TableName "one" {| NumValue = 44 |} let! after = conn.findById SqliteDb.TableName "one" if Option.isNone after then @@ -926,10 +926,10 @@ let integrationTests = testTask "succeeds when no document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + let! before = conn.findAll SqliteDb.TableName Expect.isEmpty before "There should have been no documents returned" - + // This not raising an exception is the test do! conn.patchById SqliteDb.TableName "test" {| Foo = "green" |} } @@ -939,7 +939,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.patchByFields SqliteDb.TableName Any [ Field.Equal "Value" "purple" ] {| NumValue = 77 |} let! after = conn.countByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 77 ] Expect.equal after 2L "There should have been 2 documents returned" @@ -947,10 +947,10 @@ let integrationTests = testTask "succeeds when no document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + let! before = conn.findAll SqliteDb.TableName Expect.isEmpty before "There should have been no documents returned" - + // This not raising an exception is the test do! conn.patchByFields SqliteDb.TableName Any [ Field.Equal "Value" "burgundy" ] {| Foo = "green" |} } @@ -960,7 +960,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.removeFieldsById SqliteDb.TableName "two" [ "Sub"; "Value" ] try let! _ = conn.findById SqliteDb.TableName "two" @@ -973,14 +973,14 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + // This not raising an exception is the test do! conn.removeFieldsById SqliteDb.TableName "two" [ "AFieldThatIsNotThere" ] } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + // This not raising an exception is the test do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ] } @@ -990,7 +990,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 17 ] [ "Sub" ] try let! _ = conn.findById SqliteDb.TableName "four" @@ -1003,14 +1003,14 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + // This not raising an exception is the test do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 17 ] [ "Nothing" ] } testTask "succeeds when no document is matched" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + // This not raising an exception is the test do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.NotEqual "Abracadabra" "apple" ] [ "Value" ] @@ -1021,7 +1021,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.deleteById SqliteDb.TableName "four" let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 4L "There should have been 4 documents remaining" @@ -1030,7 +1030,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.deleteById SqliteDb.TableName "thirty" let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 5L "There should have been 5 documents remaining" @@ -1041,7 +1041,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.deleteByFields SqliteDb.TableName Any [ Field.NotEqual "Value" "purple" ] let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 2L "There should have been 2 documents remaining" @@ -1050,7 +1050,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + do! conn.deleteByFields SqliteDb.TableName Any [ Field.Equal "Value" "crimson" ] let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 5L "There should have been 5 documents remaining" @@ -1061,7 +1061,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! docs = conn.customList (Query.find SqliteDb.TableName) [] fromData Expect.hasLength docs 5 "There should have been 5 documents returned" } @@ -1069,7 +1069,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! docs = conn.customList $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" @@ -1146,7 +1146,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.customSingle $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id" @@ -1159,7 +1159,7 @@ let integrationTests = use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () do! loadDocs conn - + let! doc = conn.customSingle $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id" @@ -1221,7 +1221,7 @@ let integrationTests = testTask "customScalar succeeds" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - + let! nbr = conn.customScalar "SELECT 5 AS test_value" [] _.GetInt32(0) Expect.equal nbr 5 "The query should have returned the number 5" } diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index 1ba12a1..15e5d1d 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -1402,7 +1402,7 @@ let deleteTests = testList "Delete" [ ] /// All tests for the SQLite library -let all = ftestList "Sqlite" [ +let all = testList "Sqlite" [ testList "Unit" [ queryTests; parametersTests ] testSequenced <| testList "Integration" [ configurationTests