Implement auto IDs for both dbs
- Move SQLite WithConn doc into Document module
This commit is contained in:
		
							parent
							
								
									b357b5a01f
								
							
						
					
					
						commit
						f41fa1245a
					
				@ -154,7 +154,7 @@ type ParameterName() =
 | 
			
		||||
open System.Text
 | 
			
		||||
#endif
 | 
			
		||||
 | 
			
		||||
/// Automatically-generated document ID options
 | 
			
		||||
/// Automatically-generated document ID strategies
 | 
			
		||||
[<Struct>]
 | 
			
		||||
type AutoId =
 | 
			
		||||
    /// No automatic IDs will be generated
 | 
			
		||||
@ -167,11 +167,11 @@ type AutoId =
 | 
			
		||||
    | RandomString
 | 
			
		||||
with
 | 
			
		||||
    /// Generate a GUID string
 | 
			
		||||
    static member GenerateGuid() =
 | 
			
		||||
    static member GenerateGuid () =
 | 
			
		||||
        System.Guid.NewGuid().ToString "N"
 | 
			
		||||
    
 | 
			
		||||
    /// Generate a string of random hexadecimal characters
 | 
			
		||||
    static member GenerateRandomString(length: int) =
 | 
			
		||||
    static member GenerateRandomString (length: int) =
 | 
			
		||||
#if NET8_0_OR_GREATER
 | 
			
		||||
        RandomNumberGenerator.GetHexString(length, lowercase = true)
 | 
			
		||||
#else
 | 
			
		||||
@ -179,6 +179,40 @@ with
 | 
			
		||||
        |> Array.fold (fun (str: StringBuilder) byt -> str.Append(byt.ToString "x2")) (StringBuilder length)
 | 
			
		||||
        |> function it -> it.Length <- length; it.ToString()
 | 
			
		||||
#endif
 | 
			
		||||
    
 | 
			
		||||
    /// Does the given document need an automatic ID generated?
 | 
			
		||||
    static member NeedsAutoId<'T> strategy (document: 'T) idProp =
 | 
			
		||||
        match strategy with
 | 
			
		||||
        | Disabled -> false
 | 
			
		||||
        | _ ->
 | 
			
		||||
            let prop = document.GetType().GetProperty idProp
 | 
			
		||||
            if isNull prop then invalidOp $"{idProp} not found in document"
 | 
			
		||||
            else
 | 
			
		||||
                match strategy with
 | 
			
		||||
                | Number ->
 | 
			
		||||
                    if prop.PropertyType = typeof<int8> then
 | 
			
		||||
                        let value = prop.GetValue document :?> int8
 | 
			
		||||
                        value = int8 0
 | 
			
		||||
                    elif prop.PropertyType = typeof<int16> then
 | 
			
		||||
                        let value = prop.GetValue document :?> int16
 | 
			
		||||
                        value = int16 0
 | 
			
		||||
                    elif prop.PropertyType = typeof<int> then
 | 
			
		||||
                        let value = prop.GetValue document :?> int
 | 
			
		||||
                        value = 0
 | 
			
		||||
                    elif prop.PropertyType = typeof<int64> then
 | 
			
		||||
                        let value = prop.GetValue document :?> int64
 | 
			
		||||
                        value = int64 0
 | 
			
		||||
                    else invalidOp "Document ID was not a number; cannot auto-generate a Number ID"
 | 
			
		||||
                | Guid | RandomString ->
 | 
			
		||||
                    if prop.PropertyType = typeof<string> then
 | 
			
		||||
                        let value =
 | 
			
		||||
                            prop.GetValue document
 | 
			
		||||
                            |> Option.ofObj
 | 
			
		||||
                            |> Option.map (fun it -> it :?> string)
 | 
			
		||||
                            |> Option.defaultValue ""
 | 
			
		||||
                        value = ""
 | 
			
		||||
                    else invalidOp "Document ID was not a string; cannot auto-generate GUID or random string"
 | 
			
		||||
                | Disabled -> false
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// The required document serialization implementation
 | 
			
		||||
 | 
			
		||||
@ -312,7 +312,23 @@ module WithProps =
 | 
			
		||||
        /// Insert a new document
 | 
			
		||||
        [<CompiledName "Insert">]
 | 
			
		||||
        let insert<'TDoc> tableName (document: 'TDoc) sqlProps =
 | 
			
		||||
            Custom.nonQuery (Query.insert tableName) [ jsonParam "@data" document ] sqlProps
 | 
			
		||||
            let query =
 | 
			
		||||
                match Configuration.autoIdStrategy () with 
 | 
			
		||||
                | Disabled -> Query.insert tableName
 | 
			
		||||
                | strategy ->
 | 
			
		||||
                    let idField   = Configuration.idField ()
 | 
			
		||||
                    let dataParam =
 | 
			
		||||
                        if AutoId.NeedsAutoId strategy document idField then
 | 
			
		||||
                            match strategy with
 | 
			
		||||
                            | Number ->
 | 
			
		||||
                                $"' || (SELECT COALESCE(MAX((data->>'{idField}')::numeric), 0) + 1 FROM {tableName}) || '"
 | 
			
		||||
                            | Guid -> $"\"{AutoId.GenerateGuid()}\""
 | 
			
		||||
                            | RandomString -> $"\"{AutoId.GenerateRandomString(Configuration.idStringLength ())}\""
 | 
			
		||||
                            | Disabled -> "@data"
 | 
			
		||||
                            |> function it -> $"""@data::jsonb || ('{{"{idField}":{it}}}')::jsonb"""
 | 
			
		||||
                        else "@data"
 | 
			
		||||
                    (Query.insert tableName).Replace("@data", dataParam)
 | 
			
		||||
            Custom.nonQuery query [ jsonParam "@data" document ] sqlProps
 | 
			
		||||
 | 
			
		||||
        /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
 | 
			
		||||
        [<CompiledName "Save">]
 | 
			
		||||
 | 
			
		||||
@ -35,11 +35,11 @@ module Extensions =
 | 
			
		||||
        
 | 
			
		||||
        /// Insert a new document
 | 
			
		||||
        member conn.insert<'TDoc> tableName (document: 'TDoc) =
 | 
			
		||||
            WithConn.insert<'TDoc> tableName document conn
 | 
			
		||||
            WithConn.Document.insert<'TDoc> tableName document conn
 | 
			
		||||
 | 
			
		||||
        /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
 | 
			
		||||
        member conn.save<'TDoc> tableName (document: 'TDoc) =
 | 
			
		||||
            WithConn.save tableName document conn
 | 
			
		||||
            WithConn.Document.save tableName document conn
 | 
			
		||||
 | 
			
		||||
        /// Count all documents in a table
 | 
			
		||||
        member conn.countAll tableName =
 | 
			
		||||
@ -159,12 +159,12 @@ type SqliteConnectionCSharpExtensions =
 | 
			
		||||
    /// Insert a new document
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline Insert<'TDoc>(conn, tableName, document: 'TDoc) =
 | 
			
		||||
        WithConn.insert<'TDoc> tableName document conn
 | 
			
		||||
        WithConn.Document.insert<'TDoc> tableName document conn
 | 
			
		||||
 | 
			
		||||
    /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
    static member inline Save<'TDoc>(conn, tableName, document: 'TDoc) =
 | 
			
		||||
        WithConn.save<'TDoc> tableName document conn
 | 
			
		||||
        WithConn.Document.save<'TDoc> tableName document conn
 | 
			
		||||
 | 
			
		||||
    /// Count all documents in a table
 | 
			
		||||
    [<Extension>]
 | 
			
		||||
 | 
			
		||||
@ -258,15 +258,34 @@ module WithConn =
 | 
			
		||||
        let ensureFieldIndex tableName indexName fields conn =
 | 
			
		||||
            Custom.nonQuery (Query.Definition.ensureIndexOn tableName indexName fields SQLite) [] conn
 | 
			
		||||
 | 
			
		||||
    /// Insert a new document
 | 
			
		||||
    [<CompiledName "Insert">]
 | 
			
		||||
    let insert<'TDoc> tableName (document: 'TDoc) conn =
 | 
			
		||||
        Custom.nonQuery (Query.insert tableName) [ jsonParam "@data" document ] conn
 | 
			
		||||
 | 
			
		||||
    /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
 | 
			
		||||
    [<CompiledName "Save">]
 | 
			
		||||
    let save<'TDoc> tableName (document: 'TDoc) conn =
 | 
			
		||||
        Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] conn
 | 
			
		||||
    /// Commands to add documents
 | 
			
		||||
    [<AutoOpen>]
 | 
			
		||||
    module Document =
 | 
			
		||||
        
 | 
			
		||||
        /// Insert a new document
 | 
			
		||||
        [<CompiledName "Insert">]
 | 
			
		||||
        let insert<'TDoc> tableName (document: 'TDoc) conn =
 | 
			
		||||
            let query =
 | 
			
		||||
                match Configuration.autoIdStrategy () with 
 | 
			
		||||
                | Disabled -> Query.insert tableName
 | 
			
		||||
                | strategy ->
 | 
			
		||||
                    let idField   = Configuration.idField ()
 | 
			
		||||
                    let dataParam =
 | 
			
		||||
                        if AutoId.NeedsAutoId strategy document idField then
 | 
			
		||||
                            match strategy with
 | 
			
		||||
                            | Number -> $"(SELECT coalesce(max(data->>'{idField}'), 0) + 1 FROM {tableName})"
 | 
			
		||||
                            | Guid -> $"'{AutoId.GenerateGuid()}'"
 | 
			
		||||
                            | RandomString -> $"'{AutoId.GenerateRandomString(Configuration.idStringLength ())}'"
 | 
			
		||||
                            | Disabled -> "@data"
 | 
			
		||||
                            |> function it -> $"json_set(@data, '$.{idField}', {it})"
 | 
			
		||||
                        else "@data"
 | 
			
		||||
                    (Query.insert tableName).Replace("@data", dataParam)
 | 
			
		||||
            Custom.nonQuery query [ jsonParam "@data" document ] conn
 | 
			
		||||
        
 | 
			
		||||
        /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
 | 
			
		||||
        [<CompiledName "Save">]
 | 
			
		||||
        let save<'TDoc> tableName (document: 'TDoc) conn =
 | 
			
		||||
            Custom.nonQuery (Query.save tableName) [ jsonParam "@data" document ] conn
 | 
			
		||||
 | 
			
		||||
    /// Commands to count documents
 | 
			
		||||
    [<RequireQualifiedAccess>]
 | 
			
		||||
@ -547,13 +566,13 @@ module Document =
 | 
			
		||||
    [<CompiledName "Insert">]
 | 
			
		||||
    let insert<'TDoc> tableName (document: 'TDoc) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.insert tableName document conn
 | 
			
		||||
        WithConn.Document.insert tableName document conn
 | 
			
		||||
 | 
			
		||||
    /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert")
 | 
			
		||||
    [<CompiledName "Save">]
 | 
			
		||||
    let save<'TDoc> tableName (document: 'TDoc) =
 | 
			
		||||
        use conn = Configuration.dbConn ()
 | 
			
		||||
        WithConn.save tableName document conn
 | 
			
		||||
        WithConn.Document.save tableName document conn
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
/// Commands to count documents
 | 
			
		||||
 | 
			
		||||
										
											
												File diff suppressed because it is too large
												Load Diff
											
										
									
								
							@ -170,7 +170,11 @@ public static class PostgresCSharpTests
 | 
			
		||||
                    Expect.isTrue(false, "The parameter was not a StringArray type");
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        ])
 | 
			
		||||
        ]),
 | 
			
		||||
        TestCase("None succeeds", () =>
 | 
			
		||||
        {
 | 
			
		||||
            Expect.isEmpty(Parameters.None, "The no-params sequence should be empty");
 | 
			
		||||
        })
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    /// <summary>
 | 
			
		||||
@ -310,15 +314,6 @@ public static class PostgresCSharpTests
 | 
			
		||||
        })
 | 
			
		||||
    ]);
 | 
			
		||||
    
 | 
			
		||||
    /// <summary>
 | 
			
		||||
    /// Tests which do not hit the database
 | 
			
		||||
    /// </summary>
 | 
			
		||||
    private static readonly Test Unit = TestList("Unit",
 | 
			
		||||
    [
 | 
			
		||||
        ParametersTests,
 | 
			
		||||
        QueryTests
 | 
			
		||||
    ]);
 | 
			
		||||
 | 
			
		||||
    private static readonly List<JsonDocument> TestDocuments =
 | 
			
		||||
    [
 | 
			
		||||
        new() { Id = "one", Value = "FIRST!", NumValue = 0 },
 | 
			
		||||
@ -548,6 +543,83 @@ public static class PostgresCSharpTests
 | 
			
		||||
                {
 | 
			
		||||
                    // This is what should have happened
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when adding a numeric auto ID", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Number);
 | 
			
		||||
                    Configuration.UseIdField("Key");
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    var before = await Count.All(PostgresDb.TableName);
 | 
			
		||||
                    Expect.equal(before, 0, "There should be no documents in the table");
 | 
			
		||||
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new NumIdDocument { Text = "one" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new NumIdDocument { Text = "two" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new NumIdDocument { Key = 77, Text = "three" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new NumIdDocument { Text = "four" });
 | 
			
		||||
 | 
			
		||||
                    var after = await Find.AllOrdered<NumIdDocument>(PostgresDb.TableName, [Field.Named("n:Key")]);
 | 
			
		||||
                    Expect.hasLength(after, 4, "There should have been 4 documents returned");
 | 
			
		||||
                    Expect.sequenceEqual(after.Select(x => x.Key), [1, 2, 77, 78],
 | 
			
		||||
                        "The IDs were not generated correctly");
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Disabled);
 | 
			
		||||
                    Configuration.UseIdField("Id");
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when adding a GUID auto ID", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Guid);
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    var before = await Count.All(PostgresDb.TableName);
 | 
			
		||||
                    Expect.equal(before, 0, "There should be no documents in the table");
 | 
			
		||||
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Value = "one" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Value = "two" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Id = "abc123", Value = "three" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Value = "four" });
 | 
			
		||||
 | 
			
		||||
                    var after = await Find.All<JsonDocument>(PostgresDb.TableName);
 | 
			
		||||
                    Expect.hasLength(after, 4, "There should have been 4 documents returned");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id.Length == 32), 3, "Three of the IDs should have been GUIDs");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id == "abc123"), 1, "The provided ID should have been used as-is");
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Disabled);
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when adding a RandomString auto ID", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.RandomString);
 | 
			
		||||
                    Configuration.UseIdStringLength(44);
 | 
			
		||||
                    await using var db = PostgresDb.BuildDb();
 | 
			
		||||
                    var before = await Count.All(PostgresDb.TableName);
 | 
			
		||||
                    Expect.equal(before, 0, "There should be no documents in the table");
 | 
			
		||||
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Value = "one" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Value = "two" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Id = "abc123", Value = "three" });
 | 
			
		||||
                    await Document.Insert(PostgresDb.TableName, new JsonDocument { Value = "four" });
 | 
			
		||||
 | 
			
		||||
                    var after = await Find.All<JsonDocument>(PostgresDb.TableName);
 | 
			
		||||
                    Expect.hasLength(after, 4, "There should have been 4 documents returned");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id.Length == 44), 3,
 | 
			
		||||
                        "Three of the IDs should have been 44-character random strings");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id == "abc123"), 1, "The provided ID should have been used as-is");
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Disabled);
 | 
			
		||||
                    Configuration.UseIdStringLength(16);
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        ]),
 | 
			
		||||
        TestList("Save",
 | 
			
		||||
 | 
			
		||||
@ -325,9 +325,86 @@ public static class SqliteCSharpTests
 | 
			
		||||
                {
 | 
			
		||||
                    // This is what is supposed to happen
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when adding a numeric auto ID", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Number);
 | 
			
		||||
                    Configuration.UseIdField("Key");
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    var before = await Count.All(SqliteDb.TableName);
 | 
			
		||||
                    Expect.equal(before, 0L, "There should be no documents in the table");
 | 
			
		||||
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new NumIdDocument { Text = "one" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new NumIdDocument { Text = "two" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new NumIdDocument { Key = 77, Text = "three" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new NumIdDocument { Text = "four" });
 | 
			
		||||
 | 
			
		||||
                    var after = await Find.AllOrdered<NumIdDocument>(SqliteDb.TableName, [Field.Named("Key")]);
 | 
			
		||||
                    Expect.hasLength(after, 4, "There should have been 4 documents returned");
 | 
			
		||||
                    Expect.sequenceEqual(after.Select(x => x.Key), [1, 2, 77, 78],
 | 
			
		||||
                        "The IDs were not generated correctly");
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Disabled);
 | 
			
		||||
                    Configuration.UseIdField("Id");
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when adding a GUID auto ID", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Guid);
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    var before = await Count.All(SqliteDb.TableName);
 | 
			
		||||
                    Expect.equal(before, 0L, "There should be no documents in the table");
 | 
			
		||||
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "one" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "two" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "abc123", Value = "three" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "four" });
 | 
			
		||||
 | 
			
		||||
                    var after = await Find.All<JsonDocument>(SqliteDb.TableName);
 | 
			
		||||
                    Expect.hasLength(after, 4, "There should have been 4 documents returned");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id.Length == 32), 3, "Three of the IDs should have been GUIDs");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id == "abc123"), 1, "The provided ID should have been used as-is");
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Disabled);
 | 
			
		||||
                }
 | 
			
		||||
            }),
 | 
			
		||||
            TestCase("succeeds when adding a RandomString auto ID", async () =>
 | 
			
		||||
            {
 | 
			
		||||
                try
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.RandomString);
 | 
			
		||||
                    Configuration.UseIdStringLength(44);
 | 
			
		||||
                    await using var db = await SqliteDb.BuildDb();
 | 
			
		||||
                    var before = await Count.All(SqliteDb.TableName);
 | 
			
		||||
                    Expect.equal(before, 0L, "There should be no documents in the table");
 | 
			
		||||
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "one" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "two" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Id = "abc123", Value = "three" });
 | 
			
		||||
                    await Document.Insert(SqliteDb.TableName, new JsonDocument { Value = "four" });
 | 
			
		||||
 | 
			
		||||
                    var after = await Find.All<JsonDocument>(SqliteDb.TableName);
 | 
			
		||||
                    Expect.hasLength(after, 4, "There should have been 4 documents returned");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id.Length == 44), 3,
 | 
			
		||||
                        "Three of the IDs should have been 44-character random strings");
 | 
			
		||||
                    Expect.equal(after.Count(x => x.Id == "abc123"), 1, "The provided ID should have been used as-is");
 | 
			
		||||
                }
 | 
			
		||||
                finally
 | 
			
		||||
                {
 | 
			
		||||
                    Configuration.UseAutoIdStrategy(AutoId.Disabled);
 | 
			
		||||
                    Configuration.UseIdStringLength(16);
 | 
			
		||||
                }
 | 
			
		||||
            })
 | 
			
		||||
        ]),
 | 
			
		||||
        TestList("Document.Save",
 | 
			
		||||
        TestList("Save",
 | 
			
		||||
        [
 | 
			
		||||
            TestCase("succeeds when a document is inserted", async () =>
 | 
			
		||||
            {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,11 @@
 | 
			
		||||
namespace BitBadger.Documents.Tests.CSharp;
 | 
			
		||||
 | 
			
		||||
public class NumIdDocument
 | 
			
		||||
{
 | 
			
		||||
    public int Key { get; set; } = 0;
 | 
			
		||||
    public string Text { get; set; } = "";
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
public class SubDocument
 | 
			
		||||
{
 | 
			
		||||
    public string Foo { get; set; } = "";
 | 
			
		||||
 | 
			
		||||
@ -6,8 +6,8 @@
 | 
			
		||||
  </PropertyGroup>
 | 
			
		||||
 | 
			
		||||
  <ItemGroup>
 | 
			
		||||
    <Compile Include="CommonTests.fs" />
 | 
			
		||||
    <Compile Include="Types.fs" />
 | 
			
		||||
    <Compile Include="CommonTests.fs" />
 | 
			
		||||
    <Compile Include="PostgresTests.fs" />
 | 
			
		||||
    <Compile Include="PostgresExtensionTests.fs" />
 | 
			
		||||
    <Compile Include="SqliteTests.fs" />
 | 
			
		||||
 | 
			
		||||
@ -217,6 +217,115 @@ let autoIdTests = testList "AutoId" [
 | 
			
		||||
            Expect.stringHasLength autoId length $"Random string should have been {length} characters long"
 | 
			
		||||
            Expect.equal autoId (autoId.ToLowerInvariant ()) $"Random string ({length}) should have been lowercase")
 | 
			
		||||
    }
 | 
			
		||||
    testList "NeedsAutoId" [
 | 
			
		||||
        test "succeeds when no auto ID is configured" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId Disabled (obj ()) "id") "Disabled auto-ID never needs an automatic ID"
 | 
			
		||||
        }
 | 
			
		||||
        test "fails for any when the ID property is not found" {
 | 
			
		||||
            Expect.throwsT<System.InvalidOperationException>
 | 
			
		||||
                (fun () -> AutoId.NeedsAutoId Number {| Key = "" |} "Id" |> ignore)
 | 
			
		||||
                "Non-existent ID property should have thrown an exception"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for byte when the ID is zero" {
 | 
			
		||||
            Expect.isTrue (AutoId.NeedsAutoId Number {| Id = int8 0 |} "Id") "Zero ID should have returned true"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for byte when the ID is non-zero" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId Number {| Id = int8 4 |} "Id") "Non-zero ID should have returned false"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for short when the ID is zero" {
 | 
			
		||||
            Expect.isTrue (AutoId.NeedsAutoId Number {| Id = int16 0 |} "Id") "Zero ID should have returned true"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for short when the ID is non-zero" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId Number {| Id = int16 7 |} "Id") "Non-zero ID should have returned false"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for int when the ID is zero" {
 | 
			
		||||
            Expect.isTrue (AutoId.NeedsAutoId Number {| Id = 0 |} "Id") "Zero ID should have returned true"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for int when the ID is non-zero" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId Number {| Id = 32 |} "Id") "Non-zero ID should have returned false"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for long when the ID is zero" {
 | 
			
		||||
            Expect.isTrue (AutoId.NeedsAutoId Number {| Id = 0L |} "Id") "Zero ID should have returned true"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for long when the ID is non-zero" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId Number {| Id = 80L |} "Id") "Non-zero ID should have returned false"
 | 
			
		||||
        }
 | 
			
		||||
        test "fails for number when the ID is not a number" {
 | 
			
		||||
            Expect.throwsT<System.InvalidOperationException>
 | 
			
		||||
                (fun () -> AutoId.NeedsAutoId Number {| Id = "" |} "Id" |> ignore)
 | 
			
		||||
                "Numeric ID against a string should have thrown an exception"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for GUID when the ID is blank" {
 | 
			
		||||
            Expect.isTrue (AutoId.NeedsAutoId Guid {| Id = "" |} "Id") "Blank ID should have returned true"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for GUID when the ID is filled" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId Guid {| Id = "abc" |} "Id") "Filled ID should have returned false"
 | 
			
		||||
        }
 | 
			
		||||
        test "fails for GUID when the ID is not a string" {
 | 
			
		||||
            Expect.throwsT<System.InvalidOperationException>
 | 
			
		||||
                (fun () -> AutoId.NeedsAutoId Guid {| Id = 8 |} "Id" |> ignore)
 | 
			
		||||
                "String ID against a number should have thrown an exception"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for RandomString when the ID is blank" {
 | 
			
		||||
            Expect.isTrue (AutoId.NeedsAutoId RandomString {| Id = "" |} "Id") "Blank ID should have returned true"
 | 
			
		||||
        }
 | 
			
		||||
        test "succeeds for RandomString when the ID is filled" {
 | 
			
		||||
            Expect.isFalse (AutoId.NeedsAutoId RandomString {| Id = "x" |} "Id") "Filled ID should have returned false"
 | 
			
		||||
        }
 | 
			
		||||
        test "fails for RandomString when the ID is not a string" {
 | 
			
		||||
            Expect.throwsT<System.InvalidOperationException>
 | 
			
		||||
                (fun () -> AutoId.NeedsAutoId RandomString {| Id = 33 |} "Id" |> ignore)
 | 
			
		||||
                "String ID against a number should have thrown an exception"
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/// Unit tests for the Configuration module
 | 
			
		||||
let configurationTests = testList "Configuration" [
 | 
			
		||||
    test "useSerializer succeeds" {
 | 
			
		||||
        try
 | 
			
		||||
            Configuration.useSerializer
 | 
			
		||||
                { new IDocumentSerializer with
 | 
			
		||||
                    member _.Serialize<'T>(it: 'T) : string = """{"Overridden":true}"""
 | 
			
		||||
                    member _.Deserialize<'T>(it: string) : 'T = Unchecked.defaultof<'T>
 | 
			
		||||
                }
 | 
			
		||||
            
 | 
			
		||||
            let serialized = Configuration.serializer().Serialize {| Foo = "howdy"; Bar = "bye" |}
 | 
			
		||||
            Expect.equal serialized """{"Overridden":true}""" "Specified serializer was not used"
 | 
			
		||||
            
 | 
			
		||||
            let deserialized = Configuration.serializer().Deserialize<obj> """{"Something":"here"}"""
 | 
			
		||||
            Expect.isNull deserialized "Specified serializer should have returned null"
 | 
			
		||||
        finally
 | 
			
		||||
            Configuration.useSerializer DocumentSerializer.``default``
 | 
			
		||||
    }
 | 
			
		||||
    test "serializer returns configured serializer" {
 | 
			
		||||
        Expect.isTrue (obj.ReferenceEquals(DocumentSerializer.``default``, Configuration.serializer ()))
 | 
			
		||||
            "Serializer should have been the same"
 | 
			
		||||
    }
 | 
			
		||||
    test "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"
 | 
			
		||||
    }
 | 
			
		||||
    test "useAutoIdStrategy / autoIdStrategy succeeds" {
 | 
			
		||||
        try
 | 
			
		||||
            Expect.equal (Configuration.autoIdStrategy ()) Disabled "The default auto-ID strategy was incorrect"
 | 
			
		||||
            Configuration.useAutoIdStrategy Guid
 | 
			
		||||
            Expect.equal (Configuration.autoIdStrategy ()) Guid "The auto-ID strategy was not set correctly"
 | 
			
		||||
        finally
 | 
			
		||||
            Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
    }
 | 
			
		||||
    test "useIdStringLength / idStringLength succeeds" {
 | 
			
		||||
        try
 | 
			
		||||
            Expect.equal (Configuration.idStringLength ()) 16 "The default ID string length was incorrect"
 | 
			
		||||
            Configuration.useIdStringLength 33
 | 
			
		||||
            Expect.equal (Configuration.idStringLength ()) 33 "The ID string length was not set correctly"
 | 
			
		||||
        finally
 | 
			
		||||
            Configuration.useIdStringLength 16
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/// Unit tests for the Query module
 | 
			
		||||
@ -344,54 +453,6 @@ let queryTests = testList "Query" [
 | 
			
		||||
    ]
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/// Unit tests for the Configuration module
 | 
			
		||||
let configurationTests = testList "Configuration" [
 | 
			
		||||
    test "useSerializer succeeds" {
 | 
			
		||||
        try
 | 
			
		||||
            Configuration.useSerializer
 | 
			
		||||
                { new IDocumentSerializer with
 | 
			
		||||
                    member _.Serialize<'T>(it: 'T) : string = """{"Overridden":true}"""
 | 
			
		||||
                    member _.Deserialize<'T>(it: string) : 'T = Unchecked.defaultof<'T>
 | 
			
		||||
                }
 | 
			
		||||
            
 | 
			
		||||
            let serialized = Configuration.serializer().Serialize {| Foo = "howdy"; Bar = "bye" |}
 | 
			
		||||
            Expect.equal serialized """{"Overridden":true}""" "Specified serializer was not used"
 | 
			
		||||
            
 | 
			
		||||
            let deserialized = Configuration.serializer().Deserialize<obj> """{"Something":"here"}"""
 | 
			
		||||
            Expect.isNull deserialized "Specified serializer should have returned null"
 | 
			
		||||
        finally
 | 
			
		||||
            Configuration.useSerializer DocumentSerializer.``default``
 | 
			
		||||
    }
 | 
			
		||||
    test "serializer returns configured serializer" {
 | 
			
		||||
        Expect.isTrue (obj.ReferenceEquals(DocumentSerializer.``default``, Configuration.serializer ()))
 | 
			
		||||
            "Serializer should have been the same"
 | 
			
		||||
    }
 | 
			
		||||
    test "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"
 | 
			
		||||
    }
 | 
			
		||||
    test "useAutoIdStrategy / autoIdStrategy succeeds" {
 | 
			
		||||
        try
 | 
			
		||||
            Expect.equal (Configuration.autoIdStrategy ()) Disabled "The default auto-ID strategy was incorrect"
 | 
			
		||||
            Configuration.useAutoIdStrategy Guid
 | 
			
		||||
            Expect.equal (Configuration.autoIdStrategy ()) Guid "The auto-ID strategy was not set correctly"
 | 
			
		||||
        finally
 | 
			
		||||
            Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
    }
 | 
			
		||||
    test "useIdStringLength / idStringLength succeeds" {
 | 
			
		||||
        try
 | 
			
		||||
            Expect.equal (Configuration.idStringLength ()) 16 "The default ID string length was incorrect"
 | 
			
		||||
            Configuration.useIdStringLength 33
 | 
			
		||||
            Expect.equal (Configuration.idStringLength ()) 33 "The ID string length was not set correctly"
 | 
			
		||||
        finally
 | 
			
		||||
            Configuration.useIdStringLength 16
 | 
			
		||||
    }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/// Tests which do not hit the database
 | 
			
		||||
let all = testList "Common" [
 | 
			
		||||
    opTests
 | 
			
		||||
 | 
			
		||||
@ -252,18 +252,9 @@ let queryTests = testList "Query" [
 | 
			
		||||
open ThrowawayDb.Postgres
 | 
			
		||||
open Types
 | 
			
		||||
 | 
			
		||||
/// Documents to use for integration tests
 | 
			
		||||
let documents = [
 | 
			
		||||
    { Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None }
 | 
			
		||||
    { Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } }
 | 
			
		||||
    { Id = "three"; Value = ""; NumValue = 4; Sub = None }
 | 
			
		||||
    { Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } }
 | 
			
		||||
    { Id = "five"; Value = "purple"; NumValue = 18; Sub = None }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/// Load the test documents into the database
 | 
			
		||||
let loadDocs () = backgroundTask {
 | 
			
		||||
    for doc in documents do do! insert PostgresDb.TableName doc
 | 
			
		||||
    for doc in testDocuments do do! insert PostgresDb.TableName doc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Integration tests for the Configuration module of the PostgreSQL library
 | 
			
		||||
@ -435,6 +426,68 @@ let documentTests = testList "Document" [
 | 
			
		||||
                    |> Async.RunSynchronously)
 | 
			
		||||
                "An exception should have been raised for duplicate document ID insert"
 | 
			
		||||
        }
 | 
			
		||||
        testTask "succeeds when adding a numeric auto ID" {
 | 
			
		||||
            try
 | 
			
		||||
                Configuration.useAutoIdStrategy Number
 | 
			
		||||
                Configuration.useIdField "Key"
 | 
			
		||||
                use db = PostgresDb.BuildDb()
 | 
			
		||||
                let! before = Count.all PostgresDb.TableName
 | 
			
		||||
                Expect.equal before 0 "There should be no documents in the table"
 | 
			
		||||
                
 | 
			
		||||
                do! insert PostgresDb.TableName { Key = 0; Text = "one" }
 | 
			
		||||
                do! insert PostgresDb.TableName { Key = 0; Text = "two" }
 | 
			
		||||
                do! insert PostgresDb.TableName { Key = 77; Text = "three" }
 | 
			
		||||
                do! insert PostgresDb.TableName { Key = 0; Text = "four" }
 | 
			
		||||
                
 | 
			
		||||
                let! after = Find.allOrdered<NumIdDocument> PostgresDb.TableName [ Field.Named "n:Key" ]
 | 
			
		||||
                Expect.hasLength after 4 "There should have been 4 documents returned"
 | 
			
		||||
                Expect.equal (after |> List.map _.Key) [ 1; 2; 77; 78 ] "The IDs were not generated correctly"
 | 
			
		||||
            finally
 | 
			
		||||
                Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
                Configuration.useIdField "Id"
 | 
			
		||||
        }
 | 
			
		||||
        testTask "succeeds when adding a GUID auto ID" {
 | 
			
		||||
            try
 | 
			
		||||
                Configuration.useAutoIdStrategy Guid
 | 
			
		||||
                use db = PostgresDb.BuildDb()
 | 
			
		||||
                let! before = Count.all PostgresDb.TableName
 | 
			
		||||
                Expect.equal before 0 "There should be no documents in the table"
 | 
			
		||||
                
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Value = "one" }
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Value = "two" }
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Id = "abc123"; Value = "three" }
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Value = "four" }
 | 
			
		||||
                
 | 
			
		||||
                let! after = Find.all<JsonDocument> PostgresDb.TableName
 | 
			
		||||
                Expect.hasLength after 4 "There should have been 4 documents returned"
 | 
			
		||||
                Expect.hasCountOf after 3u (fun doc -> doc.Id.Length = 32) "Three of the IDs should have been GUIDs"
 | 
			
		||||
                Expect.hasCountOf after 1u (fun doc -> doc.Id = "abc123") "The provided ID should have been used as-is"
 | 
			
		||||
            finally
 | 
			
		||||
                Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
        }
 | 
			
		||||
        testTask "succeeds when adding a RandomString auto ID" {
 | 
			
		||||
            try
 | 
			
		||||
                Configuration.useAutoIdStrategy RandomString
 | 
			
		||||
                Configuration.useIdStringLength 44
 | 
			
		||||
                use db = PostgresDb.BuildDb()
 | 
			
		||||
                let! before = Count.all PostgresDb.TableName
 | 
			
		||||
                Expect.equal before 0 "There should be no documents in the table"
 | 
			
		||||
                
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Value = "one" }
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Value = "two" }
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Id = "abc123"; Value = "three" }
 | 
			
		||||
                do! insert PostgresDb.TableName { emptyDoc with Value = "four" }
 | 
			
		||||
                
 | 
			
		||||
                let! after = Find.all<JsonDocument> PostgresDb.TableName
 | 
			
		||||
                Expect.hasLength after 4 "There should have been 4 documents returned"
 | 
			
		||||
                Expect.hasCountOf
 | 
			
		||||
                    after 3u (fun doc -> doc.Id.Length = 44)
 | 
			
		||||
                    "Three of the IDs should have been 44-character random strings"
 | 
			
		||||
                Expect.hasCountOf after 1u (fun doc -> doc.Id = "abc123") "The provided ID should have been used as-is"
 | 
			
		||||
            finally
 | 
			
		||||
                Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
                Configuration.useIdStringLength 16
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
    testList "save" [
 | 
			
		||||
        testTask "succeeds when a document is inserted" {
 | 
			
		||||
 | 
			
		||||
@ -118,18 +118,9 @@ let parametersTests = testList "Parameters" [
 | 
			
		||||
 | 
			
		||||
(** INTEGRATION TESTS **)
 | 
			
		||||
 | 
			
		||||
/// Documents used for integration tests
 | 
			
		||||
let documents = [
 | 
			
		||||
    { Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None }
 | 
			
		||||
    { Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } }
 | 
			
		||||
    { Id = "three"; Value = ""; NumValue = 4; Sub = None }
 | 
			
		||||
    { Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } }
 | 
			
		||||
    { Id = "five"; Value = "purple"; NumValue = 18; Sub = None }
 | 
			
		||||
]
 | 
			
		||||
 | 
			
		||||
/// Load a table with the test documents
 | 
			
		||||
let loadDocs () = backgroundTask {
 | 
			
		||||
    for doc in documents do do! insert SqliteDb.TableName doc
 | 
			
		||||
    for doc in testDocuments do do! insert SqliteDb.TableName doc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/// Integration tests for the Configuration module of the SQLite library
 | 
			
		||||
@ -283,6 +274,68 @@ let documentTests = testList "Document" [
 | 
			
		||||
                    insert SqliteDb.TableName {emptyDoc with Id = "test" } |> Async.AwaitTask |> Async.RunSynchronously)
 | 
			
		||||
                "An exception should have been raised for duplicate document ID insert"
 | 
			
		||||
        }
 | 
			
		||||
        testTask "succeeds when adding a numeric auto ID" {
 | 
			
		||||
            try
 | 
			
		||||
                Configuration.useAutoIdStrategy Number
 | 
			
		||||
                Configuration.useIdField "Key"
 | 
			
		||||
                use! db = SqliteDb.BuildDb()
 | 
			
		||||
                let! before = Count.all SqliteDb.TableName
 | 
			
		||||
                Expect.equal before 0L "There should be no documents in the table"
 | 
			
		||||
                
 | 
			
		||||
                do! insert SqliteDb.TableName { Key = 0; Text = "one" }
 | 
			
		||||
                do! insert SqliteDb.TableName { Key = 0; Text = "two" }
 | 
			
		||||
                do! insert SqliteDb.TableName { Key = 77; Text = "three" }
 | 
			
		||||
                do! insert SqliteDb.TableName { Key = 0; Text = "four" }
 | 
			
		||||
                
 | 
			
		||||
                let! after = Find.allOrdered<NumIdDocument> SqliteDb.TableName [ Field.Named "Key" ]
 | 
			
		||||
                Expect.hasLength after 4 "There should have been 4 documents returned"
 | 
			
		||||
                Expect.equal (after |> List.map _.Key) [ 1; 2; 77; 78 ] "The IDs were not generated correctly"
 | 
			
		||||
            finally
 | 
			
		||||
                Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
                Configuration.useIdField "Id"
 | 
			
		||||
        }
 | 
			
		||||
        testTask "succeeds when adding a GUID auto ID" {
 | 
			
		||||
            try
 | 
			
		||||
                Configuration.useAutoIdStrategy Guid
 | 
			
		||||
                use! db = SqliteDb.BuildDb()
 | 
			
		||||
                let! before = Count.all SqliteDb.TableName
 | 
			
		||||
                Expect.equal before 0L "There should be no documents in the table"
 | 
			
		||||
                
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Value = "one" }
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Value = "two" }
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Id = "abc123"; Value = "three" }
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Value = "four" }
 | 
			
		||||
                
 | 
			
		||||
                let! after = Find.all<JsonDocument> SqliteDb.TableName
 | 
			
		||||
                Expect.hasLength after 4 "There should have been 4 documents returned"
 | 
			
		||||
                Expect.hasCountOf after 3u (fun doc -> doc.Id.Length = 32) "Three of the IDs should have been GUIDs"
 | 
			
		||||
                Expect.hasCountOf after 1u (fun doc -> doc.Id = "abc123") "The provided ID should have been used as-is"
 | 
			
		||||
            finally
 | 
			
		||||
                Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
        }
 | 
			
		||||
        testTask "succeeds when adding a RandomString auto ID" {
 | 
			
		||||
            try
 | 
			
		||||
                Configuration.useAutoIdStrategy RandomString
 | 
			
		||||
                Configuration.useIdStringLength 44
 | 
			
		||||
                use! db = SqliteDb.BuildDb()
 | 
			
		||||
                let! before = Count.all SqliteDb.TableName
 | 
			
		||||
                Expect.equal before 0L "There should be no documents in the table"
 | 
			
		||||
                
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Value = "one" }
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Value = "two" }
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Id = "abc123"; Value = "three" }
 | 
			
		||||
                do! insert SqliteDb.TableName { emptyDoc with Value = "four" }
 | 
			
		||||
                
 | 
			
		||||
                let! after = Find.all<JsonDocument> SqliteDb.TableName
 | 
			
		||||
                Expect.hasLength after 4 "There should have been 4 documents returned"
 | 
			
		||||
                Expect.hasCountOf
 | 
			
		||||
                    after 3u (fun doc -> doc.Id.Length = 44)
 | 
			
		||||
                    "Three of the IDs should have been 44-character random strings"
 | 
			
		||||
                Expect.hasCountOf after 1u (fun doc -> doc.Id = "abc123") "The provided ID should have been used as-is"
 | 
			
		||||
            finally
 | 
			
		||||
                Configuration.useAutoIdStrategy Disabled
 | 
			
		||||
                Configuration.useIdStringLength 16
 | 
			
		||||
        }
 | 
			
		||||
    ]
 | 
			
		||||
    testList "save" [
 | 
			
		||||
        testTask "succeeds when a document is inserted" {
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,9 @@
 | 
			
		||||
module Types
 | 
			
		||||
 | 
			
		||||
type NumIdDocument =
 | 
			
		||||
    { Key: int
 | 
			
		||||
      Text: string }
 | 
			
		||||
 | 
			
		||||
type SubDocument =
 | 
			
		||||
    { Foo: string
 | 
			
		||||
      Bar: string }
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user