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 # JetBrains Rider
*.sln.iml *.sln.iml
**/.idea

View File

@ -105,6 +105,10 @@ type Field = {
else $"->>'{name}'" else $"->>'{name}'"
$"data{path}" $"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 /// Specify the name of the parameter for this field
member this.WithParameterName name = member this.WithParameterName name =
{ this with ParameterName = Some name } { this with ParameterName = Some name }
@ -292,3 +296,23 @@ module Query =
[<System.Obsolete "Use Find instead">] [<System.Obsolete "Use Find instead">]
let selectFromTable tableName = let selectFromTable tableName =
find 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> <TargetFrameworks>net6.0;net8.0</TargetFrameworks>
<DebugType>embedded</DebugType> <DebugType>embedded</DebugType>
<GenerateDocumentationFile>false</GenerateDocumentationFile> <GenerateDocumentationFile>false</GenerateDocumentationFile>
<AssemblyVersion>3.1.0.0</AssemblyVersion> <AssemblyVersion>4.0.0.0</AssemblyVersion>
<FileVersion>3.1.0.0</FileVersion> <FileVersion>4.0.0.0</FileVersion>
<VersionPrefix>3.1.0</VersionPrefix> <VersionPrefix>4.0.0</VersionPrefix>
<PackageReleaseNotes>Add BT (between) operator; drop .NET 7 support</PackageReleaseNotes> <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> <Authors>danieljsummers</Authors>
<Company>Bit Badger Solutions</Company> <Company>Bit Badger Solutions</Company>
<PackageReadmeFile>README.md</PackageReadmeFile> <PackageReadmeFile>README.md</PackageReadmeFile>

View File

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Library.fs" /> <Compile Include="Library.fs" />
<Compile Include="Extensions.fs" /> <Compile Include="Extensions.fs" />
<Compile Include="Compat.fs" />
<None Include="README.md" Pack="true" PackagePath="\" /> <None Include="README.md" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" /> <None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup> </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 namespace BitBadger.Documents.Postgres
open BitBadger.Documents
open Npgsql open Npgsql
open Npgsql.FSharp open Npgsql.FSharp
@ -54,11 +53,6 @@ module Extensions =
member conn.countByFields tableName howMatched fields = member conn.countByFields tableName howMatched fields =
WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn) 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 (@>) /// Count matching documents using a JSON containment query (@>)
member conn.countByContains tableName criteria = member conn.countByContains tableName criteria =
WithProps.Count.byContains tableName criteria (Sql.existingConnection conn) WithProps.Count.byContains tableName criteria (Sql.existingConnection conn)
@ -75,11 +69,6 @@ module Extensions =
member conn.existsByFields tableName howMatched fields = member conn.existsByFields tableName howMatched fields =
WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn) 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 (@>) /// Determine if documents exist using a JSON containment query (@>)
member conn.existsByContains tableName criteria = member conn.existsByContains tableName criteria =
WithProps.Exists.byContains tableName criteria (Sql.existingConnection conn) WithProps.Exists.byContains tableName criteria (Sql.existingConnection conn)
@ -92,6 +81,10 @@ module Extensions =
member conn.findAll<'TDoc> tableName = member conn.findAll<'TDoc> tableName =
WithProps.Find.all<'TDoc> tableName (Sql.existingConnection conn) 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 /// Retrieve a document by its ID; returns None if not found
member conn.findById<'TKey, 'TDoc> tableName docId = member conn.findById<'TKey, 'TDoc> tableName docId =
WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn) WithProps.Find.byId<'TKey, 'TDoc> tableName docId (Sql.existingConnection conn)
@ -100,36 +93,56 @@ module Extensions =
member conn.findByFields<'TDoc> tableName howMatched fields = member conn.findByFields<'TDoc> tableName howMatched fields =
WithProps.Find.byFields<'TDoc> tableName howMatched fields (Sql.existingConnection conn) 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
[<System.Obsolete "Use findByFields instead; will be removed in v4">] /// document
member conn.findByField<'TDoc> tableName field = member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
conn.findByFields<'TDoc> tableName Any [ field ] WithProps.Find.byFieldsOrdered<'TDoc>
tableName howMatched queryFields orderFields (Sql.existingConnection conn)
/// Retrieve documents matching a JSON containment query (@>) /// Retrieve documents matching a JSON containment query (@>)
member conn.findByContains<'TDoc> tableName (criteria: obj) = member conn.findByContains<'TDoc> tableName (criteria: obj) =
WithProps.Find.byContains<'TDoc> tableName criteria (Sql.existingConnection conn) 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 (@?) /// Retrieve documents matching a JSON Path match query (@?)
member conn.findByJsonPath<'TDoc> tableName jsonPath = member conn.findByJsonPath<'TDoc> tableName jsonPath =
WithProps.Find.byJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn) 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 /// Retrieve the first document matching a JSON field comparison query (->> =); returns None if not found
member conn.findFirstByFields<'TDoc> tableName howMatched fields = member conn.findFirstByFields<'TDoc> tableName howMatched fields =
WithProps.Find.firstByFields<'TDoc> tableName howMatched fields (Sql.existingConnection conn) 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 /// Retrieve the first document matching a JSON field comparison query (->> =) ordered by the given fields in
[<System.Obsolete "Use findFirstByFields instead; will be removed in v4">] /// the document; returns None if not found
member conn.findFirstByField<'TDoc> tableName field = member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
conn.findFirstByFields<'TDoc> tableName Any [ field ] 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 /// Retrieve the first document matching a JSON containment query (@>); returns None if not found
member conn.findFirstByContains<'TDoc> tableName (criteria: obj) = member conn.findFirstByContains<'TDoc> tableName (criteria: obj) =
WithProps.Find.firstByContains<'TDoc> tableName criteria (Sql.existingConnection conn) 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 /// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
member conn.findFirstByJsonPath<'TDoc> tableName jsonPath = member conn.findFirstByJsonPath<'TDoc> tableName jsonPath =
WithProps.Find.firstByJsonPath<'TDoc> tableName jsonPath (Sql.existingConnection conn) 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 /// Update an entire document by its ID
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) = member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
WithProps.Update.byId tableName docId document (Sql.existingConnection conn) WithProps.Update.byId tableName docId document (Sql.existingConnection conn)
@ -146,11 +159,6 @@ module Extensions =
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) = member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn) 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 (@>) /// Patch documents using a JSON containment query in the WHERE clause (@>)
member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) = member conn.patchByContains tableName (criteria: 'TCriteria) (patch: 'TPatch) =
WithProps.Patch.byContains tableName criteria patch (Sql.existingConnection conn) WithProps.Patch.byContains tableName criteria patch (Sql.existingConnection conn)
@ -167,11 +175,6 @@ module Extensions =
member conn.removeFieldsByFields tableName howMatched fields fieldNames = member conn.removeFieldsByFields tableName howMatched fields fieldNames =
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn) 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 (@>) /// Remove fields from documents via a JSON containment query (@>)
member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames = member conn.removeFieldsByContains tableName (criteria: 'TContains) fieldNames =
WithProps.RemoveFields.byContains tableName criteria fieldNames (Sql.existingConnection conn) WithProps.RemoveFields.byContains tableName criteria fieldNames (Sql.existingConnection conn)
@ -187,11 +190,6 @@ module Extensions =
member conn.deleteByFields tableName howMatched fields = member conn.deleteByFields tableName howMatched fields =
WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn) 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 (@>) /// Delete documents by matching a JSON containment query (@>)
member conn.deleteByContains tableName (criteria: 'TContains) = member conn.deleteByContains tableName (criteria: 'TContains) =
WithProps.Delete.byContains tableName criteria (Sql.existingConnection conn) WithProps.Delete.byContains tableName criteria (Sql.existingConnection conn)
@ -263,12 +261,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline CountByFields(conn, tableName, howMatched, fields) = static member inline CountByFields(conn, tableName, howMatched, fields) =
WithProps.Count.byFields tableName howMatched fields (Sql.existingConnection conn) 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 (@>) /// Count matching documents using a JSON containment query (@>)
[<Extension>] [<Extension>]
static member inline CountByContains(conn, tableName, criteria: 'TCriteria) = static member inline CountByContains(conn, tableName, criteria: 'TCriteria) =
@ -289,12 +281,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline ExistsByFields(conn, tableName, howMatched, fields) = static member inline ExistsByFields(conn, tableName, howMatched, fields) =
WithProps.Exists.byFields tableName howMatched fields (Sql.existingConnection conn) 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 (@>) /// Determine if documents exist using a JSON containment query (@>)
[<Extension>] [<Extension>]
static member inline ExistsByContains(conn, tableName, criteria: 'TCriteria) = static member inline ExistsByContains(conn, tableName, criteria: 'TCriteria) =
@ -310,6 +296,11 @@ type NpgsqlConnectionCSharpExtensions =
static member inline FindAll<'TDoc>(conn, tableName) = static member inline FindAll<'TDoc>(conn, tableName) =
WithProps.Find.All<'TDoc>(tableName, Sql.existingConnection conn) 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 /// Retrieve a document by its ID; returns None if not found
[<Extension>] [<Extension>]
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) = 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) = static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
WithProps.Find.ByFields<'TDoc>(tableName, howMatched, fields, Sql.existingConnection conn) 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>] [<Extension>]
[<System.Obsolete "Use FindByFields instead; will be removed in v4">] static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) =
static member inline FindByField<'TDoc>(conn, tableName, field) = WithProps.Find.ByFieldsOrdered<'TDoc>(
conn.FindByFields<'TDoc>(tableName, Any, [ field ]) tableName, howMatched, queryFields, orderFields, Sql.existingConnection conn)
/// Retrieve documents matching a JSON containment query (@>) /// Retrieve documents matching a JSON containment query (@>)
[<Extension>] [<Extension>]
static member inline FindByContains<'TDoc>(conn, tableName, criteria: obj) = static member inline FindByContains<'TDoc>(conn, tableName, criteria: obj) =
WithProps.Find.ByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn) 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 (@?) /// Retrieve documents matching a JSON Path match query (@?)
[<Extension>] [<Extension>]
static member inline FindByJsonPath<'TDoc>(conn, tableName, jsonPath) = static member inline FindByJsonPath<'TDoc>(conn, tableName, jsonPath) =
WithProps.Find.ByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn) 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 /// Retrieve the first document matching a JSON field comparison query (->> =); returns null if not found
[<Extension>] [<Extension>]
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) = static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
WithProps.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, Sql.existingConnection conn) 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>] [<Extension>]
[<System.Obsolete "Use FindFirstByFields instead; will be removed in v4">] static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) = conn, tableName, howMatched, queryFields, orderFields) =
conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ]) 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 /// Retrieve the first document matching a JSON containment query (@>); returns None if not found
[<Extension>] [<Extension>]
static member inline FindFirstByContains<'TDoc when 'TDoc: null>(conn, tableName, criteria: obj) = static member inline FindFirstByContains<'TDoc when 'TDoc: null>(conn, tableName, criteria: obj) =
WithProps.Find.FirstByContains<'TDoc>(tableName, criteria, Sql.existingConnection conn) 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 /// Retrieve the first document matching a JSON Path match query (@?); returns None if not found
[<Extension>] [<Extension>]
static member inline FindFirstByJsonPath<'TDoc when 'TDoc: null>(conn, tableName, jsonPath) = static member inline FindFirstByJsonPath<'TDoc when 'TDoc: null>(conn, tableName, jsonPath) =
WithProps.Find.FirstByJsonPath<'TDoc>(tableName, jsonPath, Sql.existingConnection conn) 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 /// Update an entire document by its ID
[<Extension>] [<Extension>]
static member inline UpdateById(conn, tableName, docId: 'TKey, document: 'TDoc) = 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) = static member inline PatchByFields(conn, tableName, howMatched, fields, patch: 'TPatch) =
WithProps.Patch.byFields tableName howMatched fields patch (Sql.existingConnection conn) 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 (@>) /// Patch documents using a JSON containment query in the WHERE clause (@>)
[<Extension>] [<Extension>]
static member inline PatchByContains(conn, tableName, criteria: 'TCriteria, patch: 'TPatch) = 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) = static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
WithProps.RemoveFields.byFields tableName howMatched fields fieldNames (Sql.existingConnection conn) 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 (@>) /// Remove fields from documents via a JSON containment query (@>)
[<Extension>] [<Extension>]
static member inline RemoveFieldsByContains(conn, tableName, criteria: 'TContains, fieldNames) = static member inline RemoveFieldsByContains(conn, tableName, criteria: 'TContains, fieldNames) =
@ -429,12 +433,6 @@ type NpgsqlConnectionCSharpExtensions =
static member inline DeleteByFields(conn, tableName, howMatched, fields) = static member inline DeleteByFields(conn, tableName, howMatched, fields) =
WithProps.Delete.byFields tableName howMatched fields (Sql.existingConnection conn) 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 (@>) /// Delete documents by matching a JSON containment query (@>)
[<Extension>] [<Extension>]
static member inline DeleteByContains(conn, tableName, criteria: 'TContains) = static member inline DeleteByContains(conn, tableName, criteria: 'TContains) =

View File

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

View File

@ -8,6 +8,7 @@
<ItemGroup> <ItemGroup>
<Compile Include="Library.fs" /> <Compile Include="Library.fs" />
<Compile Include="Extensions.fs" /> <Compile Include="Extensions.fs" />
<Compile Include="Compat.fs" />
<None Include="README.md" Pack="true" PackagePath="\" /> <None Include="README.md" Pack="true" PackagePath="\" />
<None Include="..\icon.png" Pack="true" PackagePath="\" /> <None Include="..\icon.png" Pack="true" PackagePath="\" />
</ItemGroup> </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 = member conn.countByFields tableName howMatched fields =
WithConn.Count.byFields tableName howMatched fields conn 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 /// Determine if a document exists for the given ID
member conn.existsById tableName (docId: 'TKey) = member conn.existsById tableName (docId: 'TKey) =
WithConn.Exists.byId tableName docId conn WithConn.Exists.byId tableName docId conn
@ -62,15 +57,14 @@ module Extensions =
member conn.existsByFields tableName howMatched fields = member conn.existsByFields tableName howMatched fields =
WithConn.Exists.byFields tableName howMatched fields conn 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 /// Retrieve all documents in the given table
member conn.findAll<'TDoc> tableName = member conn.findAll<'TDoc> tableName =
WithConn.Find.all<'TDoc> tableName conn 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 /// Retrieve a document by its ID
member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) = member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) =
WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn
@ -79,19 +73,18 @@ module Extensions =
member conn.findByFields<'TDoc> tableName howMatched fields = member conn.findByFields<'TDoc> tableName howMatched fields =
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn 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
[<System.Obsolete "Use findByFields instead; will be removed in v4">] member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
member conn.findByField<'TDoc> tableName field = WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
conn.findByFields<'TDoc> tableName Any [ field ]
/// Retrieve documents via a comparison on JSON fields, returning only the first result /// Retrieve documents via a comparison on JSON fields, returning only the first result
member conn.findFirstByFields<'TDoc> tableName howMatched fields = member conn.findFirstByFields<'TDoc> tableName howMatched fields =
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn 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
[<System.Obsolete "Use findFirstByFields instead; will be removed in v4">] /// only the first result
member conn.findFirstByField<'TDoc> tableName field = member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
conn.findFirstByFields<'TDoc> tableName Any [ field ] WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn
/// Update an entire document by its ID /// Update an entire document by its ID
member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) = member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) =
@ -109,11 +102,6 @@ module Extensions =
member conn.patchByFields tableName howMatched fields (patch: 'TPatch) = member conn.patchByFields tableName howMatched fields (patch: 'TPatch) =
WithConn.Patch.byFields tableName howMatched fields patch conn 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 /// Remove fields from a document by the document's ID
member conn.removeFieldsById tableName (docId: 'TKey) fieldNames = member conn.removeFieldsById tableName (docId: 'TKey) fieldNames =
WithConn.RemoveFields.byId tableName docId fieldNames conn WithConn.RemoveFields.byId tableName docId fieldNames conn
@ -122,11 +110,6 @@ module Extensions =
member conn.removeFieldsByFields tableName howMatched fields fieldNames = member conn.removeFieldsByFields tableName howMatched fields fieldNames =
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn 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 /// Delete a document by its ID
member conn.deleteById tableName (docId: 'TKey) = member conn.deleteById tableName (docId: 'TKey) =
WithConn.Delete.byId tableName docId conn WithConn.Delete.byId tableName docId conn
@ -135,11 +118,6 @@ module Extensions =
member conn.deleteByFields tableName howMatched fields = member conn.deleteByFields tableName howMatched fields =
WithConn.Delete.byFields tableName howMatched fields conn 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 open System.Runtime.CompilerServices
@ -198,12 +176,6 @@ type SqliteConnectionCSharpExtensions =
static member inline CountByFields(conn, tableName, howMatched, fields) = static member inline CountByFields(conn, tableName, howMatched, fields) =
WithConn.Count.byFields tableName howMatched fields conn 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 /// Determine if a document exists for the given ID
[<Extension>] [<Extension>]
static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) = static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) =
@ -214,17 +186,16 @@ type SqliteConnectionCSharpExtensions =
static member inline ExistsByFields(conn, tableName, howMatched, fields) = static member inline ExistsByFields(conn, tableName, howMatched, fields) =
WithConn.Exists.byFields tableName howMatched fields conn 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 /// Retrieve all documents in the given table
[<Extension>] [<Extension>]
static member inline FindAll<'TDoc>(conn, tableName) = static member inline FindAll<'TDoc>(conn, tableName) =
WithConn.Find.All<'TDoc>(tableName, conn) 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 /// Retrieve a document by its ID
[<Extension>] [<Extension>]
static member inline FindById<'TKey, 'TDoc when 'TDoc: null>(conn, tableName, docId: 'TKey) = 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) = static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) =
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn) 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>] [<Extension>]
[<System.Obsolete "Use FindByFields instead; will be removed in v4">] static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) =
static member inline FindByField<'TDoc>(conn, tableName, field) = WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
conn.FindByFields<'TDoc>(tableName, Any, [ field ])
/// Retrieve documents via a comparison on JSON fields, returning only the first result /// Retrieve documents via a comparison on JSON fields, returning only the first result
[<Extension>] [<Extension>]
static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) = static member inline FindFirstByFields<'TDoc when 'TDoc: null>(conn, tableName, howMatched, fields) =
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn) 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>] [<Extension>]
[<System.Obsolete "Use FindFirstByFields instead; will be removed in v4">] static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null>(
static member inline FindFirstByField<'TDoc when 'TDoc: null>(conn, tableName, field) = conn, tableName, howMatched, queryFields, orderFields) =
conn.FindFirstByFields<'TDoc>(tableName, Any, [ field ]) WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn)
/// Update an entire document by its ID /// Update an entire document by its ID
[<Extension>] [<Extension>]
@ -272,12 +243,6 @@ type SqliteConnectionCSharpExtensions =
static member inline PatchByFields<'TPatch>(conn, tableName, howMatched, fields, patch: 'TPatch) = static member inline PatchByFields<'TPatch>(conn, tableName, howMatched, fields, patch: 'TPatch) =
WithConn.Patch.byFields tableName howMatched fields patch conn 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 /// Remove fields from a document by the document's ID
[<Extension>] [<Extension>]
static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) = 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) = static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) =
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn 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 /// Delete a document by its ID
[<Extension>] [<Extension>]
static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) = static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) =

View File

@ -314,6 +314,15 @@ module WithConn =
let All<'TDoc>(tableName, conn) = let All<'TDoc>(tableName, conn) =
Custom.List(Query.find tableName, [], fromData<'TDoc>, 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) /// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">] [<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn = let byId<'TKey, 'TDoc> tableName (docId: 'TKey) conn =
@ -332,12 +341,6 @@ module WithConn =
fromData<'TDoc> fromData<'TDoc>
conn 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 /// Retrieve documents via a comparison on JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields, conn) = let ByFields<'TDoc>(tableName, howMatched, fields, conn) =
Custom.List<'TDoc>( Custom.List<'TDoc>(
@ -346,10 +349,22 @@ module WithConn =
fromData<'TDoc>, fromData<'TDoc>,
conn) 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
[<System.Obsolete "Use ByFields instead; will be removed in v4">] [<CompiledName "FSharpByFieldsOrdered">]
let ByField<'TDoc>(tableName, field, conn) = let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn =
ByFields<'TDoc>(tableName, Any, [ field ], 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 /// Retrieve documents via a comparison on JSON fields, returning only the first result
[<CompiledName "FSharpFirstByFields">] [<CompiledName "FSharpFirstByFields">]
@ -360,12 +375,6 @@ module WithConn =
fromData<'TDoc> fromData<'TDoc>
conn 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 /// Retrieve documents via a comparison on JSON fields, returning only the first result
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, conn) = let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields, conn) =
Custom.Single( Custom.Single(
@ -374,10 +383,24 @@ module WithConn =
fromData<'TDoc>, fromData<'TDoc>,
conn) 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
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">] /// only the first result
let FirstByField<'TDoc when 'TDoc: null>(tableName, field, conn) = [<CompiledName "FSharpFirstByFieldsOrdered">]
FirstByFields(tableName, Any, [ field ], conn) 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 /// Commands to update documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -418,12 +441,6 @@ module WithConn =
(addFieldParams fields [ jsonParam "@data" patch ]) (addFieldParams fields [ jsonParam "@data" patch ])
conn 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 /// Commands to remove fields from documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module RemoveFields = module RemoveFields =
@ -446,12 +463,6 @@ module WithConn =
(addFieldParams fields nameParams) (addFieldParams fields nameParams)
conn 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 /// Commands to delete documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Delete = module Delete =
@ -466,12 +477,6 @@ module WithConn =
let byFields tableName howMatched fields conn = let byFields tableName howMatched fields conn =
Custom.nonQuery (Query.byFields (Query.delete tableName) howMatched fields) (addFieldParams 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 /// Commands to execute custom SQL queries
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -516,6 +521,7 @@ module Custom =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn) WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn)
/// Functions to create tables and indexes /// Functions to create tables and indexes
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Definition = module Definition =
@ -532,6 +538,7 @@ module Definition =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Definition.ensureFieldIndex tableName indexName fields conn WithConn.Definition.ensureFieldIndex tableName indexName fields conn
/// Document insert/save functions /// Document insert/save functions
[<AutoOpen>] [<AutoOpen>]
module Document = module Document =
@ -548,6 +555,7 @@ module Document =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.save tableName document conn WithConn.save tableName document conn
/// Commands to count documents /// Commands to count documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Count = module Count =
@ -564,11 +572,6 @@ module Count =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Count.byFields tableName howMatched fields conn 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 /// Commands to determine if documents exist
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -586,11 +589,6 @@ module Exists =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Exists.byFields tableName howMatched fields conn 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 /// Commands to determine if documents exist
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -607,6 +605,17 @@ module Find =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Find.All<'TDoc>(tableName, conn) 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) /// Retrieve a document by its ID (returns None if not found)
[<CompiledName "FSharpById">] [<CompiledName "FSharpById">]
let byId<'TKey, 'TDoc> tableName docId = let byId<'TKey, 'TDoc> tableName docId =
@ -624,21 +633,21 @@ module Find =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Find.byFields<'TDoc> tableName howMatched fields conn 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 /// Retrieve documents via a comparison on JSON fields
let ByFields<'TDoc>(tableName, howMatched, fields) = let ByFields<'TDoc>(tableName, howMatched, fields) =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn) 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
[<System.Obsolete "Use ByFields instead; will be removed in v4">] [<CompiledName "FSharpByFieldsOrdered">]
let ByField<'TDoc>(tableName, field) = let byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields =
ByFields<'TDoc>(tableName, Any, [ field ]) 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 /// Retrieve documents via a comparison on JSON fields, returning only the first result
[<CompiledName "FSharpFirstByFields">] [<CompiledName "FSharpFirstByFields">]
@ -646,21 +655,24 @@ module Find =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn 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 /// Retrieve documents via a comparison on JSON fields, returning only the first result
let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) = let FirstByFields<'TDoc when 'TDoc: null>(tableName, howMatched, fields) =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn) 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
[<System.Obsolete "Use FirstByFields instead; will be removed in v4">] /// the first result
let FirstByField<'TDoc when 'TDoc: null>(tableName, field) = [<CompiledName "FSharpFirstByFieldsOrdered">]
FirstByFields<'TDoc>(tableName, Any, [ field ]) 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 /// Commands to update documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -683,6 +695,7 @@ module Update =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Update.ByFunc(tableName, idFunc, document, conn) WithConn.Update.ByFunc(tableName, idFunc, document, conn)
/// Commands to patch (partially update) documents /// Commands to patch (partially update) documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
module Patch = module Patch =
@ -699,11 +712,6 @@ module Patch =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Patch.byFields tableName howMatched fields patch conn 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 /// Commands to remove fields from documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -721,11 +729,6 @@ module RemoveFields =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn 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 /// Commands to delete documents
[<RequireQualifiedAccess>] [<RequireQualifiedAccess>]
@ -742,9 +745,3 @@ module Delete =
let byFields tableName howMatched fields = let byFields tableName howMatched fields =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
WithConn.Delete.byFields tableName howMatched fields conn 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.Name, "Rad", "Field name incorrect");
Expect.equal(field.Op, Op.NEX, "Operator 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", () => TestCase("WithParameterName succeeds", () =>
{ {
var field = Field.EQ("Bob", "Tom").WithParameterName("@name"); var field = Field.EQ("Bob", "Tom").WithParameterName("@name");
@ -305,7 +330,7 @@ public static class CommonCSharpTests
{ {
Expect.equal( Expect.equal(
Query.Definition.EnsureIndexOn("test.table", "gibberish", 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 " "CREATE INDEX IF NOT EXISTS idx_table_gibberish ON test.table "
+ "((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)", + "((data->>'taco'), (data->>'guac') DESC, (data->>'salsa') ASC)",
"CREATE INDEX for multiple field statement incorrect"); "CREATE INDEX for multiple field statement incorrect");
@ -356,7 +381,60 @@ public static class CommonCSharpTests
TestCase("Delete succeeds", () => TestCase("Delete succeeds", () =>
{ {
Expect.equal(Query.Delete("tbl"), "DELETE FROM tbl", "Delete query not correct"); 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 using var conn = MkConn(db);
await LoadDocs(); 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>); Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}), }),
@ -53,7 +53,7 @@ public class PostgresCSharpExtensionTests
var docs = await conn.CustomList( var docs = await conn.CustomList(
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", $"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>); Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned"); Expect.isEmpty(docs, "There should have been no documents returned");
}) })
@ -67,7 +67,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id", 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "one", "The incorrect document was returned"); Expect.equal(doc.Id, "one", "The incorrect document was returned");
}), }),
@ -78,7 +78,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id", 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"); Expect.isNull(doc, "There should not have been a document returned");
}) })
]), ]),
@ -102,7 +102,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
await conn.CustomNonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", 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); var remaining = await conn.CountAll(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table"); 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 db = PostgresDb.BuildDb();
await using var conn = MkConn(db); 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 exists = await TableExists();
var alsoExists = await keyExists(); var alsoExists = await KeyExists();
Expect.isFalse(exists, "The table should not exist already"); Expect.isFalse(exists, "The table should not exist already");
Expect.isFalse(alsoExists, "The key index should not exist already"); Expect.isFalse(alsoExists, "The key index should not exist already");
await conn.EnsureTable("ensured"); await conn.EnsureTable("ensured");
exists = await tableExists(); exists = await TableExists();
alsoExists = await keyExists(); alsoExists = await KeyExists();
Expect.isTrue(exists, "The table should now exist"); Expect.isTrue(exists, "The table should now exist");
Expect.isTrue(alsoExists, "The key index 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 () => TestCase("EnsureDocumentIndex succeeds", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db); 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"); Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured"); await conn.EnsureTable("ensured");
await conn.EnsureDocumentIndex("ensured", DocumentIndex.Optimized); await conn.EnsureDocumentIndex("ensured", DocumentIndex.Optimized);
exists = await indexExists(); exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist"); 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 () => TestCase("EnsureFieldIndex succeeds", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db); 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"); Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured"); await conn.EnsureTable("ensured");
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await indexExists(); exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist"); 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", TestList("Insert",
[ [
@ -240,17 +246,16 @@ public class PostgresCSharpExtensionTests
var theCount = await conn.CountAll(PostgresDb.TableName); var theCount = await conn.CountAll(PostgresDb.TableName);
Expect.equal(theCount, 5, "There should have been 5 matching documents"); Expect.equal(theCount, 5, "There should have been 5 matching documents");
}), }),
#pragma warning disable CS0618 TestCase("CountByFields succeeds", async () =>
TestCase("CountByField succeeds", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); Expect.equal(theCount, 2, "There should have been 2 matching documents");
}), }),
#pragma warning restore CS0618
TestCase("CountByContains succeeds", async () => TestCase("CountByContains succeeds", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
@ -290,7 +295,6 @@ public class PostgresCSharpExtensionTests
Expect.isFalse(exists, "There should not have been an existing document"); Expect.isFalse(exists, "There should not have been an existing document");
}) })
]), ]),
#pragma warning disable CS0618
TestList("ExistsByField", TestList("ExistsByField",
[ [
TestCase("succeeds when documents exist", async () => TestCase("succeeds when documents exist", async () =>
@ -299,7 +303,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); Expect.isTrue(exists, "There should have been existing documents");
}), }),
TestCase("succeeds when documents do not exist", async () => TestCase("succeeds when documents do not exist", async () =>
@ -308,11 +312,11 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); Expect.isFalse(exists, "There should not have been existing documents");
}) })
]), ]),
#pragma warning restore CS0618
TestList("ExistsByContains", TestList("ExistsByContains",
[ [
TestCase("succeeds when documents exist", async () => TestCase("succeeds when documents exist", async () =>
@ -377,6 +381,44 @@ public class PostgresCSharpExtensionTests
Expect.isEmpty(results, "There should have been no documents returned"); 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", TestList("FindById",
[ [
TestCase("succeeds when a document is found", async () => 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"); Expect.isNull(doc, "There should not have been a document returned");
}) })
]), ]),
#pragma warning disable CS0618 TestList("FindByFields",
TestList("FindByField",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
{ {
@ -408,7 +449,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); Expect.equal(docs.Count, 1, "There should have been one document returned");
}), }),
TestCase("succeeds when documents are not found", async () => TestCase("succeeds when documents are not found", async () =>
@ -417,11 +459,38 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); 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", TestList("FindByContains",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
@ -444,6 +513,34 @@ public class PostgresCSharpExtensionTests
Expect.isEmpty(docs, "There should have been no documents returned"); 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", TestList("FindByJsonPath",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
@ -465,8 +562,35 @@ public class PostgresCSharpExtensionTests
Expect.isEmpty(docs, "There should have been no documents returned"); Expect.isEmpty(docs, "There should have been no documents returned");
}) })
]), ]),
#pragma warning disable CS0618 TestList("FindByJsonPathOrdered",
TestList("FindFirstByField", [
// 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 () => TestCase("succeeds when a document is found", async () =>
{ {
@ -474,7 +598,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "two", "The incorrect document was 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 using var conn = MkConn(db);
await LoadDocs(); 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.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 () => TestCase("succeeds when a document is not found", async () =>
{ {
@ -494,11 +620,36 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); 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", TestList("FindFirstByContains",
[ [
TestCase("succeeds when a document is found", async () => TestCase("succeeds when a document is found", async () =>
@ -520,7 +671,7 @@ public class PostgresCSharpExtensionTests
var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName, var doc = await conn.FindFirstByContains<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }); new { Sub = new { Foo = "green" } });
Expect.isNotNull(doc, "There should have been a document returned"); 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 () => 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"); 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", TestList("FindFirstByJsonPath",
[ [
TestCase("succeeds when a document is found", async () => TestCase("succeeds when a document is found", async () =>
@ -554,7 +730,7 @@ public class PostgresCSharpExtensionTests
var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName, var doc = await conn.FindFirstByJsonPath<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")"); "$.Sub.Foo ? (@ == \"green\")");
Expect.isNotNull(doc, "There should have been a document returned"); 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 () => 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"); 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", TestList("UpdateById",
[ [
TestCase("succeeds when a document is updated", async () => TestCase("succeeds when a document is updated", async () =>
@ -650,8 +851,7 @@ public class PostgresCSharpExtensionTests
await conn.PatchById(PostgresDb.TableName, "test", new { Foo = "green" }); await conn.PatchById(PostgresDb.TableName, "test", new { Foo = "green" });
}) })
]), ]),
#pragma warning disable CS0618 TestList("PatchByFields",
TestList("PatchByField",
[ [
TestCase("succeeds when a document is updated", async () => TestCase("succeeds when a document is updated", async () =>
{ {
@ -659,8 +859,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); await LoadDocs();
await conn.PatchByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); await conn.PatchByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
var after = await conn.CountByField(PostgresDb.TableName, Field.EQ("NumValue", "77")); 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"); Expect.equal(after, 2, "There should have been 2 documents returned");
}), }),
TestCase("succeeds when no document is updated", async () => 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"); Expect.equal(before, 0, "There should have been no documents returned");
// This not raising an exception is the test // 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", TestList("PatchByContains",
[ [
TestCase("succeeds when a document is updated", async () => TestCase("succeeds when a document is updated", async () =>
@ -729,7 +931,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var conn = MkConn(db);
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -754,7 +956,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
@ -762,11 +964,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
// This not raising an exception is the test // 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("RemoveFieldsByFields",
TestList("RemoveFieldsByField",
[ [
TestCase("succeeds when multiple fields are removed", async () => TestCase("succeeds when multiple fields are removed", async () =>
{ {
@ -774,8 +975,8 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); await LoadDocs();
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
new[] { "Sub", "Value" }); ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var conn = MkConn(db);
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -800,7 +1002,8 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
@ -808,11 +1011,10 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
// This not raising an exception is the test // This not raising an exception is the test
await conn.RemoveFieldsByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), await conn.RemoveFieldsByFields(PostgresDb.TableName, FieldMatch.Any,
new[] { "Value" }); [Field.NE("Abracadabra", "apple")], ["Value"]);
}) })
]), ]),
#pragma warning restore CS0618
TestList("RemoveFieldsByContains", TestList("RemoveFieldsByContains",
[ [
TestCase("succeeds when multiple fields are removed", async () => TestCase("succeeds when multiple fields are removed", async () =>
@ -821,8 +1023,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); await LoadDocs();
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, await conn.RemoveFieldsByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub", "Value"]);
new[] { "Sub", "Value" });
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var conn = MkConn(db);
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -847,7 +1048,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
@ -855,8 +1056,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
// This not raising an exception is the test // This not raising an exception is the test
await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, await conn.RemoveFieldsByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, ["Value"]);
new[] { "Value" });
}) })
]), ]),
TestList("RemoveFieldsByJsonPath", TestList("RemoveFieldsByJsonPath",
@ -867,8 +1067,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); await LoadDocs();
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub", "Value"]);
new[] { "Sub", "Value" });
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var conn = MkConn(db);
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -893,7 +1092,7 @@ public class PostgresCSharpExtensionTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
@ -901,8 +1100,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
// This not raising an exception is the test // This not raising an exception is the test
await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", await conn.RemoveFieldsByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", ["Value"]);
new[] { "Value" });
}) })
]), ]),
TestList("DeleteById", TestList("DeleteById",
@ -928,8 +1126,7 @@ public class PostgresCSharpExtensionTests
Expect.equal(remaining, 5, "There should have been 5 documents remaining"); Expect.equal(remaining, 5, "There should have been 5 documents remaining");
}) })
]), ]),
#pragma warning disable CS0618 TestList("DeleteByFields",
TestList("DeleteByField",
[ [
TestCase("succeeds when documents are deleted", async () => TestCase("succeeds when documents are deleted", async () =>
{ {
@ -937,7 +1134,7 @@ public class PostgresCSharpExtensionTests
await using var conn = MkConn(db); await using var conn = MkConn(db);
await LoadDocs(); 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); var remaining = await conn.CountAll(PostgresDb.TableName);
Expect.equal(remaining, 2, "There should have been 2 documents remaining"); 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 using var conn = MkConn(db);
await LoadDocs(); 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); var remaining = await conn.CountAll(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should have been 5 documents remaining"); Expect.equal(remaining, 5, "There should have been 5 documents remaining");
}) })
]), ]),
#pragma warning restore CS0618
TestList("DeleteByContains", TestList("DeleteByContains",
[ [
TestCase("succeeds when documents are deleted", async () => TestCase("succeeds when documents are deleted", async () =>

View File

@ -14,11 +14,9 @@ using static Runner;
public static class PostgresCSharpTests public static class PostgresCSharpTests
{ {
/// <summary> /// <summary>
/// Tests which do not hit the database /// Unit tests for the Parameters module of the PostgreSQL library
/// </summary> /// </summary>
private static readonly Test Unit = TestList("Unit", private static readonly Test ParametersTests = TestList("Parameters",
[
TestList("Parameters",
[ [
TestList("Id", TestList("Id",
[ [
@ -69,29 +67,25 @@ public static class PostgresCSharpTests
{ {
var it = Parameters.Id((ulong)6464); var it = Parameters.Id((ulong)6464);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.int64(6464), Expect.equal(it.Item2, Sql.int64(6464), "Unsigned long ID parameter not constructed correctly");
"Unsigned long ID parameter not constructed correctly");
}), }),
TestCase("succeeds for decimal ID", () => TestCase("succeeds for decimal ID", () =>
{ {
var it = Parameters.Id((decimal)4.56); var it = Parameters.Id((decimal)4.56);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@decimal((decimal)4.56), Expect.equal(it.Item2, Sql.@decimal((decimal)4.56), "Decimal ID parameter not constructed correctly");
"Decimal ID parameter not constructed correctly");
}), }),
TestCase("succeeds for single ID", () => TestCase("succeeds for single ID", () =>
{ {
var it = Parameters.Id((float)5.67); var it = Parameters.Id((float)5.67);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@double((float)5.67), Expect.equal(it.Item2, Sql.@double((float)5.67), "Single ID parameter not constructed correctly");
"Single ID parameter not constructed correctly");
}), }),
TestCase("succeeds for double ID", () => TestCase("succeeds for double ID", () =>
{ {
var it = Parameters.Id(6.78); var it = Parameters.Id(6.78);
Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly"); Expect.equal(it.Item1, "@id", "ID parameter not constructed correctly");
Expect.equal(it.Item2, Sql.@double(6.78), Expect.equal(it.Item2, Sql.@double(6.78), "Double ID parameter not constructed correctly");
"Double ID parameter not constructed correctly");
}), }),
TestCase("succeeds for string ID", () => TestCase("succeeds for string ID", () =>
{ {
@ -176,32 +170,13 @@ public static class PostgresCSharpTests
Expect.isTrue(false, "The parameter was not a StringArray type"); 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", TestList("WhereByFields",
[ [
@ -259,8 +234,7 @@ public static class PostgresCSharpTests
[ [
TestCase("succeeds for numeric ID", () => TestCase("succeeds for numeric ID", () =>
{ {
Expect.equal(Postgres.Query.WhereById(18), "(data->>'Id')::numeric = @id", Expect.equal(Postgres.Query.WhereById(18), "(data->>'Id')::numeric = @id", "WHERE clause not correct");
"WHERE clause not correct");
}), }),
TestCase("succeeds for string ID", () => TestCase("succeeds for string ID", () =>
{ {
@ -298,8 +272,7 @@ public static class PostgresCSharpTests
]), ]),
TestCase("WhereDataContains succeeds", () => TestCase("WhereDataContains succeeds", () =>
{ {
Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test", Expect.equal(Postgres.Query.WhereDataContains("@test"), "data @> @test", "WHERE clause not correct");
"WHERE clause not correct");
}), }),
TestCase("WhereJsonPathMatches succeeds", () => TestCase("WhereJsonPathMatches succeeds", () =>
{ {
@ -318,15 +291,14 @@ public static class PostgresCSharpTests
}), }),
TestCase("ById succeeds", () => TestCase("ById succeeds", () =>
{ {
Expect.equal(Postgres.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", Expect.equal(Postgres.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", "By-ID query not correct");
"By-ID query not correct");
}), }),
TestCase("ByFields succeeds", () => TestCase("ByFields succeeds", () =>
{ {
Expect.equal(Postgres.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]), Expect.equal(Postgres.Query.ByFields("unit", FieldMatch.Any, [Field.GT("That", 14)]),
"unit WHERE (data->>'That')::numeric > @field0", "By-Field query not correct"); "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", Expect.equal(Postgres.Query.ByContains("exam"), "exam WHERE data @> @criteria",
"By-Contains query not correct"); "By-Contains query not correct");
@ -336,7 +308,15 @@ public static class PostgresCSharpTests
Expect.equal(Postgres.Query.ByPathMatch("verify"), "verify WHERE data @? @path::jsonpath", Expect.equal(Postgres.Query.ByPathMatch("verify"), "verify WHERE data @? @path::jsonpath",
"By-JSON Path query not correct"); "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 = private static readonly List<JsonDocument> TestDocuments =
@ -357,11 +337,9 @@ public static class PostgresCSharpTests
} }
/// <summary> /// <summary>
/// Integration tests for the PostgreSQL library /// Integration tests for the Configuration module of the PostgreSQL library
/// </summary> /// </summary>
private static readonly Test Integration = TestList("Integration", private static readonly Test ConfigurationTests = TestList("Configuration",
[
TestList("Configuration",
[ [
TestCase("UseDataSource disposes existing source", () => TestCase("UseDataSource disposes existing source", () =>
{ {
@ -390,8 +368,12 @@ public static class PostgresCSharpTests
Expect.isTrue(ReferenceEquals(source, Postgres.Configuration.DataSource()), Expect.isTrue(ReferenceEquals(source, Postgres.Configuration.DataSource()),
"Data source should have been the same"); "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", TestList("List",
[ [
@ -400,7 +382,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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>); Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); 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( var docs = await Custom.List(
$"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", $"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>);
Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned"); Expect.isEmpty(docs, "There should have been no documents returned");
}) })
]), ]),
@ -424,7 +405,7 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id", 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "one", "The incorrect document was returned"); Expect.equal(doc.Id, "one", "The incorrect document was returned");
}), }),
@ -434,7 +415,7 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id", 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"); Expect.isNull(doc, "There should not have been a document returned");
}) })
]), ]),
@ -456,7 +437,7 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
await Custom.NonQuery($"DELETE FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath", 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); var remaining = await Count.All(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should be 5 documents remaining in the table"); 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")); 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"); 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 () => TestCase("EnsureTable succeeds", async () =>
{ {
@ -491,6 +476,7 @@ public static class PostgresCSharpTests
Task<bool> TableExists() => Custom.Scalar( Task<bool> TableExists() => Custom.Scalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None, "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'ensured') AS it", Parameters.None,
Results.ToExists); Results.ToExists);
Task<bool> KeyExists() => Custom.Scalar( Task<bool> KeyExists() => Custom.Scalar(
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None, "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_key') AS it", Parameters.None,
Results.ToExists); Results.ToExists);
@ -520,7 +506,7 @@ public static class PostgresCSharpTests
Expect.isFalse(exists, "The index should not exist already"); Expect.isFalse(exists, "The index should not exist already");
await Definition.EnsureTable("ensured"); await Definition.EnsureTable("ensured");
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); await Definition.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await IndexExists(); exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist"); Expect.isTrue(exists, "The index should now exist");
return; 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, "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it", Parameters.None,
Results.ToExists); 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", TestList("Insert",
[ [
@ -591,8 +581,12 @@ public static class PostgresCSharpTests
Expect.equal(after.Sub!.Foo, "c", "The updated document is not correct"); 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 () => TestCase("All succeeds", async () =>
{ {
@ -602,24 +596,27 @@ public static class PostgresCSharpTests
var theCount = await Count.All(PostgresDb.TableName); var theCount = await Count.All(PostgresDb.TableName);
Expect.equal(theCount, 5, "There should have been 5 matching documents"); Expect.equal(theCount, 5, "There should have been 5 matching documents");
}), }),
#pragma warning disable CS0618 TestList("ByFields",
TestCase("ByField succeeds for numeric range", async () => [
TestCase("succeeds for numeric range", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); Expect.equal(theCount, 1, "There should have been 1 matching document");
}), })
#pragma warning restore CS0618 ]),
TestCase("ByContains succeeds", async () => TestCase("ByContains succeeds", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
@ -636,8 +633,12 @@ public static class PostgresCSharpTests
var theCount = await Count.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)"); var theCount = await Count.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ > 5)");
Expect.equal(theCount, 3, "There should have been 3 matching documents"); 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", TestList("ById",
[ [
@ -658,15 +659,14 @@ public static class PostgresCSharpTests
Expect.isFalse(exists, "There should not have been an existing document"); Expect.isFalse(exists, "There should not have been an existing document");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when documents exist", async () => TestCase("succeeds when documents exist", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); Expect.isTrue(exists, "There should have been existing documents");
}), }),
TestCase("succeeds when documents do not exist", async () => TestCase("succeeds when documents do not exist", async () =>
@ -674,11 +674,10 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); Expect.isFalse(exists, "There should not have been existing documents");
}) })
]), ]),
#pragma warning restore CS0618
TestList("ByContains", TestList("ByContains",
[ [
TestCase("succeeds when documents exist", async () => 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"); 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", TestList("All",
[ [
@ -731,7 +734,7 @@ public static class PostgresCSharpTests
await Document.Insert(PostgresDb.TableName, new SubDocument { Foo = "five", Bar = "six" }); await Document.Insert(PostgresDb.TableName, new SubDocument { Foo = "five", Bar = "six" });
var results = await Find.All<SubDocument>(PostgresDb.TableName); 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 () => 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"); 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", TestList("ById",
[ [
TestCase("succeeds when a document is found", async () => 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"); Expect.isNull(doc, "There should not have been a document returned");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); await LoadDocs();
var docs = await Find.ByField<JsonDocument>(PostgresDb.TableName, Field.EQ("Value", "another")); var docs = await Find.ByFields<JsonDocument>(PostgresDb.TableName, FieldMatch.Any,
Expect.equal(docs.Count, 1, "There should have been one document returned"); [Field.EQ("Value", "another")]);
Expect.hasLength(docs, 1, "There should have been one document returned");
}), }),
TestCase("succeeds when documents are not found", async () => TestCase("succeeds when documents are not found", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); 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", TestList("ByContains",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
@ -790,7 +853,7 @@ public static class PostgresCSharpTests
var docs = await Find.ByContains<JsonDocument>(PostgresDb.TableName, var docs = await Find.ByContains<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }); 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 () => 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"); 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", TestList("ByJsonPath",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
@ -809,7 +898,7 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
var docs = await Find.ByJsonPath<JsonDocument>(PostgresDb.TableName, "$.NumValue ? (@ < 15)"); 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 () => 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"); Expect.isEmpty(docs, "There should have been no documents returned");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByJsonPathOrdered",
TestList("FirstByField", [
// 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 () => TestCase("succeeds when a document is found", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc.Id, "two", "The incorrect document was 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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.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 () => TestCase("succeeds when a document is not found", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); 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", TestList("FirstByContains",
[ [
TestCase("succeeds when a document is found", async () => TestCase("succeeds when a document is found", async () =>
@ -870,7 +1009,7 @@ public static class PostgresCSharpTests
var doc = await Find.FirstByContains<JsonDocument>(PostgresDb.TableName, var doc = await Find.FirstByContains<JsonDocument>(PostgresDb.TableName,
new { Sub = new { Foo = "green" } }); new { Sub = new { Foo = "green" } });
Expect.isNotNull(doc, "There should have been a document returned"); 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 () => 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"); 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", TestList("FirstByJsonPath",
[ [
TestCase("succeeds when a document is found", async () => TestCase("succeeds when a document is found", async () =>
@ -901,7 +1063,7 @@ public static class PostgresCSharpTests
var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName, var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName,
"$.Sub.Foo ? (@ == \"green\")"); "$.Sub.Foo ? (@ == \"green\")");
Expect.isNotNull(doc, "There should have been a document returned"); 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 () => 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\")"); var doc = await Find.FirstByJsonPath<JsonDocument>(PostgresDb.TableName, "$.Id ? (@ == \"nope\")");
Expect.isNull(doc, "There should not have been a document returned"); 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", TestList("ById",
[ [
@ -973,8 +1162,12 @@ public static class PostgresCSharpTests
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); 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", TestList("ById",
[ [
@ -999,16 +1192,16 @@ public static class PostgresCSharpTests
await Patch.ById(PostgresDb.TableName, "test", new { Foo = "green" }); await Patch.ById(PostgresDb.TableName, "test", new { Foo = "green" });
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when a document is updated", async () => TestCase("succeeds when a document is updated", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); await LoadDocs();
await Patch.ByField(PostgresDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); await Patch.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
var after = await Count.ByField(PostgresDb.TableName, Field.EQ("NumValue", "77")); 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"); Expect.equal(after, 2, "There should have been 2 documents returned");
}), }),
TestCase("succeeds when no document is updated", async () => 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"); Expect.equal(before, 0, "There should have been no documents returned");
// This not raising an exception is the test // 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", TestList("ByContains",
[ [
TestCase("succeeds when a document is updated", async () => 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" }); 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", TestList("ById",
[ [
@ -1077,7 +1274,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1100,26 +1297,25 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test // 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("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when multiple fields are removed", async () => TestCase("succeeds when multiple fields are removed", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); await LoadDocs();
await RemoveFields.ByField(PostgresDb.TableName, Field.EQ("NumValue", "17"), await RemoveFields.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.EQ("NumValue", "17")],
new[] { "Sub", "Value" }); ["Sub", "Value"]);
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1142,18 +1339,18 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test // This not raising an exception is the test
await RemoveFields.ByField(PostgresDb.TableName, Field.NE("Abracadabra", "apple"), await RemoveFields.ByFields(PostgresDb.TableName, FieldMatch.Any, [Field.NE("Abracadabra", "apple")],
new[] { "Value" }); ["Value"]);
}) })
]), ]),
#pragma warning restore CS0618
TestList("ByContains", TestList("ByContains",
[ [
TestCase("succeeds when multiple fields are removed", async () => TestCase("succeeds when multiple fields are removed", async () =>
@ -1161,8 +1358,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); await LoadDocs();
await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, await RemoveFields.ByContains(PostgresDb.TableName, new { NumValue = 17 }, ["Sub", "Value"]);
new[] { "Sub", "Value" });
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1185,15 +1381,14 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test // This not raising an exception is the test
await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, await RemoveFields.ByContains(PostgresDb.TableName, new { Abracadabra = "apple" }, ["Value"]);
new[] { "Value" });
}) })
]), ]),
TestList("ByJsonPath", TestList("ByJsonPath",
@ -1203,8 +1398,7 @@ public static class PostgresCSharpTests
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); await LoadDocs();
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ == 17)", ["Sub", "Value"]);
new[] { "Sub", "Value" });
var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(PostgresDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.notEqual(updated.Value, "", "The string value should not have been removed"); Expect.notEqual(updated.Value, "", "The string value should not have been removed");
@ -1227,19 +1421,22 @@ public static class PostgresCSharpTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
// This not raising an exception is the test // This not raising an exception is the test
await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", await RemoveFields.ByJsonPath(PostgresDb.TableName, "$.Abracadabra ? (@ == \"apple\")", ["Value"]);
new[] { "Value" });
}) })
]) ])
]), ]);
TestList("Delete",
/// <summary>
/// Integration tests for the Delete module of the PostgreSQL library
/// </summary>
private static readonly Test DeleteTests = TestList("Delete",
[ [
TestList("ById", TestList("ById",
[ [
@ -1262,15 +1459,14 @@ public static class PostgresCSharpTests
Expect.equal(remaining, 5, "There should have been 5 documents remaining"); Expect.equal(remaining, 5, "There should have been 5 documents remaining");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when documents are deleted", async () => TestCase("succeeds when documents are deleted", async () =>
{ {
await using var db = PostgresDb.BuildDb(); await using var db = PostgresDb.BuildDb();
await LoadDocs(); 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); var remaining = await Count.All(PostgresDb.TableName);
Expect.equal(remaining, 3, "There should have been 3 documents remaining"); 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 using var db = PostgresDb.BuildDb();
await LoadDocs(); 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); var remaining = await Count.All(PostgresDb.TableName);
Expect.equal(remaining, 5, "There should have been 5 documents remaining"); Expect.equal(remaining, 5, "There should have been 5 documents remaining");
}) })
]), ]),
#pragma warning restore CS0618
TestList("ByContains", TestList("ByContains",
[ [
TestCase("succeeds when documents are deleted", async () => 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"); Expect.equal(remaining, 5, "There should have been 5 documents remaining");
}) })
]) ])
])
]); ]);
/// <summary> /// <summary>
/// All Postgres C# tests /// All Postgres C# tests
/// </summary> /// </summary>
[Tests] [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.CSharp;
using Expecto; using Expecto;
using Microsoft.Data.Sqlite;
using BitBadger.Documents.Sqlite; using BitBadger.Documents.Sqlite;
namespace BitBadger.Documents.Tests.CSharp; namespace BitBadger.Documents.Tests.CSharp;
@ -29,7 +28,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs(); await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "one", "The incorrect document was returned"); Expect.equal(doc!.Id, "one", "The incorrect document was returned");
}), }),
@ -40,7 +39,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs(); await LoadDocs();
var doc = await conn.CustomSingle($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", 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"); 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 using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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>); Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}), }),
@ -63,8 +62,8 @@ public static class SqliteCSharpExtensionTests
await LoadDocs(); await LoadDocs();
var docs = await conn.CustomList( var docs = await conn.CustomList(
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)],
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>); Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned"); Expect.isEmpty(docs, "There should have been no documents returned");
}) })
]), ]),
@ -88,7 +87,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs(); await LoadDocs();
await conn.CustomNonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", 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); var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table"); 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 db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
Func<string, ValueTask<bool>> itExists = async name => var exists = await ItExists("ensured");
await conn.CustomScalar( var alsoExists = await ItExists("idx_ensured_key");
$"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");
Expect.isFalse(exists, "The table should not exist already"); Expect.isFalse(exists, "The table should not exist already");
Expect.isFalse(alsoExists, "The key index should not exist already"); Expect.isFalse(alsoExists, "The key index should not exist already");
await conn.EnsureTable("ensured"); await conn.EnsureTable("ensured");
exists = await itExists("ensured"); exists = await ItExists("ensured");
alsoExists = await itExists("idx_ensured_key"); alsoExists = await ItExists("idx_ensured_key");
Expect.isTrue(exists, "The table should now exist"); Expect.isTrue(exists, "The table should now exist");
Expect.isTrue(alsoExists, "The key index 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 () => TestCase("EnsureFieldIndex succeeds", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn(); 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"); Expect.isFalse(exists, "The index should not exist already");
await conn.EnsureTable("ensured"); await conn.EnsureTable("ensured");
await conn.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); await conn.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await indexExists(); exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist"); 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", TestList("Insert",
[ [
@ -213,17 +215,15 @@ public static class SqliteCSharpExtensionTests
var theCount = await conn.CountAll(SqliteDb.TableName); var theCount = await conn.CountAll(SqliteDb.TableName);
Expect.equal(theCount, 5L, "There should have been 5 matching documents"); Expect.equal(theCount, 5L, "There should have been 5 matching documents");
}), }),
#pragma warning disable CS0618
TestCase("CountByField succeeds", async () => TestCase("CountByField succeeds", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); Expect.equal(theCount, 2L, "There should have been 2 matching documents");
}), }),
#pragma warning restore CS0618
TestList("ExistsById", TestList("ExistsById",
[ [
TestCase("succeeds when a document exists", async () => 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"); Expect.isFalse(exists, "There should not have been an existing document");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ExistsByFields",
TestList("ExistsByField",
[ [
TestCase("succeeds when documents exist", async () => TestCase("succeeds when documents exist", async () =>
{ {
@ -254,7 +253,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); Expect.isTrue(exists, "There should have been existing documents");
}), }),
TestCase("succeeds when no matching documents exist", async () => TestCase("succeeds when no matching documents exist", async () =>
@ -263,11 +262,11 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); Expect.isFalse(exists, "There should not have been any existing documents");
}) })
]), ]),
#pragma warning restore CS0618
TestList("FindAll", TestList("FindAll",
[ [
TestCase("succeeds when there is data", async () => 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"); 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", TestList("FindById",
[ [
TestCase("succeeds when a document is found", async () => 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"); Expect.isNull(doc, "There should not have been a document returned");
}) })
]), ]),
#pragma warning disable CS0618 TestList("FindByFields",
TestList("FindByField",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
{ {
@ -321,7 +356,8 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); Expect.equal(docs.Count, 2, "There should have been two documents returned");
}), }),
TestCase("succeeds when documents are not found", async () => TestCase("succeeds when documents are not found", async () =>
@ -330,11 +366,37 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); 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 () => TestCase("succeeds when a document is found", async () =>
{ {
@ -342,7 +404,8 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "two", "The incorrect document was 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 using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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.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 () => 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 using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); 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", TestList("UpdateById",
[ [
TestCase("succeeds when a document is updated", async () => 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" }); await conn.PatchById(SqliteDb.TableName, "test", new { Foo = "green" });
}) })
]), ]),
#pragma warning disable CS0618 TestList("PatchByFields",
TestList("PatchByField",
[ [
TestCase("succeeds when a document is updated", async () => TestCase("succeeds when a document is updated", async () =>
{ {
@ -459,8 +547,9 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); await LoadDocs();
await conn.PatchByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); await conn.PatchByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
var after = await conn.CountByField(SqliteDb.TableName, Field.EQ("NumValue", 77)); 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"); Expect.equal(after, 2L, "There should have been 2 documents returned");
}), }),
TestCase("succeeds when no document is updated", async () => 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"); Expect.isEmpty(before, "There should have been no documents returned");
// This not raising an exception is the test // 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", TestList("RemoveFieldsById",
[ [
TestCase("succeeds when fields are removed", async () => TestCase("succeeds when fields are removed", async () =>
@ -483,7 +572,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); Expect.equal(updated.Value, "", "The string value should have been removed");
@ -496,7 +585,7 @@ public static class SqliteCSharpExtensionTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
@ -504,11 +593,10 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
// This not raising an exception is the test // 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("RemoveFieldsByFields",
TestList("RemoveFieldsByField",
[ [
TestCase("succeeds when a field is removed", async () => TestCase("succeeds when a field is removed", async () =>
{ {
@ -516,7 +604,8 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.isNull(updated.Sub, "The sub-document should have been removed"); Expect.isNull(updated.Sub, "The sub-document should have been removed");
@ -528,7 +617,8 @@ public static class SqliteCSharpExtensionTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
@ -536,10 +626,10 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
// This not raising an exception is the test // 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", TestList("DeleteById",
[ [
TestCase("succeeds when a document is deleted", async () => 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"); Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
}) })
]), ]),
#pragma warning disable CS0618 TestList("DeleteByFields",
TestList("DeleteByField",
[ [
TestCase("succeeds when documents are deleted", async () => TestCase("succeeds when documents are deleted", async () =>
{ {
@ -572,7 +661,7 @@ public static class SqliteCSharpExtensionTests
await using var conn = Sqlite.Configuration.DbConn(); await using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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); var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 2L, "There should have been 2 documents remaining"); 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 using var conn = Sqlite.Configuration.DbConn();
await LoadDocs(); 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); var remaining = await conn.CountAll(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); 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:")) TestCase("Clean up database", () => Sqlite.Configuration.UseConnectionString("data source=:memory:"))
]); ]);
} }

View File

@ -1,6 +1,5 @@
using Expecto.CSharp; using Expecto.CSharp;
using Expecto; using Expecto;
using Microsoft.Data.Sqlite;
using Microsoft.FSharp.Core; using Microsoft.FSharp.Core;
using BitBadger.Documents.Sqlite; using BitBadger.Documents.Sqlite;
@ -14,19 +13,16 @@ using static Runner;
public static class SqliteCSharpTests public static class SqliteCSharpTests
{ {
/// <summary> /// <summary>
/// Unit tests for the SQLite library /// Unit tests for the Query module of the SQLite library
/// </summary> /// </summary>
private static readonly Test Unit = TestList("Unit", private static readonly Test QueryTests = TestList("Query",
[
TestList("Query",
[ [
TestList("WhereByFields", TestList("WhereByFields",
[ [
TestCase("succeeds for a single field when a logical operator is passed", () => TestCase("succeeds for a single field when a logical operator is passed", () =>
{ {
Expect.equal( Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.Any, Sqlite.Query.WhereByFields(FieldMatch.Any, [Field.GT("theField", 0).WithParameterName("@test")]),
[Field.GT("theField", 0).WithParameterName("@test")]),
"data->>'theField' > @test", "WHERE clause not correct"); "data->>'theField' > @test", "WHERE clause not correct");
}), }),
TestCase("succeeds for a single field when an existence operator is passed", () => 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", () => TestCase("succeeds for all multiple fields with logical operators", () =>
{ {
Expect.equal( Expect.equal(
Sqlite.Query.WhereByFields(FieldMatch.All, Sqlite.Query.WhereByFields(FieldMatch.All, [Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")]),
[Field.EQ("theFirst", "1"), Field.EQ("numberTwo", "2")]),
"data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1", "WHERE clause not correct"); "data->>'theFirst' = @field0 AND data->>'numberTwo' = @field1", "WHERE clause not correct");
}), }),
TestCase("succeeds for any multiple fields with an existence operator", () => TestCase("succeeds for any multiple fields with an existence operator", () =>
@ -80,8 +75,7 @@ public static class SqliteCSharpTests
}), }),
TestCase("ById succeeds", () => TestCase("ById succeeds", () =>
{ {
Expect.equal(Sqlite.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", Expect.equal(Sqlite.Query.ById("test", "14"), "test WHERE data->>'Id' = @id", "By-ID query not correct");
"By-ID query not correct");
}), }),
TestCase("ByFields succeeds", () => TestCase("ByFields succeeds", () =>
{ {
@ -93,8 +87,12 @@ public static class SqliteCSharpTests
Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"), Expect.equal(Sqlite.Query.Definition.EnsureTable("tbl"),
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)", "CREATE TABLE statement not correct"); "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", () => TestCase("Id succeeds", () =>
{ {
@ -127,10 +125,13 @@ public static class SqliteCSharpTests
{ {
Expect.isEmpty(Parameters.None, "The parameter list should have been empty"); 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 = private static readonly List<JsonDocument> TestDocuments =
[ [
new() { Id = "one", Value = "FIRST!", NumValue = 0 }, 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); foreach (var doc in TestDocuments) await Document.Insert(SqliteDb.TableName, doc);
} }
private static readonly Test Integration = TestList("Integration", /// <summary>
[ /// Integration tests for the Configuration module of the SQLite library
TestCase("Configuration.UseConnectionString succeeds", () => /// </summary>
private static readonly Test ConfigurationTests = TestCase("Configuration.UseConnectionString succeeds", () =>
{ {
try try
{ {
@ -162,8 +164,12 @@ public static class SqliteCSharpTests
{ {
Sqlite.Configuration.UseConnectionString("Data Source=:memory:"); 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", TestList("Single",
[ [
@ -173,7 +179,7 @@ public static class SqliteCSharpTests
await LoadDocs(); await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "one", "The incorrect document was returned"); Expect.equal(doc!.Id, "one", "The incorrect document was returned");
}), }),
@ -183,7 +189,7 @@ public static class SqliteCSharpTests
await LoadDocs(); await LoadDocs();
var doc = await Custom.Single($"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id", 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"); 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 using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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>); Results.FromData<JsonDocument>);
Expect.equal(docs.Count, 5, "There should have been 5 documents returned"); Expect.equal(docs.Count, 5, "There should have been 5 documents returned");
}), }),
@ -204,8 +210,8 @@ public static class SqliteCSharpTests
await LoadDocs(); await LoadDocs();
var docs = await Custom.List( var docs = await Custom.List(
$"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", [new("@value", 100)],
new[] { new SqliteParameter("@value", 100) }, Results.FromData<JsonDocument>); Results.FromData<JsonDocument>);
Expect.isEmpty(docs, "There should have been no documents returned"); Expect.isEmpty(docs, "There should have been no documents returned");
}) })
]), ]),
@ -227,7 +233,7 @@ public static class SqliteCSharpTests
await LoadDocs(); await LoadDocs();
await Custom.NonQuery($"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value", 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); var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should be 5 documents remaining in the table"); 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)); 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"); 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 () => TestCase("EnsureTable succeeds", async () =>
{ {
@ -262,9 +272,8 @@ public static class SqliteCSharpTests
async ValueTask<bool> ItExists(string name) async ValueTask<bool> ItExists(string name)
{ {
return await Custom.Scalar( return await Custom.Scalar($"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it",
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = @name) AS it", [new("@name", name)], Results.ToExists);
new SqliteParameter[] { new("@name", name) }, Results.ToExists);
} }
}), }),
TestCase("EnsureFieldIndex succeeds", async () => TestCase("EnsureFieldIndex succeeds", async () =>
@ -275,7 +284,7 @@ public static class SqliteCSharpTests
Expect.isFalse(exists, "The index should not exist already"); Expect.isFalse(exists, "The index should not exist already");
await Definition.EnsureTable("ensured"); await Definition.EnsureTable("ensured");
await Definition.EnsureFieldIndex("ensured", "test", new[] { "Id", "Category" }); await Definition.EnsureFieldIndex("ensured", "test", ["Id", "Category"]);
exists = await IndexExists(); exists = await IndexExists();
Expect.isTrue(exists, "The index should now exist"); Expect.isTrue(exists, "The index should now exist");
return; return;
@ -284,8 +293,14 @@ public static class SqliteCSharpTests
$"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it", $"SELECT EXISTS (SELECT 1 FROM {SqliteDb.Catalog} WHERE name = 'idx_ensured_test') AS it",
Parameters.None, Results.ToExists); 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 () => TestCase("succeeds", async () =>
{ {
@ -344,8 +359,13 @@ public static class SqliteCSharpTests
Expect.equal(after!.Id, "test", "The updated document is not correct"); 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"); 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 () => TestCase("All succeeds", async () =>
{ {
@ -355,26 +375,32 @@ public static class SqliteCSharpTests
var theCount = await Count.All(SqliteDb.TableName); var theCount = await Count.All(SqliteDb.TableName);
Expect.equal(theCount, 5L, "There should have been 5 matching documents"); Expect.equal(theCount, 5L, "There should have been 5 matching documents");
}), }),
#pragma warning disable CS0618 TestList("ByFields",
TestCase("ByField succeeds for numeric range", async () => [
TestCase("succeeds for numeric range", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); 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 using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); 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", TestList("ById",
[ [
@ -395,15 +421,14 @@ public static class SqliteCSharpTests
Expect.isFalse(exists, "There should not have been an existing document"); Expect.isFalse(exists, "There should not have been an existing document");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when documents exist", async () => TestCase("succeeds when documents exist", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); Expect.isTrue(exists, "There should have been existing documents");
}), }),
TestCase("succeeds when no matching documents exist", async () => TestCase("succeeds when no matching documents exist", async () =>
@ -411,13 +436,16 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); 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", TestList("All",
[ [
@ -439,6 +467,39 @@ public static class SqliteCSharpTests
Expect.isEmpty(results, "There should have been no documents returned"); 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", TestList("ById",
[ [
TestCase("succeeds when a document is found", async () => 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"); Expect.isNull(doc, "There should not have been a document returned");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when documents are found", async () => TestCase("succeeds when documents are found", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); Expect.equal(docs.Count, 2, "There should have been two documents returned");
}), }),
TestCase("succeeds when documents are not found", async () => TestCase("succeeds when documents are not found", async () =>
@ -475,18 +536,43 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); 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 () => TestCase("succeeds when a document is found", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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.isNotNull(doc, "There should have been a document returned");
Expect.equal(doc!.Id, "two", "The incorrect document was 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 using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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.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 () => TestCase("succeeds when a document is not found", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); 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", TestList("ById",
[ [
@ -568,8 +682,12 @@ public static class SqliteCSharpTests
new JsonDocument { Id = "one", Value = "le un", NumValue = 1 }); 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", TestList("ById",
[ [
@ -595,16 +713,16 @@ public static class SqliteCSharpTests
await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" }); await Patch.ById(SqliteDb.TableName, "test", new { Foo = "green" });
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when a document is updated", async () => TestCase("succeeds when a document is updated", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); await LoadDocs();
await Patch.ByField(SqliteDb.TableName, Field.EQ("Value", "purple"), new { NumValue = 77 }); await Patch.ByFields(SqliteDb.TableName, FieldMatch.Any, [Field.EQ("Value", "purple")],
var after = await Count.ByField(SqliteDb.TableName, Field.EQ("NumValue", 77)); 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"); Expect.equal(after, 2L, "There should have been 2 documents returned");
}), }),
TestCase("succeeds when no document is updated", async () => 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"); Expect.isEmpty(before, "There should have been no documents returned");
// This not raising an exception is the test // 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", TestList("ById",
[ [
@ -629,7 +751,7 @@ public static class SqliteCSharpTests
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "two");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.equal(updated.Value, "", "The string value should have been removed"); Expect.equal(updated.Value, "", "The string value should have been removed");
@ -641,25 +763,24 @@ public static class SqliteCSharpTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
// This not raising an exception is the test // 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("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when a field is removed", async () => TestCase("succeeds when a field is removed", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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"); var updated = await Find.ById<string, JsonDocument>(SqliteDb.TableName, "four");
Expect.isNotNull(updated, "The updated document should have been retrieved"); Expect.isNotNull(updated, "The updated document should have been retrieved");
Expect.isNull(updated.Sub, "The sub-document should have been removed"); Expect.isNull(updated.Sub, "The sub-document should have been removed");
@ -670,19 +791,24 @@ public static class SqliteCSharpTests
await LoadDocs(); await LoadDocs();
// This not raising an exception is the test // 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 () => TestCase("succeeds when no document is matched", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
// This not raising an exception is the test // 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", TestList("ById",
[ [
@ -705,15 +831,14 @@ public static class SqliteCSharpTests
Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); Expect.equal(remaining, 5L, "There should have been 5 documents remaining");
}) })
]), ]),
#pragma warning disable CS0618 TestList("ByFields",
TestList("ByField",
[ [
TestCase("succeeds when documents are deleted", async () => TestCase("succeeds when documents are deleted", async () =>
{ {
await using var db = await SqliteDb.BuildDb(); await using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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); var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 2L, "There should have been 2 documents remaining"); 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 using var db = await SqliteDb.BuildDb();
await LoadDocs(); 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); var remaining = await Count.All(SqliteDb.TableName);
Expect.equal(remaining, 5L, "There should have been 5 documents remaining"); 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> /// <summary>
/// All tests for SQLite C# functions and methods /// All tests for SQLite C# functions and methods
/// </summary> /// </summary>
[Tests] [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> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<NoWarn>1182</NoWarn>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@ -109,6 +109,28 @@ let all =
Expect.isNone field.ParameterName "The default parameter name should be None" Expect.isNone field.ParameterName "The default parameter name should be None"
Expect.isNone field.Qualifier "The default table qualifier 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" { test "WithParameterName succeeds" {
let field = (Field.EQ "Bob" "Tom").WithParameterName "@name" let field = (Field.EQ "Bob" "Tom").WithParameterName "@name"
Expect.isSome field.ParameterName "The parameter name should have been filled" Expect.isSome field.ParameterName "The parameter name should have been filled"
@ -253,5 +275,51 @@ let all =
test "delete succeeds" { test "delete succeeds" {
Expect.equal (Query.delete tbl) $"DELETE FROM {tbl}" "Delete query not correct" 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 Npgsql
open Types open Types
#nowarn "0044"
/// Open a connection to the throwaway database /// Open a connection to the throwaway database
let private mkConn (db: ThrowawayPostgresDb) = let private mkConn (db: ThrowawayPostgresDb) =
let conn = new NpgsqlConnection(db.ConnectionString) let conn = new NpgsqlConnection(db.ConnectionString)
@ -27,7 +25,7 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" Expect.equal (List.length docs) 5 "There should have been 5 documents returned"
} }
testTask "succeeds when data is not found" { testTask "succeeds when data is not found" {
@ -211,12 +209,12 @@ let integrationTests =
let! theCount = conn.countAll PostgresDb.TableName let! theCount = conn.countAll PostgresDb.TableName
Expect.equal theCount 5 "There should have been 5 matching documents" Expect.equal theCount 5 "There should have been 5 matching documents"
} }
testTask "countByField succeeds" { testTask "countByFields succeeds" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" Expect.equal theCount 2 "There should have been 2 matching documents"
} }
testTask "countByContains succeeds" { testTask "countByContains succeeds" {
@ -253,13 +251,13 @@ let integrationTests =
Expect.isFalse exists "There should not have been an existing document" Expect.isFalse exists "There should not have been an existing document"
} }
] ]
testList "existsByField" [ testList "existsByFields" [
testTask "succeeds when documents exist" { testTask "succeeds when documents exist" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" Expect.isTrue exists "There should have been existing documents"
} }
testTask "succeeds when documents do not exist" { testTask "succeeds when documents do not exist" {
@ -267,7 +265,7 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" 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" } do! conn.insert PostgresDb.TableName { Foo = "five"; Bar = "six" }
let! results = conn.findAll<SubDocument> PostgresDb.TableName let! results = conn.findAll<SubDocument> PostgresDb.TableName
let expected = [ Expect.equal
{ Foo = "one"; Bar = "two" } results
{ Foo = "three"; Bar = "four" } [ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
{ Foo = "five"; Bar = "six" } "There should have been 3 documents returned"
]
Expect.equal results expected "There should have been 3 documents returned"
} }
testTask "succeeds when there is no data" { testTask "succeeds when there is no data" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -331,6 +327,44 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned" 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" [ testList "findById" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -350,13 +384,13 @@ let integrationTests =
Expect.isNone 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" { testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" Expect.equal (List.length docs) 1 "There should have been one document returned"
} }
testTask "succeeds when documents are not found" { testTask "succeeds when documents are not found" {
@ -364,10 +398,36 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" 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" [ testList "findByContains" [
testTask "succeeds when documents are found" { testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -386,6 +446,33 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned" 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" [ testList "findByJsonPath" [
testTask "succeeds when documents are found" { testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -404,13 +491,40 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned" 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" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned"
} }
@ -419,7 +533,7 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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.isSome doc "There should have been a document returned"
Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned" Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
} }
@ -428,10 +542,34 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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" 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" [ testList "findFirstByContains" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -460,6 +598,30 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned" 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" [ testList "findFirstByJsonPath" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -488,6 +650,30 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned" 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" [ testList "updateById" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -558,14 +744,14 @@ let integrationTests =
do! conn.patchById PostgresDb.TableName "test" {| Foo = "green" |} do! conn.patchById PostgresDb.TableName "test" {| Foo = "green" |}
} }
] ]
testList "patchByField" [ testList "patchByFields" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn do! loadDocs conn
do! conn.patchByField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} do! conn.patchByFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = conn.countByField PostgresDb.TableName (Field.EQ "NumValue" "77") let! after = conn.countByFields PostgresDb.TableName Any [ Field.EQ "NumValue" "77" ]
Expect.equal after 2 "There should have been 2 documents returned" Expect.equal after 2 "There should have been 2 documents returned"
} }
testTask "succeeds when no document is updated" { testTask "succeeds when no document is updated" {
@ -575,7 +761,7 @@ let integrationTests =
Expect.equal before 0 "There should have been no documents returned" Expect.equal before 0 "There should have been no documents returned"
// This not raising an exception is the test // 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" [ testList "patchByContains" [
@ -625,9 +811,9 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub"; "Value" ] 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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -636,9 +822,9 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Sub" ] 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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -657,16 +843,16 @@ let integrationTests =
do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ] do! conn.removeFieldsById PostgresDb.TableName "two" [ "Value" ]
} }
] ]
testList "removeFieldsByField" [ testList "removeFieldsByFields" [
testTask "succeeds when multiple fields are removed" { testTask "succeeds when multiple fields are removed" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ] do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -674,10 +860,10 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsByField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ] do! conn.removeFieldsByFields PostgresDb.TableName Any [ Field.EQ "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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -686,14 +872,14 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
// This not raising an exception is the test // 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" { testTask "succeeds when no document is matched" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
// This not raising an exception is the test // 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" [ testList "removeFieldsByContains" [
@ -703,9 +889,9 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ] 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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -714,9 +900,9 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsByContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ] 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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -742,9 +928,9 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ] 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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -753,9 +939,9 @@ let integrationTests =
do! loadDocs conn do! loadDocs conn
do! conn.removeFieldsByJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ] 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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { 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" Expect.equal remaining 5 "There should have been 5 documents remaining"
} }
] ]
testList "deleteByField" [ testList "deleteByFields" [
testTask "succeeds when documents are deleted" { testTask "succeeds when documents are deleted" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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 let! remaining = conn.countAll PostgresDb.TableName
Expect.equal remaining 3 "There should have been 3 documents remaining" Expect.equal remaining 3 "There should have been 3 documents remaining"
} }
@ -809,7 +995,7 @@ let integrationTests =
use conn = mkConn db use conn = mkConn db
do! loadDocs conn 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 let! remaining = conn.countAll PostgresDb.TableName
Expect.equal remaining 5 "There should have been 5 documents remaining" 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.Postgres
open BitBadger.Documents.Tests open BitBadger.Documents.Tests
#nowarn "0044" (** UNIT TESTS **)
/// Tests which do not hit the database /// Unit tests for the Parameters module of the PostgreSQL library
let unitTests = let parametersTests = testList "Parameters" [
testList "Unit" [
testList "Parameters" [
testList "idParam" [ testList "idParam" [
// NOTE: these tests also exercise all branches of the internal parameterFor function // NOTE: these tests also exercise all branches of the internal parameterFor function
test "succeeds for byte ID" { test "succeeds for byte ID" {
Expect.equal Expect.equal (idParam (sbyte 7)) ("@id", Sql.int8 (sbyte 7)) "Byte ID parameter not constructed correctly"
(idParam (sbyte 7)) ("@id", Sql.int8 (sbyte 7)) "Byte ID parameter not constructed correctly"
} }
test "succeeds for unsigned byte ID" { test "succeeds for unsigned byte ID" {
Expect.equal Expect.equal
@ -25,9 +22,7 @@ let unitTests =
} }
test "succeeds for short ID" { test "succeeds for short ID" {
Expect.equal Expect.equal
(idParam (int16 44)) (idParam (int16 44)) ("@id", Sql.int16 (int16 44)) "Short ID parameter not constructed correctly"
("@id", Sql.int16 (int16 44))
"Short ID parameter not constructed correctly"
} }
test "succeeds for unsigned short ID" { test "succeeds for unsigned short ID" {
Expect.equal Expect.equal
@ -39,14 +34,11 @@ let unitTests =
Expect.equal (idParam 88) ("@id", Sql.int 88) "Int ID parameter not constructed correctly" Expect.equal (idParam 88) ("@id", Sql.int 88) "Int ID parameter not constructed correctly"
} }
test "succeeds for unsigned integer ID" { test "succeeds for unsigned integer ID" {
Expect.equal Expect.equal (idParam (uint 889)) ("@id", Sql.int 889) "Unsigned int ID parameter not constructed correctly"
(idParam (uint 889)) ("@id", Sql.int 889) "Unsigned int ID parameter not constructed correctly"
} }
test "succeeds for long ID" { test "succeeds for long ID" {
Expect.equal Expect.equal
(idParam (int64 123)) (idParam (int64 123)) ("@id", Sql.int64 (int64 123)) "Long ID parameter not constructed correctly"
("@id", Sql.int64 (int64 123))
"Long ID parameter not constructed correctly"
} }
test "succeeds for unsigned long ID" { test "succeeds for unsigned long ID" {
Expect.equal Expect.equal
@ -113,8 +105,7 @@ let unitTests =
Expect.isEmpty paramList "There should not have been any parameters added" Expect.isEmpty paramList "There should not have been any parameters added"
} }
test "succeeds when two parameters are added for one field" { test "succeeds when two parameters are added for one field" {
let paramList = let paramList = addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] []
addFieldParams [ { Field.BT "that" "eh" "zed" with ParameterName = Some "@test" } ] []
Expect.hasLength paramList 2 "There should have been 2 parameters added" Expect.hasLength paramList 2 "There should have been 2 parameters added"
let name, value = Seq.head paramList let name, value = Seq.head paramList
Expect.equal name "@testmin" "Minimum field name not correct" 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" 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" { test "noParams succeeds" {
Expect.isEmpty noParams "The no-params sequence should be empty" 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" [ testList "whereByFields" [
test "succeeds for a single field when a logical operator is passed" { test "succeeds for a single field when a logical operator is passed" {
Expect.equal Expect.equal
@ -206,9 +187,7 @@ let unitTests =
} }
test "succeeds for non-numeric non-string ID" { test "succeeds for non-numeric non-string ID" {
Expect.equal Expect.equal
(Query.whereById (System.Uri "https://example.com")) (Query.whereById (System.Uri "https://example.com")) "data->>'Id' = @id" "WHERE clause not correct"
"data->>'Id' = @id"
"WHERE clause not correct"
} }
] ]
testList "Definition" [ testList "Definition" [
@ -266,27 +245,29 @@ let unitTests =
Expect.equal Expect.equal
(Query.byPathMatch "verify") "verify WHERE data @? @path::jsonpath" "By-JSON Path query not correct" (Query.byPathMatch "verify") "verify WHERE data @? @path::jsonpath" "By-JSON Path query not correct"
} }
] ]
]
(** INTEGRATION TESTS **)
open ThrowawayDb.Postgres open ThrowawayDb.Postgres
open Types open Types
let isTrue<'T> (_ : 'T) = true /// Documents to use for integration tests
let documents = [
let integrationTests =
let documents = [
{ Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None } { Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None }
{ Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } } { Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } }
{ Id = "three"; Value = ""; NumValue = 4; Sub = None } { Id = "three"; Value = ""; NumValue = 4; Sub = None }
{ Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } } { Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } }
{ Id = "five"; Value = "purple"; NumValue = 18; Sub = None } { 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 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" { test "useDataSource disposes existing source" {
use db1 = ThrowawayDatabase.Create PostgresDb.ConnStr.Value use db1 = ThrowawayDatabase.Create PostgresDb.ConnStr.Value
let source = PostgresDb.MkDataSource db1.ConnectionString let source = PostgresDb.MkDataSource db1.ConnectionString
@ -301,26 +282,29 @@ let integrationTests =
let source = PostgresDb.MkDataSource db.ConnectionString let source = PostgresDb.MkDataSource db.ConnectionString
Configuration.useDataSource source Configuration.useDataSource source
Expect.isTrue (obj.ReferenceEquals(source, Configuration.dataSource ())) Expect.isTrue (obj.ReferenceEquals(source, Configuration.dataSource ())) "Data source should have been the same"
"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" [ testList "list" [
testTask "succeeds when data is found" { testTask "succeeds when data is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! docs = Custom.list (Query.selectFromTable PostgresDb.TableName) [] fromData<JsonDocument> let! docs = Custom.list (Query.find PostgresDb.TableName) [] fromData<JsonDocument>
Expect.hasCountOf docs 5u isTrue "There should have been 5 documents returned" Expect.hasLength docs 5 "There should have been 5 documents returned"
} }
testTask "succeeds when data is not found" { testTask "succeeds when data is not found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! docs = let! docs =
Custom.list $"SELECT data FROM {PostgresDb.TableName} WHERE data @? @path::jsonpath" Custom.list
[ "@path", Sql.string "$.NumValue ? (@ > 100)" ] fromData<JsonDocument> $"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" Expect.isEmpty docs "There should have been no documents returned"
} }
] ]
@ -330,8 +314,10 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
let! doc = let! doc =
Custom.single $"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id" Custom.single
[ "@id", Sql.string "one"] fromData<JsonDocument> $"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.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "one" "The incorrect document was returned" Expect.equal doc.Value.Id "one" "The incorrect document was returned"
} }
@ -340,8 +326,10 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
let! doc = let! doc =
Custom.single $"SELECT data FROM {PostgresDb.TableName} WHERE data ->> 'Id' = @id" Custom.single
[ "@id", Sql.string "eighty" ] fromData<JsonDocument> $"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" Expect.isNone doc "There should not have been a document returned"
} }
] ]
@ -359,7 +347,8 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () 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)" ] [ "@path", Sql.string "$.NumValue ? (@ > 100)" ]
let! remaining = Count.all PostgresDb.TableName 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") 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" 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" { testTask "ensureTable succeeds" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
let tableExists () = let tableExists () =
@ -396,9 +387,7 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
let indexExists () = let indexExists () =
Custom.scalar Custom.scalar
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it" "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_document') AS it" [] toExists
[]
toExists
let! exists = indexExists () let! exists = indexExists ()
Expect.isFalse exists "The index should not exist already" Expect.isFalse exists "The index should not exist already"
@ -411,8 +400,7 @@ let integrationTests =
testTask "ensureFieldIndex succeeds" { testTask "ensureFieldIndex succeeds" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
let indexExists () = let indexExists () =
Custom.scalar Custom.scalar "SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it" [] toExists
"SELECT EXISTS (SELECT 1 FROM pg_class WHERE relname = 'idx_ensured_test') AS it" [] toExists
let! exists = indexExists () let! exists = indexExists ()
Expect.isFalse exists "The index should not exist already" Expect.isFalse exists "The index should not exist already"
@ -422,7 +410,10 @@ let integrationTests =
let! exists' = indexExists () let! exists' = indexExists ()
Expect.isTrue exists' "The index should now exist" 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" [ testList "insert" [
testTask "succeeds" { testTask "succeeds" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -472,7 +463,10 @@ let integrationTests =
Expect.equal after.Value upd8Doc "The updated document is not correct" 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" { testTask "all succeeds" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
@ -485,8 +479,7 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! theCount = let! theCount = Count.byFields PostgresDb.TableName Any [ Field.BT "NumValue" 15 20; Field.EQ "NumValue" 0 ]
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" Expect.equal theCount 3 "There should have been 3 matching documents"
} }
testTask "succeeds when items are not found" { testTask "succeeds when items are not found" {
@ -497,22 +490,6 @@ let integrationTests =
Expect.equal theCount 0 "There should have been no matching documents" 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" { testTask "byContains succeeds" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
@ -527,8 +504,10 @@ let integrationTests =
let! theCount = Count.byJsonPath PostgresDb.TableName "$.NumValue ? (@ > 5)" let! theCount = Count.byJsonPath PostgresDb.TableName "$.NumValue ? (@ > 5)"
Expect.equal theCount 3 "There should have been 3 matching documents" 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" [ testList "byId" [
testTask "succeeds when a document exists" { testTask "succeeds when a document exists" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -557,24 +536,7 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! exists = let! exists = Exists.byFields PostgresDb.TableName All [ Field.EQ "NumValue" "six"; Field.EX "Nope" ]
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")
Expect.isFalse exists "There should not have been existing documents" 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" 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" [ testList "all" [
testTask "succeeds when there is data" { testTask "succeeds when there is data" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -621,12 +585,10 @@ let integrationTests =
do! insert PostgresDb.TableName { Foo = "five"; Bar = "six" } do! insert PostgresDb.TableName { Foo = "five"; Bar = "six" }
let! results = Find.all<SubDocument> PostgresDb.TableName let! results = Find.all<SubDocument> PostgresDb.TableName
let expected = [ Expect.equal
{ Foo = "one"; Bar = "two" } results
{ Foo = "three"; Bar = "four" } [ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
{ Foo = "five"; Bar = "six" } "There should have been 3 documents returned"
]
Expect.equal results expected "There should have been 3 documents returned"
} }
testTask "succeeds when there is no data" { testTask "succeeds when there is no data" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -634,6 +596,41 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned" 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" [ testList "byId" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -657,8 +654,7 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
let! docs = let! docs =
Find.byFields<JsonDocument> Find.byFields<JsonDocument> PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ]
PostgresDb.TableName All [ Field.EQ "Value" "purple"; Field.EX "Sub" ]
Expect.equal (List.length docs) 1 "There should have been one document returned" Expect.equal (List.length docs) 1 "There should have been one document returned"
} }
testTask "succeeds when documents are not found" { testTask "succeeds when documents are not found" {
@ -671,20 +667,28 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned" Expect.isEmpty docs "There should have been no documents returned"
} }
] ]
testList "byField" [ testList "byFieldsOrdered" [
testTask "succeeds when documents are found" { testTask "succeeds when sorting ascending" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! docs = Find.byField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "another") let! docs =
Expect.equal (List.length docs) 1 "There should have been one document returned" 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() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! docs = Find.byField<JsonDocument> PostgresDb.TableName (Field.EQ "Value" "mauve") let! docs =
Expect.isEmpty docs "There should have been no documents returned" 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" [ testList "byContains" [
@ -703,6 +707,31 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned" 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" [ testList "byJsonPath" [
testTask "succeeds when documents are found" { testTask "succeeds when documents are found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -719,12 +748,37 @@ let integrationTests =
Expect.isEmpty docs "There should have been no documents returned" 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" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () 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.isSome doc "There should have been a document returned"
Expect.equal doc.Value.Id "two" "The incorrect document was returned" Expect.equal doc.Value.Id "two" "The incorrect document was returned"
} }
@ -732,7 +786,7 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () 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.isSome doc "There should have been a document returned"
Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned" Expect.contains [ "five"; "four" ] doc.Value.Id "An incorrect document was returned"
} }
@ -740,10 +794,32 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () 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" 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" [ testList "firstByContains" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -769,6 +845,28 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned" 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" [ testList "firstByJsonPath" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -794,8 +892,32 @@ let integrationTests =
Expect.isNone doc "There should not have been a document returned" 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" [ testList "byId" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -815,9 +937,7 @@ let integrationTests =
// This not raising an exception is the test // This not raising an exception is the test
do! Update.byId do! Update.byId
PostgresDb.TableName PostgresDb.TableName "test" { emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
"test"
{ emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
} }
] ]
testList "byFunc" [ testList "byFunc" [
@ -825,8 +945,7 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! Update.byFunc PostgresDb.TableName (_.Id) do! Update.byFunc PostgresDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
{ Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
let! after = Find.byId<string, JsonDocument> PostgresDb.TableName "one" let! after = Find.byId<string, JsonDocument> PostgresDb.TableName "one"
Expect.isSome after "There should have been a document returned post-update" Expect.isSome after "There should have been a document returned post-update"
Expect.equal Expect.equal
@ -841,12 +960,13 @@ let integrationTests =
Expect.equal before 0 "There should have been no documents returned" Expect.equal before 0 "There should have been no documents returned"
// This not raising an exception is the test // This not raising an exception is the test
do! Update.byFunc do! Update.byFunc PostgresDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
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" [ testList "byId" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -867,13 +987,13 @@ let integrationTests =
do! Patch.byId PostgresDb.TableName "test" {| Foo = "green" |} do! Patch.byId PostgresDb.TableName "test" {| Foo = "green" |}
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! Patch.byField PostgresDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} do! Patch.byFields PostgresDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = Count.byField PostgresDb.TableName (Field.EQ "NumValue" "77") let! after = Count.byFields PostgresDb.TableName Any [ Field.EQ "NumValue" 77 ]
Expect.equal after 2 "There should have been 2 documents returned" Expect.equal after 2 "There should have been 2 documents returned"
} }
testTask "succeeds when no document is updated" { testTask "succeeds when no document is updated" {
@ -883,7 +1003,7 @@ let integrationTests =
Expect.equal before 0 "There should have been no documents returned" Expect.equal before 0 "There should have been no documents returned"
// This not raising an exception is the test // 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" [ testList "byContains" [
@ -924,17 +1044,19 @@ let integrationTests =
do! Patch.byJsonPath PostgresDb.TableName "$.NumValue ? (@ < 0)" {| Foo = "green" |} 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" [ testList "byId" [
testTask "succeeds when multiple fields are removed" { testTask "succeeds when multiple fields are removed" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub"; "Value" ] 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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -942,9 +1064,9 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
do! RemoveFields.byId PostgresDb.TableName "two" [ "Sub" ] 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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -961,25 +1083,25 @@ let integrationTests =
do! RemoveFields.byId PostgresDb.TableName "two" [ "Value" ] do! RemoveFields.byId PostgresDb.TableName "two" [ "Value" ]
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when multiple fields are removed" { testTask "succeeds when multiple fields are removed" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub"; "Value" ] do! RemoveFields.byFields PostgresDb.TableName Any [ Field.EQ "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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! RemoveFields.byField PostgresDb.TableName (Field.EQ "NumValue" "17") [ "Sub" ] do! RemoveFields.byFields PostgresDb.TableName Any [ Field.EQ "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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -987,13 +1109,13 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
// This not raising an exception is the test // 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" { testTask "succeeds when no document is matched" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
// This not raising an exception is the test // 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" [ testList "byContains" [
@ -1002,9 +1124,9 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub"; "Value" ] 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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -1012,9 +1134,9 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
do! RemoveFields.byContains PostgresDb.TableName {| NumValue = 17 |} [ "Sub" ] 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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -1037,9 +1159,9 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub"; "Value" ] 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" 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" Expect.equal noValue 1 "There should be 1 document without Value fields"
} }
testTask "succeeds when a single field is removed" { testTask "succeeds when a single field is removed" {
@ -1047,9 +1169,9 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
do! RemoveFields.byJsonPath PostgresDb.TableName "$.NumValue ? (@ == 17)" [ "Sub" ] 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" 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" Expect.equal noValue 0 "There should be no documents without Value fields"
} }
testTask "succeeds when a field is not removed" { testTask "succeeds when a field is not removed" {
@ -1066,8 +1188,10 @@ let integrationTests =
do! RemoveFields.byJsonPath PostgresDb.TableName "$.Abracadabra ? (@ == \"apple\")" [ "Value" ] 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" [ testList "byId" [
testTask "succeeds when a document is deleted" { testTask "succeeds when a document is deleted" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
@ -1086,12 +1210,12 @@ let integrationTests =
Expect.equal remaining 5 "There should have been 5 documents remaining" Expect.equal remaining 5 "There should have been 5 documents remaining"
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when documents are deleted" { testTask "succeeds when documents are deleted" {
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () 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 let! remaining = Count.all PostgresDb.TableName
Expect.equal remaining 3 "There should have been 3 documents remaining" Expect.equal remaining 3 "There should have been 3 documents remaining"
} }
@ -1099,7 +1223,7 @@ let integrationTests =
use db = PostgresDb.BuildDb() use db = PostgresDb.BuildDb()
do! loadDocs () 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 let! remaining = Count.all PostgresDb.TableName
Expect.equal remaining 5 "There should have been 5 documents remaining" 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" Expect.equal remaining 5 "There should have been 5 documents remaining"
} }
] ]
] ]
]
|> testSequenced
/// All tests for the PostgreSQL library
let all = testList "Postgres" [ unitTests; integrationTests ] 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 Microsoft.Data.Sqlite
open Types open Types
#nowarn "0044"
/// Integration tests for the F# extensions on the SqliteConnection data type /// Integration tests for the F# extensions on the SqliteConnection data type
let integrationTests = let integrationTests =
let loadDocs () = backgroundTask { let loadDocs () = backgroundTask {
@ -115,12 +113,12 @@ let integrationTests =
let! theCount = conn.countAll SqliteDb.TableName let! theCount = conn.countAll SqliteDb.TableName
Expect.equal theCount 5L "There should have been 5 matching documents" Expect.equal theCount 5L "There should have been 5 matching documents"
} }
testTask "countByField succeeds" { testTask "countByFields succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () 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" Expect.equal theCount 2L "There should have been 2 matching documents"
} }
testList "existsById" [ testList "existsById" [
@ -141,13 +139,13 @@ let integrationTests =
Expect.isFalse exists "There should not have been an existing document" Expect.isFalse exists "There should not have been an existing document"
} }
] ]
testList "existsByField" [ testList "existsByFields" [
testTask "succeeds when documents exist" { testTask "succeeds when documents exist" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () 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" Expect.isTrue exists "There should have been existing documents"
} }
testTask "succeeds when no matching documents exist" { testTask "succeeds when no matching documents exist" {
@ -155,7 +153,7 @@ let integrationTests =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () 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" 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" 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" [ testList "findById" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -190,7 +226,7 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "two" 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" Expect.equal doc.Value.Id "two" "The incorrect document was returned"
} }
testTask "succeeds when a document is not found" { testTask "succeeds when a document is not found" {
@ -199,35 +235,59 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
let! doc = conn.findById<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven" 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" { testTask "succeeds when documents are found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green") let! docs = conn.findByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
Expect.equal (List.length docs) 2 "There should have been two documents returned" Expect.hasLength docs 2 "There should have been two documents returned"
} }
testTask "succeeds when documents are not found" { testTask "succeeds when documents are not found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
let! docs = conn.findByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "mauve") let! docs = conn.findByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "mauve" ]
Expect.isTrue (List.isEmpty docs) "There should have been no documents returned" 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" { testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another") let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "another" ]
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" Expect.equal doc.Value.Id "two" "The incorrect document was returned"
} }
testTask "succeeds when multiple documents are found" { testTask "succeeds when multiple documents are found" {
@ -235,8 +295,8 @@ let integrationTests =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green") let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
Expect.isTrue (Option.isSome doc) "There should have been a document returned" Expect.isSome doc "There should have been a document returned"
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned" Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
} }
testTask "succeeds when a document is not found" { testTask "succeeds when a document is not found" {
@ -244,8 +304,32 @@ let integrationTests =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
let! doc = conn.findFirstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent") let! doc = conn.findFirstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "absent" ]
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 "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" [ testList "updateById" [
@ -326,14 +410,14 @@ let integrationTests =
do! conn.patchById SqliteDb.TableName "test" {| Foo = "green" |} do! conn.patchById SqliteDb.TableName "test" {| Foo = "green" |}
} }
] ]
testList "patchByField" [ testList "patchByFields" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
do! conn.patchByField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} do! conn.patchByFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = conn.countByField SqliteDb.TableName (Field.EQ "NumValue" 77) let! after = conn.countByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 77 ]
Expect.equal after 2L "There should have been 2 documents returned" Expect.equal after 2L "There should have been 2 documents returned"
} }
testTask "succeeds when no document is updated" { testTask "succeeds when no document is updated" {
@ -344,7 +428,7 @@ let integrationTests =
Expect.isEmpty before "There should have been no documents returned" Expect.isEmpty before "There should have been no documents returned"
// This not raising an exception is the test // 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" [ testList "removeFieldsById" [
@ -377,13 +461,13 @@ let integrationTests =
do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ] do! conn.removeFieldsById SqliteDb.TableName "two" [ "Value" ]
} }
] ]
testList "removeFieldByField" [ testList "removeFieldByFields" [
testTask "succeeds when a field is removed" { testTask "succeeds when a field is removed" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
do! conn.removeFieldsByField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ] do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Sub" ]
try try
let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "four" let! _ = conn.findById<string, JsonDocument> SqliteDb.TableName "four"
Expect.isTrue false "The updated document should have failed to parse" Expect.isTrue false "The updated document should have failed to parse"
@ -397,14 +481,14 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
// This not raising an exception is the test // 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" { testTask "succeeds when no document is matched" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
// This not raising an exception is the test // 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" [ testList "deleteById" [
@ -427,13 +511,13 @@ let integrationTests =
Expect.equal remaining 5L "There should have been 5 documents remaining" Expect.equal remaining 5L "There should have been 5 documents remaining"
} }
] ]
testList "deleteByField" [ testList "deleteByFields" [
testTask "succeeds when documents are deleted" { testTask "succeeds when documents are deleted" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () 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 let! remaining = conn.countAll SqliteDb.TableName
Expect.equal remaining 2L "There should have been 2 documents remaining" Expect.equal remaining 2L "There should have been 2 documents remaining"
} }
@ -442,7 +526,7 @@ let integrationTests =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () 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 let! remaining = conn.countAll SqliteDb.TableName
Expect.equal remaining 5L "There should have been 5 documents remaining" Expect.equal remaining 5L "There should have been 5 documents remaining"
} }
@ -480,8 +564,8 @@ let integrationTests =
use conn = Configuration.dbConn () use conn = Configuration.dbConn ()
do! loadDocs () do! loadDocs ()
let! docs = conn.customList (Query.selectFromTable SqliteDb.TableName) [] fromData<JsonDocument> let! docs = conn.customList (Query.find SqliteDb.TableName) [] fromData<JsonDocument>
Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned" Expect.hasLength docs 5 "There should have been 5 documents returned"
} }
testTask "succeeds when data is not found" { testTask "succeeds when data is not found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()

View File

@ -10,10 +10,10 @@ open Types
#nowarn "0044" #nowarn "0044"
/// Unit tests for the SQLite library (** UNIT TESTS **)
let unitTests =
testList "Unit" [ /// Unit tests for the Query module of the SQLite library
testList "Query" [ let queryTests = testList "Query" [
testList "whereByFields" [ testList "whereByFields" [
test "succeeds for a single field when a logical operator is passed" { test "succeeds for a single field when a logical operator is passed" {
Expect.equal Expect.equal
@ -82,8 +82,10 @@ let unitTests =
"CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)" "CREATE TABLE IF NOT EXISTS tbl (data TEXT NOT NULL)"
"CREATE TABLE statement not correct" "CREATE TABLE statement not correct"
} }
] ]
testList "Parameters" [
/// Unit tests for the Parameters module of the SQLite library
let parametersTests = testList "Parameters" [
test "idParam succeeds" { test "idParam succeeds" {
let theParam = idParam 7 let theParam = idParam 7
Expect.equal theParam.ParameterName "@id" "The parameter name is incorrect" Expect.equal theParam.ParameterName "@id" "The parameter name is incorrect"
@ -110,24 +112,28 @@ let unitTests =
test "noParams succeeds" { test "noParams succeeds" {
Expect.isEmpty noParams "The parameter list should have been empty" 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 = (** INTEGRATION TESTS **)
let documents = [
/// Documents used for integration tests
let documents = [
{ Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None } { Id = "one"; Value = "FIRST!"; NumValue = 0; Sub = None }
{ Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } } { Id = "two"; Value = "another"; NumValue = 10; Sub = Some { Foo = "green"; Bar = "blue" } }
{ Id = "three"; Value = ""; NumValue = 4; Sub = None } { Id = "three"; Value = ""; NumValue = 4; Sub = None }
{ Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } } { Id = "four"; Value = "purple"; NumValue = 17; Sub = Some { Foo = "green"; Bar = "red" } }
{ Id = "five"; Value = "purple"; NumValue = 18; Sub = None } { 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 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" { test "useConnectionString / connectionString succeed" {
try try
Configuration.useConnectionString "Data Source=test.db" Configuration.useConnectionString "Data Source=test.db"
@ -164,8 +170,10 @@ let integrationTests =
Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field" Expect.equal (Configuration.idField ()) "id" "useIdField did not set the ID field"
Configuration.useIdField "Id" Configuration.useIdField "Id"
} }
] ]
testList "Custom" [
/// Integration tests for the Custom module of the SQLite library
let customTests = testList "Custom" [
testList "single" [ testList "single" [
testTask "succeeds when a row is found" { testTask "succeeds when a row is found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -196,7 +204,7 @@ let integrationTests =
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned"
} }
testTask "succeeds when data is not found" { testTask "succeeds when data is not found" {
@ -239,8 +247,10 @@ let integrationTests =
let! nbr = Custom.scalar "SELECT 5 AS test_value" [] _.GetInt32(0) let! nbr = Custom.scalar "SELECT 5 AS test_value" [] _.GetInt32(0)
Expect.equal nbr 5 "The query should have returned the number 5" 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" { testTask "ensureTable succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
let itExists (name: string) = let itExists (name: string) =
@ -276,7 +286,10 @@ let integrationTests =
let! exists' = indexExists () let! exists' = indexExists ()
Expect.isTrue exists' "The index should now exist" 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" [ testList "insert" [
testTask "succeeds" { testTask "succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -324,7 +337,10 @@ let integrationTests =
Expect.equal after.Value upd8Doc "The updated document is not correct" 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" { testTask "all succeeds" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
@ -332,22 +348,26 @@ let integrationTests =
let! theCount = Count.all SqliteDb.TableName let! theCount = Count.all SqliteDb.TableName
Expect.equal theCount 5L "There should have been 5 matching documents" 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() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" 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() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" 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" [ testList "byId" [
testTask "succeeds when a document exists" { testTask "succeeds when a document exists" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -364,24 +384,26 @@ let integrationTests =
Expect.isFalse exists "There should not have been an existing document" Expect.isFalse exists "There should not have been an existing document"
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when documents exist" { testTask "succeeds when documents exist" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" Expect.isTrue exists "There should have been existing documents"
} }
testTask "succeeds when no matching documents exist" { testTask "succeeds when no matching documents exist" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" 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" [ testList "all" [
testTask "succeeds when there is data" { testTask "succeeds when there is data" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -391,12 +413,10 @@ let integrationTests =
do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" } do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" }
let! results = Find.all<SubDocument> SqliteDb.TableName let! results = Find.all<SubDocument> SqliteDb.TableName
let expected = [ Expect.equal
{ Foo = "one"; Bar = "two" } results
{ Foo = "three"; Bar = "four" } [ { Foo = "one"; Bar = "two" }; { Foo = "three"; Bar = "four" }; { Foo = "five"; Bar = "six" } ]
{ Foo = "five"; Bar = "six" } "There should have been 3 documents returned"
]
Expect.equal results expected "There should have been 3 documents returned"
} }
testTask "succeeds when there is no data" { testTask "succeeds when there is no data" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -404,13 +424,48 @@ let integrationTests =
Expect.equal results [] "There should have been no documents returned" 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" [ testList "byId" [
testTask "succeeds when a document is found" { testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "two" 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" Expect.equal doc.Value.Id "two" "The incorrect document was returned"
} }
testTask "succeeds when a document is not found" { testTask "succeeds when a document is not found" {
@ -418,52 +473,98 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
let! doc = Find.byId<string, JsonDocument> SqliteDb.TableName "three hundred eighty-seven" 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" { testTask "succeeds when documents are found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" Expect.equal (List.length docs) 2 "There should have been two documents returned"
} }
testTask "succeeds when documents are not found" { testTask "succeeds when documents are not found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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" 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" { testTask "succeeds when a document is found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "another") let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "another" ]
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" Expect.equal doc.Value.Id "two" "The incorrect document was returned"
} }
testTask "succeeds when multiple documents are found" { testTask "succeeds when multiple documents are found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Sub.Foo" "green") let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Sub.Foo" "green" ]
Expect.isTrue (Option.isSome doc) "There should have been a document returned" Expect.isSome doc "There should have been a document returned"
Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned" Expect.contains [ "two"; "four" ] doc.Value.Id "An incorrect document was returned"
} }
testTask "succeeds when a document is not found" { testTask "succeeds when a document is not found" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
let! doc = Find.firstByField<JsonDocument> SqliteDb.TableName (Field.EQ "Value" "absent") let! doc = Find.firstByFields<JsonDocument> SqliteDb.TableName Any [ Field.EQ "Value" "absent" ]
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 "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" [ testList "byId" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -483,9 +584,7 @@ let integrationTests =
// This not raising an exception is the test // This not raising an exception is the test
do! Update.byId do! Update.byId
SqliteDb.TableName SqliteDb.TableName "test" { emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
"test"
{ emptyDoc with Id = "x"; Sub = Some { Foo = "blue"; Bar = "red" } }
} }
] ]
testList "byFunc" [ testList "byFunc" [
@ -493,8 +592,7 @@ let integrationTests =
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! Update.byFunc do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
let! after = Find.byId<string, JsonDocument> SqliteDb.TableName "one" let! after = Find.byId<string, JsonDocument> SqliteDb.TableName "one"
Expect.isSome after "There should have been a document returned post-update" Expect.isSome after "There should have been a document returned post-update"
Expect.equal Expect.equal
@ -509,12 +607,13 @@ let integrationTests =
Expect.isEmpty before "There should have been no documents returned" Expect.isEmpty before "There should have been no documents returned"
// This not raising an exception is the test // This not raising an exception is the test
do! Update.byFunc do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None }
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" [ testList "byId" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -535,13 +634,13 @@ let integrationTests =
do! Patch.byId SqliteDb.TableName "test" {| Foo = "green" |} do! Patch.byId SqliteDb.TableName "test" {| Foo = "green" |}
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when a document is updated" { testTask "succeeds when a document is updated" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! Patch.byField SqliteDb.TableName (Field.EQ "Value" "purple") {| NumValue = 77 |} do! Patch.byFields SqliteDb.TableName Any [ Field.EQ "Value" "purple" ] {| NumValue = 77 |}
let! after = Count.byField SqliteDb.TableName (Field.EQ "NumValue" 77) let! after = Count.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 77 ]
Expect.equal after 2L "There should have been 2 documents returned" Expect.equal after 2L "There should have been 2 documents returned"
} }
testTask "succeeds when no document is updated" { testTask "succeeds when no document is updated" {
@ -551,11 +650,13 @@ let integrationTests =
Expect.isEmpty before "There should have been no documents returned" Expect.isEmpty before "There should have been no documents returned"
// This not raising an exception is the test // 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" [ testList "byId" [
testTask "succeeds when fields is removed" { testTask "succeeds when fields is removed" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -583,12 +684,12 @@ let integrationTests =
do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ] do! RemoveFields.byId SqliteDb.TableName "two" [ "Value" ]
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when a field is removed" { testTask "succeeds when a field is removed" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () do! loadDocs ()
do! RemoveFields.byField SqliteDb.TableName (Field.EQ "NumValue" 17) [ "Sub" ] do! RemoveFields.byFields SqliteDb.TableName Any [ Field.EQ "NumValue" 17 ] [ "Sub" ]
try try
let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "four" let! _ = Find.byId<string, JsonDocument> SqliteDb.TableName "four"
Expect.isTrue false "The updated document should have failed to parse" Expect.isTrue false "The updated document should have failed to parse"
@ -601,17 +702,19 @@ let integrationTests =
do! loadDocs () do! loadDocs ()
// This not raising an exception is the test // 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" { testTask "succeeds when no document is matched" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
// This not raising an exception is the test // 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" [ testList "byId" [
testTask "succeeds when a document is deleted" { testTask "succeeds when a document is deleted" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
@ -630,12 +733,12 @@ let integrationTests =
Expect.equal remaining 5L "There should have been 5 documents remaining" Expect.equal remaining 5L "There should have been 5 documents remaining"
} }
] ]
testList "byField" [ testList "byFields" [
testTask "succeeds when documents are deleted" { testTask "succeeds when documents are deleted" {
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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 let! remaining = Count.all SqliteDb.TableName
Expect.equal remaining 2L "There should have been 2 documents remaining" Expect.equal remaining 2L "There should have been 2 documents remaining"
} }
@ -643,16 +746,28 @@ let integrationTests =
use! db = SqliteDb.BuildDb() use! db = SqliteDb.BuildDb()
do! loadDocs () 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 let! remaining = Count.all SqliteDb.TableName
Expect.equal remaining 5L "There should have been 5 documents remaining" 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:" }
]
]