Compare commits

...

11 Commits

Author SHA1 Message Date
88f11a854f Add compat namespace functions
- Update package release notes
2024-08-17 14:08:46 -04:00
e2948ea6a1 Add ordered* tests for SQLite
- Ignore "n:" prefix for SQLite fields
- Remove byFIeld functions
2024-08-17 12:38:14 -04:00
d993f71788 Reorg SQLite tests 2024-08-16 22:54:27 -04:00
fba8ec7bc5 Finish ordered tests for PostgreSQL
- Rename tests, remove tested obsolete byField funcs
2024-08-16 22:04:23 -04:00
8e33299e22 WIP on ordered tests, test reorg 2024-08-15 22:32:25 -04:00
e07844570a Fix numeric field rendering in order by 2024-08-15 19:16:00 -04:00
bac3bd2ef0 Add *Ordered functions
- WIP on PostgreSQL numeric ordering
2024-08-14 22:13:46 -04:00
edd4d006da First cut of field order by function 2024-08-13 09:03:05 -04:00
96f5e1515d Remove byField functions 2024-08-12 09:07:25 -04:00
d382699a17 Merge branch 'wip-1' of https://git.bitbadger.solutions/bit-badger/BitBadger.Documents into wip-1 2024-08-11 17:06:07 -04:00
77a6aaa583 Ignore IDE settings 2024-08-11 12:10:07 -04:00
22 changed files with 6140 additions and 4233 deletions

1
.gitignore vendored
View File

@ -396,3 +396,4 @@ FodyWeavers.xsd
# JetBrains Rider
*.sln.iml
**/.idea

View File

@ -105,6 +105,10 @@ type Field = {
else $"->>'{name}'"
$"data{path}"
/// Create a field with a given name, but no other properties filled (op will be EQ, value will be "")
static member Named name =
{ Name = name; Op = EQ; Value = ""; ParameterName = None; Qualifier = None }
/// Specify the name of the parameter for this field
member this.WithParameterName name =
{ this with ParameterName = Some name }
@ -292,3 +296,23 @@ module Query =
[<System.Obsolete "Use Find instead">]
let selectFromTable tableName =
find tableName
/// Create an ORDER BY clause for the given fields
[<CompiledName "OrderBy">]
let orderBy fields dialect =
if Seq.isEmpty fields then ""
else
fields
|> Seq.map (fun it ->
if it.Name.Contains ' ' then
let parts = it.Name.Split ' '
{ it with Name = parts[0] }, Some $" {parts[1]}"
else it, None)
|> Seq.map (fun (field, direction) ->
match dialect, field.Name.StartsWith "n:" with
| PostgreSQL, true -> $"({ { field with Name = field.Name[2..] }.Path PostgreSQL})::numeric"
| SQLite, true -> { field with Name = field.Name[2..] }.Path SQLite
| _, _ -> field.Path dialect
|> function path -> path + defaultArg direction "")
|> String.concat ", "
|> function it -> $" ORDER BY {it}"

View File

@ -3,10 +3,10 @@
<TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<DebugType>embedded</DebugType>
<GenerateDocumentationFile>false</GenerateDocumentationFile>
<AssemblyVersion>3.1.0.0</AssemblyVersion>
<FileVersion>3.1.0.0</FileVersion>
<VersionPrefix>3.1.0</VersionPrefix>
<PackageReleaseNotes>Add BT (between) operator; drop .NET 7 support</PackageReleaseNotes>
<AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>4.0.0.0</FileVersion>
<VersionPrefix>4.0.0</VersionPrefix>
<PackageReleaseNotes>Change ByField to ByFields; support dot-access to nested document fields; add Find*Ordered functions/methods; see project site for breaking changes and compatibility</PackageReleaseNotes>
<Authors>danieljsummers</Authors>
<Company>Bit Badger Solutions</Company>
<PackageReadmeFile>README.md</PackageReadmeFile>

View File

@ -8,6 +8,7 @@
<ItemGroup>
<Compile Include="Library.fs" />
<Compile Include="Extensions.fs" />
<Compile Include="Compat.fs" />
<None Include="README.md" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>

270
src/Postgres/Compat.fs Normal file
View File

@ -0,0 +1,270 @@
namespace BitBadger.Documents.Postgres.Compat
open BitBadger.Documents
open BitBadger.Documents.Postgres
[<AutoOpen>]
module Parameters =
/// Create a JSON field parameter
[<CompiledName "AddField">]
[<System.Obsolete "Use addFieldParams (F#) / AddFields (C#) instead ~ will be removed in v4.1">]
let addFieldParam name field parameters =
addFieldParams [ { field with ParameterName = Some name } ] parameters
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FieldName">]
[<System.Obsolete "Use fieldNameParams (F#) / FieldNames (C#) instead ~ will be removed in v4.1">]
let fieldNameParam fieldNames =
fieldNameParams fieldNames
[<RequireQualifiedAccess>]
module Query =
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
[<CompiledName "WhereByField">]
[<System.Obsolete "Use WhereByFields instead ~ will be removed in v4.1">]
let whereByField field paramName =
Query.whereByFields Any [ { field with ParameterName = Some paramName } ]
module WithProps =
[<RequireQualifiedAccess>]
module Count =
/// Count matching documents using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field sqlProps =
WithProps.Count.byFields tableName Any [ field ] sqlProps
[<RequireQualifiedAccess>]
module Exists =
/// Determine if a document exists using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field sqlProps =
WithProps.Exists.byFields tableName Any [ field ] sqlProps
[<RequireQualifiedAccess>]
module Find =
/// Retrieve documents matching a JSON field comparison (->> =)
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
let byField<'TDoc> tableName field sqlProps =
WithProps.Find.byFields<'TDoc> tableName Any [ field ] sqlProps
/// Retrieve documents matching a JSON field comparison (->> =)
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let ByField<'TDoc>(tableName, field, sqlProps) =
WithProps.Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field, sqlProps)
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
let firstByField<'TDoc> tableName field sqlProps =
WithProps.Find.firstByFields<'TDoc> tableName Any [ field ] sqlProps
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) =
WithProps.Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field, sqlProps)
[<RequireQualifiedAccess>]
module Patch =
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field (patch: 'TPatch) sqlProps =
WithProps.Patch.byFields tableName Any [ field ] patch sqlProps
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field fieldNames sqlProps =
WithProps.RemoveFields.byFields tableName Any [ field ] fieldNames sqlProps
[<RequireQualifiedAccess>]
module Delete =
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field sqlProps =
WithProps.Delete.byFields tableName Any [ field ] sqlProps
[<RequireQualifiedAccess>]
module Count =
/// Count matching documents using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field =
Count.byFields tableName Any [ field ]
[<RequireQualifiedAccess>]
module Exists =
/// Determine if a document exists using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field =
Exists.byFields tableName Any [ field ]
[<RequireQualifiedAccess>]
module Find =
/// Retrieve documents matching a JSON field comparison (->> =)
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
let byField<'TDoc> tableName field =
Find.byFields<'TDoc> tableName Any [ field ]
/// Retrieve documents matching a JSON field comparison (->> =)
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let ByField<'TDoc>(tableName, field) =
Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field)
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
let firstByField<'TDoc> tableName field =
Find.firstByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field)
[<RequireQualifiedAccess>]
module Patch =
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field (patch: 'TPatch) =
Patch.byFields tableName Any [ field ] patch
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field fieldNames =
RemoveFields.byFields tableName Any [ field ] fieldNames
[<RequireQualifiedAccess>]
module Delete =
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field =
Delete.byFields tableName Any [ field ]
open Npgsql
/// F# Extensions for the NpgsqlConnection type
[<AutoOpen>]
module Extensions =
type NpgsqlConnection with
/// Count matching documents using a JSON field comparison query (->> =)
[<System.Obsolete "Use countByFields instead ~ will be removed in v4.1">]
member conn.countByField tableName field =
conn.countByFields tableName Any [ field ]
/// Determine if documents exist using a JSON field comparison query (->> =)
[<System.Obsolete "Use existsByFields instead ~ will be removed in v4.1">]
member conn.existsByField tableName field =
conn.existsByFields tableName Any [ field ]
/// Retrieve documents matching a JSON field comparison query (->> =)
[<System.Obsolete "Use findByFields instead ~ will be removed in v4.1">]
member conn.findByField<'TDoc> tableName field =
conn.findByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
[<System.Obsolete "Use findFirstByFields instead ~ will be removed in v4.1">]
member conn.findFirstByField<'TDoc> tableName field =
conn.findFirstByFields<'TDoc> tableName Any [ field ]
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<System.Obsolete "Use patchByFields instead ~ will be removed in v4.1">]
member conn.patchByField tableName field (patch: 'TPatch) =
conn.patchByFields tableName Any [ field ] patch
/// Remove fields from documents via a comparison on a JSON field in the document
[<System.Obsolete "Use removeFieldsByFields instead ~ will be removed in v4.1">]
member conn.removeFieldsByField tableName field fieldNames =
conn.removeFieldsByFields tableName Any [ field ] fieldNames
/// Delete documents by matching a JSON field comparison query (->> =)
[<System.Obsolete "Use deleteByFields instead ~ will be removed in v4.1">]
member conn.deleteByField tableName field =
conn.deleteByFields tableName Any [ field ]
open System.Runtime.CompilerServices
open Npgsql.FSharp
type NpgsqlConnectionCSharpCompatExtensions =
/// Count matching documents using a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use CountByFields instead ~ will be removed in v4.1">]
static member inline CountByField(conn, tableName, field) =
WithProps.Count.byFields tableName Any [ field ] (Sql.existingConnection conn)
/// Determine if documents exist using a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use ExistsByFields instead ~ will be removed in v4.1">]
static member inline ExistsByField(conn, tableName, field) =
WithProps.Exists.byFields tableName Any [ field ] (Sql.existingConnection conn)
/// Retrieve documents matching a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use FindByFields instead ~ will be removed in v4.1">]
static member inline FindByField<'TDoc>(conn, tableName, field) =
WithProps.Find.ByFields<'TDoc>(tableName, Any, [ field ], Sql.existingConnection conn)
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
[<Extension>]
[<System.Obsolete "Use FindFirstByFields instead ~ will be removed in v4.1">]
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
WithProps.Find.FirstByFields<'TDoc>(tableName, Any, [ field ], Sql.existingConnection conn)
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<Extension>]
[<System.Obsolete "Use PatchByFields instead ~ will be removed in v4.1">]
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
WithProps.Patch.byFields tableName Any [ field ] patch (Sql.existingConnection conn)
/// Remove fields from documents via a comparison on a JSON field in the document
[<Extension>]
[<System.Obsolete "Use RemoveFieldsByFields instead ~ will be removed in v4.1">]
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
WithProps.RemoveFields.byFields tableName Any [ field ] fieldNames (Sql.existingConnection conn)
/// Delete documents by matching a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use DeleteByFields instead ~ will be removed in v4.1">]
static member inline DeleteByField(conn, tableName, field) =
WithProps.Delete.byFields tableName Any [ field ] (Sql.existingConnection conn)

View File

@ -1,6 +1,5 @@
namespace BitBadger.Documents.Postgres
open BitBadger.Documents
open Npgsql
open Npgsql.FSharp
@ -54,11 +53,6 @@ module Extensions =
member conn.countByFields tableName howMatched fields =
WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn)
/// Count matching documents using a JSON field comparison query (->> =)
[<System.Obsolete "Use countByFields instead; will be removed in v4">]
member conn.countByField tableName field =
conn.countByFields tableName Any [ field ]
/// Count matching documents using a JSON containment query (@>)
member conn.countByContains tableName criteria =
WithProps.Count.byContains tableName criteria (Sql.existingConnection conn)
@ -75,11 +69,6 @@ module Extensions =
member conn.existsByFields tableName howMatched fields =
WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn)
/// Determine if documents exist using a JSON field comparison query (->> =)
[<System.Obsolete "Use existsByFields instead; will be removed in v4">]
member conn.existsByField tableName field =
conn.existsByFields tableName Any [ field ]
/// Determine if documents exist using a JSON containment query (@>)
member conn.existsByContains tableName criteria =
WithProps.Exists.byContains tableName criteria (Sql.existingConnection conn)
@ -92,6 +81,10 @@ module Extensions =
member conn.findAll<'TDoc> tableName =
WithProps.Find.all<'TDoc> tableName (Sql.existingConnection conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
member conn.findAllOrdered<'TDoc> tableName orderFields =
WithProps.Find.allOrdered<'TDoc> tableName orderFields (Sql.existingConnection conn)
/// Retrieve a document by its ID; returns None if not found
member conn.findById<'TKey, 'TDoc> tableName docId =
WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn)
@ -100,36 +93,56 @@ module Extensions =
member conn.findByFields<'TDoc> tableName howMatched fields =
WithProps.Find.byFields<'TDoc> tableName howMatched fields (Sql.existingConnection conn)
/// Retrieve documents matching a JSON field comparison query (->> =)
[<System.Obsolete "Use findByFields instead; will be removed in v4">]
member conn.findByField<'TDoc> tableName field =
conn.findByFields<'TDoc> tableName Any [ field ]
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the
/// document
member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
WithProps.Find.byFieldsOrdered<'TDoc>
tableName howMatched queryFields orderFields (Sql.existingConnection conn)
/// Retrieve documents matching a JSON containment query (@>)
member conn.findByContains<'TDoc> tableName (criteria: obj) =
WithProps.Find.byContains<'TDoc> tableName criteria (Sql.existingConnection conn)
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
member conn.findByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
WithProps.Find.byContainsOrdered<'TDoc> tableName criteria orderFields (Sql.existingConnection conn)
/// Retrieve documents matching a JSON Path match query (@?)
member conn.findByJsonPath<'TDoc> tableName jsonPath =
WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
member conn.findByJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
WithProps.Find.byJsonPathOrdered<'TDoc> tableName jsonPath orderFields (Sql.existingConnection conn)
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
member conn.findFirstByFields<'TDoc> tableName howMatched fields =
WithProps.Find.firstByFields<'TDoc> tableName howMatched fields (Sql.existingConnection conn)
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
[<System.Obsolete "Use findFirstByFields instead; will be removed in v4">]
member conn.findFirstByField<'TDoc> tableName field =
conn.findFirstByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in
/// the document; returns None if not found
member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
WithProps.Find.firstByFieldsOrdered<'TDoc>
tableName howMatched queryFields orderFields (Sql.existingConnection conn)
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
member conn.findFirstByContains<'TDoc> tableName (criteria: obj) =
WithProps.Find.firstByContains<'TDoc> tableName criteria (Sql.existingConnection conn)
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the
/// document; returns None if not found
member conn.findFirstByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
WithProps.Find.firstByContainsOrdered<'TDoc> tableName criteria orderFields (Sql.existingConnection conn)
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
member conn.findFirstByJsonPath<'TDoc> tableName jsonPath =
WithProps.Find.firstByJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn)
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the
/// document; returns None if not found
member conn.findFirstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
WithProps.Find.firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields (Sql.existingConnection conn)
/// Update an entire document by its ID
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
WithProps.Update.byId tableName docId document (Sql.existingConnection conn)
@ -146,11 +159,6 @@ module Extensions =
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn)
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<System.Obsolete "Use patchByFields instead; will be removed in v4">]
member conn.patchByField tableName field (patch: 'TPatch) =
conn.patchByFields tableName Any [ field ] patch
/// Patch documents using a JSON containment query in the WHERE clause (@>)
member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
WithProps.Patch.byContains tableName criteria patch (Sql.existingConnection conn)
@ -167,11 +175,6 @@ module Extensions =
member conn.removeFieldsByFields tableName howMatched fields fieldNames =
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn)
/// Remove fields from documents via a comparison on a JSON field in the document
[<System.Obsolete "Use removeFieldsByFields instead; will be removed in v4">]
member conn.removeFieldsByField tableName field fieldNames =
conn.removeFieldsByFields tableName Any [ field ] fieldNames
/// Remove fields from documents via a JSON containment query (@>)
member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames =
WithProps.RemoveFields.byContains tableName criteria fieldNames (Sql.existingConnection conn)
@ -187,11 +190,6 @@ module Extensions =
member conn.deleteByFields tableName howMatched fields =
WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn)
/// Delete documents by matching a JSON field comparison query (->> =)
[<System.Obsolete "Use deleteByFields instead; will be removed in v4">]
member conn.deleteByField tableName field =
conn.deleteByFields tableName Any [ field ]
/// Delete documents by matching a JSON containment query (@>)
member conn.deleteByContains tableName (criteria: 'TContains) =
WithProps.Delete.byContains tableName criteria (Sql.existingConnection conn)
@ -263,12 +261,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline CountByFields(conn, tableName, howMatched, fields) =
WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn)
/// Count matching documents using a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use CountByFields instead; will be removed in v4">]
static member inline CountByField(conn, tableName, field) =
conn.CountByFields(tableName, Any, [ field ])
/// Count matching documents using a JSON containment query (@>)
[<Extension>]
static member inline CountByContains(conn, tableName, criteria: 'TCriteria) =
@ -289,12 +281,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline ExistsByFields(conn, tableName, howMatched, fields) =
WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn)
/// Determine if documents exist using a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use ExistsByFields instead; will be removed in v4">]
static member inline ExistsByField(conn, tableName, field) =
conn.ExistsByFields(tableName, Any, [ field ])
/// Determine if documents exist using a JSON containment query (@>)
[<Extension>]
static member inline ExistsByContains(conn, tableName, criteria: 'TCriteria) =
@ -310,6 +296,11 @@ type NpgsqlConnectionCSharpExtensions =
static member inline FindAll<'TDoc>(conn, tableName) =
WithProps.Find.All<'TDoc>(tableName, Sql.existingConnection conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<Extension>]
static member inline FindAllOrdered<'TDoc>(conn, tableName, orderFields) =
WithProps.Find.AllOrdered<'TDoc>(tableName, orderFields, Sql.existingConnection conn)
/// Retrieve a document by its ID; returns None if not found
[<Extension>]
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
@ -320,43 +311,68 @@ type NpgsqlConnectionCSharpExtensions =
static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
WithProps.Find.ByFields<'TDoc>(tableName, howMatched, fields, Sql.existingConnection conn)
/// Retrieve documents matching a JSON field comparison query (->> =)
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the document
[<Extension>]
[<System.Obsolete "Use FindByFields instead; will be removed in v4">]
static member inline FindByField<'TDoc>(conn, tableName, field) =
conn.FindByFields<'TDoc>(tableName, Any, [ field ])
static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) =
WithProps.Find.ByFieldsOrdered<'TDoc>(
tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn)
/// Retrieve documents matching a JSON containment query (@>)
[<Extension>]
static member inline FindByContains<'TDoc>(conn, tableName, criteria: obj) =
WithProps.Find.ByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn)
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
[<Extension>]
static member inline FindByContainsOrdered<'TDoc>(conn, tableName, criteria: obj, orderFields) =
WithProps.Find.ByContainsOrdered<'TDoc>(tableName, criteria, orderFields, Sql.existingConnection conn)
/// Retrieve documents matching a JSON Path match query (@?)
[<Extension>]
static member inline FindByJsonPath<'TDoc>(conn, tableName, jsonPath) =
WithProps.Find.ByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn)
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
[<Extension>]
static member inline FindByJsonPathOrdered<'TDoc>(conn, tableName, jsonPath, orderFields) =
WithProps.Find.ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, Sql.existingConnection conn)
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
[<Extension>]
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
WithProps.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, Sql.existingConnection conn)
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in the
/// document; returns null if not found
[<Extension>]
[<System.Obsolete "Use FindFirstByFields instead; will be removed in v4">]
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ])
static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
conn, tableName, howMatched, queryFields, orderFields) =
WithProps.Find.FirstByFieldsOrdered<'TDoc>(
tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn)
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<Extension>]
static member inline FindFirstByContains<'TDoc when 'TDoc: null>(conn, tableName, criteria: obj) =
WithProps.Find.FirstByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn)
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the document;
/// returns None if not found
[<Extension>]
static member inline FindFirstByContainsOrdered<'TDoc when 'TDoc: null>(
conn, tableName, criteria: obj, orderFields) =
WithProps.Find.FirstByContainsOrdered<'TDoc>(tableName, criteria, orderFields, Sql.existingConnection conn)
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
[<Extension>]
static member inline FindFirstByJsonPath<'TDoc when 'TDoc: null>(conn, tableName, jsonPath) =
WithProps.Find.FirstByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn)
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the document;
/// returns None if not found
[<Extension>]
static member inline FindFirstByJsonPathOrdered<'TDoc when 'TDoc: null>(conn, tableName, jsonPath, orderFields) =
WithProps.Find.FirstByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, Sql.existingConnection conn)
/// Update an entire document by its ID
[<Extension>]
static member inline UpdateById(conn, tableName, docId: 'TKey, document: 'TDoc) =
@ -377,12 +393,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline PatchByFields(conn, tableName, howMatched, fields, patch: 'TPatch) =
WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn)
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<Extension>]
[<System.Obsolete "Use PatchByFields instead; will be removed in v4">]
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
conn.PatchByFields(tableName, Any, [ field ], patch)
/// Patch documents using a JSON containment query in the WHERE clause (@>)
[<Extension>]
static member inline PatchByContains(conn, tableName, criteria: 'TCriteria, patch: 'TPatch) =
@ -403,12 +413,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn)
/// Remove fields from documents via a comparison on a JSON field in the document
[<Extension>]
[<System.Obsolete "Use RemoveFieldsByFields instead; will be removed in v4">]
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
conn.RemoveFieldsByFields(tableName, Any, [ field ], fieldNames)
/// Remove fields from documents via a JSON containment query (@>)
[<Extension>]
static member inline RemoveFieldsByContains(conn, tableName, criteria: 'TContains, fieldNames) =
@ -429,12 +433,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline DeleteByFields(conn, tableName, howMatched, fields) =
WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn)
/// Delete documents by matching a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use DeleteByFields instead; will be removed in v4">]
static member inline DeleteByField(conn, tableName, field) =
conn.DeleteByFields(tableName, Any, [ field ])
/// Delete documents by matching a JSON containment query (@>)
[<Extension>]
static member inline DeleteByContains(conn, tableName, criteria: 'TContains) =

View File

@ -104,24 +104,12 @@ module Parameters =
|> Seq.toList
|> Seq.ofList
/// Create a JSON field parameter
[<CompiledName "AddField">]
[<System.Obsolete "Use addFieldParams (F#) / AddFields (C#) instead; will be removed in v4">]
let addFieldParam name field parameters =
addFieldParams [ { field with ParameterName = Some name } ] parameters
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FieldNames">]
let fieldNameParams (fieldNames: string seq) =
if Seq.length fieldNames = 1 then "@name", Sql.string (Seq.head fieldNames)
else "@name", Sql.stringArray (Array.ofSeq fieldNames)
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FieldName">]
[<System.Obsolete "Use fieldNameParams (F#) / FieldNames (C#) instead; will be removed in v4">]
let fieldNameParam fieldNames =
fieldNameParams fieldNames
/// An empty parameter sequence
[<CompiledName "None">]
let noParams =
@ -407,6 +395,16 @@ module WithProps =
let All<'TDoc>(tableName, sqlProps) =
Custom.List<'TDoc>(Query.find tableName, [], fromData<'TDoc>, sqlProps)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields sqlProps =
Custom.list<'TDoc> (Query.find tableName + Query.orderBy orderFields PostgreSQL) [] fromData<'TDoc> sqlProps
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc>(tableName, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.find tableName + Query.orderBy orderFields PostgreSQL, [], fromData<'TDoc>, sqlProps)
/// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) sqlProps =
@ -426,12 +424,6 @@ module WithProps =
fromData<'TDoc>
sqlProps
/// Retrieve documents matching a JSON field comparison (->> =)
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField<'TDoc> tableName field sqlProps =
byFields<'TDoc> tableName Any [ field ] sqlProps
/// Retrieve documents matching JSON field comparisons (->> =)
let ByFields<'TDoc>(tableName, howMatched, fields, sqlProps) =
Custom.List<'TDoc>(
@ -440,10 +432,22 @@ module WithProps =
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON field comparison (->> =)
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let ByField<'TDoc>(tableName, field, sqlProps) =
ByFields<'TDoc>(tableName, Any, Seq.singleton field, sqlProps)
/// Retrieve documents matching JSON field comparisons (->> =) ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields sqlProps =
Custom.list<'TDoc>
(Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields PostgreSQL)
(addFieldParams queryFields [])
fromData<'TDoc>
sqlProps
/// Retrieve documents matching JSON field comparisons (->> =) ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields PostgreSQL,
addFieldParams queryFields [],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON containment query (@>)
[<CompiledName "FSharpByContains">]
@ -459,6 +463,23 @@ module WithProps =
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
[<CompiledName "FSharpByContainsOrdered">]
let byContainsOrdered<'TDoc> tableName (criteria: obj) orderFields sqlProps =
Custom.list<'TDoc>
(Query.byContains (Query.find tableName) + Query.orderBy orderFields PostgreSQL)
[ jsonParam "@criteria" criteria ]
fromData<'TDoc>
sqlProps
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
let ByContainsOrdered<'TDoc>(tableName, criteria: obj, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.byContains (Query.find tableName) + Query.orderBy orderFields PostgreSQL,
[ jsonParam "@criteria" criteria ],
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON Path match query (@?)
[<CompiledName "FSharpByJsonPath">]
let byJsonPath<'TDoc> tableName jsonPath sqlProps =
@ -473,6 +494,23 @@ module WithProps =
fromData<'TDoc>,
sqlProps)
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
[<CompiledName "FSharpByJsonPathOrdered">]
let byJsonPathOrdered<'TDoc> tableName jsonPath orderFields sqlProps =
Custom.list<'TDoc>
(Query.byPathMatch (Query.find tableName) + Query.orderBy orderFields PostgreSQL)
[ "@path", Sql.string jsonPath ]
fromData<'TDoc>
sqlProps
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
let ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, sqlProps) =
Custom.List<'TDoc>(
Query.byPathMatch (Query.find tableName) + Query.orderBy orderFields PostgreSQL,
[ "@path", Sql.string jsonPath ],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching JSON field comparisons (->> =); returns None if not found
[<CompiledName "FSharpFirstByFields">]
let firstByFields<'TDoc> tableName howMatched fields sqlProps =
@ -482,12 +520,6 @@ module WithProps =
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead; will be removed in v4">]
let firstByField<'TDoc> tableName field sqlProps =
firstByFields<'TDoc> tableName Any [ field ] sqlProps
/// Retrieve the first document matching JSON field comparisons (->> =); returns null if not found
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, sqlProps) =
Custom.Single<'TDoc>(
@ -496,10 +528,24 @@ module WithProps =
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, sqlProps) =
FirstByFields<'TDoc>(tableName, Any, Seq.singleton field, sqlProps)
/// Retrieve the first document matching JSON field comparisons (->> =) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields sqlProps =
Custom.single<'TDoc>
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields PostgreSQL} LIMIT 1"
(addFieldParams queryFields [])
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching JSON field comparisons (->> =) ordered by the given fields in the
/// document; returns null if not found
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields PostgreSQL} LIMIT 1",
addFieldParams queryFields [],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<CompiledName "FSharpFirstByContains">]
@ -518,6 +564,25 @@ module WithProps =
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByContainsOrdered">]
let firstByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields sqlProps =
Custom.single<'TDoc>
$"{Query.byContains (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1"
[ jsonParam "@criteria" criteria ]
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the
/// document; returns null if not found
let FirstByContainsOrdered<'TDoc when 'TDoc: null>(tableName, criteria: obj, orderFields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byContains (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1",
[ jsonParam "@criteria" criteria ],
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
[<CompiledName "FSharpFirstByJsonPath">]
let firstByJsonPath<'TDoc> tableName jsonPath sqlProps =
@ -535,6 +600,25 @@ module WithProps =
fromData<'TDoc>,
sqlProps)
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByJsonPathOrdered">]
let firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields sqlProps =
Custom.single<'TDoc>
$"{Query.byPathMatch (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1"
[ "@path", Sql.string jsonPath ]
fromData<'TDoc>
sqlProps
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the
/// document; returns null if not found
let FirstByJsonPathOrdered<'TDoc when 'TDoc: null>(tableName, jsonPath, orderFields, sqlProps) =
Custom.Single<'TDoc>(
$"{Query.byPathMatch (Query.find tableName)}{Query.orderBy orderFields PostgreSQL} LIMIT 1",
[ "@path", Sql.string jsonPath ],
fromData<'TDoc>,
sqlProps)
/// Commands to update documents
[<RequireQualifiedAccess>]
module Update =
@ -572,12 +656,6 @@ module WithProps =
(addFieldParams fields [ jsonParam "@data" patch ])
sqlProps
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field (patch: 'TPatch) sqlProps =
byFields tableName Any [ field ] patch sqlProps
/// Patch documents using a JSON containment query in the WHERE clause (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) (patch: 'TPatch) sqlProps =
@ -612,12 +690,6 @@ module WithProps =
(addFieldParams fields [ fieldNameParams fieldNames ])
sqlProps
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField tableName field fieldNames sqlProps =
byFields tableName Any [ field ] fieldNames sqlProps
/// Remove fields from documents via a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) fieldNames sqlProps =
@ -649,12 +721,6 @@ module WithProps =
Custom.nonQuery
(Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) sqlProps
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field sqlProps =
byFields tableName Any [ field ] sqlProps
/// Delete documents by matching a JSON contains query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TCriteria) sqlProps =
@ -752,12 +818,6 @@ module Count =
let byFields tableName howMatched fields =
WithProps.Count.byFields tableName howMatched fields (fromDataSource ())
/// Count matching documents using a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
byFields tableName Any [ field ]
/// Count matching documents using a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName criteria =
@ -783,12 +843,6 @@ module Exists =
let byFields tableName howMatched fields =
WithProps.Exists.byFields tableName howMatched fields (fromDataSource ())
/// Determine if documents exist using a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
byFields tableName Any [ field ]
/// Determine if documents exist using a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName criteria =
@ -813,6 +867,15 @@ module Find =
let All<'TDoc> tableName =
WithProps.Find.All<'TDoc>(tableName, fromDataSource ())
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields =
WithProps.Find.allOrdered<'TDoc> tableName orderFields (fromDataSource ())
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc> tableName orderFields =
WithProps.Find.AllOrdered<'TDoc>(tableName, orderFields, fromDataSource ())
/// Retrieve a document by its ID; returns None if not found
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName docId =
@ -827,20 +890,18 @@ module Find =
let byFields<'TDoc> tableName howMatched fields =
WithProps.Find.byFields<'TDoc> tableName howMatched fields (fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =)
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField<'TDoc> tableName field =
byFields<'TDoc> tableName Any [ field ]
/// Retrieve documents matching a JSON field comparison query (->> =)
let ByFields<'TDoc>(tableName, howMatched, fields) =
WithProps.Find.ByFields<'TDoc>(tableName, howMatched, fields, fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =)
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let ByField<'TDoc>(tableName, field) =
ByFields<'TDoc>(tableName, Any, Seq.singleton field)
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryField orderFields =
WithProps.Find.byFieldsOrdered<'TDoc> tableName howMatched queryField orderFields (fromDataSource ())
/// Retrieve documents matching a JSON field comparison query (->> =) ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
WithProps.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>)
[<CompiledName "FSharpByContains">]
@ -851,6 +912,15 @@ module Find =
let ByContains<'TDoc>(tableName, criteria: obj) =
WithProps.Find.ByContains<'TDoc>(tableName, criteria, fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
[<CompiledName "FSharpByContainsOrdered">]
let byContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
WithProps.Find.byContainsOrdered<'TDoc> tableName criteria orderFields (fromDataSource ())
/// Retrieve documents matching a JSON containment query (@>) ordered by the given fields in the document
let ByContainsOrdered<'TDoc>(tableName, criteria: obj, orderFields) =
WithProps.Find.ByContainsOrdered<'TDoc>(tableName, criteria, orderFields, fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?)
[<CompiledName "FSharpByJsonPath">]
let byJsonPath<'TDoc> tableName jsonPath =
@ -860,25 +930,34 @@ module Find =
let ByJsonPath<'TDoc>(tableName, jsonPath) =
WithProps.Find.ByJsonPath<'TDoc>(tableName, jsonPath, fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
[<CompiledName "FSharpByJsonPathOrdered">]
let byJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
WithProps.Find.byJsonPathOrdered<'TDoc> tableName jsonPath orderFields (fromDataSource ())
/// Retrieve documents matching a JSON Path match query (@?) ordered by the given fields in the document
let ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields) =
WithProps.Find.ByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
[<CompiledName "FSharpFirstByFields">]
let firstByFields<'TDoc> tableName howMatched fields =
WithProps.Find.firstByFields<'TDoc> tableName howMatched fields (fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead; will be removed in v4">]
let firstByField<'TDoc> tableName field =
firstByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
WithProps.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
FirstByFields<'TDoc>(tableName, Any, Seq.singleton field)
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in the
/// document; returns None if not found
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
WithProps.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields (fromDataSource ())
/// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in the
/// document; returns null if not found
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields) =
WithProps.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<CompiledName "FSharpFirstByContains">]
@ -889,6 +968,17 @@ module Find =
let FirstByContains<'TDoc when 'TDoc: null>(tableName, criteria: obj) =
WithProps.Find.FirstByContains<'TDoc>(tableName, criteria, fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the document;
/// returns None if not found
[<CompiledName "FSharpFirstByContainsOrdered">]
let firstByContainsOrdered<'TDoc> tableName (criteria: obj) orderFields =
WithProps.Find.firstByContainsOrdered<'TDoc> tableName criteria orderFields (fromDataSource ())
/// Retrieve the first document matching a JSON containment query (@>) ordered by the given fields in the document;
/// returns null if not found
let FirstByContainsOrdered<'TDoc when 'TDoc: null>(tableName, criteria: obj, orderFields) =
WithProps.Find.FirstByContainsOrdered<'TDoc>(tableName, criteria, orderFields, fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
[<CompiledName "FSharpFirstByJsonPath">]
let firstByJsonPath<'TDoc> tableName jsonPath =
@ -898,6 +988,17 @@ module Find =
let FirstByJsonPath<'TDoc when 'TDoc: null>(tableName, jsonPath) =
WithProps.Find.FirstByJsonPath<'TDoc>(tableName, jsonPath, fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the document;
/// returns None if not found
[<CompiledName "FSharpFirstByJsonPathOrdered">]
let firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields =
WithProps.Find.firstByJsonPathOrdered<'TDoc> tableName jsonPath orderFields (fromDataSource ())
/// Retrieve the first document matching a JSON Path match query (@?) ordered by the given fields in the document;
/// returns null if not found
let FirstByJsonPathOrdered<'TDoc when 'TDoc: null>(tableName, jsonPath, orderFields) =
WithProps.Find.FirstByJsonPathOrdered<'TDoc>(tableName, jsonPath, orderFields, fromDataSource ())
/// Commands to update documents
[<RequireQualifiedAccess>]
@ -932,12 +1033,6 @@ module Patch =
let byFields tableName howMatched fields (patch: 'TPatch) =
WithProps.Patch.byFields tableName howMatched fields patch (fromDataSource ())
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field (patch: 'TPatch) =
byFields tableName Any [ field ] patch
/// Patch documents using a JSON containment query in the WHERE clause (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
@ -963,12 +1058,6 @@ module RemoveFields =
let byFields tableName howMatched fields fieldNames =
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (fromDataSource ())
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField tableName field fieldNames =
byFields tableName Any [ field ] fieldNames
/// Remove fields from documents via a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) fieldNames =
@ -994,12 +1083,6 @@ module Delete =
let byFields tableName howMatched fields =
WithProps.Delete.byFields tableName howMatched fields (fromDataSource ())
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
byFields tableName Any [ field ]
/// Delete documents by matching a JSON containment query (@>)
[<CompiledName "ByContains">]
let byContains tableName (criteria: 'TContains) =

View File

@ -8,6 +8,7 @@
<ItemGroup>
<Compile Include="Library.fs" />
<Compile Include="Extensions.fs" />
<Compile Include="Compat.fs" />
<None Include="README.md" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup>

269
src/Sqlite/Compat.fs Normal file
View File

@ -0,0 +1,269 @@
namespace BitBadger.Documents.Sqlite.Compat
open BitBadger.Documents
open BitBadger.Documents.Sqlite
[<AutoOpen>]
module Parameters =
/// Create a JSON field parameter
[<CompiledName "AddField">]
[<System.Obsolete "Use addFieldParams (F#) / AddFields (C#) instead ~ will be removed in v4.1">]
let addFieldParam name field parameters =
addFieldParams [ { field with ParameterName = Some name } ] parameters
/// Append JSON field name parameters for the given field names to the given parameters
[<CompiledName "FieldName">]
[<System.Obsolete "Use fieldNameParams (F#) / FieldNames (C#) instead ~ will be removed in v4.1">]
let fieldNameParam fieldNames =
fieldNameParams fieldNames
[<RequireQualifiedAccess>]
module Query =
/// Create a WHERE clause fragment to implement a comparison on a field in a JSON document
[<CompiledName "WhereByField">]
[<System.Obsolete "Use WhereByFields instead ~ will be removed in v4.1">]
let whereByField field paramName =
Query.whereByFields Any [ { field with ParameterName = Some paramName } ]
module WithConn =
[<RequireQualifiedAccess>]
module Count =
/// Count matching documents using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field conn =
WithConn.Count.byFields tableName Any [ field ] conn
[<RequireQualifiedAccess>]
module Exists =
/// Determine if a document exists using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field conn =
WithConn.Exists.byFields tableName Any [ field ] conn
[<RequireQualifiedAccess>]
module Find =
/// Retrieve documents matching a JSON field comparison (->> =)
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
let byField<'TDoc> tableName field conn =
WithConn.Find.byFields<'TDoc> tableName Any [ field ] conn
/// Retrieve documents matching a JSON field comparison (->> =)
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let ByField<'TDoc>(tableName, field, conn) =
WithConn.Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field, conn)
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
let firstByField<'TDoc> tableName field conn =
WithConn.Find.firstByFields<'TDoc> tableName Any [ field ] conn
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
WithConn.Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field, conn)
[<RequireQualifiedAccess>]
module Patch =
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field (patch: 'TPatch) conn =
WithConn.Patch.byFields tableName Any [ field ] patch conn
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field fieldNames conn =
WithConn.RemoveFields.byFields tableName Any [ field ] fieldNames conn
[<RequireQualifiedAccess>]
module Delete =
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field conn =
WithConn.Delete.byFields tableName Any [ field ] conn
[<RequireQualifiedAccess>]
module Count =
/// Count matching documents using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field =
Count.byFields tableName Any [ field ]
[<RequireQualifiedAccess>]
module Exists =
/// Determine if a document exists using a JSON field comparison (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field =
Exists.byFields tableName Any [ field ]
[<RequireQualifiedAccess>]
module Find =
/// Retrieve documents matching a JSON field comparison (->> =)
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead ~ will be removed in v4.1">]
let byField<'TDoc> tableName field =
Find.byFields<'TDoc> tableName Any [ field ]
/// Retrieve documents matching a JSON field comparison (->> =)
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let ByField<'TDoc>(tableName, field) =
Find.ByFields<'TDoc>(tableName, Any, Seq.singleton field)
/// Retrieve the first document matching a JSON field comparison (->> =); returns None if not found
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead ~ will be removed in v4.1">]
let firstByField<'TDoc> tableName field =
Find.firstByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON field comparison (->> =); returns null if not found
[<System.Obsolete "Use FirstByFields instead ~ will be removed in v4.1">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
Find.FirstByFields<'TDoc>(tableName, Any, Seq.singleton field)
[<RequireQualifiedAccess>]
module Patch =
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field (patch: 'TPatch) =
Patch.byFields tableName Any [ field ] patch
[<RequireQualifiedAccess>]
module RemoveFields =
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field fieldNames =
RemoveFields.byFields tableName Any [ field ] fieldNames
[<RequireQualifiedAccess>]
module Delete =
/// Delete documents by matching a JSON field comparison query (->> =)
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead ~ will be removed in v4.1">]
let byField tableName field =
Delete.byFields tableName Any [ field ]
open Microsoft.Data.Sqlite
/// F# Extensions for the NpgsqlConnection type
[<AutoOpen>]
module Extensions =
type SqliteConnection with
/// Count matching documents using a JSON field comparison query (->> =)
[<System.Obsolete "Use countByFields instead ~ will be removed in v4.1">]
member conn.countByField tableName field =
conn.countByFields tableName Any [ field ]
/// Determine if documents exist using a JSON field comparison query (->> =)
[<System.Obsolete "Use existsByFields instead ~ will be removed in v4.1">]
member conn.existsByField tableName field =
conn.existsByFields tableName Any [ field ]
/// Retrieve documents matching a JSON field comparison query (->> =)
[<System.Obsolete "Use findByFields instead ~ will be removed in v4.1">]
member conn.findByField<'TDoc> tableName field =
conn.findByFields<'TDoc> tableName Any [ field ]
/// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
[<System.Obsolete "Use findFirstByFields instead ~ will be removed in v4.1">]
member conn.findFirstByField<'TDoc> tableName field =
conn.findFirstByFields<'TDoc> tableName Any [ field ]
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<System.Obsolete "Use patchByFields instead ~ will be removed in v4.1">]
member conn.patchByField tableName field (patch: 'TPatch) =
conn.patchByFields tableName Any [ field ] patch
/// Remove fields from documents via a comparison on a JSON field in the document
[<System.Obsolete "Use removeFieldsByFields instead ~ will be removed in v4.1">]
member conn.removeFieldsByField tableName field fieldNames =
conn.removeFieldsByFields tableName Any [ field ] fieldNames
/// Delete documents by matching a JSON field comparison query (->> =)
[<System.Obsolete "Use deleteByFields instead ~ will be removed in v4.1">]
member conn.deleteByField tableName field =
conn.deleteByFields tableName Any [ field ]
open System.Runtime.CompilerServices
type SqliteConnectionCSharpCompatExtensions =
/// Count matching documents using a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use CountByFields instead ~ will be removed in v4.1">]
static member inline CountByField(conn, tableName, field) =
WithConn.Count.byFields tableName Any [ field ] conn
/// Determine if documents exist using a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use ExistsByFields instead ~ will be removed in v4.1">]
static member inline ExistsByField(conn, tableName, field) =
WithConn.Exists.byFields tableName Any [ field ] conn
/// Retrieve documents matching a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use FindByFields instead ~ will be removed in v4.1">]
static member inline FindByField<'TDoc>(conn, tableName, field) =
WithConn.Find.ByFields<'TDoc>(tableName, Any, [ field ], conn)
/// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
[<Extension>]
[<System.Obsolete "Use FindFirstByFields instead ~ will be removed in v4.1">]
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
WithConn.Find.FirstByFields<'TDoc>(tableName, Any, [ field ], conn)
/// Patch documents using a JSON field comparison query in the WHERE clause (->> =)
[<Extension>]
[<System.Obsolete "Use PatchByFields instead ~ will be removed in v4.1">]
static member inline PatchByField(conn, tableName, field, patch: 'TPatch) =
WithConn.Patch.byFields tableName Any [ field ] patch conn
/// Remove fields from documents via a comparison on a JSON field in the document
[<Extension>]
[<System.Obsolete "Use RemoveFieldsByFields instead ~ will be removed in v4.1">]
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
WithConn.RemoveFields.byFields tableName Any [ field ] fieldNames conn
/// Delete documents by matching a JSON field comparison query (->> =)
[<Extension>]
[<System.Obsolete "Use DeleteByFields instead ~ will be removed in v4.1">]
static member inline DeleteByField(conn, tableName, field) =
WithConn.Delete.byFields tableName Any [ field ] conn

View File

@ -49,11 +49,6 @@ module Extensions =
member conn.countByFields tableName howMatched fields =
WithConn.Count.byFields tableName howMatched fields conn
/// Count matching documents using a comparison on a JSON field
[<System.Obsolete "Use countByFields instead; will be removed in v4">]
member conn.countByField tableName field =
conn.countByFields tableName Any [ field ]
/// Determine if a document exists for the given ID
member conn.existsById tableName (docId: 'TKey) =
WithConn.Exists.byId tableName docId conn
@ -62,15 +57,14 @@ module Extensions =
member conn.existsByFields tableName howMatched fields =
WithConn.Exists.byFields tableName howMatched fields conn
/// Determine if a document exists using a comparison on a JSON field
[<System.Obsolete "Use existsByFields instead; will be removed in v4">]
member conn.existsByField tableName field =
conn.existsByFields tableName Any [ field ]
/// Retrieve all documents in the given table
member conn.findAll<'TDoc> tableName =
WithConn.Find.all<'TDoc> tableName conn
/// Retrieve all documents in the given table ordered by the given fields in the document
member conn.findAllOrdered<'TDoc> tableName orderFields =
WithConn.Find.allOrdered<'TDoc> tableName orderFields conn
/// Retrieve a document by its ID
member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) =
WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
@ -79,19 +73,18 @@ module Extensions =
member conn.findByFields<'TDoc> tableName howMatched fields =
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on a JSON field
[<System.Obsolete "Use findByFields instead; will be removed in v4">]
member conn.findByField<'TDoc> tableName field =
conn.findByFields<'TDoc> tableName Any [ field ]
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Retrieve documents via a comparison on JSON fields, returning only the first result
member conn.findFirstByFields<'TDoc> tableName howMatched fields =
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<System.Obsolete "Use findFirstByFields instead; will be removed in v4">]
member conn.findFirstByField<'TDoc> tableName field =
conn.findFirstByFields<'TDoc> tableName Any [ field ]
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
/// only the first result
member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Update an entire document by its ID
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
@ -109,11 +102,6 @@ module Extensions =
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
WithConn.Patch.byFields tableName howMatched fields patch conn
/// Patch documents using a comparison on a JSON field
[<System.Obsolete "Use patchByFields instead; will be removed in v4">]
member conn.patchByField tableName field (patch: 'TPatch) =
conn.patchByFields tableName Any [ field ] patch
/// Remove fields from a document by the document's ID
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
WithConn.RemoveFields.byId tableName docId fieldNames conn
@ -122,11 +110,6 @@ module Extensions =
member conn.removeFieldsByFields tableName howMatched fields fieldNames =
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
/// Remove a field from a document via a comparison on a JSON field in the document
[<System.Obsolete "Use removeFieldsByFields instead; will be removed in v4">]
member conn.removeFieldsByField tableName field fieldNames =
conn.removeFieldsByFields tableName Any [ field ] fieldNames
/// Delete a document by its ID
member conn.deleteById tableName (docId: 'TKey) =
WithConn.Delete.byId tableName docId conn
@ -135,11 +118,6 @@ module Extensions =
member conn.deleteByFields tableName howMatched fields =
WithConn.Delete.byFields tableName howMatched fields conn
/// Delete documents by matching a comparison on a JSON field
[<System.Obsolete "Use deleteByFields instead; will be removed in v4">]
member conn.deleteByField tableName field =
conn.deleteByFields tableName Any [ field ]
open System.Runtime.CompilerServices
@ -198,12 +176,6 @@ type SqliteConnectionCSharpExtensions =
static member inline CountByFields(conn, tableName, howMatched, fields) =
WithConn.Count.byFields tableName howMatched fields conn
/// Count matching documents using a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use CountByFields instead; will be removed in v4">]
static member inline CountByField(conn, tableName, field) =
conn.CountByFields(tableName, Any, [ field ])
/// Determine if a document exists for the given ID
[<Extension>]
static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) =
@ -214,17 +186,16 @@ type SqliteConnectionCSharpExtensions =
static member inline ExistsByFields(conn, tableName, howMatched, fields) =
WithConn.Exists.byFields tableName howMatched fields conn
/// Determine if a document exists using a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use ExistsByFields instead; will be removed in v4">]
static member inline ExistsByField(conn, tableName, field) =
conn.ExistsByFields(tableName, Any, [ field ])
/// Retrieve all documents in the given table
[<Extension>]
static member inline FindAll<'TDoc>(conn, tableName) =
WithConn.Find.All<'TDoc>(tableName, conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<Extension>]
static member inline FindAllOrdered<'TDoc>(conn, tableName, orderFields) =
WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
/// Retrieve a document by its ID
[<Extension>]
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) =
@ -235,22 +206,22 @@ type SqliteConnectionCSharpExtensions =
static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
[<Extension>]
[<System.Obsolete "Use FindByFields instead; will be removed in v4">]
static member inline FindByField<'TDoc>(conn, tableName, field) =
conn.FindByFields<'TDoc>(tableName, Any, [ field ])
static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) =
WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Retrieve documents via a comparison on JSON fields, returning only the first result
[<Extension>]
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
/// the first result
[<Extension>]
[<System.Obsolete "Use FindFirstByFields instead; will be removed in v4">]
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) =
conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ])
static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
conn, tableName, howMatched, queryFields, orderFields) =
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Update an entire document by its ID
[<Extension>]
@ -272,12 +243,6 @@ type SqliteConnectionCSharpExtensions =
static member inline PatchByFields<'TPatch>(conn, tableName, howMatched, fields, patch: 'TPatch) =
WithConn.Patch.byFields tableName howMatched fields patch conn
/// Patch documents using a comparison on a JSON field
[<Extension>]
[<System.Obsolete "Use PatchByFields instead; will be removed in v4">]
static member inline PatchByField<'TPatch>(conn, tableName, field, patch: 'TPatch) =
conn.PatchByFields(tableName, Any, [ field ], patch)
/// Remove fields from a document by the document's ID
[<Extension>]
static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) =
@ -288,12 +253,6 @@ type SqliteConnectionCSharpExtensions =
static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
/// Remove fields from documents via a comparison on a JSON field in the document
[<Extension>]
[<System.Obsolete "Use RemoveFieldsByFields instead; will be removed in v4">]
static member inline RemoveFieldsByField(conn, tableName, field, fieldNames) =
conn.RemoveFieldsByFields(tableName, Any, [ field ], fieldNames)
/// Delete a document by its ID
[<Extension>]
static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) =

View File

@ -314,6 +314,15 @@ module WithConn =
let All<'TDoc>(tableName, conn) =
Custom.List(Query.find tableName, [], fromData<'TDoc>, conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields conn =
Custom.list<'TDoc> (Query.find tableName + Query.orderBy orderFields SQLite) [] fromData<'TDoc> conn
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc>(tableName, orderFields, conn) =
Custom.List(Query.find tableName + Query.orderBy orderFields SQLite, [], fromData<'TDoc>, conn)
/// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
@ -332,12 +341,6 @@ module WithConn =
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on a JSON field
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField<'TDoc> tableName field conn =
byFields<'TDoc> tableName Any [ field ] conn
/// Retrieve documents via a comparison on JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields, conn) =
Custom.List<'TDoc>(
@ -346,10 +349,22 @@ module WithConn =
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on a JSON field
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let ByField<'TDoc>(tableName, field, conn) =
ByFields<'TDoc>(tableName, Any, [ field ], conn)
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
Custom.list<'TDoc>
(Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields SQLite)
(addFieldParams queryFields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) =
Custom.List<'TDoc>(
Query.byFields (Query.find tableName) howMatched queryFields + Query.orderBy orderFields SQLite,
addFieldParams queryFields [],
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on JSON fields, returning only the first result
[<CompiledName "FSharpFirstByFields">]
@ -360,12 +375,6 @@ module WithConn =
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead; will be removed in v4">]
let firstByField<'TDoc> tableName field conn =
firstByFields<'TDoc> tableName Any [ field ] conn
/// Retrieve documents via a comparison on JSON fields, returning only the first result
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, conn) =
Custom.Single(
@ -374,10 +383,24 @@ module WithConn =
fromData<'TDoc>,
conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) =
FirstByFields(tableName, Any, [ field ], conn)
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
/// only the first result
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
Custom.single
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1"
(addFieldParams queryFields [])
fromData<'TDoc>
conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning
/// only the first result
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields, conn) =
Custom.Single(
$"{Query.byFields (Query.find tableName) howMatched queryFields}{Query.orderBy orderFields SQLite} LIMIT 1",
addFieldParams queryFields [],
fromData<'TDoc>,
conn)
/// Commands to update documents
[<RequireQualifiedAccess>]
@ -418,12 +441,6 @@ module WithConn =
(addFieldParams fields [ jsonParam "@data" patch ])
conn
/// Patch documents using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field (patch: 'TPatch) conn =
byFields tableName Any [ field ] patch conn
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
module RemoveFields =
@ -446,12 +463,6 @@ module WithConn =
(addFieldParams fields nameParams)
conn
/// Remove fields from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field fieldNames conn =
byFields tableName Any [ field ] fieldNames conn
/// Commands to delete documents
[<RequireQualifiedAccess>]
module Delete =
@ -466,12 +477,6 @@ module WithConn =
let byFields tableName howMatched fields conn =
Custom.nonQuery (Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams fields []) conn
/// Delete documents by matching a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field conn =
byFields tableName Any [ field ] conn
/// Commands to execute custom SQL queries
[<RequireQualifiedAccess>]
@ -516,6 +521,7 @@ module Custom =
use conn = Configuration.dbConn ()
WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
/// Functions to create tables and indexes
[<RequireQualifiedAccess>]
module Definition =
@ -532,6 +538,7 @@ module Definition =
use conn = Configuration.dbConn ()
WithConn.Definition.ensureFieldIndex tableName indexName fields conn
/// Document insert/save functions
[<AutoOpen>]
module Document =
@ -548,6 +555,7 @@ module Document =
use conn = Configuration.dbConn ()
WithConn.save tableName document conn
/// Commands to count documents
[<RequireQualifiedAccess>]
module Count =
@ -564,11 +572,6 @@ module Count =
use conn = Configuration.dbConn ()
WithConn.Count.byFields tableName howMatched fields conn
/// Count matching documents using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
byFields tableName Any [ field ]
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@ -586,11 +589,6 @@ module Exists =
use conn = Configuration.dbConn ()
WithConn.Exists.byFields tableName howMatched fields conn
/// Determine if a document exists using a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
byFields tableName Any [ field ]
/// Commands to determine if documents exist
[<RequireQualifiedAccess>]
@ -607,6 +605,17 @@ module Find =
use conn = Configuration.dbConn ()
WithConn.Find.All<'TDoc>(tableName, conn)
/// Retrieve all documents in the given table ordered by the given fields in the document
[<CompiledName "FSharpAllOrdered">]
let allOrdered<'TDoc> tableName orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.allOrdered<'TDoc> tableName orderFields conn
/// Retrieve all documents in the given table ordered by the given fields in the document
let AllOrdered<'TDoc> tableName orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn)
/// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName docId =
@ -624,21 +633,21 @@ module Find =
use conn = Configuration.dbConn ()
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on a JSON field
[<CompiledName "FSharpByField">]
[<System.Obsolete "Use byFields instead; will be removed in v4">]
let byField<'TDoc> tableName field =
byFields tableName Any [ field ]
/// Retrieve documents via a comparison on JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields) =
use conn = Configuration.dbConn ()
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let ByField<'TDoc>(tableName, field) =
ByFields<'TDoc>(tableName, Any, [ field ])
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
[<CompiledName "FSharpByFieldsOrdered">]
let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document
let ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields) =
use conn = Configuration.dbConn ()
WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Retrieve documents via a comparison on JSON fields, returning only the first result
[<CompiledName "FSharpFirstByFields">]
@ -646,21 +655,24 @@ module Find =
use conn = Configuration.dbConn ()
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<CompiledName "FSharpFirstByField">]
[<System.Obsolete "Use firstByFields instead; will be removed in v4">]
let firstByField<'TDoc> tableName field =
firstByFields<'TDoc> tableName Any [ field ]
/// Retrieve documents via a comparison on JSON fields, returning only the first result
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
use conn = Configuration.dbConn ()
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn)
/// Retrieve documents via a comparison on a JSON field, returning only the first result
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">]
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) =
FirstByFields<'TDoc>(tableName, Any, [ field ])
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
/// the first result
[<CompiledName "FSharpFirstByFieldsOrdered">]
let firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
use conn = Configuration.dbConn ()
WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Retrieve documents via a comparison on JSON fields ordered by the given fields in the document, returning only
/// the first result
let FirstByFieldsOrdered<'TDoc when 'TDoc: null>(tableName, howMatched, queryFields, orderFields) =
use conn = Configuration.dbConn ()
WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Commands to update documents
[<RequireQualifiedAccess>]
@ -683,6 +695,7 @@ module Update =
use conn = Configuration.dbConn ()
WithConn.Update.ByFunc(tableName, idFunc, document, conn)
/// Commands to patch (partially update) documents
[<RequireQualifiedAccess>]
module Patch =
@ -699,11 +712,6 @@ module Patch =
use conn = Configuration.dbConn ()
WithConn.Patch.byFields tableName howMatched fields patch conn
/// Patch documents using a comparison on a JSON field in the WHERE clause
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field (patch: 'TPatch) =
byFields tableName Any [ field ] patch
/// Commands to remove fields from documents
[<RequireQualifiedAccess>]
@ -721,11 +729,6 @@ module RemoveFields =
use conn = Configuration.dbConn ()
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn
/// Remove field from documents via a comparison on a JSON field in the document
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field fieldNames =
byFields tableName Any [ field ] fieldNames
/// Commands to delete documents
[<RequireQualifiedAccess>]
@ -742,9 +745,3 @@ module Delete =
let byFields tableName howMatched fields =
use conn = Configuration.dbConn ()
WithConn.Delete.byFields tableName howMatched fields conn
/// Delete documents by matching a comparison on a JSON field
[<CompiledName "ByField">]
[<System.Obsolete "Use ByFields instead; will be removed in v4">]
let byField tableName field =
byFields tableName Any [ field ]

View File

@ -174,6 +174,31 @@ public static class CommonCSharpTests
Expect.equal(field.Name, "Rad", "Field name incorrect");
Expect.equal(field.Op, Op.NEX, "Operator incorrect");
}),
TestList("NameToPath",
[
TestCase("succeeds for PostgreSQL and a simple name", () =>
{
Expect.equal("data->>'Simple'", Field.NameToPath("Simple", Dialect.PostgreSQL),
"Path not constructed correctly");
}),
TestCase("succeeds for SQLite and a simple name", () =>
{
Expect.equal("data->>'Simple'", Field.NameToPath("Simple", Dialect.SQLite),
"Path not constructed correctly");
}),
TestCase("succeeds for PostgreSQL and a nested name", () =>
{
Expect.equal("data#>>'{A,Long,Path,to,the,Property}'",
Field.NameToPath("A.Long.Path.to.the.Property", Dialect.PostgreSQL),
"Path not constructed correctly");
}),
TestCase("succeeds for SQLite and a nested name", () =>
{
Expect.equal("data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'",
Field.NameToPath("A.Long.Path.to.the.Property", Dialect.SQLite),
"Path not constructed correctly");
})
]),
TestCase("WithParameterName succeeds", () =>
{
var field = Field.EQ("Bob", "Tom").WithParameterName("@name");
@ -305,7 +330,7 @@ public static class CommonCSharpTests
{
Expect.equal(
Query.Definition.EnsureIndexOn("test.table", "gibberish",
new[] { "taco", "guac DESC", "salsa ASC" }, Dialect.SQLite),
["taco", "guac DESC", "salsa ASC"], Dialect.SQLite),
"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");
@ -356,7 +381,60 @@ public static class CommonCSharpTests
TestCase("Delete succeeds", () =>
{
Expect.equal(Query.Delete("tbl"), "DELETE FROM tbl", "Delete query not correct");
}),
TestList("OrderBy",
[
TestCase("succeeds for no fields", () =>
{
Expect.equal(Query.OrderBy([], Dialect.PostgreSQL), "",
"Order By should have been blank (PostgreSQL)");
Expect.equal(Query.OrderBy([], Dialect.SQLite), "", "Order By should have been blank (SQLite)");
}),
TestCase("succeeds for PostgreSQL with one field and no direction", () =>
{
Expect.equal(Query.OrderBy([Field.Named("TestField")], Dialect.PostgreSQL),
" ORDER BY data->>'TestField'", "Order By not constructed correctly");
}),
TestCase("succeeds for SQLite with one field and no direction", () =>
{
Expect.equal(Query.OrderBy([Field.Named("TestField")], Dialect.SQLite),
" ORDER BY data->>'TestField'", "Order By not constructed correctly");
}),
TestCase("succeeds for PostgreSQL with multiple fields and direction", () =>
{
Expect.equal(
Query.OrderBy(
[
Field.Named("Nested.Test.Field DESC"), Field.Named("AnotherField"),
Field.Named("It DESC")
],
Dialect.PostgreSQL),
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC",
"Order By not constructed correctly");
}),
TestCase("succeeds for SQLite with multiple fields and direction", () =>
{
Expect.equal(
Query.OrderBy(
[
Field.Named("Nested.Test.Field DESC"), Field.Named("AnotherField"),
Field.Named("It DESC")
],
Dialect.SQLite),
" ORDER BY data->>'Nested'->>'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC",
"Order By not constructed correctly");
}),
TestCase("succeeds for PostgreSQL numeric fields", () =>
{
Expect.equal(Query.OrderBy([Field.Named("n:Test")], Dialect.PostgreSQL),
" ORDER BY (data->>'Test')::numeric", "Order By not constructed correctly for numeric field");
}),
TestCase("succeeds for SQLite numeric fields", () =>
{
Expect.equal(Query.OrderBy([Field.Named("n:Test")], Dialect.SQLite), " ORDER BY data->>'Test'",
"Order By not constructed correctly for numeric field");
})
])
])
]);
}

View File

@ -41,7 +41,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.CustomList(Query.SelectFromTable(PostgresDb.TableName), Parameters.None,
var docs = await conn.CustomList(Query.Find(PostgresDb.TableName), Parameters.None,
Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}),
@ -53,7 +53,7 @@ public class PostgresCSharpExtensionTests
var docs = await conn.CustomList(
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) },
[Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))],
Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned");
})
@ -67,7 +67,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Tuple.Create("@id", Sql.@string("one")) }, Results.FromData<JsonDocument>);
[Tuple.Create("@id", Sql.@string("one"))], Results.FromData<JsonDocument>);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "one", "The incorrect document was returned");
}),
@ -78,7 +78,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData<JsonDocument>);
[Tuple.Create("@id", Sql.@string("eighty"))], Results.FromData<JsonDocument>);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
@ -102,7 +102,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
await conn.CustomNonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) });
[Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))]);
var remaining = await conn.CountAll(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table");
@ -119,55 +119,61 @@ public class PostgresCSharpExtensionTests
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
var tableExists = () => conn.CustomScalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None,
Results.ToExists);
var keyExists = () => conn.CustomScalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None,
Results.ToExists);
var exists = await tableExists();
var alsoExists = await keyExists();
var exists = await TableExists();
var alsoExists = await KeyExists();
Expect.isFalse(exists, "The table should not exist already");
Expect.isFalse(alsoExists, "The key index should not exist already");
await conn.EnsureTable("ensured");
exists = await tableExists();
alsoExists = await keyExists();
exists = await TableExists();
alsoExists = await KeyExists();
Expect.isTrue(exists, "The table should now exist");
Expect.isTrue(alsoExists, "The key index should now exist");
return;
Task<bool> KeyExists() =>
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it",
Parameters.None, Results.ToExists);
Task<bool> TableExists() =>
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it",
Parameters.None, Results.ToExists);
}),
TestCase("EnsureDocumentIndex succeeds", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
var indexExists = () => conn.CustomScalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it", Parameters.None,
Results.ToExists);
var exists = await indexExists();
var exists = await IndexExists();
Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured");
await conn.EnsureDocumentIndex("ensured", DocumentIndex.Optimized);
exists = await indexExists();
exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist");
return;
Task<bool> IndexExists() =>
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it",
Parameters.None, Results.ToExists);
}),
TestCase("EnsureFieldIndex succeeds", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
var indexExists = () => conn.CustomScalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None,
Results.ToExists);
var exists = await indexExists();
var exists = await IndexExists();
Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured");
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
exists = await indexExists();
await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist");
return;
Task<bool> IndexExists() =>
conn.CustomScalar("SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists);
}),
TestList("Insert",
[
@ -240,17 +246,16 @@ public class PostgresCSharpExtensionTests
var theCount = await conn.CountAll(PostgresDb.TableName);
Expect.equal(theCount, 5, "There should have been 5 matching documents");
}),
#pragma warning disable CS0618
TestCase("CountByField succeeds", async () =>
TestCase("CountByFields succeeds", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var theCount = await conn.CountByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
var theCount = await conn.CountByFields(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")]);
Expect.equal(theCount, 2, "There should have been 2 matching documents");
}),
#pragma warning restore CS0618
TestCase("CountByContains succeeds", async () =>
{
await using var db = PostgresDb.BuildDb();
@ -290,7 +295,6 @@ public class PostgresCSharpExtensionTests
Expect.isFalse(exists, "There should not have been an existing document");
})
]),
#pragma warning disable CS0618
TestList("ExistsByField",
[
TestCase("succeeds when documents exist", async () =>
@ -299,7 +303,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EX("Sub"));
var exists = await conn.ExistsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EX("Sub")]);
Expect.isTrue(exists, "There should have been existing documents");
}),
TestCase("succeeds when documents do not exist", async () =>
@ -308,11 +312,11 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var exists = await conn.ExistsByField(PostgresDb.TableName, Field.EQ("NumValue", "six"));
var exists =
await conn.ExistsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "six")]);
Expect.isFalse(exists, "There should not have been existing documents");
})
]),
#pragma warning restore CS0618
TestList("ExistsByContains",
[
TestCase("succeeds when documents exist", async () =>
@ -377,6 +381,44 @@ public class PostgresCSharpExtensionTests
Expect.isEmpty(results, "There should have been no documents returned");
})
]),
TestList("FindAllOrdered",
[
TestCase("succeeds when ordering numerically", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var results =
await conn.FindAllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("n:NumValue")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering numerically descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var results =
await conn.FindAllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("n:NumValue DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering alphabetically", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var results = await conn.FindAllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("Id DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
"The documents were not ordered correctly");
})
]),
TestList("FindById",
[
TestCase("succeeds when a document is found", async () =>
@ -399,8 +441,7 @@ public class PostgresCSharpExtensionTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning disable CS0618
TestList("FindByField",
TestList("FindByFields",
[
TestCase("succeeds when documents are found", async () =>
{
@ -408,7 +449,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
var docs = await conn.FindByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
Expect.equal(docs.Count, 1, "There should have been one document returned");
}),
TestCase("succeeds when documents are not found", async () =>
@ -417,11 +459,38 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve"));
var docs = await conn.FindByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "mauve")]);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
#pragma warning restore CS0618
TestList("FindByFieldsOrdered",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id")]);
Expect.hasLength(docs, 2, "There should have been two document returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
"The documents were not ordered correctly");
}),
TestCase("succeeds when documents are not found", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id DESC")]);
Expect.hasLength(docs, 2, "There should have been two document returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
"The documents were not ordered correctly");
})
]),
TestList("FindByContains",
[
TestCase("succeeds when documents are found", async () =>
@ -444,6 +513,34 @@ public class PostgresCSharpExtensionTests
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
TestList("FindByContainsOrdered",
[
// Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar")]);
Expect.hasLength(docs, 2, "There should have been two documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|four",
"Documents not ordered correctly");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar DESC")]);
Expect.hasLength(docs, 2, "There should have been two documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|two",
"Documents not ordered correctly");
})
]),
TestList("FindByJsonPath",
[
TestCase("succeeds when documents are found", async () =>
@ -465,8 +562,35 @@ public class PostgresCSharpExtensionTests
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
#pragma warning disable CS0618
TestList("FindFirstByField",
TestList("FindByJsonPathOrdered",
[
// Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByJsonPathOrdered<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
[Field.Named("n:NumValue")]);
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "one|three|two",
"Documents not ordered correctly");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var docs = await conn.FindByJsonPathOrdered<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
[Field.Named("n:NumValue DESC")]);
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|three|one",
"Documents not ordered correctly");
})
]),
TestList("FindFirstByFields",
[
TestCase("succeeds when a document is found", async () =>
{
@ -474,7 +598,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
var doc = await conn.FindFirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "two", "The incorrect document was returned");
}),
@ -484,9 +609,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "purple"));
var doc = await conn.FindFirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned");
Expect.contains(["five", "four"], doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
@ -494,11 +620,36 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent"));
var doc = await conn.FindFirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "absent")]);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning restore CS0618
TestList("FindFirstByFieldsOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("five", doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc.Id, "An incorrect document was returned");
})
]),
TestList("FindFirstByContains",
[
TestCase("succeeds when a document is found", async () =>
@ -520,7 +671,7 @@ public class PostgresCSharpExtensionTests
var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } });
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned");
Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
@ -532,6 +683,31 @@ public class PostgresCSharpExtensionTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
TestList("FindFirstByContainsOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Value")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Value DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc.Id, "An incorrect document was returned");
})
]),
TestList("FindFirstByJsonPath",
[
TestCase("succeeds when a document is found", async () =>
@ -554,7 +730,7 @@ public class PostgresCSharpExtensionTests
var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")");
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned");
Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
@ -566,6 +742,31 @@ public class PostgresCSharpExtensionTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
TestList("FindFirstByJsonPathOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByJsonPathOrdered<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db);
await LoadDocs();
var doc = await conn.FindFirstByJsonPathOrdered<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc.Id, "An incorrect document was returned");
})
]),
TestList("UpdateById",
[
TestCase("succeeds when a document is updated", async () =>
@ -650,8 +851,7 @@ public class PostgresCSharpExtensionTests
await conn.PatchById(PostgresDb.TableName, "test", new { Foo = "green" });
})
]),
#pragma warning disable CS0618
TestList("PatchByField",
TestList("PatchByFields",
[
TestCase("succeeds when a document is updated", async () =>
{
@ -659,8 +859,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
var after = await conn.CountByField(PostgresDb.TableName, Field.EQ("NumValue", "77"));
await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
new { NumValue = 77 });
var after = await conn.CountByFields(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("NumValue", "77")]);
Expect.equal(after, 2, "There should have been 2 documents returned");
}),
TestCase("succeeds when no document is updated", async () =>
@ -671,10 +873,10 @@ public class PostgresCSharpExtensionTests
Expect.equal(before, 0, "There should have been no documents returned");
// This not raising an exception is the test
await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
new { Foo = "green" });
})
]),
#pragma warning restore CS0618
TestList("PatchByContains",
[
TestCase("succeeds when a document is updated", async () =>
@ -729,7 +931,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub", "Value" });
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -741,7 +943,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Sub" });
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -754,7 +956,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
// This not raising an exception is the test
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["AFieldThatIsNotThere"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
@ -762,11 +964,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
// This not raising an exception is the test
await conn.RemoveFieldsById(PostgresDb.TableName, "two", new[] { "Value" });
await conn.RemoveFieldsById(PostgresDb.TableName, "two", ["Value"]);
})
]),
#pragma warning disable CS0618
TestList("RemoveFieldsByField",
TestList("RemoveFieldsByFields",
[
TestCase("succeeds when multiple fields are removed", async () =>
{
@ -774,8 +975,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"),
new[] { "Sub", "Value" });
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -787,7 +988,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" });
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -800,7 +1002,8 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
// This not raising an exception is the test
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" });
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
@ -808,11 +1011,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
// This not raising an exception is the test
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"),
new[] { "Value" });
await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any,
[Field.NE("Abracadabra", "apple")], ["Value"]);
})
]),
#pragma warning restore CS0618
TestList("RemoveFieldsByContains",
[
TestCase("succeeds when multiple fields are removed", async () =>
@ -821,8 +1023,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 },
new[] { "Sub", "Value" });
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -834,7 +1035,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" });
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -847,7 +1048,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
// This not raising an exception is the test
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" });
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
@ -855,8 +1056,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
// This not raising an exception is the test
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" },
new[] { "Value" });
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, ["Value"]);
})
]),
TestList("RemoveFieldsByJsonPath",
@ -867,8 +1067,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)",
new[] { "Sub", "Value" });
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -880,7 +1079,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" });
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -893,7 +1092,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs();
// This not raising an exception is the test
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" });
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
@ -901,8 +1100,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
// This not raising an exception is the test
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")",
new[] { "Value" });
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", ["Value"]);
})
]),
TestList("DeleteById",
@ -928,8 +1126,7 @@ public class PostgresCSharpExtensionTests
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
})
]),
#pragma warning disable CS0618
TestList("DeleteByField",
TestList("DeleteByFields",
[
TestCase("succeeds when documents are deleted", async () =>
{
@ -937,7 +1134,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.DeleteByField(PostgresDb.TableName, Field.NE("Value", "purple"));
await conn.DeleteByFields(PostgresDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
var remaining = await conn.CountAll(PostgresDb.TableName);
Expect.equal(remaining, 2, "There should have been 2 documents remaining");
}),
@ -947,12 +1144,11 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db);
await LoadDocs();
await conn.DeleteByField(PostgresDb.TableName, Field.EQ("Value", "crimson"));
await conn.DeleteByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "crimson")]);
var remaining = await conn.CountAll(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
})
]),
#pragma warning restore CS0618
TestList("DeleteByContains",
[
TestCase("succeeds when documents are deleted", async () =>

View File

@ -14,11 +14,9 @@ using static Runner;
public static class PostgresCSharpTests
{
/// <summary>
/// Tests which do not hit the database
/// Unit tests for the Parameters module of the PostgreSQL library
/// </summary>
private static readonly Test Unit = TestList("Unit",
[
TestList("Parameters",
private static readonly Test ParametersTests = TestList("Parameters",
[
TestList("Id",
[
@ -69,29 +67,25 @@ public static class PostgresCSharpTests
{
var it = Parameters.Id((ulong)6464);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.int64(6464),
"Unsigned long ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.int64(6464), "Unsigned long ID parameter not constructed correctly");
}),
TestCase("succeeds for decimal ID", () =>
{
var it = Parameters.Id((decimal)4.56);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@decimal((decimal)4.56),
"Decimal ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@decimal((decimal)4.56), "Decimal ID parameter not constructed correctly");
}),
TestCase("succeeds for single ID", () =>
{
var it = Parameters.Id((float)5.67);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@double((float)5.67),
"Single ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@double((float)5.67), "Single ID parameter not constructed correctly");
}),
TestCase("succeeds for double ID", () =>
{
var it = Parameters.Id(6.78);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@double(6.78),
"Double ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@double(6.78), "Double ID parameter not constructed correctly");
}),
TestCase("succeeds for string ID", () =>
{
@ -176,32 +170,13 @@ public static class PostgresCSharpTests
Expect.isTrue(false, "The parameter was not a StringArray type");
}
})
]),
#pragma warning disable CS0618
TestList("FieldName",
[
TestCase("succeeds for one name", () =>
{
var (name, value) = Parameters.FieldName(["bob"]);
Expect.equal(name, "@name", "The parameter name was incorrect");
if (!value.IsString)
{
Expect.isTrue(false, "The parameter was not a String type");
}
}),
TestCase("succeeds for multiple names", () =>
{
var (name, value) = Parameters.FieldName(["bob", "tom", "mike"]);
Expect.equal(name, "@name", "The parameter name was incorrect");
if (!value.IsStringArray)
{
Expect.isTrue(false, "The parameter was not a StringArray type");
}
})
])
#pragma warning restore CS0618
]),
TestList("Query",
]);
/// <summary>
/// Unit tests for the Query module of the PostgreSQL library
/// </summary>
private static readonly Test QueryTests = TestList("Query",
[
TestList("WhereByFields",
[
@ -259,8 +234,7 @@ public static class PostgresCSharpTests
[
TestCase("succeeds for numeric ID", () =>
{
Expect.equal(Postgres.Query.WhereById(18), "(data->>'Id')::numeric = @id",
"WHERE clause not correct");
Expect.equal(Postgres.Query.WhereById(18), "(data->>'Id')::numeric = @id", "WHERE clause not correct");
}),
TestCase("succeeds for string ID", () =>
{
@ -298,8 +272,7 @@ public static class PostgresCSharpTests
]),
TestCase("WhereDataContains succeeds", () =>
{
Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test",
"WHERE clause not correct");
Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test", "WHERE clause not correct");
}),
TestCase("WhereJsonPathMatches succeeds", () =>
{
@ -318,15 +291,14 @@ public static class PostgresCSharpTests
}),
TestCase("ById succeeds", () =>
{
Expect.equal(Postgres.Query.ById("test", "14"), "test WHERE data->>'Id' = @id",
"By-ID query not correct");
Expect.equal(Postgres.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", "By-ID query not correct");
}),
TestCase("ByFields succeeds", () =>
{
Expect.equal(Postgres.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]),
"unit WHERE (data->>'That')::numeric > @field0", "By-Field query not correct");
}),
TestCase ("ByContains succeeds", () =>
TestCase("ByContains succeeds", () =>
{
Expect.equal(Postgres.Query.ByContains("exam"), "exam WHERE data @> @criteria",
"By-Contains query not correct");
@ -336,7 +308,15 @@ public static class PostgresCSharpTests
Expect.equal(Postgres.Query.ByPathMatch("verify"), "verify WHERE data @? @path::jsonpath",
"By-JSON Path query not correct");
})
])
]);
/// <summary>
/// Tests which do not hit the database
/// </summary>
private static readonly Test Unit = TestList("Unit",
[
ParametersTests,
QueryTests
]);
private static readonly List<JsonDocument> TestDocuments =
@ -357,11 +337,9 @@ public static class PostgresCSharpTests
}
/// <summary>
/// Integration tests for the PostgreSQL library
/// Integration tests for the Configuration module of the PostgreSQL library
/// </summary>
private static readonly Test Integration = TestList("Integration",
[
TestList("Configuration",
private static readonly Test ConfigurationTests = TestList("Configuration",
[
TestCase("UseDataSource disposes existing source", () =>
{
@ -390,8 +368,12 @@ public static class PostgresCSharpTests
Expect.isTrue(ReferenceEquals(source, Postgres.Configuration.DataSource()),
"Data source should have been the same");
})
]),
TestList("Custom",
]);
/// <summary>
/// Integration tests for the Custom module of the PostgreSQL library
/// </summary>
private static readonly Test CustomTests = TestList("Custom",
[
TestList("List",
[
@ -400,7 +382,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Custom.List(Query.SelectFromTable(PostgresDb.TableName), Parameters.None,
var docs = await Custom.List(Query.Find(PostgresDb.TableName), Parameters.None,
Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}),
@ -411,8 +393,7 @@ public static class PostgresCSharpTests
var docs = await Custom.List(
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) },
Results.FromData<JsonDocument>);
[Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))], Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
@ -424,7 +405,7 @@ public static class PostgresCSharpTests
await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Tuple.Create("@id", Sql.@string("one")) }, Results.FromData<JsonDocument>);
[Tuple.Create("@id", Sql.@string("one"))], Results.FromData<JsonDocument>);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "one", "The incorrect document was returned");
}),
@ -434,7 +415,7 @@ public static class PostgresCSharpTests
await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Tuple.Create("@id", Sql.@string("eighty")) }, Results.FromData<JsonDocument>);
[Tuple.Create("@id", Sql.@string("eighty"))], Results.FromData<JsonDocument>);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
@ -456,7 +437,7 @@ public static class PostgresCSharpTests
await LoadDocs();
await Custom.NonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath",
new[] { Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)")) });
[Tuple.Create("@path", Sql.@string("$.NumValue ? (@ > 100)"))]);
var remaining = await Count.All(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table");
@ -469,8 +450,12 @@ public static class PostgresCSharpTests
var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, row => row.@int("test_value"));
Expect.equal(nbr, 5, "The query should have returned the number 5");
})
]),
TestList("Definition",
]);
/// <summary>
/// Integration tests for the Definition module of the PostgreSQL library
/// </summary>
private static readonly Test DefinitionTests = TestList("Definition",
[
TestCase("EnsureTable succeeds", async () =>
{
@ -491,6 +476,7 @@ public static class PostgresCSharpTests
Task<bool> TableExists() => Custom.Scalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None,
Results.ToExists);
Task<bool> KeyExists() => Custom.Scalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None,
Results.ToExists);
@ -520,7 +506,7 @@ public static class PostgresCSharpTests
Expect.isFalse(exists, "The index should not exist already");
await Definition.EnsureTable("ensured");
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
await Definition.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist");
return;
@ -529,8 +515,12 @@ public static class PostgresCSharpTests
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None,
Results.ToExists);
})
]),
TestList("Document",
]);
/// <summary>
/// Integration tests for the Document module of the PostgreSQL library
/// </summary>
private static readonly Test DocumentTests = TestList("Document",
[
TestList("Insert",
[
@ -591,8 +581,12 @@ public static class PostgresCSharpTests
Expect.equal(after.Sub!.Foo, "c", "The updated document is not correct");
})
])
]),
TestList("Count",
]);
/// <summary>
/// Integration tests for the Count module of the PostgreSQL library
/// </summary>
private static readonly Test CountTests = TestList("Count",
[
TestCase("All succeeds", async () =>
{
@ -602,24 +596,27 @@ public static class PostgresCSharpTests
var theCount = await Count.All(PostgresDb.TableName);
Expect.equal(theCount, 5, "There should have been 5 matching documents");
}),
#pragma warning disable CS0618
TestCase("ByField succeeds for numeric range", async () =>
TestList("ByFields",
[
TestCase("succeeds for numeric range", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var theCount = await Count.ByField(PostgresDb.TableName, Field.BT("NumValue", 10, 20));
var theCount = await Count.ByFields(PostgresDb.TableName, FieldMatch.Any,
[Field.BT("NumValue", 10, 20)]);
Expect.equal(theCount, 3, "There should have been 3 matching documents");
}),
TestCase("ByField succeeds for non-numeric range", async () =>
TestCase("succeeds for non-numeric range", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var theCount = await Count.ByField(PostgresDb.TableName, Field.BT("Value", "aardvark", "apple"));
var theCount = await Count.ByFields(PostgresDb.TableName, FieldMatch.All,
[Field.BT("Value", "aardvark", "apple")]);
Expect.equal(theCount, 1, "There should have been 1 matching document");
}),
#pragma warning restore CS0618
})
]),
TestCase("ByContains succeeds", async () =>
{
await using var db = PostgresDb.BuildDb();
@ -636,8 +633,12 @@ public static class PostgresCSharpTests
var theCount = await Count.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)");
Expect.equal(theCount, 3, "There should have been 3 matching documents");
})
]),
TestList("Exists",
]);
/// <summary>
/// Integration tests for the Exists module of the PostgreSQL library
/// </summary>
private static readonly Test ExistsTests = TestList("Exists",
[
TestList("ById",
[
@ -658,15 +659,14 @@ public static class PostgresCSharpTests
Expect.isFalse(exists, "There should not have been an existing document");
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when documents exist", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var exists = await Exists.ByField(PostgresDb.TableName, Field.NEX("Sub"));
var exists = await Exists.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.NEX("Sub")]);
Expect.isTrue(exists, "There should have been existing documents");
}),
TestCase("succeeds when documents do not exist", async () =>
@ -674,11 +674,10 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var exists = await Exists.ByField(PostgresDb.TableName, Field.EQ("NumValue", "six"));
var exists = await Exists.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "six")]);
Expect.isFalse(exists, "There should not have been existing documents");
})
]),
#pragma warning restore CS0618
TestList("ByContains",
[
TestCase("succeeds when documents exist", async () =>
@ -717,8 +716,12 @@ public static class PostgresCSharpTests
Expect.isFalse(exists, "There should not have been any existing documents");
})
])
]),
TestList("Find",
]);
/// <summary>
/// Integration tests for the Find module of the PostgreSQL library
/// </summary>
private static readonly Test FindTests = TestList("Find",
[
TestList("All",
[
@ -731,7 +734,7 @@ public static class PostgresCSharpTests
await Document.Insert(PostgresDb.TableName, new SubDocument { Foo = "five", Bar = "six" });
var results = await Find.All<SubDocument>(PostgresDb.TableName);
Expect.equal(results.Count, 3, "There should have been 3 documents returned");
Expect.hasLength(results, 3, "There should have been 3 documents returned");
}),
TestCase("succeeds when there is no data", async () =>
{
@ -740,6 +743,41 @@ public static class PostgresCSharpTests
Expect.isEmpty(results, "There should have been no documents returned");
})
]),
TestList("AllOrdered",
[
TestCase("succeeds when ordering numerically", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var results =
await Find.AllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("n:NumValue")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering numerically descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var results =
await Find.AllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("n:NumValue DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering alphabetically", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var results = await Find.AllOrdered<JsonDocument>(PostgresDb.TableName, [Field.Named("Id DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
"The documents were not ordered correctly");
})
]),
TestList("ById",
[
TestCase("succeeds when a document is found", async () =>
@ -760,27 +798,52 @@ public static class PostgresCSharpTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
Expect.equal(docs.Count, 1, "There should have been one document returned");
var docs = await Find.ByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
Expect.hasLength(docs, 1, "There should have been one document returned");
}),
TestCase("succeeds when documents are not found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "mauve"));
var docs = await Find.ByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "mauve")]);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
#pragma warning restore CS0618
TestList("ByFieldsOrdered",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id")]);
Expect.hasLength(docs, 2, "There should have been two document returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
"The documents were not ordered correctly");
}),
TestCase("succeeds when documents are not found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id DESC")]);
Expect.hasLength(docs, 2, "There should have been two document returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
"The documents were not ordered correctly");
})
]),
TestList("ByContains",
[
TestCase("succeeds when documents are found", async () =>
@ -790,7 +853,7 @@ public static class PostgresCSharpTests
var docs = await Find.ByContains<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } });
Expect.equal(docs.Count, 2, "There should have been two documents returned");
Expect.hasLength(docs, 2, "There should have been two documents returned");
}),
TestCase("succeeds when documents are not found", async () =>
{
@ -801,6 +864,32 @@ public static class PostgresCSharpTests
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
TestList("ByContainsOrdered",
[
// Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar")]);
Expect.hasLength(docs, 2, "There should have been two documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|four",
"Documents not ordered correctly");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Sub.Bar DESC")]);
Expect.hasLength(docs, 2, "There should have been two documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|two",
"Documents not ordered correctly");
})
]),
TestList("ByJsonPath",
[
TestCase("succeeds when documents are found", async () =>
@ -809,7 +898,7 @@ public static class PostgresCSharpTests
await LoadDocs();
var docs = await Find.ByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)");
Expect.equal(docs.Count, 3, "There should have been 3 documents returned");
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
}),
TestCase("succeeds when documents are not found", async () =>
{
@ -820,15 +909,41 @@ public static class PostgresCSharpTests
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
#pragma warning disable CS0618
TestList("FirstByField",
TestList("ByJsonPathOrdered",
[
// Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByJsonPathOrdered<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
[Field.Named("n:NumValue")]);
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "one|three|two",
"Documents not ordered correctly");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var docs = await Find.ByJsonPathOrdered<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
[Field.Named("n:NumValue DESC")]);
Expect.hasLength(docs, 3, "There should have been 3 documents returned");
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "two|three|one",
"Documents not ordered correctly");
})
]),
TestList("FirstByFields",
[
TestCase("succeeds when a document is found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another"));
var doc = await Find.FirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "two", "The incorrect document was returned");
}),
@ -837,20 +952,44 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "purple"));
var doc = await Find.FirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "five", "four" }, doc.Id, "An incorrect document was returned");
Expect.contains(["five", "four"], doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "absent"));
var doc = await Find.FirstByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "absent")]);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning restore CS0618
TestList("FirstByFieldsOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("five", doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "purple")], [Field.Named("Id DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc.Id, "An incorrect document was returned");
})
]),
TestList("FirstByContains",
[
TestCase("succeeds when a document is found", async () =>
@ -870,7 +1009,7 @@ public static class PostgresCSharpTests
var doc = await Find.FirstByContains<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } });
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned");
Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
@ -881,6 +1020,29 @@ public static class PostgresCSharpTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
TestList("FirstByContainsOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Value")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByContainsOrdered<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }, [Field.Named("Value DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc.Id, "An incorrect document was returned");
})
]),
TestList("FirstByJsonPath",
[
TestCase("succeeds when a document is found", async () =>
@ -901,7 +1063,7 @@ public static class PostgresCSharpTests
var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")");
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "two", "four" }, doc.Id, "An incorrect document was returned");
Expect.contains(["two", "four"], doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
@ -911,9 +1073,36 @@ public static class PostgresCSharpTests
var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")");
Expect.isNull(doc, "There should not have been a document returned");
})
])
]),
TestList("Update",
TestList("FirstByJsonPathOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByJsonPathOrdered<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc.Id, "An incorrect document was returned");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByJsonPathOrdered<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")", [Field.Named("Sub.Bar DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc.Id, "An incorrect document was returned");
})
])
]);
/// <summary>
/// Integration tests for the Update module of the PostgreSQL library
/// </summary>
private static readonly Test UpdateTests = TestList("Update",
[
TestList("ById",
[
@ -973,8 +1162,12 @@ public static class PostgresCSharpTests
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
})
])
]),
TestList("Patch",
]);
/// <summary>
/// Integration tests for the Patch module of the PostgreSQL library
/// </summary>
private static readonly Test PatchTests = TestList("Patch",
[
TestList("ById",
[
@ -999,16 +1192,16 @@ public static class PostgresCSharpTests
await Patch.ById(PostgresDb.TableName, "test", new { Foo = "green" });
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when a document is updated", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
var after = await Count.ByField(PostgresDb.TableName, Field.EQ("NumValue", "77"));
await Patch.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
new { NumValue = 77 });
var after = await Count.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "77")]);
Expect.equal(after, 2, "There should have been 2 documents returned");
}),
TestCase("succeeds when no document is updated", async () =>
@ -1019,10 +1212,10 @@ public static class PostgresCSharpTests
Expect.equal(before, 0, "There should have been no documents returned");
// This not raising an exception is the test
await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
await Patch.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
new { Foo = "green" });
})
]),
#pragma warning restore CS0618
TestList("ByContains",
[
TestCase("succeeds when a document is updated", async () =>
@ -1067,8 +1260,12 @@ public static class PostgresCSharpTests
await Patch.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)", new { Foo = "green" });
})
])
]),
TestList("RemoveFields",
]);
/// <summary>
/// Integration tests for the RemoveFields module of the PostgreSQL library
/// </summary>
private static readonly Test RemoveFieldsTests = TestList("RemoveFields",
[
TestList("ById",
[
@ -1077,7 +1274,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Sub", "Value" });
await RemoveFields.ById(PostgresDb.TableName, "two", ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -1088,7 +1285,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Sub" });
await RemoveFields.ById(PostgresDb.TableName, "two", ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1100,26 +1297,25 @@ public static class PostgresCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
await RemoveFields.ById(PostgresDb.TableName, "two", ["AFieldThatIsNotThere"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ById(PostgresDb.TableName, "two", new[] { "Value" });
await RemoveFields.ById(PostgresDb.TableName, "two", ["Value"]);
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when multiple fields are removed", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"),
new[] { "Sub", "Value" });
await RemoveFields.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -1130,7 +1326,8 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Sub" });
await RemoveFields.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1142,18 +1339,18 @@ public static class PostgresCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), new[] { "Nothing" });
await RemoveFields.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"),
new[] { "Value" });
await RemoveFields.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
["Value"]);
})
]),
#pragma warning restore CS0618
TestList("ByContains",
[
TestCase("succeeds when multiple fields are removed", async () =>
@ -1161,8 +1358,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 },
new[] { "Sub", "Value" });
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -1173,7 +1369,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Sub" });
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1185,15 +1381,14 @@ public static class PostgresCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, new[] { "Nothing" });
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" },
new[] { "Value" });
await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, ["Value"]);
})
]),
TestList("ByJsonPath",
@ -1203,8 +1398,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)",
new[] { "Sub", "Value" });
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -1215,7 +1409,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Sub" });
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1227,19 +1421,22 @@ public static class PostgresCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", new[] { "Nothing" });
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")",
new[] { "Value" });
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", ["Value"]);
})
])
]),
TestList("Delete",
]);
/// <summary>
/// Integration tests for the Delete module of the PostgreSQL library
/// </summary>
private static readonly Test DeleteTests = TestList("Delete",
[
TestList("ById",
[
@ -1262,15 +1459,14 @@ public static class PostgresCSharpTests
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when documents are deleted", async () =>
{
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await Delete.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"));
await Delete.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")]);
var remaining = await Count.All(PostgresDb.TableName);
Expect.equal(remaining, 3, "There should have been 3 documents remaining");
}),
@ -1279,12 +1475,11 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb();
await LoadDocs();
await Delete.ByField(PostgresDb.TableName, Field.EQ("Value", "crimson"));
await Delete.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "crimson")]);
var remaining = await Count.All(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
})
]),
#pragma warning restore CS0618
TestList("ByContains",
[
TestCase("succeeds when documents are deleted", async () =>
@ -1327,12 +1522,28 @@ public static class PostgresCSharpTests
Expect.equal(remaining, 5, "There should have been 5 documents remaining");
})
])
])
]);
/// <summary>
/// All Postgres C# tests
/// </summary>
[Tests]
public static readonly Test All = TestList("Postgres.C#", [Unit, TestSequenced(Integration)]);
public static readonly Test All = TestList("Postgres.C#",
[
TestList("Unit", [ParametersTests, QueryTests]),
TestSequenced(TestList("Integration",
[
ConfigurationTests,
CustomTests,
DefinitionTests,
DocumentTests,
CountTests,
ExistsTests,
FindTests,
UpdateTests,
PatchTests,
RemoveFieldsTests,
DeleteTests
]))
]);
}

View File

@ -1,6 +1,5 @@
using Expecto.CSharp;
using Expecto;
using Microsoft.Data.Sqlite;
using BitBadger.Documents.Sqlite;
namespace BitBadger.Documents.Tests.CSharp;
@ -29,7 +28,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Parameters.Id("one") }, Results.FromData<JsonDocument>);
[Parameters.Id("one")], Results.FromData<JsonDocument>);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "one", "The incorrect document was returned");
}),
@ -40,7 +39,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>);
[Parameters.Id("eighty")], Results.FromData<JsonDocument>);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
@ -52,7 +51,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var docs = await conn.CustomList(Query.SelectFromTable(SqliteDb.TableName), Parameters.None,
var docs = await conn.CustomList(Query.Find(SqliteDb.TableName), Parameters.None,
Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}),
@ -63,8 +62,8 @@ public static class SqliteCSharpExtensionTests
await LoadDocs();
var docs = await conn.CustomList(
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>);
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)],
Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
@ -88,7 +87,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs();
await conn.CustomNonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
new[] { new SqliteParameter("@value", 100) });
[new("@value", 100)]);
var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
@ -107,38 +106,41 @@ public static class SqliteCSharpExtensionTests
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
Func<string, ValueTask<bool>> itExists = async name =>
await conn.CustomScalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
new SqliteParameter[] { new("@name", name) }, Results.ToExists);
var exists = await itExists("ensured");
var alsoExists = await itExists("idx_ensured_key");
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 conn.EnsureTable("ensured");
exists = await itExists("ensured");
alsoExists = await itExists("idx_ensured_key");
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;
Task<bool> ItExists(string name) =>
conn.CustomScalar($"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
[new("@name", name)], Results.ToExists);
}),
TestCase("EnsureFieldIndex succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
var indexExists = () => conn.CustomScalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists);
var exists = await indexExists();
var exists = await IndexExists();
Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured");
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
exists = await indexExists();
await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist");
return;
Task<bool> IndexExists() =>
conn.CustomScalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists);
}),
TestList("Insert",
[
@ -213,17 +215,15 @@ public static class SqliteCSharpExtensionTests
var theCount = await conn.CountAll(SqliteDb.TableName);
Expect.equal(theCount, 5L, "There should have been 5 matching documents");
}),
#pragma warning disable CS0618
TestCase("CountByField succeeds", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var theCount = await conn.CountByField(SqliteDb.TableName, Field.EQ("Value", "purple"));
var theCount = await conn.CountByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")]);
Expect.equal(theCount, 2L, "There should have been 2 matching documents");
}),
#pragma warning restore CS0618
TestList("ExistsById",
[
TestCase("succeeds when a document exists", async () =>
@ -245,8 +245,7 @@ public static class SqliteCSharpExtensionTests
Expect.isFalse(exists, "There should not have been an existing document");
})
]),
#pragma warning disable CS0618
TestList("ExistsByField",
TestList("ExistsByFields",
[
TestCase("succeeds when documents exist", async () =>
{
@ -254,7 +253,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var exists = await conn.ExistsByField(SqliteDb.TableName, Field.GE("NumValue", 10));
var exists = await conn.ExistsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.GE("NumValue", 10)]);
Expect.isTrue(exists, "There should have been existing documents");
}),
TestCase("succeeds when no matching documents exist", async () =>
@ -263,11 +262,11 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var exists = await conn.ExistsByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
var exists =
await conn.ExistsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Nothing", "none")]);
Expect.isFalse(exists, "There should not have been any existing documents");
})
]),
#pragma warning restore CS0618
TestList("FindAll",
[
TestCase("succeeds when there is data", async () =>
@ -290,6 +289,43 @@ public static class SqliteCSharpExtensionTests
Expect.isEmpty(results, "There should have been no documents returned");
})
]),
TestList("FindAllOrdered",
[
TestCase("succeeds when ordering numerically", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var results = await conn.FindAllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering numerically descending", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var results =
await conn.FindAllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering alphabetically", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var results = await conn.FindAllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("Id DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
"The documents were not ordered correctly");
})
]),
TestList("FindById",
[
TestCase("succeeds when a document is found", async () =>
@ -312,8 +348,7 @@ public static class SqliteCSharpExtensionTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning disable CS0618
TestList("FindByField",
TestList("FindByFields",
[
TestCase("succeeds when documents are found", async () =>
{
@ -321,7 +356,8 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.GT("NumValue", 15));
var docs = await conn.FindByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)]);
Expect.equal(docs.Count, 2, "There should have been two documents returned");
}),
TestCase("succeeds when documents are not found", async () =>
@ -330,11 +366,37 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var docs = await conn.FindByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
var docs = await conn.FindByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "mauve")]);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
TestList("FindFirstByField",
TestList("ByFieldsOrdered",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var docs = await conn.FindByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)], [Field.Named("Id")]);
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
"There should have been two documents returned");
}),
TestCase("succeeds when documents are not found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var docs = await conn.FindByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)], [Field.Named("Id DESC")]);
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
"There should have been two documents returned");
})
]),
TestList("FindFirstByFields",
[
TestCase("succeeds when a document is found", async () =>
{
@ -342,7 +404,8 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "another"));
var doc = await conn.FindFirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
}),
@ -352,9 +415,10 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Sub.Foo", "green"));
var doc = await conn.FindFirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned");
Expect.contains(["two", "four"], doc!.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
@ -362,11 +426,36 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var doc = await conn.FindFirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
var doc = await conn.FindFirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "absent")]);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning restore CS0618
TestList("FindFirstByFieldsOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc!.Id, "An incorrect document was returned");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
var doc = await conn.FindFirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc!.Id, "An incorrect document was returned");
})
]),
TestList("UpdateById",
[
TestCase("succeeds when a document is updated", async () =>
@ -450,8 +539,7 @@ public static class SqliteCSharpExtensionTests
await conn.PatchById(SqliteDb.TableName, "test", new { Foo = "green" });
})
]),
#pragma warning disable CS0618
TestList("PatchByField",
TestList("PatchByFields",
[
TestCase("succeeds when a document is updated", async () =>
{
@ -459,8 +547,9 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
var after = await conn.CountByField(SqliteDb.TableName, Field.EQ("NumValue", 77));
await conn.PatchByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
new { NumValue = 77 });
var after = await conn.CountByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 77)]);
Expect.equal(after, 2L, "There should have been 2 documents returned");
}),
TestCase("succeeds when no document is updated", async () =>
@ -471,10 +560,10 @@ public static class SqliteCSharpExtensionTests
Expect.isEmpty(before, "There should have been no documents returned");
// This not raising an exception is the test
await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
await conn.PatchByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
new { Foo = "green" });
})
]),
#pragma warning restore CS0618
TestList("RemoveFieldsById",
[
TestCase("succeeds when fields are removed", async () =>
@ -483,7 +572,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Sub", "Value" });
await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -496,7 +585,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs();
// This not raising an exception is the test
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["AFieldThatIsNotThere"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
@ -504,11 +593,10 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
// This not raising an exception is the test
await conn.RemoveFieldsById(SqliteDb.TableName, "two", new[] { "Value" });
await conn.RemoveFieldsById(SqliteDb.TableName, "two", ["Value"]);
})
]),
#pragma warning disable CS0618
TestList("RemoveFieldsByField",
TestList("RemoveFieldsByFields",
[
TestCase("succeeds when a field is removed", async () =>
{
@ -516,7 +604,8 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" });
await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
["Sub"]);
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.isNull(updated.Sub, "The sub-document should have been removed");
@ -528,7 +617,8 @@ public static class SqliteCSharpExtensionTests
await LoadDocs();
// This not raising an exception is the test
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" });
await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
@ -536,10 +626,10 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
// This not raising an exception is the test
await conn.RemoveFieldsByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
await conn.RemoveFieldsByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
["Value"]);
})
]),
#pragma warning restore CS0618
TestList("DeleteById",
[
TestCase("succeeds when a document is deleted", async () =>
@ -563,8 +653,7 @@ public static class SqliteCSharpExtensionTests
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
]),
#pragma warning disable CS0618
TestList("DeleteByField",
TestList("DeleteByFields",
[
TestCase("succeeds when documents are deleted", async () =>
{
@ -572,7 +661,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
await conn.DeleteByField(SqliteDb.TableName, Field.NE("Value", "purple"));
await conn.DeleteByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
}),
@ -582,12 +671,11 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs();
await conn.DeleteByField(SqliteDb.TableName, Field.EQ("Value", "crimson"));
await conn.DeleteByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "crimson")]);
var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
]),
#pragma warning restore CS0618
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
]);
}

View File

@ -1,6 +1,5 @@
using Expecto.CSharp;
using Expecto;
using Microsoft.Data.Sqlite;
using Microsoft.FSharp.Core;
using BitBadger.Documents.Sqlite;
@ -14,19 +13,16 @@ using static Runner;
public static class SqliteCSharpTests
{
/// <summary>
/// Unit tests for the SQLite library
/// Unit tests for the Query module of the SQLite library
/// </summary>
private static readonly Test Unit = TestList("Unit",
[
TestList("Query",
private static readonly Test QueryTests = TestList("Query",
[
TestList("WhereByFields",
[
TestCase("succeeds for a single field when a logical operator is passed", () =>
{
Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.Any,
[Field.GT("theField", 0).WithParameterName("@test")]),
Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.GT("theField", 0).WithParameterName("@test")]),
"data->>'theField' > @test", "WHERE clause not correct");
}),
TestCase("succeeds for a single field when an existence operator is passed", () =>
@ -44,8 +40,7 @@ public static class SqliteCSharpTests
TestCase("succeeds for all multiple fields with logical operators", () =>
{
Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.All,
[Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")]),
Sqlite.Query.WhereByFields(FieldMatch.All, [Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")]),
"data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1", "WHERE clause not correct");
}),
TestCase("succeeds for any multiple fields with an existence operator", () =>
@ -80,8 +75,7 @@ public static class SqliteCSharpTests
}),
TestCase("ById succeeds", () =>
{
Expect.equal(Sqlite.Query.ById("test", "14"), "test WHERE data->>'Id' = @id",
"By-ID query not correct");
Expect.equal(Sqlite.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", "By-ID query not correct");
}),
TestCase("ByFields succeeds", () =>
{
@ -93,8 +87,12 @@ public static class SqliteCSharpTests
Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"),
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)", "CREATE TABLE statement not correct");
})
]),
TestList("Parameters",
]);
/// <summary>
/// Unit tests for the Parameters module of the SQLite library
/// </summary>
private static readonly Test ParametersTests = TestList("Parameters",
[
TestCase("Id succeeds", () =>
{
@ -127,10 +125,13 @@ public static class SqliteCSharpTests
{
Expect.isEmpty(Parameters.None, "The parameter list should have been empty");
})
])
// Results are exhaustively executed in the context of other tests
]);
// Results are exhaustively executed in the context of other tests
/// <summary>
/// Documents used for integration tests
/// </summary>
private static readonly List<JsonDocument> TestDocuments =
[
new() { Id = "one", Value = "FIRST!", NumValue = 0 },
@ -148,9 +149,10 @@ public static class SqliteCSharpTests
foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
}
private static readonly Test Integration = TestList("Integration",
[
TestCase("Configuration.UseConnectionString succeeds", () =>
/// <summary>
/// Integration tests for the Configuration module of the SQLite library
/// </summary>
private static readonly Test ConfigurationTests = TestCase("Configuration.UseConnectionString succeeds", () =>
{
try
{
@ -162,8 +164,12 @@ public static class SqliteCSharpTests
{
Sqlite.Configuration.UseConnectionString("Data Source=:memory:");
}
}),
TestList("Custom",
});
/// <summary>
/// Integration tests for the Custom module of the SQLite library
/// </summary>
private static readonly Test CustomTests = TestList("Custom",
[
TestList("Single",
[
@ -173,7 +179,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Parameters.Id("one") }, Results.FromData<JsonDocument>);
[Parameters.Id("one")], Results.FromData<JsonDocument>);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "one", "The incorrect document was returned");
}),
@ -183,7 +189,7 @@ public static class SqliteCSharpTests
await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id",
new[] { Parameters.Id("eighty") }, Results.FromData<JsonDocument>);
[Parameters.Id("eighty")], Results.FromData<JsonDocument>);
Expect.isNull(doc, "There should not have been a document returned");
})
]),
@ -194,7 +200,7 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Custom.List(Query.SelectFromTable(SqliteDb.TableName), Parameters.None,
var docs = await Custom.List(Query.Find(SqliteDb.TableName), Parameters.None,
Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}),
@ -204,8 +210,8 @@ public static class SqliteCSharpTests
await LoadDocs();
var docs = await Custom.List(
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>);
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)],
Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
@ -227,7 +233,7 @@ public static class SqliteCSharpTests
await LoadDocs();
await Custom.NonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value",
new[] { new SqliteParameter("@value", 100) });
[new("@value", 100)]);
var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table");
@ -240,8 +246,12 @@ public static class SqliteCSharpTests
var nbr = await Custom.Scalar("SELECT 5 AS test_value", Parameters.None, rdr => rdr.GetInt32(0));
Expect.equal(nbr, 5, "The query should have returned the number 5");
})
]),
TestList("Definition",
]);
/// <summary>
/// Integration tests for the Definition module of the SQLite library
/// </summary>
private static readonly Test DefinitionTests = TestList("Definition",
[
TestCase("EnsureTable succeeds", async () =>
{
@ -262,9 +272,8 @@ public static class SqliteCSharpTests
async ValueTask<bool> ItExists(string name)
{
return await Custom.Scalar(
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
new SqliteParameter[] { new("@name", name) }, Results.ToExists);
return await Custom.Scalar($"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
[new("@name", name)], Results.ToExists);
}
}),
TestCase("EnsureFieldIndex succeeds", async () =>
@ -275,7 +284,7 @@ public static class SqliteCSharpTests
Expect.isFalse(exists, "The index should not exist already");
await Definition.EnsureTable("ensured");
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" });
await Definition.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist");
return;
@ -284,8 +293,14 @@ public static class SqliteCSharpTests
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists);
})
]),
TestList("Document.Insert",
]);
/// <summary>
/// Integration tests for the Document module of the SQLite library
/// </summary>
private static readonly Test DocumentTests = TestList("Document",
[
TestList("Insert",
[
TestCase("succeeds", async () =>
{
@ -344,8 +359,13 @@ public static class SqliteCSharpTests
Expect.equal(after!.Id, "test", "The updated document is not correct");
Expect.isNull(after.Sub, "There should not have been a sub-document in the updated document");
})
]),
TestList("Count",
])
]);
/// <summary>
/// Integration tests for the Count module of the SQLite library
/// </summary>
private static readonly Test CountTests = TestList("Count",
[
TestCase("All succeeds", async () =>
{
@ -355,26 +375,32 @@ public static class SqliteCSharpTests
var theCount = await Count.All(SqliteDb.TableName);
Expect.equal(theCount, 5L, "There should have been 5 matching documents");
}),
#pragma warning disable CS0618
TestCase("ByField succeeds for numeric range", async () =>
TestList("ByFields",
[
TestCase("succeeds for numeric range", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("NumValue", 10, 20));
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.BT("NumValue", 10, 20)]);
Expect.equal(theCount, 3L, "There should have been 3 matching documents");
}),
TestCase("ByField succeeds for non-numeric range", async () =>
TestCase("succeeds for non-numeric range", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var theCount = await Count.ByField(SqliteDb.TableName, Field.BT("Value", "aardvark", "apple"));
var theCount = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any,
[Field.BT("Value", "aardvark", "apple")]);
Expect.equal(theCount, 1L, "There should have been 1 matching document");
})
#pragma warning restore CS0618
]),
TestList("Exists",
])
]);
/// <summary>
/// Integration tests for the Exists module of the SQLite library
/// </summary>
private static readonly Test ExistsTests = TestList("Exists",
[
TestList("ById",
[
@ -395,15 +421,14 @@ public static class SqliteCSharpTests
Expect.isFalse(exists, "There should not have been an existing document");
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when documents exist", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var exists = await Exists.ByField(SqliteDb.TableName, Field.GE("NumValue", 10));
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.GE("NumValue", 10)]);
Expect.isTrue(exists, "There should have been existing documents");
}),
TestCase("succeeds when no matching documents exist", async () =>
@ -411,13 +436,16 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var exists = await Exists.ByField(SqliteDb.TableName, Field.EQ("Nothing", "none"));
var exists = await Exists.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Nothing", "none")]);
Expect.isFalse(exists, "There should not have been any existing documents");
})
])
#pragma warning restore CS0618
]),
TestList("Find",
]);
/// <summary>
/// Integration tests for the Find module of the SQLite library
/// </summary>
private static readonly Test FindTests = TestList("Find",
[
TestList("All",
[
@ -439,6 +467,39 @@ public static class SqliteCSharpTests
Expect.isEmpty(results, "There should have been no documents returned");
})
]),
TestList("AllOrdered",
[
TestCase("succeeds when ordering numerically", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var results = await Find.AllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "one|three|two|four|five",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering numerically descending", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var results = await Find.AllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("n:NumValue DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "five|four|two|three|one",
"The documents were not ordered correctly");
}),
TestCase("succeeds when ordering alphabetically", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var results = await Find.AllOrdered<JsonDocument>(SqliteDb.TableName, [Field.Named("Id DESC")]);
Expect.hasLength(results, 5, "There should have been 5 documents returned");
Expect.equal(string.Join('|', results.Select(x => x.Id)), "two|three|one|four|five",
"The documents were not ordered correctly");
})
]),
TestList("ById",
[
TestCase("succeeds when a document is found", async () =>
@ -459,15 +520,15 @@ public static class SqliteCSharpTests
Expect.isNull(doc, "There should not have been a document returned");
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.GT("NumValue", 15));
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)]);
Expect.equal(docs.Count, 2, "There should have been two documents returned");
}),
TestCase("succeeds when documents are not found", async () =>
@ -475,18 +536,43 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Find.ByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "mauve"));
var docs = await Find.ByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "mauve")]);
Expect.isEmpty(docs, "There should have been no documents returned");
})
]),
TestList("FirstByField",
TestList("ByFieldsOrdered",
[
TestCase("succeeds when documents are found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)], [Field.Named("Id")]);
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "five|four",
"There should have been two documents returned");
}),
TestCase("succeeds when documents are not found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var docs = await Find.ByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.GT("NumValue", 15)], [Field.Named("Id DESC")]);
Expect.equal(string.Join('|', docs.Select(x => x.Id)), "four|five",
"There should have been two documents returned");
})
]),
TestList("FirstByFields",
[
TestCase("succeeds when a document is found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "another"));
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "another")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "two", "The incorrect document was returned");
}),
@ -495,22 +581,50 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Sub.Foo", "green"));
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.contains(new[] { "two", "four" }, doc!.Id, "An incorrect document was returned");
Expect.contains(["two", "four"], doc!.Id, "An incorrect document was returned");
}),
TestCase("succeeds when a document is not found", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByField<JsonDocument>(SqliteDb.TableName, Field.EQ("Value", "absent"));
var doc = await Find.FirstByFields<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Value", "absent")]);
Expect.isNull(doc, "There should not have been a document returned");
})
])
#pragma warning restore CS0618
]),
TestList("Update",
TestList("FirstByFieldsOrdered",
[
TestCase("succeeds when sorting ascending", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("two", doc!.Id, "An incorrect document was returned");
}),
TestCase("succeeds when sorting descending", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
var doc = await Find.FirstByFieldsOrdered<JsonDocument>(SqliteDb.TableName, FieldMatch.Any,
[Field.EQ("Sub.Foo", "green")], [Field.Named("Sub.Bar DESC")]);
Expect.isNotNull(doc, "There should have been a document returned");
Expect.equal("four", doc!.Id, "An incorrect document was returned");
})
])
]);
/// <summary>
/// Integration tests for the Update module of the SQLite library
/// </summary>
private static readonly Test UpdateTests = TestList("Update",
[
TestList("ById",
[
@ -568,8 +682,12 @@ public static class SqliteCSharpTests
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 });
})
]),
]),
TestList("Patch",
]);
/// <summary>
/// Integration tests for the Patch module of the SQLite library
/// </summary>
private static readonly Test PatchTests = TestList("Patch",
[
TestList("ById",
[
@ -595,16 +713,16 @@ public static class SqliteCSharpTests
await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" });
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when a document is updated", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 });
var after = await Count.ByField(SqliteDb.TableName, Field.EQ("NumValue", 77));
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
new { NumValue = 77 });
var after = await Count.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 77)]);
Expect.equal(after, 2L, "There should have been 2 documents returned");
}),
TestCase("succeeds when no document is updated", async () =>
@ -615,12 +733,16 @@ public static class SqliteCSharpTests
Expect.isEmpty(before, "There should have been no documents returned");
// This not raising an exception is the test
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "burgundy"), new { Foo = "green" });
await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "burgundy")],
new { Foo = "green" });
})
])
#pragma warning restore CS0618
]),
TestList("RemoveFields",
]);
/// <summary>
/// Integration tests for the RemoveFields module of the SQLite library
/// </summary>
private static readonly Test RemoveFieldsTests = TestList("RemoveFields",
[
TestList("ById",
[
@ -629,7 +751,7 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Sub", "Value" });
await RemoveFields.ById(SqliteDb.TableName, "two", ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed");
@ -641,25 +763,24 @@ public static class SqliteCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "AFieldThatIsNotThere" });
await RemoveFields.ById(SqliteDb.TableName, "two", ["AFieldThatIsNotThere"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = await SqliteDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ById(SqliteDb.TableName, "two", new[] { "Value" });
await RemoveFields.ById(SqliteDb.TableName, "two", ["Value"]);
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when a field is removed", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Sub" });
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)], ["Sub"]);
var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.isNull(updated.Sub, "The sub-document should have been removed");
@ -670,19 +791,24 @@ public static class SqliteCSharpTests
await LoadDocs();
// This not raising an exception is the test
await RemoveFields.ByField(SqliteDb.TableName, Field.EQ("NumValue", 17), new[] { "Nothing" });
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", 17)],
["Nothing"]);
}),
TestCase("succeeds when no document is matched", async () =>
{
await using var db = await SqliteDb.BuildDb();
// This not raising an exception is the test
await RemoveFields.ByField(SqliteDb.TableName, Field.NE("Abracadabra", "apple"), new[] { "Value" });
await RemoveFields.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
["Value"]);
})
])
#pragma warning restore CS0618
]),
TestList("Delete",
]);
/// <summary>
/// Integration tests for the Delete module of the SQLite library
/// </summary>
private static readonly Test DeleteTests = TestList("Delete",
[
TestList("ById",
[
@ -705,15 +831,14 @@ public static class SqliteCSharpTests
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
]),
#pragma warning disable CS0618
TestList("ByField",
TestList("ByFields",
[
TestCase("succeeds when documents are deleted", async () =>
{
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await Delete.ByField(SqliteDb.TableName, Field.NE("Value", "purple"));
await Delete.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.NE("Value", "purple")]);
var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 2L, "There should have been 2 documents remaining");
}),
@ -722,19 +847,34 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb();
await LoadDocs();
await Delete.ByField(SqliteDb.TableName, Field.EQ("Value", "crimson"));
await Delete.ByFields(SqliteDb.TableName, FieldMatch.All, [Field.EQ("Value", "crimson")]);
var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
})
])
#pragma warning restore CS0618
]),
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
]);
/// <summary>
/// All tests for SQLite C# functions and methods
/// </summary>
[Tests]
public static readonly Test All = TestList("Sqlite.C#", [Unit, TestSequenced(Integration)]);
public static readonly Test All = TestList("Sqlite.C#",
[
TestList("Unit", [QueryTests, ParametersTests]),
TestSequenced(TestList("Integration",
[
ConfigurationTests,
CustomTests,
DefinitionTests,
DocumentTests,
CountTests,
ExistsTests,
FindTests,
UpdateTests,
PatchTests,
RemoveFieldsTests,
DeleteTests,
TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
]))
]);
}

View File

@ -2,6 +2,7 @@
<PropertyGroup>
<OutputType>Exe</OutputType>
<NoWarn>1182</NoWarn>
</PropertyGroup>
<ItemGroup>

View File

@ -109,6 +109,28 @@ let all =
Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier should be None"
}
testList "NameToPath" [
test "succeeds for PostgreSQL and a simple name" {
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"
}
test "succeeds for PostgreSQL and a nested name" {
Expect.equal
"data#>>'{A,Long,Path,to,the,Property}'"
(Field.NameToPath "A.Long.Path.to.the.Property" PostgreSQL)
"Path not constructed correctly"
}
test "succeeds for SQLite and a nested name" {
Expect.equal
"data->>'A'->>'Long'->>'Path'->>'to'->>'the'->>'Property'"
(Field.NameToPath "A.Long.Path.to.the.Property" SQLite)
"Path not constructed correctly"
}
]
test "WithParameterName succeeds" {
let field = (Field.EQ "Bob" "Tom").WithParameterName "@name"
Expect.isSome field.ParameterName "The parameter name should have been filled"
@ -253,5 +275,51 @@ let all =
test "delete succeeds" {
Expect.equal (Query.delete tbl) $"DELETE FROM {tbl}" "Delete query not correct"
}
testList "orderBy" [
test "succeeds for no fields" {
Expect.equal (Query.orderBy [] PostgreSQL) "" "Order By should have been blank (PostgreSQL)"
Expect.equal (Query.orderBy [] SQLite) "" "Order By should have been blank (SQLite)"
}
test "succeeds for PostgreSQL with one field and no direction" {
Expect.equal
(Query.orderBy [ Field.Named "TestField" ] PostgreSQL)
" ORDER BY data->>'TestField'"
"Order By not constructed correctly"
}
test "succeeds for SQLite with one field and no direction" {
Expect.equal
(Query.orderBy [ Field.Named "TestField" ] SQLite)
" ORDER BY data->>'TestField'"
"Order By not constructed correctly"
}
test "succeeds for PostgreSQL with multiple fields and direction" {
Expect.equal
(Query.orderBy
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
PostgreSQL)
" ORDER BY data#>>'{Nested,Test,Field}' DESC, data->>'AnotherField', data->>'It' DESC"
"Order By not constructed correctly"
}
test "succeeds for SQLite with multiple fields and direction" {
Expect.equal
(Query.orderBy
[ Field.Named "Nested.Test.Field DESC"; Field.Named "AnotherField"; Field.Named "It DESC" ]
SQLite)
" ORDER BY data->>'Nested'->>'Test'->>'Field' DESC, data->>'AnotherField', data->>'It' DESC"
"Order By not constructed correctly"
}
test "succeeds for PostgreSQL numeric fields" {
Expect.equal
(Query.orderBy [ Field.Named "n:Test" ] PostgreSQL)
" ORDER BY (data->>'Test')::numeric"
"Order By not constructed correctly for numeric field"
}
test "succeeds for SQLite numeric fields" {
Expect.equal
(Query.orderBy [ Field.Named "n:Test" ] SQLite)
" ORDER BY data->>'Test'"
"Order By not constructed correctly for numeric field"
}
]
]
]

View File

@ -7,8 +7,6 @@ open Expecto
open Npgsql
open Types
#nowarn "0044"
/// Open a connection to the throwaway database
let private mkConn (db: ThrowawayPostgresDb) =
let conn = new NpgsqlConnection(db.ConnectionString)
@ -27,7 +25,7 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
let! docs = conn.customList (Query.selectFromTable PostgresDb.TableName) [] fromData<JsonDocument>
let! docs = conn.customList (Query.find PostgresDb.TableName) [] fromData<JsonDocument>
Expect.equal (List.length docs) 5 "There should have been 5 documents returned"
}
testTask "succeeds when data is not found" {
@ -211,12 +209,12 @@ let integrationTests =
let! theCount = conn.countAll PostgresDb.TableName
Expect.equal theCount 5 "There should have been 5 matching documents"
}
testTask "countByField succeeds" {
testTask "countByFields succeeds" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! theCount = conn.countByField PostgresDb.TableName (Field.EQ "Value" "purple")
let! theCount = conn.countByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
Expect.equal theCount 2 "There should have been 2 matching documents"
}
testTask "countByContains succeeds" {
@ -253,13 +251,13 @@ let integrationTests =
Expect.isFalse exists "There should not have been an existing document"
}
]
testList "existsByField" [
testList "existsByFields" [
testTask "succeeds when documents exist" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! exists = conn.existsByField PostgresDb.TableName (Field.EX "Sub")
let! exists = conn.existsByFields PostgresDb.TableName Any [ Field.EX "Sub" ]
Expect.isTrue exists "There should have been existing documents"
}
testTask "succeeds when documents do not exist" {
@ -267,7 +265,7 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
let! exists = conn.existsByField PostgresDb.TableName (Field.EQ "NumValue" "six")
let! exists = conn.existsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "six" ]
Expect.isFalse exists "There should not have been existing documents"
}
]
@ -317,12 +315,10 @@ let integrationTests =
do! conn.insert PostgresDb.TableName { Foo = "five"; Bar = "six" }
let! results = conn.findAll<SubDocument> PostgresDb.TableName
let expected = [
{ Foo = "one"; Bar = "two" }
{ Foo = "three"; Bar = "four" }
{ Foo = "five"; Bar = "six" }
]
Expect.equal results expected "There should have been 3 documents returned"
Expect.equal
results
[ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
"There should have been 3 documents returned"
}
testTask "succeeds when there is no data" {
use db = PostgresDb.BuildDb()
@ -331,6 +327,44 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned"
}
]
testList "findAllOrdered" [
testTask "succeeds when ordering numerically" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! results = conn.findAllOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "n:NumValue" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"one|three|two|four|five"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering numerically descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! results = conn.findAllOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "n:NumValue DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"five|four|two|three|one"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering alphabetically" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! results = conn.findAllOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "Id DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"two|three|one|four|five"
"The documents were not ordered correctly"
}
]
testList "findById" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
@ -350,13 +384,13 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned"
}
]
testList "findByField" [
testList "findByFields" [
testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs = conn.findByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
let! docs = conn.findByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "another" ]
Expect.equal (List.length docs) 1 "There should have been one document returned"
}
testTask "succeeds when documents are not found" {
@ -364,10 +398,36 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
let! docs = conn.findByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "mauve")
let! docs = conn.findByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "mauve" ]
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "findByFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs =
conn.findByFieldsOrdered<JsonDocument>
PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "five|four" "Documents not ordered correctly"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs =
conn.findByFieldsOrdered<JsonDocument>
PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "four|five" "Documents not ordered correctly"
}
]
testList "findByContains" [
testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb()
@ -386,6 +446,33 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "findByContainsOrdered" [
// Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs =
conn.findByContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "two|four" "Documents not ordered correctly"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs =
conn.findByContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar DESC" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "four|two" "Documents not ordered correctly"
}
]
testList "findByJsonPath" [
testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb()
@ -404,13 +491,40 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "findFirstByField" [
testList "findByJsonPathOrdered" [
// Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs =
conn.findByJsonPathOrdered<JsonDocument>
PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue" ]
Expect.hasLength docs 3 "There should have been 3 documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "one|three|two" "Documents not ordered correctly"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! docs =
conn.findByJsonPathOrdered<JsonDocument>
PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue DESC" ]
Expect.hasLength docs 3 "There should have been 3 documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "two|three|one" "Documents not ordered correctly"
}
]
testList "findFirstByFields" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
let! doc = conn.findFirstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "another" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
}
@ -419,7 +533,7 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "purple")
let! doc = conn.findFirstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
Expect.isSome doc "There should have been a document returned"
Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
}
@ -428,10 +542,34 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
let! doc = conn.findFirstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "absent")
let! doc = conn.findFirstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "absent" ]
Expect.isNone doc "There should not have been a document returned"
}
]
testList "findFirstByFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc =
conn.findFirstByFieldsOrdered<JsonDocument>
PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "five" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc =
conn.findFirstByFieldsOrdered<JsonDocument>
PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "findFirstByContains" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
@ -460,6 +598,30 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned"
}
]
testList "findFirstByContainsOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc =
conn.findFirstByContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc =
conn.findFirstByContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "findFirstByJsonPath" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
@ -488,6 +650,30 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned"
}
]
testList "findFirstByJsonPathOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc =
conn.findFirstByJsonPathOrdered<JsonDocument>
PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
let! doc =
conn.findFirstByJsonPathOrdered<JsonDocument>
PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "updateById" [
testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb()
@ -558,14 +744,14 @@ let integrationTests =
do! conn.patchById PostgresDb.TableName "test" {| Foo = "green" |}
}
]
testList "patchByField" [
testList "patchByFields" [
testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
let! after = conn.countByField PostgresDb.TableName (Field.EQ "NumValue" "77")
do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = conn.countByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "77" ]
Expect.equal after 2 "There should have been 2 documents returned"
}
testTask "succeeds when no document is updated" {
@ -575,7 +761,7 @@ let integrationTests =
Expect.equal before 0 "There should have been no documents returned"
// This not raising an exception is the test
do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
}
]
testList "patchByContains" [
@ -625,9 +811,9 @@ let integrationTests =
do! loadDocs conn
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -636,9 +822,9 @@ let integrationTests =
do! loadDocs conn
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -657,16 +843,16 @@ let integrationTests =
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ]
}
]
testList "removeFieldsByField" [
testList "removeFieldsByFields" [
testTask "succeeds when multiple fields are removed" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub"; "Value" ]
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -674,10 +860,10 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub" ]
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -686,14 +872,14 @@ let integrationTests =
do! loadDocs conn
// This not raising an exception is the test
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ]
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Nothing" ]
}
testTask "succeeds when no document is matched" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
// This not raising an exception is the test
do! conn.removeFieldsByField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
}
]
testList "removeFieldsByContains" [
@ -703,9 +889,9 @@ let integrationTests =
do! loadDocs conn
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -714,9 +900,9 @@ let integrationTests =
do! loadDocs conn
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -742,9 +928,9 @@ let integrationTests =
do! loadDocs conn
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -753,9 +939,9 @@ let integrationTests =
do! loadDocs conn
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ]
let! noSubs = conn.countByField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = conn.countByField PostgresDb.TableName (Field.NEX "Value")
let! noValue = conn.countByFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -794,13 +980,13 @@ let integrationTests =
Expect.equal remaining 5 "There should have been 5 documents remaining"
}
]
testList "deleteByField" [
testList "deleteByFields" [
testTask "succeeds when documents are deleted" {
use db = PostgresDb.BuildDb()
use conn = mkConn db
do! loadDocs conn
do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "purple")
do! conn.deleteByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
let! remaining = conn.countAll PostgresDb.TableName
Expect.equal remaining 3 "There should have been 3 documents remaining"
}
@ -809,7 +995,7 @@ let integrationTests =
use conn = mkConn db
do! loadDocs conn
do! conn.deleteByField PostgresDb.TableName (Field.EQ "Value" "crimson")
do! conn.deleteByFields PostgresDb.TableName Any [ Field.EQ "Value" "crimson" ]
let! remaining = conn.countAll PostgresDb.TableName
Expect.equal remaining 5 "There should have been 5 documents remaining"
}

View File

@ -5,17 +5,14 @@ open BitBadger.Documents
open BitBadger.Documents.Postgres
open BitBadger.Documents.Tests
#nowarn "0044"
(** UNIT TESTS **)
/// Tests which do not hit the database
let unitTests =
testList "Unit" [
testList "Parameters" [
/// Unit tests for the Parameters module of the PostgreSQL library
let parametersTests = testList "Parameters" [
testList "idParam" [
// NOTE: these tests also exercise all branches of the internal parameterFor function
test "succeeds for byte ID" {
Expect.equal
(idParam (sbyte 7)) ("@id", Sql.int8 (sbyte 7)) "Byte ID parameter not constructed correctly"
Expect.equal (idParam (sbyte 7)) ("@id", Sql.int8 (sbyte 7)) "Byte ID parameter not constructed correctly"
}
test "succeeds for unsigned byte ID" {
Expect.equal
@ -25,9 +22,7 @@ let unitTests =
}
test "succeeds for short ID" {
Expect.equal
(idParam (int16 44))
("@id", Sql.int16 (int16 44))
"Short ID parameter not constructed correctly"
(idParam (int16 44)) ("@id", Sql.int16 (int16 44)) "Short ID parameter not constructed correctly"
}
test "succeeds for unsigned short ID" {
Expect.equal
@ -39,14 +34,11 @@ let unitTests =
Expect.equal (idParam 88) ("@id", Sql.int 88) "Int ID parameter not constructed correctly"
}
test "succeeds for unsigned integer ID" {
Expect.equal
(idParam (uint 889)) ("@id", Sql.int 889) "Unsigned int ID parameter not constructed correctly"
Expect.equal (idParam (uint 889)) ("@id", Sql.int 889) "Unsigned int ID parameter not constructed correctly"
}
test "succeeds for long ID" {
Expect.equal
(idParam (int64 123))
("@id", Sql.int64 (int64 123))
"Long ID parameter not constructed correctly"
(idParam (int64 123)) ("@id", Sql.int64 (int64 123)) "Long ID parameter not constructed correctly"
}
test "succeeds for unsigned long ID" {
Expect.equal
@ -113,8 +105,7 @@ let unitTests =
Expect.isEmpty paramList "There should not have been any parameters added"
}
test "succeeds when two parameters are added for one field" {
let paramList =
addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] []
let paramList = addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] []
Expect.hasLength paramList 2 "There should have been 2 parameters added"
let name, value = Seq.head paramList
Expect.equal name "@testmin" "Minimum field name not correct"
@ -136,23 +127,13 @@ let unitTests =
Expect.equal value (Sql.stringArray [| "bob"; "tom"; "mike" |]) "The parameter value was incorrect"
}
]
testList "fieldNameParam" [
test "succeeds for one name" {
let name, value = fieldNameParam [ "bob" ]
Expect.equal name "@name" "The parameter name was incorrect"
Expect.equal value (Sql.string "bob") "The parameter value was incorrect"
}
test "succeeds for multiple names" {
let name, value = fieldNameParam [ "bob"; "tom"; "mike" ]
Expect.equal name "@name" "The parameter name was incorrect"
Expect.equal value (Sql.stringArray [| "bob"; "tom"; "mike" |]) "The parameter value was incorrect"
}
]
test "noParams succeeds" {
Expect.isEmpty noParams "The no-params sequence should be empty"
}
]
testList "Query" [
]
/// Unit tests for the Query module of the PostgreSQL library
let queryTests = testList "Query" [
testList "whereByFields" [
test "succeeds for a single field when a logical operator is passed" {
Expect.equal
@ -206,9 +187,7 @@ let unitTests =
}
test "succeeds for non-numeric non-string ID" {
Expect.equal
(Query.whereById (System.Uri "https://example.com"))
"data->>'Id' = @id"
"WHERE clause not correct"
(Query.whereById (System.Uri "https://example.com")) "data->>'Id' = @id" "WHERE clause not correct"
}
]
testList "Definition" [
@ -266,27 +245,29 @@ let unitTests =
Expect.equal
(Query.byPathMatch "verify") "verify WHERE data @? @path::jsonpath" "By-JSON Path query not correct"
}
]
]
]
(** INTEGRATION TESTS **)
open ThrowawayDb.Postgres
open Types
let isTrue<'T> (_ : 'T) = true
let integrationTests =
let documents = [
/// 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 }
]
let loadDocs () = backgroundTask {
]
/// Load the test documents into the database
let loadDocs () = backgroundTask {
for doc in documents do do! insert PostgresDb.TableName doc
}
testList "Integration" [
testList "Configuration" [
}
/// Integration tests for the Configuration module of the PostgreSQL library
let configurationTests = testList "Configuration" [
test "useDataSource disposes existing source" {
use db1 = ThrowawayDatabase.Create PostgresDb.ConnStr.Value
let source = PostgresDb.MkDataSource db1.ConnectionString
@ -301,26 +282,29 @@ let integrationTests =
let source = PostgresDb.MkDataSource db.ConnectionString
Configuration.useDataSource source
Expect.isTrue (obj.ReferenceEquals(source, Configuration.dataSource ()))
"Data source should have been the same"
Expect.isTrue (obj.ReferenceEquals(source, Configuration.dataSource ())) "Data source should have been the same"
}
]
testList "Custom" [
]
/// Integration tests for the Custom module of the PostgreSQL library
let customTests = testList "Custom" [
testList "list" [
testTask "succeeds when data is found" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs = Custom.list (Query.selectFromTable PostgresDb.TableName) [] fromData<JsonDocument>
Expect.hasCountOf docs 5u isTrue "There should have been 5 documents returned"
let! docs = Custom.list (Query.find PostgresDb.TableName) [] fromData<JsonDocument>
Expect.hasLength docs 5 "There should have been 5 documents returned"
}
testTask "succeeds when data is not found" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs =
Custom.list $"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath"
[ "@path", Sql.string "$.NumValue ? (@ > 100)" ] fromData<JsonDocument>
Custom.list
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath"
[ "@path", Sql.string "$.NumValue ? (@ > 100)" ]
fromData<JsonDocument>
Expect.isEmpty docs "There should have been no documents returned"
}
]
@ -330,8 +314,10 @@ let integrationTests =
do! loadDocs ()
let! doc =
Custom.single $"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id"
[ "@id", Sql.string "one"] fromData<JsonDocument>
Custom.single
$"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id"
[ "@id", Sql.string "one"]
fromData<JsonDocument>
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "one" "The incorrect document was returned"
}
@ -340,8 +326,10 @@ let integrationTests =
do! loadDocs ()
let! doc =
Custom.single $"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id"
[ "@id", Sql.string "eighty" ] fromData<JsonDocument>
Custom.single
$"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id"
[ "@id", Sql.string "eighty" ]
fromData<JsonDocument>
Expect.isNone doc "There should not have been a document returned"
}
]
@ -359,7 +347,8 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! Custom.nonQuery $"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath"
do! Custom.nonQuery
$"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath"
[ "@path", Sql.string "$.NumValue ? (@ > 100)" ]
let! remaining = Count.all PostgresDb.TableName
@ -371,8 +360,10 @@ let integrationTests =
let! nbr = Custom.scalar "SELECT 5 AS test_value" [] (fun row -> row.int "test_value")
Expect.equal nbr 5 "The query should have returned the number 5"
}
]
testList "Definition" [
]
/// Integration tests for the Definition module of the PostgreSQL library
let definitionTests = testList "Definition" [
testTask "ensureTable succeeds" {
use db = PostgresDb.BuildDb()
let tableExists () =
@ -396,9 +387,7 @@ let integrationTests =
use db = PostgresDb.BuildDb()
let indexExists () =
Custom.scalar
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it"
[]
toExists
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it" [] toExists
let! exists = indexExists ()
Expect.isFalse exists "The index should not exist already"
@ -411,8 +400,7 @@ let integrationTests =
testTask "ensureFieldIndex succeeds" {
use db = PostgresDb.BuildDb()
let indexExists () =
Custom.scalar
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it" [] toExists
Custom.scalar "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it" [] toExists
let! exists = indexExists ()
Expect.isFalse exists "The index should not exist already"
@ -422,7 +410,10 @@ let integrationTests =
let! exists' = indexExists ()
Expect.isTrue exists' "The index should now exist"
}
]
]
/// Integration tests for the (auto-opened) Document module of the PostgreSQL library
let documentTests = testList "Document" [
testList "insert" [
testTask "succeeds" {
use db = PostgresDb.BuildDb()
@ -472,7 +463,10 @@ let integrationTests =
Expect.equal after.Value upd8Doc "The updated document is not correct"
}
]
testList "Count" [
]
/// Integration tests for the Count module of the PostgreSQL library
let countTests = testList "Count" [
testTask "all succeeds" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
@ -485,8 +479,7 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! theCount =
Count.byFields PostgresDb.TableName Any [ Field.BT "NumValue" 15 20; Field.EQ "NumValue" 0 ]
let! theCount = Count.byFields PostgresDb.TableName Any [ Field.BT "NumValue" 15 20; Field.EQ "NumValue" 0 ]
Expect.equal theCount 3 "There should have been 3 matching documents"
}
testTask "succeeds when items are not found" {
@ -497,22 +490,6 @@ let integrationTests =
Expect.equal theCount 0 "There should have been no matching documents"
}
]
testList "byField" [
testTask "succeeds for numeric range" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! theCount = Count.byField PostgresDb.TableName (Field.BT "NumValue" 10 20)
Expect.equal theCount 3 "There should have been 3 matching documents"
}
testTask "succeeds for non-numeric range" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! theCount = Count.byField PostgresDb.TableName (Field.BT "Value" "aardvark" "apple")
Expect.equal theCount 1 "There should have been 1 matching document"
}
]
testTask "byContains succeeds" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
@ -527,8 +504,10 @@ let integrationTests =
let! theCount = Count.byJsonPath PostgresDb.TableName "$.NumValue ? (@ > 5)"
Expect.equal theCount 3 "There should have been 3 matching documents"
}
]
testList "Exists" [
]
/// Integration tests for the Exists module of the PostgreSQL library
let existsTests = testList "Exists" [
testList "byId" [
testTask "succeeds when a document exists" {
use db = PostgresDb.BuildDb()
@ -557,24 +536,7 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! exists =
Exists.byFields PostgresDb.TableName All [ Field.EQ "NumValue" "six"; Field.EX "Nope" ]
Expect.isFalse exists "There should not have been existing documents"
}
]
testList "byField" [
testTask "succeeds when documents exist" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! exists = Exists.byField PostgresDb.TableName (Field.EX "Sub")
Expect.isTrue exists "There should have been existing documents"
}
testTask "succeeds when documents do not exist" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! exists = Exists.byField PostgresDb.TableName (Field.EQ "NumValue" "six")
let! exists = Exists.byFields PostgresDb.TableName All [ Field.EQ "NumValue" "six"; Field.EX "Nope" ]
Expect.isFalse exists "There should not have been existing documents"
}
]
@ -610,8 +572,10 @@ let integrationTests =
Expect.isFalse exists "There should not have been any existing documents"
}
]
]
testList "Find" [
]
/// Integration tests for the Find module of the PostgreSQL library
let findTests = testList "Find" [
testList "all" [
testTask "succeeds when there is data" {
use db = PostgresDb.BuildDb()
@ -621,12 +585,10 @@ let integrationTests =
do! insert PostgresDb.TableName { Foo = "five"; Bar = "six" }
let! results = Find.all<SubDocument> PostgresDb.TableName
let expected = [
{ Foo = "one"; Bar = "two" }
{ Foo = "three"; Bar = "four" }
{ Foo = "five"; Bar = "six" }
]
Expect.equal results expected "There should have been 3 documents returned"
Expect.equal
results
[ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
"There should have been 3 documents returned"
}
testTask "succeeds when there is no data" {
use db = PostgresDb.BuildDb()
@ -634,6 +596,41 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned"
}
]
testList "allOrdered" [
testTask "succeeds when ordering numerically" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! results = Find.allOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "n:NumValue" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"one|three|two|four|five"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering numerically descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! results = Find.allOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "n:NumValue DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"five|four|two|three|one"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering alphabetically" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! results = Find.allOrdered<JsonDocument> PostgresDb.TableName [ Field.Named "Id DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"two|three|one|four|five"
"The documents were not ordered correctly"
}
]
testList "byId" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
@ -657,8 +654,7 @@ let integrationTests =
do! loadDocs ()
let! docs =
Find.byFields<JsonDocument>
PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ]
Find.byFields<JsonDocument> PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ]
Expect.equal (List.length docs) 1 "There should have been one document returned"
}
testTask "succeeds when documents are not found" {
@ -671,20 +667,28 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "byField" [
testTask "succeeds when documents are found" {
testList "byFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs = Find.byField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
Expect.equal (List.length docs) 1 "There should have been one document returned"
let! docs =
Find.byFieldsOrdered<JsonDocument>
PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "five|four" "Documents not ordered correctly"
}
testTask "succeeds when documents are not found" {
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs = Find.byField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "mauve")
Expect.isEmpty docs "There should have been no documents returned"
let! docs =
Find.byFieldsOrdered<JsonDocument>
PostgresDb.TableName All [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "four|five" "Documents not ordered correctly"
}
]
testList "byContains" [
@ -703,6 +707,31 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "byContainsOrdered" [
// Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs =
Find.byContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "two|four" "Documents not ordered correctly"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs =
Find.byContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Sub.Bar DESC" ]
Expect.hasLength docs 2 "There should have been two documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "four|two" "Documents not ordered correctly"
}
]
testList "byJsonPath" [
testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb()
@ -719,12 +748,37 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "firstByField" [
testList "byJsonPathOrdered" [
// Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs =
Find.byJsonPathOrdered<JsonDocument>
PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue" ]
Expect.hasLength docs 3 "There should have been 3 documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "one|three|two" "Documents not ordered correctly"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! docs =
Find.byJsonPathOrdered<JsonDocument>
PostgresDb.TableName "$.NumValue ? (@ < 15)" [ Field.Named "n:NumValue DESC" ]
Expect.hasLength docs 3 "There should have been 3 documents returned"
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "two|three|one" "Documents not ordered correctly"
}
]
testList "firstByFields" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another")
let! doc = Find.firstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "another" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
}
@ -732,7 +786,7 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "purple")
let! doc = Find.firstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
Expect.isSome doc "There should have been a document returned"
Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
}
@ -740,10 +794,32 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "absent")
let! doc = Find.firstByFields<JsonDocument> PostgresDb.TableName Any [ Field.EQ "Value" "absent" ]
Expect.isNone doc "There should not have been a document returned"
}
]
testList "firstByFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByFieldsOrdered<JsonDocument>
PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "five" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByFieldsOrdered<JsonDocument>
PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] [ Field.Named "Id DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "firstByContains" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
@ -769,6 +845,28 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned"
}
]
testList "firstByContainsOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByContainsOrdered<JsonDocument>
PostgresDb.TableName {| Sub = {| Foo = "green" |} |} [ Field.Named "Value DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "firstByJsonPath" [
testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb()
@ -794,8 +892,32 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned"
}
]
testList "firstByJsonPathOrdered" [
testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByJsonPathOrdered<JsonDocument>
PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByJsonPathOrdered<JsonDocument>
PostgresDb.TableName """$.Sub.Foo ? (@ == "green")""" [ Field.Named "Sub.Bar DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "Update" [
]
/// Integration tests for the Update module of the PostgreSQL library
let updateTests = testList "Update" [
testList "byId" [
testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb()
@ -815,9 +937,7 @@ let integrationTests =
// This not raising an exception is the test
do! Update.byId
PostgresDb.TableName
"test"
{ emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
PostgresDb.TableName "test" { emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
}
]
testList "byFunc" [
@ -825,8 +945,7 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! Update.byFunc PostgresDb.TableName (_.Id)
{ Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
do! Update.byFunc PostgresDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
let! after = Find.byId<string, JsonDocument> PostgresDb.TableName "one"
Expect.isSome after "There should have been a document returned post-update"
Expect.equal
@ -841,12 +960,13 @@ let integrationTests =
Expect.equal before 0 "There should have been no documents returned"
// This not raising an exception is the test
do! Update.byFunc
PostgresDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
do! Update.byFunc PostgresDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
}
]
]
testList "Patch" [
]
/// Integration tests for the Patch module of the PostgreSQL library
let patchTests = testList "Patch" [
testList "byId" [
testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb()
@ -867,13 +987,13 @@ let integrationTests =
do! Patch.byId PostgresDb.TableName "test" {| Foo = "green" |}
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
let! after = Count.byField PostgresDb.TableName (Field.EQ "NumValue" "77")
do! Patch.byFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = Count.byFields PostgresDb.TableName Any [ Field.EQ "NumValue" 77 ]
Expect.equal after 2 "There should have been 2 documents returned"
}
testTask "succeeds when no document is updated" {
@ -883,7 +1003,7 @@ let integrationTests =
Expect.equal before 0 "There should have been no documents returned"
// This not raising an exception is the test
do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
do! Patch.byFields PostgresDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
}
]
testList "byContains" [
@ -924,17 +1044,19 @@ let integrationTests =
do! Patch.byJsonPath PostgresDb.TableName "$.NumValue ? (@ < 0)" {| Foo = "green" |}
}
]
]
testList "RemoveFields" [
]
/// Integration tests for the RemoveFields module of the PostgreSQL library
let removeFieldsTests = testList "RemoveFields" [
testList "byId" [
testTask "succeeds when multiple fields are removed" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub"; "Value" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -942,9 +1064,9 @@ let integrationTests =
do! loadDocs ()
do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -961,25 +1083,25 @@ let integrationTests =
do! RemoveFields.byId PostgresDb.TableName "two" [ "Value" ]
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when multiple fields are removed" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
do! RemoveFields.byFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub"; "Value" ]
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
do! RemoveFields.byFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Sub" ]
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -987,13 +1109,13 @@ let integrationTests =
do! loadDocs ()
// This not raising an exception is the test
do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Nothing" ]
do! RemoveFields.byFields PostgresDb.TableName Any [ Field.EQ "NumValue" "17" ] [ "Nothing" ]
}
testTask "succeeds when no document is matched" {
use db = PostgresDb.BuildDb()
// This not raising an exception is the test
do! RemoveFields.byField PostgresDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
do! RemoveFields.byFields PostgresDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
}
]
testList "byContains" [
@ -1002,9 +1124,9 @@ let integrationTests =
do! loadDocs ()
do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -1012,9 +1134,9 @@ let integrationTests =
do! loadDocs ()
do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -1037,9 +1159,9 @@ let integrationTests =
do! loadDocs ()
do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 1 "There should be 1 document without Value fields"
}
testTask "succeeds when a single field is removed" {
@ -1047,9 +1169,9 @@ let integrationTests =
do! loadDocs ()
do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ]
let! noSubs = Count.byField PostgresDb.TableName (Field.NEX "Sub")
let! noSubs = Count.byFields PostgresDb.TableName Any [ Field.NEX "Sub" ]
Expect.equal noSubs 4 "There should now be 4 documents without Sub fields"
let! noValue = Count.byField PostgresDb.TableName (Field.NEX "Value")
let! noValue = Count.byFields PostgresDb.TableName Any [ Field.NEX "Value" ]
Expect.equal noValue 0 "There should be no documents without Value fields"
}
testTask "succeeds when a field is not removed" {
@ -1066,8 +1188,10 @@ let integrationTests =
do! RemoveFields.byJsonPath PostgresDb.TableName "$.Abracadabra ? (@ == \"apple\")" [ "Value" ]
}
]
]
testList "Delete" [
]
/// Integration tests for the Delete module of the PostgreSQL library
let deleteTests = testList "Delete" [
testList "byId" [
testTask "succeeds when a document is deleted" {
use db = PostgresDb.BuildDb()
@ -1086,12 +1210,12 @@ let integrationTests =
Expect.equal remaining 5 "There should have been 5 documents remaining"
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when documents are deleted" {
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! Delete.byField PostgresDb.TableName (Field.EQ "Value" "purple")
do! Delete.byFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ]
let! remaining = Count.all PostgresDb.TableName
Expect.equal remaining 3 "There should have been 3 documents remaining"
}
@ -1099,7 +1223,7 @@ let integrationTests =
use db = PostgresDb.BuildDb()
do! loadDocs ()
do! Delete.byField PostgresDb.TableName (Field.EQ "Value" "crimson")
do! Delete.byFields PostgresDb.TableName Any [ Field.EQ "Value" "crimson" ]
let! remaining = Count.all PostgresDb.TableName
Expect.equal remaining 5 "There should have been 5 documents remaining"
}
@ -1140,9 +1264,22 @@ let integrationTests =
Expect.equal remaining 5 "There should have been 5 documents remaining"
}
]
]
]
|> testSequenced
]
let all = testList "Postgres" [ unitTests; integrationTests ]
/// All tests for the PostgreSQL library
let all = testList "Postgres" [
testList "Unit" [ parametersTests; queryTests ]
testSequenced <| testList "Integration" [
configurationTests
customTests
definitionTests
documentTests
countTests
existsTests
findTests
updateTests
patchTests
removeFieldsTests
deleteTests
]
]

View File

@ -8,8 +8,6 @@ open Expecto
open Microsoft.Data.Sqlite
open Types
#nowarn "0044"
/// Integration tests for the F# extensions on the SqliteConnection data type
let integrationTests =
let loadDocs () = backgroundTask {
@ -115,12 +113,12 @@ let integrationTests =
let! theCount = conn.countAll SqliteDb.TableName
Expect.equal theCount 5L "There should have been 5 matching documents"
}
testTask "countByField succeeds" {
testTask "countByFields succeeds" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! theCount = conn.countByField SqliteDb.TableName (Field.EQ "Value" "purple")
let! theCount = conn.countByFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ]
Expect.equal theCount 2L "There should have been 2 matching documents"
}
testList "existsById" [
@ -141,13 +139,13 @@ let integrationTests =
Expect.isFalse exists "There should not have been an existing document"
}
]
testList "existsByField" [
testList "existsByFields" [
testTask "succeeds when documents exist" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "NumValue" 10)
let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 10 ]
Expect.isTrue exists "There should have been existing documents"
}
testTask "succeeds when no matching documents exist" {
@ -155,7 +153,7 @@ let integrationTests =
use conn = Configuration.dbConn ()
do! loadDocs ()
let! exists = conn.existsByField SqliteDb.TableName (Field.EQ "Nothing" "none")
let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.EQ "Nothing" "none" ]
Expect.isFalse exists "There should not have been any existing documents"
}
]
@ -183,6 +181,44 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned"
}
]
testList "findAllOrdered" [
testTask "succeeds when ordering numerically" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! results = conn.findAllOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"one|three|two|four|five"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering numerically descending" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! results = conn.findAllOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"five|four|two|three|one"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering alphabetically" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! results = conn.findAllOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "Id DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"two|three|one|four|five"
"The documents were not ordered correctly"
}
]
testList "findById" [
testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb()
@ -190,7 +226,7 @@ let integrationTests =
do! loadDocs ()
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "two"
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
}
testTask "succeeds when a document is not found" {
@ -199,35 +235,59 @@ let integrationTests =
do! loadDocs ()
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven"
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
Expect.isNone doc "There should not have been a document returned"
}
]
testList "findByField" [
testList "findByFields" [
testTask "succeeds when documents are found" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
Expect.equal (List.length docs) 2 "There should have been two documents returned"
let! docs = conn.findByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
Expect.hasLength docs 2 "There should have been two documents returned"
}
testTask "succeeds when documents are not found" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "mauve")
Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
let! docs = conn.findByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "mauve" ]
Expect.isEmpty docs "There should have been no documents returned"
}
]
testList "findFirstByField" [
testList "findByFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! docs =
conn.findByFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id" ]
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "five|four" "The documents were not ordered correctly"
}
testTask "succeeds when sorting descending" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! docs =
conn.findByFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id DESC" ]
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "four|five" "The documents were not ordered correctly"
}
]
testList "findFirstByFields" [
testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another")
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "another" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
}
testTask "succeeds when multiple documents are found" {
@ -235,8 +295,8 @@ let integrationTests =
use conn = Configuration.dbConn ()
do! loadDocs ()
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
Expect.isSome doc "There should have been a document returned"
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when a document is not found" {
@ -244,8 +304,32 @@ let integrationTests =
use conn = Configuration.dbConn ()
do! loadDocs ()
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent")
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "absent" ]
Expect.isNone doc "There should not have been a document returned"
}
]
testList "findFirstByFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! doc =
conn.findFirstByFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
let! doc =
conn.findFirstByFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "updateById" [
@ -326,14 +410,14 @@ let integrationTests =
do! conn.patchById SqliteDb.TableName "test" {| Foo = "green" |}
}
]
testList "patchByField" [
testList "patchByFields" [
testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
let! after = conn.countByField SqliteDb.TableName (Field.EQ "NumValue" 77)
do! conn.patchByFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = conn.countByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 77 ]
Expect.equal after 2L "There should have been 2 documents returned"
}
testTask "succeeds when no document is updated" {
@ -344,7 +428,7 @@ let integrationTests =
Expect.isEmpty before "There should have been no documents returned"
// This not raising an exception is the test
do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
do! conn.patchByFields SqliteDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
}
]
testList "removeFieldsById" [
@ -377,13 +461,13 @@ let integrationTests =
do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ]
}
]
testList "removeFieldByField" [
testList "removeFieldByFields" [
testTask "succeeds when a field is removed" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ]
do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Sub" ]
try
let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "four"
Expect.isTrue false "The updated document should have failed to parse"
@ -397,14 +481,14 @@ let integrationTests =
do! loadDocs ()
// This not raising an exception is the test
do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ]
do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Nothing" ]
}
testTask "succeeds when no document is matched" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
// This not raising an exception is the test
do! conn.removeFieldsByField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
}
]
testList "deleteById" [
@ -427,13 +511,13 @@ let integrationTests =
Expect.equal remaining 5L "There should have been 5 documents remaining"
}
]
testList "deleteByField" [
testList "deleteByFields" [
testTask "succeeds when documents are deleted" {
use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn ()
do! loadDocs ()
do! conn.deleteByField SqliteDb.TableName (Field.NE "Value" "purple")
do! conn.deleteByFields SqliteDb.TableName Any [ Field.NE "Value" "purple" ]
let! remaining = conn.countAll SqliteDb.TableName
Expect.equal remaining 2L "There should have been 2 documents remaining"
}
@ -442,7 +526,7 @@ let integrationTests =
use conn = Configuration.dbConn ()
do! loadDocs ()
do! conn.deleteByField SqliteDb.TableName (Field.EQ "Value" "crimson")
do! conn.deleteByFields SqliteDb.TableName Any [ Field.EQ "Value" "crimson" ]
let! remaining = conn.countAll SqliteDb.TableName
Expect.equal remaining 5L "There should have been 5 documents remaining"
}
@ -480,8 +564,8 @@ let integrationTests =
use conn = Configuration.dbConn ()
do! loadDocs ()
let! docs = conn.customList (Query.selectFromTable SqliteDb.TableName) [] fromData<JsonDocument>
Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned"
let! docs = conn.customList (Query.find SqliteDb.TableName) [] fromData<JsonDocument>
Expect.hasLength docs 5 "There should have been 5 documents returned"
}
testTask "succeeds when data is not found" {
use! db = SqliteDb.BuildDb()

View File

@ -10,10 +10,10 @@ open Types
#nowarn "0044"
/// Unit tests for the SQLite library
let unitTests =
testList "Unit" [
testList "Query" [
(** UNIT TESTS **)
/// Unit tests for the Query module of the SQLite library
let queryTests = testList "Query" [
testList "whereByFields" [
test "succeeds for a single field when a logical operator is passed" {
Expect.equal
@ -82,8 +82,10 @@ let unitTests =
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)"
"CREATE TABLE statement not correct"
}
]
testList "Parameters" [
]
/// Unit tests for the Parameters module of the SQLite library
let parametersTests = testList "Parameters" [
test "idParam succeeds" {
let theParam = idParam 7
Expect.equal theParam.ParameterName "@id" "The parameter name is incorrect"
@ -110,24 +112,28 @@ let unitTests =
test "noParams succeeds" {
Expect.isEmpty noParams "The parameter list should have been empty"
}
]
// Results are exhaustively executed in the context of other tests
]
]
// Results are exhaustively executed in the context of other tests
/// These tests each use a fresh copy of a SQLite database
let integrationTests =
let documents = [
(** 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 }
]
let loadDocs () = backgroundTask {
]
/// Load a table with the test documents
let loadDocs () = backgroundTask {
for doc in documents do do! insert SqliteDb.TableName doc
}
testList "Integration" [
testList "Configuration" [
}
/// Integration tests for the Configuration module of the SQLite library
let configurationTests = testList "Configuration" [
test "useConnectionString / connectionString succeed" {
try
Configuration.useConnectionString "Data Source=test.db"
@ -164,8 +170,10 @@ let integrationTests =
Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field"
Configuration.useIdField "Id"
}
]
testList "Custom" [
]
/// Integration tests for the Custom module of the SQLite library
let customTests = testList "Custom" [
testList "single" [
testTask "succeeds when a row is found" {
use! db = SqliteDb.BuildDb()
@ -196,7 +204,7 @@ let integrationTests =
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! docs = Custom.list (Query.selectFromTable SqliteDb.TableName) [] fromData<JsonDocument>
let! docs = Custom.list (Query.find SqliteDb.TableName) [] fromData<JsonDocument>
Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned"
}
testTask "succeeds when data is not found" {
@ -239,8 +247,10 @@ let integrationTests =
let! nbr = Custom.scalar "SELECT 5 AS test_value" [] _.GetInt32(0)
Expect.equal nbr 5 "The query should have returned the number 5"
}
]
testList "Definition" [
]
/// Integration tests for the Definition module of the SQLite library
let definitionTests = testList "Definition" [
testTask "ensureTable succeeds" {
use! db = SqliteDb.BuildDb()
let itExists (name: string) =
@ -276,7 +286,10 @@ let integrationTests =
let! exists' = indexExists ()
Expect.isTrue exists' "The index should now exist"
}
]
]
/// Integration tests for the (auto-opened) Document module of the SQLite library
let documentTests = testList "Document" [
testList "insert" [
testTask "succeeds" {
use! db = SqliteDb.BuildDb()
@ -324,7 +337,10 @@ let integrationTests =
Expect.equal after.Value upd8Doc "The updated document is not correct"
}
]
testList "Count" [
]
/// Integration tests for the Count module of the SQLite library
let countTests = testList "Count" [
testTask "all succeeds" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
@ -332,22 +348,26 @@ let integrationTests =
let! theCount = Count.all SqliteDb.TableName
Expect.equal theCount 5L "There should have been 5 matching documents"
}
testTask "byField succeeds for a numeric range" {
testList "byFields" [
testTask "succeeds for a numeric range" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! theCount = Count.byField SqliteDb.TableName (Field.BT "NumValue" 10 20)
let! theCount = Count.byFields SqliteDb.TableName Any [ Field.BT "NumValue" 10 20 ]
Expect.equal theCount 3L "There should have been 3 matching documents"
}
testTask "byField succeeds for a non-numeric range" {
testTask "succeeds for a non-numeric range" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! theCount = Count.byField SqliteDb.TableName (Field.BT "Value" "aardvark" "apple")
let! theCount = Count.byFields SqliteDb.TableName Any [ Field.BT "Value" "aardvark" "apple" ]
Expect.equal theCount 1L "There should have been 1 matching document"
}
]
testList "Exists" [
]
/// Integration tests for the Exists module of the SQLite library
let existsTests = testList "Exists" [
testList "byId" [
testTask "succeeds when a document exists" {
use! db = SqliteDb.BuildDb()
@ -364,24 +384,26 @@ let integrationTests =
Expect.isFalse exists "There should not have been an existing document"
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when documents exist" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! exists = Exists.byField SqliteDb.TableName (Field.EQ "NumValue" 10)
let! exists = Exists.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 10 ]
Expect.isTrue exists "There should have been existing documents"
}
testTask "succeeds when no matching documents exist" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! exists = Exists.byField SqliteDb.TableName (Field.LT "Nothing" "none")
let! exists = Exists.byFields SqliteDb.TableName Any [ Field.LT "Nothing" "none" ]
Expect.isFalse exists "There should not have been any existing documents"
}
]
]
testList "Find" [
]
/// Integration tests for the Find module of the SQLite library
let findTests = testList "Find" [
testList "all" [
testTask "succeeds when there is data" {
use! db = SqliteDb.BuildDb()
@ -391,12 +413,10 @@ let integrationTests =
do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" }
let! results = Find.all<SubDocument> SqliteDb.TableName
let expected = [
{ Foo = "one"; Bar = "two" }
{ Foo = "three"; Bar = "four" }
{ Foo = "five"; Bar = "six" }
]
Expect.equal results expected "There should have been 3 documents returned"
Expect.equal
results
[ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
"There should have been 3 documents returned"
}
testTask "succeeds when there is no data" {
use! db = SqliteDb.BuildDb()
@ -404,13 +424,48 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned"
}
]
testList "allOrdered" [
testTask "succeeds when ordering numerically" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! results = Find.allOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"one|three|two|four|five"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering numerically descending" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! results = Find.allOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "n:NumValue DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"five|four|two|three|one"
"The documents were not ordered correctly"
}
testTask "succeeds when ordering alphabetically" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! results = Find.allOrdered<JsonDocument> SqliteDb.TableName [ Field.Named "Id DESC" ]
Expect.hasLength results 5 "There should have been 5 documents returned"
Expect.equal
(results |> List.map _.Id |> String.concat "|")
"two|three|one|four|five"
"The documents were not ordered correctly"
}
]
testList "byId" [
testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "two"
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
}
testTask "succeeds when a document is not found" {
@ -418,52 +473,98 @@ let integrationTests =
do! loadDocs ()
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven"
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
Expect.isNone doc "There should not have been a document returned"
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when documents are found" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! docs = Find.byField<JsonDocument> SqliteDb.TableName (Field.GT "NumValue" 15)
let! docs = Find.byFields<JsonDocument> SqliteDb.TableName Any [ Field.GT "NumValue" 15 ]
Expect.equal (List.length docs) 2 "There should have been two documents returned"
}
testTask "succeeds when documents are not found" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! docs = Find.byField<JsonDocument> SqliteDb.TableName (Field.GT "NumValue" 100)
let! docs = Find.byFields<JsonDocument> SqliteDb.TableName Any [ Field.GT "NumValue" 100 ]
Expect.isTrue (List.isEmpty docs) "There should have been no documents returned"
}
]
testList "firstByField" [
testList "byFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! docs =
Find.byFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id" ]
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "five|four" "The documents were not ordered correctly"
}
testTask "succeeds when sorting descending" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! docs =
Find.byFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.GT "NumValue" 15 ] [ Field.Named "Id DESC" ]
Expect.equal
(docs |> List.map _.Id |> String.concat "|") "four|five" "The documents were not ordered correctly"
}
]
testList "firstByFields" [
testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another")
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "another" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned"
}
testTask "succeeds when multiple documents are found" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green")
Expect.isTrue (Option.isSome doc) "There should have been a document returned"
let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
Expect.isSome doc "There should have been a document returned"
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when a document is not found" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent")
Expect.isFalse (Option.isSome doc) "There should not have been a document returned"
let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "absent" ]
Expect.isNone doc "There should not have been a document returned"
}
]
testList "firstByFieldsOrdered" [
testTask "succeeds when sorting ascending" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "two" doc.Value.Id "An incorrect document was returned"
}
testTask "succeeds when sorting descending" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
let! doc =
Find.firstByFieldsOrdered<JsonDocument>
SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ]
Expect.isSome doc "There should have been a document returned"
Expect.equal "four" doc.Value.Id "An incorrect document was returned"
}
]
testList "Update" [
]
/// Integration tests for the Update module of the SQLite library
let updateTests = testList "Update" [
testList "byId" [
testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb()
@ -483,9 +584,7 @@ let integrationTests =
// This not raising an exception is the test
do! Update.byId
SqliteDb.TableName
"test"
{ emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
SqliteDb.TableName "test" { emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
}
]
testList "byFunc" [
@ -493,8 +592,7 @@ let integrationTests =
use! db = SqliteDb.BuildDb()
do! loadDocs ()
do! Update.byFunc
SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
let! after = Find.byId<string, JsonDocument> SqliteDb.TableName "one"
Expect.isSome after "There should have been a document returned post-update"
Expect.equal
@ -509,12 +607,13 @@ let integrationTests =
Expect.isEmpty before "There should have been no documents returned"
// This not raising an exception is the test
do! Update.byFunc
SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
}
]
]
testList "Patch" [
]
/// Integration tests for the Patch module of the SQLite library
let patchTests = testList "Patch" [
testList "byId" [
testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb()
@ -535,13 +634,13 @@ let integrationTests =
do! Patch.byId SqliteDb.TableName "test" {| Foo = "green" |}
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |}
let! after = Count.byField SqliteDb.TableName (Field.EQ "NumValue" 77)
do! Patch.byFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = Count.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 77 ]
Expect.equal after 2L "There should have been 2 documents returned"
}
testTask "succeeds when no document is updated" {
@ -551,11 +650,13 @@ let integrationTests =
Expect.isEmpty before "There should have been no documents returned"
// This not raising an exception is the test
do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "burgundy") {| Foo = "green" |}
do! Patch.byFields SqliteDb.TableName Any [ Field.EQ "Value" "burgundy" ] {| Foo = "green" |}
}
]
]
testList "RemoveFields" [
]
/// Integration tests for the RemoveFields module of the SQLite library
let removeFieldsTests = testList "RemoveFields" [
testList "byId" [
testTask "succeeds when fields is removed" {
use! db = SqliteDb.BuildDb()
@ -583,12 +684,12 @@ let integrationTests =
do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ]
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when a field is removed" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ]
do! RemoveFields.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Sub" ]
try
let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "four"
Expect.isTrue false "The updated document should have failed to parse"
@ -601,17 +702,19 @@ let integrationTests =
do! loadDocs ()
// This not raising an exception is the test
do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Nothing" ]
do! RemoveFields.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Nothing" ]
}
testTask "succeeds when no document is matched" {
use! db = SqliteDb.BuildDb()
// This not raising an exception is the test
do! RemoveFields.byField SqliteDb.TableName (Field.NE "Abracadabra" "apple") [ "Value" ]
do! RemoveFields.byFields SqliteDb.TableName Any [ Field.NE "Abracadabra" "apple" ] [ "Value" ]
}
]
]
testList "Delete" [
]
/// Integration tests for the Delete module of the SQLite library
let deleteTests = testList "Delete" [
testList "byId" [
testTask "succeeds when a document is deleted" {
use! db = SqliteDb.BuildDb()
@ -630,12 +733,12 @@ let integrationTests =
Expect.equal remaining 5L "There should have been 5 documents remaining"
}
]
testList "byField" [
testList "byFields" [
testTask "succeeds when documents are deleted" {
use! db = SqliteDb.BuildDb()
do! loadDocs ()
do! Delete.byField SqliteDb.TableName (Field.NE "Value" "purple")
do! Delete.byFields SqliteDb.TableName Any [ Field.NE "Value" "purple" ]
let! remaining = Count.all SqliteDb.TableName
Expect.equal remaining 2L "There should have been 2 documents remaining"
}
@ -643,16 +746,28 @@ let integrationTests =
use! db = SqliteDb.BuildDb()
do! loadDocs ()
do! Delete.byField SqliteDb.TableName (Field.EQ "Value" "crimson")
do! Delete.byFields SqliteDb.TableName Any [ Field.EQ "Value" "crimson" ]
let! remaining = Count.all SqliteDb.TableName
Expect.equal remaining 5L "There should have been 5 documents remaining"
}
]
]
test "clean up database" {
Configuration.useConnectionString "data source=:memory:"
}
]
|> testSequenced
]
let all = testList "Sqlite" [ unitTests; integrationTests ]
/// All tests for the SQLite library
let all = testList "Sqlite" [
testList "Unit" [ queryTests; parametersTests ]
testSequenced <| testList "Integration" [
configurationTests
customTests
definitionTests
documentTests
countTests
existsTests
findTests
updateTests
patchTests
removeFieldsTests
deleteTests
test "clean up database" { Configuration.useConnectionString "data source=:memory:" }
]
]