diff --git a/src/BitBadger.Documents.sln b/src/BitBadger.Documents.sln index da810bf..f3d49d0 100644 --- a/src/BitBadger.Documents.sln +++ b/src/BitBadger.Documents.sln @@ -11,6 +11,8 @@ Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "BitBadger.Documents.Tests", EndProject Project("{F2A71F9B-5D33-465A-A702-920D77279786}") = "BitBadger.Documents.Sqlite", "Sqlite\BitBadger.Documents.Sqlite.fsproj", "{B8A82483-1E72-46D2-B29A-1C371AC5DD20}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "BitBadger.Documents.Tests.CSharp", "Tests.CSharp\BitBadger.Documents.Tests.CSharp.csproj", "{AB58418C-7F90-467E-8F67-F4E0AD9D8875}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -36,5 +38,9 @@ Global {B8A82483-1E72-46D2-B29A-1C371AC5DD20}.Debug|Any CPU.Build.0 = Debug|Any CPU {B8A82483-1E72-46D2-B29A-1C371AC5DD20}.Release|Any CPU.ActiveCfg = Release|Any CPU {B8A82483-1E72-46D2-B29A-1C371AC5DD20}.Release|Any CPU.Build.0 = Release|Any CPU + {AB58418C-7F90-467E-8F67-F4E0AD9D8875}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AB58418C-7F90-467E-8F67-F4E0AD9D8875}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AB58418C-7F90-467E-8F67-F4E0AD9D8875}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AB58418C-7F90-467E-8F67-F4E0AD9D8875}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/src/Common/Library.fs b/src/Common/Library.fs index 0083626..7123a52 100644 --- a/src/Common/Library.fs +++ b/src/Common/Library.fs @@ -123,6 +123,7 @@ module Query = module Definition = /// SQL statement to create a document table + [] let ensureTableFor name dataType = $"CREATE TABLE IF NOT EXISTS %s{name} (data %s{dataType} NOT NULL)" @@ -132,6 +133,7 @@ module Query = if Array.length parts = 1 then "", tableName else parts[0], parts[1] /// SQL statement to create an index on one or more fields in a JSON document + [] let ensureIndexOn tableName indexName (fields: string seq) = let _, tbl = splitSchemaAndTable tableName let jsonFields = @@ -145,6 +147,7 @@ module Query = $"CREATE INDEX IF NOT EXISTS idx_{tbl}_%s{indexName} ON {tableName} ({jsonFields})" /// SQL statement to create a key index for a document table + [] let ensureKey tableName = (ensureIndexOn tableName "key" [ Configuration.idField () ]).Replace("INDEX", "UNIQUE INDEX") diff --git a/src/Sqlite/BitBadger.Documents.Sqlite.fsproj b/src/Sqlite/BitBadger.Documents.Sqlite.fsproj index 4bdb116..9a59aed 100644 --- a/src/Sqlite/BitBadger.Documents.Sqlite.fsproj +++ b/src/Sqlite/BitBadger.Documents.Sqlite.fsproj @@ -13,6 +13,9 @@ <_Parameter1>BitBadger.Documents.Tests + + <_Parameter1>BitBadger.Documents.Tests.CSharp + diff --git a/src/Sqlite/Library.fs b/src/Sqlite/Library.fs index 6cf6dc9..1ef745f 100644 --- a/src/Sqlite/Library.fs +++ b/src/Sqlite/Library.fs @@ -608,7 +608,6 @@ module Extensions = open System.Runtime.CompilerServices /// C# extensions on the SqliteConnection type -[] type SqliteConnectionCSharpExtensions = /// Execute a query that returns a list of results diff --git a/src/Test/Test.csproj b/src/Test/Test.csproj index 809da3f..7153813 100644 --- a/src/Test/Test.csproj +++ b/src/Test/Test.csproj @@ -8,6 +8,7 @@ + diff --git a/src/Tests.CSharp/BitBadger.Documents.Tests.CSharp.csproj b/src/Tests.CSharp/BitBadger.Documents.Tests.CSharp.csproj new file mode 100644 index 0000000..c70e5b0 --- /dev/null +++ b/src/Tests.CSharp/BitBadger.Documents.Tests.CSharp.csproj @@ -0,0 +1,17 @@ + + + + enable + enable + + + + + + + + + + + + diff --git a/src/Tests.CSharp/CommonCSharpTests.cs b/src/Tests.CSharp/CommonCSharpTests.cs new file mode 100644 index 0000000..a90cb8f --- /dev/null +++ b/src/Tests.CSharp/CommonCSharpTests.cs @@ -0,0 +1,253 @@ +namespace BitBadger.Documents.Tests.CSharp; + +using Documents; +using Expecto.CSharp; +using Expecto; + +/// +/// A test serializer that returns known values +/// +internal class TestSerializer : IDocumentSerializer +{ + public string Serialize(T it) => "{\"Overridden\":true}"; + public T Deserialize(string it) => default!; +} + +/// +/// C# Tests for common functionality in BitBadger.Documents +/// +public static class CommonCSharpTests +{ + /// + /// Unit tests + /// + [Tests] public static Test Unit = + Runner.TestList("Common.C# Unit", new[] + { + Runner.TestSequenced( + Runner.TestList("Configuration", new[] + { + Runner.TestCase("UseSerializer succeeds", () => + { + try + { + Configuration.UseSerializer(new TestSerializer()); + + var serialized = Configuration.Serializer().Serialize(new SubDocument + { + Foo = "howdy", + Bar = "bye" + }); + Expect.equal(serialized, "{\"Overridden\":true}", "Specified serializer was not used"); + + var deserialized = Configuration.Serializer() + .Deserialize("{\"Something\":\"here\"}"); + Expect.isNull(deserialized, "Specified serializer should have returned null"); + } + finally + { + Configuration.UseSerializer(DocumentSerializer.Default); + } + }), + Runner.TestCase("Serializer returns configured serializer", () => + { + Expect.isTrue(ReferenceEquals(DocumentSerializer.Default, Configuration.Serializer()), + "Serializer should have been the same"); + }), + Runner.TestCase("UseIdField / IdField succeeds", () => + { + try + { + Expect.equal(Configuration.IdField(), "Id", + "The default configured ID field was incorrect"); + Configuration.UseIdField("id"); + Expect.equal(Configuration.IdField(), "id", "UseIdField did not set the ID field"); + } + finally + { + Configuration.UseIdField("Id"); + } + }) + })), + Runner.TestList("Op", new[] + { + Runner.TestCase("EQ succeeds", () => + { + Expect.equal(Op.EQ.ToString(), "=", "The equals operator was not correct"); + }), + Runner.TestCase("GT succeeds", () => + { + Expect.equal(Op.GT.ToString(), ">", "The greater than operator was not correct"); + }), + Runner.TestCase("GE succeeds", () => + { + Expect.equal(Op.GE.ToString(), ">=", "The greater than or equal to operator was not correct"); + }), + Runner.TestCase("LT succeeds", () => + { + Expect.equal(Op.LT.ToString(), "<", "The less than operator was not correct"); + }), + Runner.TestCase("LE succeeds", () => + { + Expect.equal(Op.LE.ToString(), "<=", "The less than or equal to operator was not correct"); + }), + Runner.TestCase("NE succeeds", () => + { + Expect.equal(Op.NE.ToString(), "<>", "The not equal to operator was not correct"); + }), + Runner.TestCase("EX succeeds", () => + { + Expect.equal(Op.EX.ToString(), "IS NOT NULL", "The \"exists\" operator was not correct"); + }), + Runner.TestCase("NEX succeeds", () => + { + Expect.equal(Op.NEX.ToString(), "IS NULL", "The \"not exists\" operator was not correct"); + }) + }), + Runner.TestList("Query", new[] + { + Runner.TestCase("SelectFromTable succeeds", () => + { + Expect.equal(Query.SelectFromTable("test.table"), "SELECT data FROM test.table", + "SELECT statement not correct"); + }), + Runner.TestCase("WhereById succeeds", () => + { + Expect.equal(Query.WhereById("@id"), "data ->> 'Id' = @id", "WHERE clause not correct"); + }), + Runner.TestList("WhereByField", new[] + { + Runner.TestCase("succeeds when a logical operator is passed", () => + { + Expect.equal(Query.WhereByField("theField", Op.GT, "@test"), "data ->> 'theField' > @test", + "WHERE clause not correct"); + }), + Runner.TestCase("succeeds when an existence operator is passed", () => + { + Expect.equal(Query.WhereByField("thatField", Op.NEX, ""), "data ->> 'thatField' IS NULL", + "WHERE clause not correct"); + }) + }), + Runner.TestList("Definition", new[] + { + Runner.TestCase("EnsureTableFor succeeds", () => + { + Expect.equal(Query.Definition.EnsureTableFor("my.table", "JSONB"), + "CREATE TABLE IF NOT EXISTS my.table (data JSONB NOT NULL)", + "CREATE TABLE statement not constructed correctly"); + }), + Runner.TestList("EnsureKey", new[] + { + Runner.TestCase("succeeds when a schema is present", () => + { + Expect.equal(Query.Definition.EnsureKey("test.table"), + "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON test.table ((data ->> 'Id'))", + "CREATE INDEX for key statement with schema not constructed correctly"); + }), + Runner.TestCase("succeeds when a schema is not present", () => + { + Expect.equal(Query.Definition.EnsureKey("table"), + "CREATE UNIQUE INDEX IF NOT EXISTS idx_table_key ON table ((data ->> 'Id'))", + "CREATE INDEX for key statement without schema not constructed correctly"); + }) + }), + Runner.TestCase("EnsureIndexOn succeeds for multiple fields and directions", () => + { + Expect.equal( + Query.Definition.EnsureIndexOn("test.table", "gibberish", + new[] { "taco", "guac DESC", "salsa ASC" }), + "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table " + + "((data ->> 'taco'), (data ->> 'guac') DESC, (data ->> 'salsa') ASC)", + "CREATE INDEX for multiple field statement incorrect"); + }) + }), + Runner.TestCase("Insert succeeds", () => + { + Expect.equal(Query.Insert("tbl"), "INSERT INTO tbl VALUES (@data)", "INSERT statement not correct"); + }), + Runner.TestCase("Save succeeds", () => + { + Expect.equal(Query.Save("tbl"), + $"INSERT INTO tbl VALUES (@data) ON CONFLICT ((data ->> 'Id')) DO UPDATE SET data = EXCLUDED.data", + "INSERT ON CONFLICT UPDATE statement not correct"); + }), + Runner.TestList("Count", new[] + { + Runner.TestCase("All succeeds", () => + { + Expect.equal(Query.Count.All("tbl"), "SELECT COUNT(*) AS it FROM tbl", + "Count query not correct"); + }), + Runner.TestCase("ByField succeeds", () => + { + Expect.equal(Query.Count.ByField("tbl", "thatField", Op.EQ), + "SELECT COUNT(*) AS it FROM tbl WHERE data ->> 'thatField' = @field", + "JSON field text comparison count query not correct"); + }) + }), + Runner.TestList("Exists", new[] + { + Runner.TestCase("ById succeeds", () => + { + Expect.equal(Query.Exists.ById("tbl"), + "SELECT EXISTS (SELECT 1 FROM tbl WHERE data ->> 'Id' = @id) AS it", + "ID existence query not correct"); + }), + Runner.TestCase("ByField succeeds", () => + { + Expect.equal(Query.Exists.ByField("tbl", "Test", Op.LT), + "SELECT EXISTS (SELECT 1 FROM tbl WHERE data ->> 'Test' < @field) AS it", + "JSON field text comparison exists query not correct"); + }) + }), + Runner.TestList("Find", new[] + { + Runner.TestCase("ById succeeds", () => + { + Expect.equal(Query.Find.ById("tbl"), "SELECT data FROM tbl WHERE data ->> 'Id' = @id", + "SELECT by ID query not correct"); + }), + Runner.TestCase("ByField succeeds", () => + { + Expect.equal(Query.Find.ByField("tbl", "Golf", Op.GE), + "SELECT data FROM tbl WHERE data ->> 'Golf' >= @field", + "SELECT by JSON comparison query not correct"); + }) + }), + Runner.TestList("Update", new[] + { + Runner.TestCase("Full succeeds", () => + { + Expect.equal(Query.Update.Full("tbl"), "UPDATE tbl SET data = @data WHERE data ->> 'Id' = @id", + "UPDATE full statement not correct"); + }), + Runner.TestCase("PartialById succeeds", () => + { + Expect.equal(Query.Update.PartialById("tbl"), + "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Id' = @id", + "UPDATE partial by ID statement not correct"); + }), + Runner.TestCase("PartialByField succeeds", () => + { + Expect.equal(Query.Update.PartialByField("tbl", "Part", Op.NE), + "UPDATE tbl SET data = json_patch(data, json(@data)) WHERE data ->> 'Part' <> @field", + "UPDATE partial by JSON comparison query not correct"); + }) + }), + Runner.TestList("Delete", new[] + { + Runner.TestCase("ById succeeds", () => + { + Expect.equal(Query.Delete.ById("tbl"), "DELETE FROM tbl WHERE data ->> 'Id' = @id", + "DELETE by ID query not correct"); + }), + Runner.TestCase("ByField succeeds", () => + { + Expect.equal(Query.Delete.ByField("tbl", "gone", Op.NEX), + "DELETE FROM tbl WHERE data ->> 'gone' IS NULL", + "DELETE by JSON comparison query not correct"); + }) + }) + }) + }); +} diff --git a/src/Tests.CSharp/SqliteCSharpTests.cs b/src/Tests.CSharp/SqliteCSharpTests.cs new file mode 100644 index 0000000..7c5d0fc --- /dev/null +++ b/src/Tests.CSharp/SqliteCSharpTests.cs @@ -0,0 +1,939 @@ +using Microsoft.Data.Sqlite; +using Microsoft.FSharp.Core; + +namespace BitBadger.Documents.Tests.CSharp; + +using Expecto.CSharp; +using Expecto; +using static Sqlite; + +/// +/// C# tests for the SQLite implementation of BitBadger.Documents +/// +public static class SqliteCSharpTests +{ + private static readonly List Documents = new() + { + new() { Id = "one", Value = "FIRST!", NumValue = 0 }, + new() { Id = "two", Value = "another", NumValue = 10, Sub = new() { Foo = "green", Bar = "blue" } }, + new() { Id = "three", Value = "", NumValue = 4 }, + new() { Id = "four", Value = "purple", NumValue = 17, Sub = new() { Foo = "green", Bar = "red" } }, + new() { Id = "five", Value = "purple", NumValue = 18 } + }; + + private static async Task LoadDocs() + { + foreach (var doc in Documents) await Insert(SqliteDb.TableName, doc); + } + + [Tests] + public static Test Integration = + Runner.TestList("Sqlite.C# Integration", new[] + { + Runner.TestCase("Configuration.UseConnectionString succeeds", () => + { + try + { + Configuration.UseConnectionString("Data Source=test.db"); + Expect.equal(Configuration.connectionString, + new FSharpOption("Data Source=test.db;Foreign Keys=True"), + "Connection string incorrect"); + } + finally + { + Configuration.UseConnectionString("Data Source=:memory:"); + } + }), + Runner.TestList("Definition", new[] + { + Runner.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) + { + var result = await Custom.Scalar( + $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it", + new SqliteParameter[] { new("@name", name) }, + rdr => rdr.GetInt64(0)); + return result > 0L; + } + }) + }), + Runner.TestList("Insert", new[] + { + Runner.TestCase("succeeds", async () => + { + await using var db = await SqliteDb.BuildDb(); + var before = await Find.All(SqliteDb.TableName); + Expect.equal(before.Count, 0, "There should be no documents in the table"); + await 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"); + }), + Runner.TestCase("fails for duplicate key", async () => + { + await using var db = await SqliteDb.BuildDb(); + await Insert(SqliteDb.TableName, new JsonDocument { Id = "test" }); + try + { + await 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 + } + }) + }), + Runner.TestList("Save", new[] + { + Runner.TestCase("succeeds when a document is inserted", async () => + { + await using var db = await SqliteDb.BuildDb(); + var before = await Find.All(SqliteDb.TableName); + Expect.equal(before.Count, 0, "There should be no documents in the table"); + + await 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"); + }), + Runner.TestCase("succeeds when a document is updated", async () => + { + await using var db = await SqliteDb.BuildDb(); + await Insert(SqliteDb.TableName, + new JsonDocument { Id = "test", Sub = new() { Foo = "a", Bar = "b" } }); + + var before = await Find.ById(SqliteDb.TableName, "test"); + if (before is null) Expect.isTrue(false, "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 Save(SqliteDb.TableName, new JsonDocument { Id = "test" }); + var after = await Find.ById(SqliteDb.TableName, "test"); + if (after is null) Expect.isTrue(false, "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"); + }) + }), + Runner.TestList("Count", new[] + { + Runner.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"); + }), + Runner.TestCase("ByField succeeds", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var theCount = await Count.ByField(SqliteDb.TableName, "Value", Op.EQ, "purple"); + Expect.equal(theCount, 2L, "There should have been 2 matching documents"); + }) + }), + Runner.TestList("Exists", new[] + { + Runner.TestList("ById", new[] + { + Runner.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"); + }), + Runner.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"); + }) + }), + Runner.TestList("ByField", new[] + { + Runner.TestCase("succeeds when documents exist", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var exists = await Exists.ByField(SqliteDb.TableName, "NumValue", Op.GE, 10); + Expect.isTrue(exists, "There should have been existing documents"); + }), + Runner.TestCase("succeeds when no matching documents exist", async () => + { + await using var db = await SqliteDb.BuildDb(); + await LoadDocs(); + + var exists = await Exists.ByField(SqliteDb.TableName, "Nothing", Op.EQ, "none"); + Expect.isFalse(exists, "There should not have been any existing documents"); + }) + }) + }), + // testList "Find" [ + // testList "All" [ + // testTask "succeeds when there is data" { + // use! db = Db.buildDb () + // + // do! Insert(Db.tableName, JsonDocument(Id = "one", Value = "two")) + // do! Insert(Db.tableName, JsonDocument(Id = "three", Value = "four")) + // do! Insert(Db.tableName, JsonDocument(Id = "five", Value = "six")) + // + // let! results = Find.All Db.tableName + // Expect.hasCountOf results 3u isTrue "There should have been 3 documents returned" + // } + // testTask "succeeds when there is no data" { + // use! db = Db.buildDb () + // let! results = Find.All Db.tableName + // Expect.hasCountOf results 0u isTrue "There should have been no documents returned" + // } + // ] + // testList "ById" [ + // testTask "succeeds when a document is found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = Find.ById(Db.tableName, "two") + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.equal (doc :> JsonDocument).Id "two" "The incorrect document was returned" + // } + // testTask "succeeds when a document is not found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = Find.ById(Db.tableName, "three hundred eighty-seven") + // Expect.isNull doc "There should not have been a document returned" + // } + // ] + // testList "ByField" [ + // testTask "succeeds when documents are found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! docs = Find.ByField(Db.tableName, "NumValue", Op.GT, 15) + // Expect.hasCountOf docs 2u isTrue "There should have been two documents returned" + // } + // testTask "succeeds when documents are not found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! docs = Find.ByField(Db.tableName, "Value", Op.EQ, "mauve") + // Expect.hasCountOf docs 0u isTrue "There should have been no documents returned" + // } + // ] + // testList "FirstByField" [ + // testTask "succeeds when a document is found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = Find.FirstByField(Db.tableName, "Value", Op.EQ, "another") + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.equal (doc :> JsonDocument).Id "two" "The incorrect document was returned" + // } + // testTask "succeeds when multiple documents are found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = Find.FirstByField(Db.tableName, "Sub.Foo", Op.EQ, "green") + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.contains [ "two"; "four" ] (doc :> JsonDocument).Id "An incorrect document was returned" + // } + // testTask "succeeds when a document is not found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = Find.FirstByField(Db.tableName, "Value", Op.EQ, "absent") + // Expect.isNull doc "There should not have been a document returned" + // } + // ] + // ] + // testList "Update" [ + // testList "Full" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let testDoc = JsonDocument(Id = "one", Sub = Some (SubDocument(Foo = "blue", Bar = "red"))) + // do! Update.Full(Db.tableName, "one", testDoc) + // let! after = Find.ById(Db.tableName, "one") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // Expect.equal after.Id "one" "The updated document is not correct" + // Expect.isSome after.Sub "The updated document should have had a sub-document" + // Expect.equal after.Sub.Value.Foo "blue" "The updated sub-document is not correct" + // Expect.equal after.Sub.Value.Bar "red" "The updated sub-document is not correct" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // + // let! before = Find.All Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! Update.Full( + // Db.tableName, + // "test", + // JsonDocument(Id = "x", Sub = Some (SubDocument(Foo = "blue", Bar = "red")))) + // } + // ] + // testList "FullFunc" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Update.FullFunc( + // Db.tableName, + // System.Func _.Id, + // JsonDocument(Id = "one", Value = "le un", NumValue = 1, Sub = None)) + // let! after = Find.ById(Db.tableName, "one") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // 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.isNone after.Sub "The updated document should not have a sub-document" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // + // let! before = Find.All Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! Update.FullFunc( + // Db.tableName, + // System.Func _.Id, + // JsonDocument(Id = "one", Value = "le un", NumValue = 1, Sub = None)) + // } + // ] + // testList "PartialById" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Update.PartialById(Db.tableName, "one", {| NumValue = 44 |}) + // let! after = Find.ById(Db.tableName, "one") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // Expect.equal after.Id "one" "The updated document is not correct" + // Expect.equal after.NumValue 44 "The updated document is not correct" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // + // let! before = Find.All Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! Update.PartialById(Db.tableName, "test", {| Foo = "green" |}) + // } + // ] + // testList "PartialByField" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Update.PartialByField(Db.tableName, "Value", Op.EQ, "purple", {| NumValue = 77 |}) + // let! after = Count.ByField(Db.tableName, "NumValue", Op.EQ, 77) + // Expect.equal after 2L "There should have been 2 documents returned" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // + // let! before = Find.All Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! Update.PartialByField(Db.tableName, "Value", Op.EQ, "burgundy", {| Foo = "green" |}) + // } + // ] + // ] + // testList "Delete" [ + // testList "ById" [ + // testTask "succeeds when a document is deleted" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Delete.ById(Db.tableName, "four") + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 4L "There should have been 4 documents remaining" + // } + // testTask "succeeds when a document is not deleted" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Delete.ById(Db.tableName, "thirty") + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 5L "There should have been 5 documents remaining" + // } + // ] + // testList "ByField" [ + // testTask "succeeds when documents are deleted" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Delete.ByField(Db.tableName, "Value", Op.NE, "purple") + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 2L "There should have been 2 documents remaining" + // } + // testTask "succeeds when documents are not deleted" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Delete.ByField(Db.tableName, "Value", Op.EQ, "crimson") + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 5L "There should have been 5 documents remaining" + // } + // ] + // ] + // testList "Custom" [ + // testList "Single" [ + // testTask "succeeds when a row is found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = + // Custom.Single( + // $"SELECT data FROM {Db.tableName} WHERE data ->> 'Id' = @id", + // [ SqliteParameter("@id", "one") ], + // FromData) + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.equal (doc :> JsonDocument).Id "one" "The incorrect document was returned" + // } + // testTask "succeeds when a row is not found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! doc = + // Custom.Single( + // $"SELECT data FROM {Db.tableName} WHERE data ->> 'Id' = @id", + // [ SqliteParameter("@id", "eighty") ], + // FromData) + // Expect.isNull doc "There should not have been a document returned" + // } + // ] + // testList "List" [ + // testTask "succeeds when data is found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! docs = Custom.List(Query.SelectFromTable Db.tableName, [], FromData) + // Expect.hasCountOf docs 5u isTrue "There should have been 5 documents returned" + // } + // testTask "succeeds when data is not found" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // let! docs = + // Custom.List( + // $"SELECT data FROM {Db.tableName} WHERE data ->> 'NumValue' > @value", + // [ SqliteParameter("@value", 100) ], + // FromData) + // Expect.isEmpty docs "There should have been no documents returned" + // } + // ] + // testList "NonQuery" [ + // testTask "succeeds when operating on data" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Custom.NonQuery($"DELETE FROM {Db.tableName}", []) + // + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 0L "There should be no documents remaining in the table" + // } + // testTask "succeeds when no data matches where clause" { + // use! db = Db.buildDb () + // do! loadDocs () + // + // do! Custom.NonQuery( + // $"DELETE FROM {Db.tableName} WHERE data ->> 'NumValue' > @value", + // [ SqliteParameter("@value", 100) ]) + // + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 5L "There should be 5 documents remaining in the table" + // } + // ] + // testTask "Scalar succeeds" { + // use! db = Db.buildDb () + // + // let! nbr = Custom.Scalar("SELECT 5 AS test_value", [], System.Func _.GetInt32(0)) + // Expect.equal nbr 5 "The query should have returned the number 5" + // } + // ] + // testList "Extensions" [ + // testTask "EnsureTable succeeds" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let itExists (name: string) = task { + // let! result = + // conn.CustomScalar( + // $"SELECT EXISTS (SELECT 1 FROM {Db.catalog} WHERE name = @name) AS it", + // [ SqliteParameter("@name", name) ], + // System.Func _.GetInt64(0)) + // return result > 0L + // } + // + // 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! Definition.EnsureTable "ensured" + // let! exists' = itExists "ensured" + // let! alsoExists' = itExists "idx_ensured_key" + // Expect.isTrue exists' "The table should now exist" + // Expect.isTrue alsoExists' "The key index should now exist" + // } + // testList "Insert" [ + // testTask "succeeds" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! before = conn.FindAll Db.tableName + // Expect.hasCountOf before 0u isTrue "There should be no documents in the table" + // do! conn.Insert( + // Db.tableName, + // JsonDocument(Id = "turkey", Sub = Some (SubDocument(Foo = "gobble", Bar = "gobble")))) + // let! after = conn.FindAll Db.tableName + // Expect.hasCountOf after 1u isTrue "There should have been one document inserted" + // } + // testTask "fails for duplicate key" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! conn.Insert(Db.tableName, JsonDocument(Id = "test")) + // Expect.throws + // (fun () -> + // conn.Insert(Db.tableName, JsonDocument(Id = "test")) + // |> Async.AwaitTask + // |> Async.RunSynchronously) + // "An exception should have been raised for duplicate document ID insert" + // } + // ] + // testList "Save" [ + // testTask "succeeds when a document is inserted" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! before = conn.FindAll Db.tableName + // Expect.hasCountOf before 0u isTrue "There should be no documents in the table" + // + // do! conn.Save( + // Db.tableName, + // JsonDocument(Id = "test", Sub = Some (SubDocument(Foo = "a", Bar = "b")))) + // let! after = conn.FindAll Db.tableName + // Expect.hasCountOf after 1u isTrue "There should have been one document inserted" + // } + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! conn.Insert( + // Db.tableName, + // JsonDocument(Id = "test", Sub = Some (SubDocument(Foo = "a", Bar = "b")))) + // + // let! before = conn.FindById(Db.tableName, "test") + // if isNull before then Expect.isTrue false "There should have been a document returned" + // let before = before :> JsonDocument + // Expect.equal before.Id "test" "The document is not correct" + // Expect.isSome before.Sub "There should have been a sub-document" + // Expect.equal before.Sub.Value.Foo "a" "The document is not correct" + // Expect.equal before.Sub.Value.Bar "b" "The document is not correct" + // + // do! Save(Db.tableName, JsonDocument(Id = "test")) + // let! after = conn.FindById(Db.tableName, "test") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // Expect.equal after.Id "test" "The updated document is not correct" + // Expect.isNone after.Sub "There should not have been a sub-document in the updated document" + // } + // ] + // testTask "CountAll succeeds" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! theCount = conn.CountAll Db.tableName + // Expect.equal theCount 5L "There should have been 5 matching documents" + // } + // testTask "CountByField succeeds" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! theCount = conn.CountByField(Db.tableName, "Value", Op.EQ, "purple") + // Expect.equal theCount 2L "There should have been 2 matching documents" + // } + // testList "ExistsById" [ + // testTask "succeeds when a document exists" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! exists = conn.ExistsById(Db.tableName, "three") + // Expect.isTrue exists "There should have been an existing document" + // } + // testTask "succeeds when a document does not exist" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! exists = conn.ExistsById(Db.tableName, "seven") + // Expect.isFalse exists "There should not have been an existing document" + // } + // ] + // testList "ExistsByField" [ + // testTask "succeeds when documents exist" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! exists = conn.ExistsByField(Db.tableName, "NumValue", Op.GE, 10) + // Expect.isTrue exists "There should have been existing documents" + // } + // testTask "succeeds when no matching documents exist" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! exists = conn.ExistsByField(Db.tableName, "Nothing", Op.EQ, "none") + // Expect.isFalse exists "There should not have been any existing documents" + // } + // ] + // testList "FindAll" [ + // testTask "succeeds when there is data" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // + // do! conn.Insert(Db.tableName, JsonDocument(Id = "one", Value = "two")) + // do! conn.Insert(Db.tableName, JsonDocument(Id = "three", Value = "four")) + // do! conn.Insert(Db.tableName, JsonDocument(Id = "five", Value = "six")) + // + // let! results = conn.FindAll Db.tableName + // Expect.hasCountOf results 3u isTrue "There should have been 3 documents returned" + // } + // testTask "succeeds when there is no data" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! results = conn.FindAll Db.tableName + // Expect.hasCountOf results 0u isTrue "There should have been no documents returned" + // } + // ] + // testList "FindById" [ + // testTask "succeeds when a document is found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = conn.FindById(Db.tableName, "two") + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.equal (doc :> JsonDocument).Id "two" "The incorrect document was returned" + // } + // testTask "succeeds when a document is not found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = conn.FindById(Db.tableName, "three hundred eighty-seven") + // Expect.isNull doc "There should not have been a document returned" + // } + // ] + // testList "FindByField" [ + // testTask "succeeds when documents are found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! docs = conn.FindByField(Db.tableName, "NumValue", Op.GT, 15) + // Expect.hasCountOf docs 2u isTrue "There should have been two documents returned" + // } + // testTask "succeeds when documents are not found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! docs = conn.FindByField(Db.tableName, "Value", Op.EQ, "mauve") + // Expect.hasCountOf docs 0u isTrue "There should have been no documents returned" + // } + // ] + // testList "FindFirstByField" [ + // testTask "succeeds when a document is found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = conn.FindFirstByField(Db.tableName, "Value", Op.EQ, "another") + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.equal (doc :> JsonDocument).Id "two" "The incorrect document was returned" + // } + // testTask "succeeds when multiple documents are found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = conn.FindFirstByField(Db.tableName, "Sub.Foo", Op.EQ, "green") + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.contains [ "two"; "four" ] (doc :> JsonDocument).Id "An incorrect document was returned" + // } + // testTask "succeeds when a document is not found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = conn.FindFirstByField(Db.tableName, "Value", Op.EQ, "absent") + // Expect.isNull doc "There should not have been a document returned" + // } + // ] + // testList "UpdateFull" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let testDoc = JsonDocument(Id = "one", Sub = Some (SubDocument(Foo = "blue", Bar = "red"))) + // do! conn.UpdateFull(Db.tableName, "one", testDoc) + // let! after = conn.FindById(Db.tableName, "one") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // Expect.equal after.Id "one" "The updated document is not correct" + // Expect.isSome after.Sub "The updated document should have had a sub-document" + // Expect.equal after.Sub.Value.Foo "blue" "The updated sub-document is not correct" + // Expect.equal after.Sub.Value.Bar "red" "The updated sub-document is not correct" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! before = conn.FindAll Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! conn.UpdateFull( + // Db.tableName, + // "test", + // JsonDocument(Id = "x", Sub = Some (SubDocument(Foo = "blue", Bar = "red")))) + // } + // ] + // testList "UpdateFullFunc" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.UpdateFullFunc( + // Db.tableName, + // System.Func _.Id, + // JsonDocument(Id = "one", Value = "le un", NumValue = 1, Sub = None)) + // let! after = conn.FindById(Db.tableName, "one") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // 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.isNone after.Sub "The updated document should not have a sub-document" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! before = conn.FindAll Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! conn.UpdateFullFunc( + // Db.tableName, + // System.Func _.Id, + // JsonDocument(Id = "one", Value = "le un", NumValue = 1, Sub = None)) + // } + // ] + // testList "UpdatePartialById" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.UpdatePartialById(Db.tableName, "one", {| NumValue = 44 |}) + // let! after = conn.FindById(Db.tableName, "one") + // if isNull after then Expect.isTrue false "There should have been a document returned post-update" + // let after = after :> JsonDocument + // Expect.equal after.Id "one" "The updated document is not correct" + // Expect.equal after.NumValue 44 "The updated document is not correct" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! before = conn.FindAll Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! conn.UpdatePartialById(Db.tableName, "test", {| Foo = "green" |}) + // } + // ] + // testList "UpdatePartialByField" [ + // testTask "succeeds when a document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.UpdatePartialByField(Db.tableName, "Value", Op.EQ, "purple", {| NumValue = 77 |}) + // let! after = conn.CountByField(Db.tableName, "NumValue", Op.EQ, 77) + // Expect.equal after 2L "There should have been 2 documents returned" + // } + // testTask "succeeds when no document is updated" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // let! before = conn.FindAll Db.tableName + // Expect.hasCountOf before 0u isTrue "There should have been no documents returned" + // + // // This not raising an exception is the test + // do! conn.UpdatePartialByField(Db.tableName, "Value", Op.EQ, "burgundy", {| Foo = "green" |}) + // } + // ] + // testList "DeleteById" [ + // testTask "succeeds when a document is deleted" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.DeleteById(Db.tableName, "four") + // let! remaining = conn.CountAll Db.tableName + // Expect.equal remaining 4L "There should have been 4 documents remaining" + // } + // testTask "succeeds when a document is not deleted" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.DeleteById(Db.tableName, "thirty") + // let! remaining = conn.CountAll Db.tableName + // Expect.equal remaining 5L "There should have been 5 documents remaining" + // } + // ] + // testList "DeleteByField" [ + // testTask "succeeds when documents are deleted" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.DeleteByField(Db.tableName, "Value", Op.NE, "purple") + // let! remaining = conn.CountAll Db.tableName + // Expect.equal remaining 2L "There should have been 2 documents remaining" + // } + // testTask "succeeds when documents are not deleted" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.DeleteByField(Db.tableName, "Value", Op.EQ, "crimson") + // let! remaining = Count.All Db.tableName + // Expect.equal remaining 5L "There should have been 5 documents remaining" + // } + // ] + // testList "CustomSingle" [ + // testTask "succeeds when a row is found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = + // conn.CustomSingle( + // $"SELECT data FROM {Db.tableName} WHERE data ->> 'Id' = @id", + // [ SqliteParameter("@id", "one") ], + // FromData) + // if isNull doc then Expect.isTrue false "There should have been a document returned" + // Expect.equal (doc :> JsonDocument).Id "one" "The incorrect document was returned" + // } + // testTask "succeeds when a row is not found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! doc = + // conn.CustomSingle( + // $"SELECT data FROM {Db.tableName} WHERE data ->> 'Id' = @id", + // [ SqliteParameter("@id", "eighty") ], + // FromData) + // Expect.isNull doc "There should not have been a document returned" + // } + // ] + // testList "CustomList" [ + // testTask "succeeds when data is found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! docs = conn.CustomList(Query.SelectFromTable Db.tableName, [], FromData) + // Expect.hasCountOf docs 5u isTrue "There should have been 5 documents returned" + // } + // testTask "succeeds when data is not found" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // let! docs = + // conn.CustomList( + // $"SELECT data FROM {Db.tableName} WHERE data ->> 'NumValue' > @value", + // [ SqliteParameter("@value", 100) ], + // FromData) + // Expect.isEmpty docs "There should have been no documents returned" + // } + // ] + // testList "CustomNonQuery" [ + // testTask "succeeds when operating on data" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.CustomNonQuery($"DELETE FROM {Db.tableName}", []) + // + // let! remaining = conn.CountAll Db.tableName + // Expect.equal remaining 0L "There should be no documents remaining in the table" + // } + // testTask "succeeds when no data matches where clause" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // do! loadDocs () + // + // do! conn.CustomNonQuery( + // $"DELETE FROM {Db.tableName} WHERE data ->> 'NumValue' > @value", + // [ SqliteParameter("@value", 100) ]) + // + // let! remaining = conn.CountAll Db.tableName + // Expect.equal remaining 5L "There should be 5 documents remaining in the table" + // } + // ] + // testTask "CustomScalar succeeds" { + // use! db = Db.buildDb () + // use conn = Configuration.DbConn() + // + // let! nbr = + // conn.CustomScalar("SELECT 5 AS test_value", [], System.Func _.GetInt32(0)) + // Expect.equal nbr 5 "The query should have returned the number 5" + // } + // ] + // test "clean up database" { + // Configuration.UseConnectionString "data source=:memory:" + // } + // } + // ] + // |> testSequenced) + }); +} diff --git a/src/Tests.CSharp/SqliteDb.cs b/src/Tests.CSharp/SqliteDb.cs new file mode 100644 index 0000000..a34adc0 --- /dev/null +++ b/src/Tests.CSharp/SqliteDb.cs @@ -0,0 +1,59 @@ +namespace BitBadger.Documents.Tests; + +using System; +using System.IO; +using System.Threading.Tasks; +using static Sqlite; + +/// +/// A throwaway SQLite database file, which will be deleted when it goes out of scope +/// +public class ThrowawaySqliteDb : IDisposable, IAsyncDisposable +{ + private readonly string _dbName; + + public ThrowawaySqliteDb(string dbName) + { + _dbName = dbName; + } + + public void Dispose() + { + if (File.Exists(_dbName)) File.Delete(_dbName); + GC.SuppressFinalize(this); + } + + public ValueTask DisposeAsync() + { + if (File.Exists(_dbName)) File.Delete(_dbName); + GC.SuppressFinalize(this); + return ValueTask.CompletedTask; + } +} + +/// +/// Utility functions for dealing with SQLite databases +/// +public static class SqliteDb +{ + /// + /// The table name for the catalog metadata + /// + public static readonly string Catalog = "sqlite_master"; + + /// + /// The name of the table used for testing + /// + public static readonly string TableName = "test_table"; + + /// + /// Create a throwaway database file with the test_table defined + /// + public static async Task BuildDb() + { + var dbName = $"test-db-{Guid.NewGuid():n}.db"; + Configuration.UseConnectionString($"data source={dbName}"); + await Definition.EnsureTable(TableName); + return new ThrowawaySqliteDb(dbName); + } +} diff --git a/src/Tests.CSharp/Types.cs b/src/Tests.CSharp/Types.cs new file mode 100644 index 0000000..5e7f972 --- /dev/null +++ b/src/Tests.CSharp/Types.cs @@ -0,0 +1,15 @@ +namespace BitBadger.Documents.Tests.CSharp; + +public class SubDocument +{ + public string Foo { get; set; } = ""; + public string Bar { get; set; } = ""; +} + +public class JsonDocument +{ + public string Id { get; set; } = ""; + public string Value { get; set; } = ""; + public int NumValue { get; set; } = 0; + public SubDocument? Sub { get; set; } = null; +} diff --git a/src/Tests/BitBadger.Documents.Tests.fsproj b/src/Tests/BitBadger.Documents.Tests.fsproj index d6f064a..9dae8da 100644 --- a/src/Tests/BitBadger.Documents.Tests.fsproj +++ b/src/Tests/BitBadger.Documents.Tests.fsproj @@ -17,6 +17,7 @@ + diff --git a/src/Tests/CommonTests.fs b/src/Tests/CommonTests.fs index fff4000..5dab352 100644 --- a/src/Tests/CommonTests.fs +++ b/src/Tests/CommonTests.fs @@ -29,10 +29,10 @@ let all = Expect.equal (string NE) "<>" "The not equal to operator was not correct" } test "EX succeeds" { - Expect.equal (string EX) "IS NOT NULL" """The "exists" operator ws not correct""" + Expect.equal (string EX) "IS NOT NULL" """The "exists" operator was not correct""" } test "NEX succeeds" { - Expect.equal (string NEX) "IS NULL" """The "not exists" operator ws not correct""" + Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct""" } ] testList "Query" [ diff --git a/src/Tests/Program.fs b/src/Tests/Program.fs index 4bd23f4..80749e8 100644 --- a/src/Tests/Program.fs +++ b/src/Tests/Program.fs @@ -1,6 +1,13 @@ open Expecto +open BitBadger.Documents.Tests.CSharp -let allTests = testList "BitBadger.Documents" [ CommonTests.all; SqliteTests.all ] +let allTests = + testList + "BitBadger.Documents" + [ CommonTests.all + CommonCSharpTests.Unit + SqliteTests.all + testSequenced SqliteCSharpTests.Integration ] [] let main args = runTestsWithCLIArgs [] args allTests