Version 4 #6
|
@ -1,5 +1,7 @@
|
|||
namespace BitBadger.Documents
|
||||
|
||||
open System.Security.Cryptography
|
||||
|
||||
/// The types of logical operations available for JSON fields
|
||||
[<Struct>]
|
||||
type Op =
|
||||
|
@ -148,6 +150,36 @@ type ParameterName() =
|
|||
currentIdx <- currentIdx + 1
|
||||
$"@field{currentIdx}"
|
||||
|
||||
#if NET6_0
|
||||
open System.Text
|
||||
#endif
|
||||
|
||||
/// Automatically-generated document ID options
|
||||
[<Struct>]
|
||||
type AutoId =
|
||||
/// No automatic IDs will be generated
|
||||
| Disabled
|
||||
/// Generate a MAX-plus-1 numeric value for documents
|
||||
| Number
|
||||
/// Generate a GUID for each document (as a lowercase, no-dashes, 32-character string)
|
||||
| Guid
|
||||
/// Generate a random string of hexadecimal characters for each document
|
||||
| RandomString
|
||||
with
|
||||
/// Generate a GUID string
|
||||
static member GenerateGuid() =
|
||||
System.Guid.NewGuid().ToString "N"
|
||||
|
||||
/// Generate a string of random hexadecimal characters
|
||||
static member GenerateRandomString(length: int) =
|
||||
#if NET8_0_OR_GREATER
|
||||
RandomNumberGenerator.GetHexString(length, lowercase = true)
|
||||
#else
|
||||
RandomNumberGenerator.GetBytes((length / 2) + 1)
|
||||
|> Array.fold (fun (str: StringBuilder) byt -> str.Append(byt.ToString "x2")) (StringBuilder length)
|
||||
|> function it -> it.Length <- length; it.ToString()
|
||||
#endif
|
||||
|
||||
|
||||
/// The required document serialization implementation
|
||||
type IDocumentSerializer =
|
||||
|
@ -200,7 +232,7 @@ module Configuration =
|
|||
serializerValue
|
||||
|
||||
/// The serialized name of the ID field for documents
|
||||
let mutable idFieldValue = "Id"
|
||||
let mutable private idFieldValue = "Id"
|
||||
|
||||
/// Specify the name of the ID field for documents
|
||||
[<CompiledName "UseIdField">]
|
||||
|
@ -212,6 +244,32 @@ module Configuration =
|
|||
let idField () =
|
||||
idFieldValue
|
||||
|
||||
/// The automatic ID strategy used by the library
|
||||
let mutable private autoIdValue = Disabled
|
||||
|
||||
/// Specify the automatic ID generation strategy used by the library
|
||||
[<CompiledName "UseAutoIdStrategy">]
|
||||
let useAutoIdStrategy it =
|
||||
autoIdValue <- it
|
||||
|
||||
/// Retrieve the currently configured automatic ID generation strategy
|
||||
[<CompiledName "AutoIdStrategy">]
|
||||
let autoIdStrategy () =
|
||||
autoIdValue
|
||||
|
||||
/// The length of automatically generated random strings
|
||||
let mutable private idStringLengthValue = 16
|
||||
|
||||
/// Specify the length of automatically generated random strings
|
||||
[<CompiledName "UseIdStringLength">]
|
||||
let useIdStringLength length =
|
||||
idStringLengthValue <- length
|
||||
|
||||
/// Retrieve the currently configured length of automatically generated random strings
|
||||
[<CompiledName "IdStringLength">]
|
||||
let idStringLength () =
|
||||
idStringLengthValue
|
||||
|
||||
|
||||
/// Query construction functions
|
||||
[<RequireQualifiedAccess>]
|
||||
|
|
|
@ -70,6 +70,34 @@ public static class CommonCSharpTests
|
|||
{
|
||||
Configuration.UseIdField("Id");
|
||||
}
|
||||
}),
|
||||
TestCase("UseAutoIdStrategy / AutoIdStrategy succeeds", () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Disabled,
|
||||
"The default auto-ID strategy was incorrect");
|
||||
Configuration.UseAutoIdStrategy(AutoId.Guid);
|
||||
Expect.equal(Configuration.AutoIdStrategy(), AutoId.Guid,
|
||||
"The auto-ID strategy was not set correctly");
|
||||
}
|
||||
finally
|
||||
{
|
||||
Configuration.UseAutoIdStrategy(AutoId.Disabled);
|
||||
}
|
||||
}),
|
||||
TestCase("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);
|
||||
}
|
||||
})
|
||||
])),
|
||||
TestList("Op",
|
||||
|
@ -295,6 +323,27 @@ public static class CommonCSharpTests
|
|||
"Counter should have advanced from previous call");
|
||||
})
|
||||
]),
|
||||
TestList("AutoId",
|
||||
[
|
||||
TestCase("GenerateGuid succeeds", () =>
|
||||
{
|
||||
var autoId = AutoId.GenerateGuid();
|
||||
Expect.isNotNull(autoId, "The GUID auto-ID should not have been null");
|
||||
Expect.stringHasLength(autoId, 32, "The GUID auto-ID should have been 32 characters long");
|
||||
Expect.equal(autoId, autoId.ToLowerInvariant(), "The GUID auto-ID should have been lowercase");
|
||||
}),
|
||||
TestCase("GenerateRandomString succeeds", () =>
|
||||
{
|
||||
foreach (var length in (int[])[6, 8, 12, 20, 32, 57, 64])
|
||||
{
|
||||
var autoId = AutoId.GenerateRandomString(length);
|
||||
Expect.isNotNull(autoId, $"Random string ({length}) should not have been null");
|
||||
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("Query",
|
||||
[
|
||||
TestCase("StatementWhere succeeds", () =>
|
||||
|
|
|
@ -6,10 +6,8 @@ open Expecto
|
|||
/// Test table name
|
||||
let tbl = "test_table"
|
||||
|
||||
/// Tests which do not hit the database
|
||||
let all =
|
||||
testList "Common" [
|
||||
testList "Op" [
|
||||
/// Unit tests for the Op DU
|
||||
let opTests = testList "Op" [
|
||||
test "EQ succeeds" {
|
||||
Expect.equal (string EQ) "=" "The equals operator was not correct"
|
||||
}
|
||||
|
@ -37,8 +35,10 @@ let all =
|
|||
test "NEX succeeds" {
|
||||
Expect.equal (string NEX) "IS NULL" """The "not exists" operator was not correct"""
|
||||
}
|
||||
]
|
||||
testList "Field" [
|
||||
]
|
||||
|
||||
/// Unit tests for the Field class
|
||||
let fieldTests = testList "Field" [
|
||||
test "EQ succeeds" {
|
||||
let field = Field.EQ "Test" 14
|
||||
Expect.equal field.Name "Test" "Field name incorrect"
|
||||
|
@ -111,12 +111,10 @@ let all =
|
|||
}
|
||||
testList "NameToPath" [
|
||||
test "succeeds for PostgreSQL and a simple name" {
|
||||
Expect.equal
|
||||
"data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly"
|
||||
Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" PostgreSQL) "Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for SQLite and a simple name" {
|
||||
Expect.equal
|
||||
"data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly"
|
||||
Expect.equal "data->>'Simple'" (Field.NameToPath "Simple" SQLite) "Path not constructed correctly"
|
||||
}
|
||||
test "succeeds for PostgreSQL and a nested name" {
|
||||
Expect.equal
|
||||
|
@ -148,8 +146,7 @@ let all =
|
|||
}
|
||||
test "succeeds for a PostgreSQL single field with a qualifier" {
|
||||
let field = { Field.LT "SomethingElse" 9 with Qualifier = Some "this" }
|
||||
Expect.equal
|
||||
"this.data->>'SomethingElse'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||
Expect.equal "this.data->>'SomethingElse'" (field.Path PostgreSQL) "The PostgreSQL path is incorrect"
|
||||
}
|
||||
test "succeeds for a PostgreSQL nested field with no qualifier" {
|
||||
let field = Field.EQ "My.Nested.Field" "howdy"
|
||||
|
@ -176,16 +173,20 @@ let all =
|
|||
Expect.equal "bird.data->>'Nest'->>'Away'" (field.Path SQLite) "The SQLite path is incorrect"
|
||||
}
|
||||
]
|
||||
]
|
||||
testList "FieldMatch.ToString" [
|
||||
]
|
||||
|
||||
/// Unit tests for the FieldMatch DU
|
||||
let fieldMatchTests = testList "FieldMatch.ToString" [
|
||||
test "succeeds for Any" {
|
||||
Expect.equal (string Any) "OR" "SQL for Any is incorrect"
|
||||
}
|
||||
test "succeeds for All" {
|
||||
Expect.equal (string All) "AND" "SQL for All is incorrect"
|
||||
}
|
||||
]
|
||||
testList "ParameterName.Derive" [
|
||||
]
|
||||
|
||||
/// Unit tests for the ParameterName class
|
||||
let parameterNameTests = testList "ParameterName.Derive" [
|
||||
test "succeeds with existing name" {
|
||||
let name = ParameterName()
|
||||
Expect.equal (name.Derive(Some "@taco")) "@taco" "Name should have been @taco"
|
||||
|
@ -198,8 +199,28 @@ let all =
|
|||
Expect.equal (name.Derive None) "@field2" "Counter should have advanced from previous call"
|
||||
Expect.equal (name.Derive None) "@field3" "Counter should have advanced from previous call"
|
||||
}
|
||||
]
|
||||
testList "Query" [
|
||||
]
|
||||
|
||||
/// Unit tests for the AutoId DU
|
||||
let autoIdTests = testList "AutoId" [
|
||||
test "GenerateGuid succeeds" {
|
||||
let autoId = AutoId.GenerateGuid()
|
||||
Expect.isNotNull autoId "The GUID auto-ID should not have been null"
|
||||
Expect.stringHasLength autoId 32 "The GUID auto-ID should have been 32 characters long"
|
||||
Expect.equal autoId (autoId.ToLowerInvariant ()) "The GUID auto-ID should have been lowercase"
|
||||
}
|
||||
test "GenerateRandomString succeeds" {
|
||||
[ 6; 8; 12; 20; 32; 57; 64 ]
|
||||
|> List.iter (fun length ->
|
||||
let autoId = AutoId.GenerateRandomString length
|
||||
Expect.isNotNull autoId $"Random string ({length}) should not have been null"
|
||||
Expect.stringHasLength autoId length $"Random string should have been {length} characters long"
|
||||
Expect.equal autoId (autoId.ToLowerInvariant ()) $"Random string ({length}) should have been lowercase")
|
||||
}
|
||||
]
|
||||
|
||||
/// Unit tests for the Query module
|
||||
let queryTests = testList "Query" [
|
||||
test "statementWhere succeeds" {
|
||||
Expect.equal (Query.statementWhere "x" "y") "x WHERE y" "Statements not combined correctly"
|
||||
}
|
||||
|
@ -321,5 +342,63 @@ let all =
|
|||
"Order By not constructed correctly for numeric field"
|
||||
}
|
||||
]
|
||||
]
|
||||
]
|
||||
]
|
||||
|
||||
/// 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
|
||||
fieldTests
|
||||
fieldMatchTests
|
||||
parameterNameTests
|
||||
autoIdTests
|
||||
queryTests
|
||||
testSequenced configurationTests
|
||||
]
|
||||
|
|
|
@ -144,32 +144,6 @@ let configurationTests = testList "Configuration" [
|
|||
finally
|
||||
Configuration.useConnectionString "Data Source=:memory:"
|
||||
}
|
||||
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" {
|
||||
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"
|
||||
Configuration.useIdField "Id"
|
||||
}
|
||||
]
|
||||
|
||||
/// Integration tests for the Custom module of the SQLite library
|
||||
|
|
Loading…
Reference in New Issue
Block a user