From 812ef06af5ceac3e933eeb165dafb83d97995797 Mon Sep 17 00:00:00 2001 From: "Daniel J. Summers" Date: Mon, 7 Apr 2025 17:26:47 -0400 Subject: [PATCH] Add F# SQLite Json tests --- src/Sqlite/Extensions.fs | 555 ++++++++++++++++++----- src/Tests/SqliteExtensionTests.fs | 725 +++++++++++++++++++++++++++--- src/Tests/SqliteTests.fs | 587 +++++++++++++++++++++++- src/Tests/Types.fs | 16 + 4 files changed, 1706 insertions(+), 177 deletions(-) diff --git a/src/Sqlite/Extensions.fs b/src/Sqlite/Extensions.fs index 901bc0a..30160ae 100644 --- a/src/Sqlite/Extensions.fs +++ b/src/Sqlite/Extensions.fs @@ -1,6 +1,7 @@ namespace BitBadger.Documents.Sqlite open Microsoft.Data.Sqlite +open WithConn /// F# extensions for the SqliteConnection type [] @@ -14,21 +15,45 @@ module Extensions = /// The mapping function between the document and the domain item /// A list of results for the given query member conn.customList<'TDoc> query parameters mapFunc = - WithConn.Custom.list<'TDoc> query parameters mapFunc conn + Custom.list<'TDoc> query parameters mapFunc conn + + /// Execute a query that returns a JSON array of results + /// The query to retrieve the results + /// Parameters to use for the query + /// The mapping function to extract the document + /// A JSON array of results for the given query + member conn.customJsonArray query parameters mapFunc = + Custom.jsonArray query parameters mapFunc conn + + /// Execute a query, writing its results to the given StreamWriter + /// The query to retrieve the results + /// Parameters to use for the query + /// The StreamWriter to which the results should be written + /// The mapping function to extract the document + member conn.writeCustomJsonArray query parameters writer mapFunc = + Custom.writeJsonArray query parameters writer mapFunc conn /// Execute a query that returns one or no results /// The query to retrieve the results /// Parameters to use for the query /// The mapping function between the document and the domain item - /// Some with the first matching result, or None if not found + /// Some with the first matching result, or None if not found member conn.customSingle<'TDoc> query parameters mapFunc = - WithConn.Custom.single<'TDoc> query parameters mapFunc conn + Custom.single<'TDoc> query parameters mapFunc conn + + /// Execute a query that returns one or no JSON documents + /// The query to retrieve the results + /// Parameters to use for the query + /// The mapping function to extract the document + /// The JSON document with the first matching result, or an empty document if not found + member conn.customJsonSingle query parameters mapFunc = + Custom.jsonSingle query parameters mapFunc conn /// Execute a query that returns no results /// The query to retrieve the results /// Parameters to use for the query member conn.customNonQuery query parameters = - WithConn.Custom.nonQuery query parameters conn + Custom.nonQuery query parameters conn /// Execute a query that returns a scalar value /// The query to retrieve the value @@ -36,25 +61,25 @@ module Extensions = /// The mapping function to obtain the value /// The scalar value for the query member conn.customScalar<'T when 'T: struct> query parameters mapFunc = - WithConn.Custom.scalar<'T> query parameters mapFunc conn + Custom.scalar<'T> query parameters mapFunc conn /// Create a document table /// The table whose existence should be ensured (may include schema) member conn.ensureTable name = - WithConn.Definition.ensureTable name conn + Definition.ensureTable name conn /// Create an index on field(s) within documents in the specified table /// The table to be indexed (may include schema) /// The name of the index to create /// One or more fields to be indexed member conn.ensureFieldIndex tableName indexName fields = - WithConn.Definition.ensureFieldIndex tableName indexName fields conn + Definition.ensureFieldIndex tableName indexName fields conn /// Insert a new document /// The table into which the document should be inserted (may include schema) /// The document to be inserted member conn.insert<'TDoc> tableName (document: 'TDoc) = - WithConn.Document.insert<'TDoc> tableName document conn + insert<'TDoc> tableName document conn /// /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert") @@ -62,68 +87,68 @@ module Extensions = /// The table into which the document should be saved (may include schema) /// The document to be saved member conn.save<'TDoc> tableName (document: 'TDoc) = - WithConn.Document.save tableName document conn + save tableName document conn /// Count all documents in a table /// The table in which documents should be counted (may include schema) /// The count of the documents in the table member conn.countAll tableName = - WithConn.Count.all tableName conn + Count.all tableName conn - /// Count matching documents using JSON field comparisons (->> =, etc.) + /// Count matching documents using JSON field comparisons (->> =, etc.) /// The table in which documents should be counted (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// The count of matching documents in the table member conn.countByFields tableName howMatched fields = - WithConn.Count.byFields tableName howMatched fields conn + Count.byFields tableName howMatched fields conn /// Determine if a document exists for the given ID /// The table in which existence should be checked (may include schema) /// The ID of the document whose existence should be checked /// True if a document exists, false if not member conn.existsById tableName (docId: 'TKey) = - WithConn.Exists.byId tableName docId conn + Exists.byId tableName docId conn - /// Determine if a document exists using JSON field comparisons (->> =, etc.) + /// Determine if a document exists using JSON field comparisons (->> =, etc.) /// The table in which existence should be checked (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// True if any matching documents exist, false if not member conn.existsByFields tableName howMatched fields = - WithConn.Exists.byFields tableName howMatched fields conn + Exists.byFields tableName howMatched fields conn /// Retrieve all documents in the given table /// The table from which documents should be retrieved (may include schema) /// All documents from the given table member conn.findAll<'TDoc> tableName = - WithConn.Find.all<'TDoc> tableName conn + Find.all<'TDoc> tableName conn /// Retrieve all documents in the given table ordered by the given fields in the document /// The table from which documents should be retrieved (may include schema) /// Fields by which the results should be ordered /// All documents from the given table, ordered by the given fields member conn.findAllOrdered<'TDoc> tableName orderFields = - WithConn.Find.allOrdered<'TDoc> tableName orderFields conn + Find.allOrdered<'TDoc> tableName orderFields conn /// Retrieve a document by its ID /// The table from which a document should be retrieved (may include schema) /// The ID of the document to retrieve - /// Some with the document if found, None otherwise + /// Some with the document if found, None otherwise member conn.findById<'TKey, 'TDoc> tableName (docId: 'TKey) = - WithConn.Find.byId<'TKey, 'TDoc> tableName docId conn + Find.byId<'TKey, 'TDoc> tableName docId conn - /// Retrieve documents matching JSON field comparisons (->> =, etc.) + /// Retrieve documents matching JSON field comparisons (->> =, etc.) /// The table from which documents should be retrieved (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// All documents matching the given fields member conn.findByFields<'TDoc> tableName howMatched fields = - WithConn.Find.byFields<'TDoc> tableName howMatched fields conn + Find.byFields<'TDoc> tableName howMatched fields conn /// - /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields - /// in the document + /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in + /// the document /// /// The table from which documents should be retrieved (may include schema) /// Whether to match any or all of the field conditions @@ -131,18 +156,18 @@ module Extensions = /// Fields by which the results should be ordered /// All documents matching the given fields, ordered by the other given fields member conn.findByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields = - WithConn.Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn + Find.byFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn - /// Retrieve the first document matching JSON field comparisons (->> =, etc.) + /// Retrieve the first document matching JSON field comparisons (->> =, etc.) /// The table from which a document should be retrieved (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match - /// Some with the first document, or None if not found + /// Some with the first document, or None if not found member conn.findFirstByFields<'TDoc> tableName howMatched fields = - WithConn.Find.firstByFields<'TDoc> tableName howMatched fields conn + Find.firstByFields<'TDoc> tableName howMatched fields conn /// - /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the + /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the /// given fields in the document /// /// The table from which a document should be retrieved (may include schema) @@ -150,17 +175,148 @@ module Extensions = /// The field conditions to match /// Fields by which the results should be ordered /// - /// Some with the first document ordered by the given fields, or None if not found + /// Some with the first document ordered by the given fields, or None if not found /// member conn.findFirstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields = - WithConn.Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn + Find.firstByFieldsOrdered<'TDoc> tableName howMatched queryFields orderFields conn + + /// Retrieve all JSON documents in the given table + /// The table from which documents should be retrieved (may include schema) + /// All JSON documents from the given table + member conn.jsonAll tableName = + Json.all tableName conn + + /// Retrieve all JSON documents in the given table ordered by the given fields in the document + /// The table from which documents should be retrieved (may include schema) + /// Fields by which the results should be ordered + /// All JSON documents from the given table, ordered by the given fields + member conn.jsonAllOrdered tableName orderFields = + Json.allOrdered tableName orderFields conn + + /// Retrieve a JSON document by its ID + /// The table from which a document should be retrieved (may include schema) + /// The ID of the document to retrieve + /// The JSON document if found, an empty JSON document otherwise + member conn.jsonById<'TKey> tableName (docId: 'TKey) = + Json.byId tableName docId conn + + /// Retrieve JSON documents matching JSON field comparisons (->> =, etc.) + /// The table from which documents should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// All JSON documents matching the given fields + member conn.jsonByFields tableName howMatched fields = + Json.byFields tableName howMatched fields conn + + /// + /// Retrieve JSON documents matching JSON field comparisons (->> =, etc.) ordered by the given + /// fields in the document + /// + /// The table from which documents should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + /// All JSON documents matching the given fields, ordered by the other given fields + member conn.jsonByFieldsOrdered tableName howMatched queryFields orderFields = + Json.byFieldsOrdered tableName howMatched queryFields orderFields conn + + /// + /// Retrieve the first JSON document matching JSON field comparisons (->> =, etc.) + /// + /// The table from which a document should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// The first JSON document if found, an empty JSON document otherwise + member conn.jsonFirstByFields tableName howMatched fields = + Json.firstByFields tableName howMatched fields conn + + /// + /// Retrieve the first JSON document matching JSON field comparisons (->> =, etc.) ordered by the + /// given fields in the document + /// + /// The table from which a document should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + /// The first JSON document (in order) if found, an empty JSON document otherwise + member conn.jsonFirstByFieldsOrdered tableName howMatched queryFields orderFields = + Json.firstByFieldsOrdered tableName howMatched queryFields orderFields conn + + /// Write all JSON documents in the given table to the given StreamWriter + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + member conn.writeJsonAll tableName writer = + Json.writeAll tableName writer conn + + /// + /// Write all JSON all documents in the given table to the given StreamWriter, ordered by the given + /// fields in the document + /// + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Fields by which the results should be ordered + member conn.writeJsonAllOrdered tableName writer orderFields = + Json.writeAllOrdered tableName writer orderFields conn + + /// Write a JSON document to the given StreamWriter by its ID + /// The table from which a document should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// The ID of the document to retrieve + member conn.writeJsonById<'TKey> tableName writer (docId: 'TKey) = + Json.writeById tableName writer docId conn + + /// + /// Write JSON documents to the given StreamWriter matching JSON field comparisons (->> =, + /// etc.) + /// + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + member conn.writeJsonByFields tableName writer howMatched fields = + Json.writeByFields tableName writer howMatched fields conn + + /// + /// Write JSON documents to the given StreamWriter matching JSON field comparisons (->> =, + /// etc.) ordered by the given fields in the document + /// + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + member conn.writeJsonByFieldsOrdered tableName writer howMatched queryFields orderFields = + Json.writeByFieldsOrdered tableName writer howMatched queryFields orderFields conn + + /// + /// Write the first JSON document to the given StreamWriter matching JSON field comparisons + /// (->> =, etc.) + /// + /// The table from which a document should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + member conn.writeJsonFirstByFields tableName writer howMatched fields = + Json.writeFirstByFields tableName writer howMatched fields conn + + /// + /// Write the first JSON document to the given StreamWriter matching JSON field comparisons + /// (->> =, etc.) ordered by the given fields in the document + /// + /// The table from which a document should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + member conn.writeJsonFirstByFieldsOrdered tableName writer howMatched queryFields orderFields = + Json.writeFirstByFieldsOrdered tableName writer howMatched queryFields orderFields conn /// Update (replace) an entire document by its ID /// The table in which a document should be updated (may include schema) /// The ID of the document to be updated (replaced) /// The new document member conn.updateById tableName (docId: 'TKey) (document: 'TDoc) = - WithConn.Update.byId tableName docId document conn + Update.byId tableName docId document conn /// /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the @@ -170,32 +326,31 @@ module Extensions = /// The function to obtain the ID of the document /// The new document member conn.updateByFunc tableName (idFunc: 'TDoc -> 'TKey) (document: 'TDoc) = - WithConn.Update.byFunc tableName idFunc document conn + Update.byFunc tableName idFunc document conn /// Patch a document by its ID /// The table in which a document should be patched (may include schema) /// The ID of the document to patch /// The partial document to patch the existing document member conn.patchById tableName (docId: 'TKey) (patch: 'TPatch) = - WithConn.Patch.byId tableName docId patch conn + Patch.byId tableName docId patch conn /// - /// Patch documents using a JSON field comparison query in the WHERE clause (->> =, - /// etc.) + /// Patch documents using a JSON field comparison query in the WHERE clause (->> =, etc.) /// /// The table in which documents should be patched (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// The partial document to patch the existing document member conn.patchByFields tableName howMatched fields (patch: 'TPatch) = - WithConn.Patch.byFields tableName howMatched fields patch conn + Patch.byFields tableName howMatched fields patch conn /// Remove fields from a document by the document's ID /// The table in which a document should be modified (may include schema) /// The ID of the document to modify /// One or more field names to remove from the document member conn.removeFieldsById tableName (docId: 'TKey) fieldNames = - WithConn.RemoveFields.byId tableName docId fieldNames conn + RemoveFields.byId tableName docId fieldNames conn /// Remove fields from documents via a comparison on JSON fields in the document /// The table in which documents should be modified (may include schema) @@ -203,20 +358,20 @@ module Extensions = /// The field conditions to match /// One or more field names to remove from the matching documents member conn.removeFieldsByFields tableName howMatched fields fieldNames = - WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn + RemoveFields.byFields tableName howMatched fields fieldNames conn /// Delete a document by its ID /// The table in which a document should be deleted (may include schema) /// The ID of the document to delete member conn.deleteById tableName (docId: 'TKey) = - WithConn.Delete.byId tableName docId conn + Delete.byId tableName docId conn - /// Delete documents by matching a JSON field comparison query (->> =, etc.) + /// Delete documents by matching a JSON field comparison query (->> =, etc.) /// The table in which documents should be deleted (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match member conn.deleteByFields tableName howMatched fields = - WithConn.Delete.byFields tableName howMatched fields conn + Delete.byFields tableName howMatched fields conn open System.Runtime.CompilerServices @@ -225,36 +380,66 @@ open System.Runtime.CompilerServices type SqliteConnectionCSharpExtensions = /// Execute a query that returns a list of results - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The query to retrieve the results /// Parameters to use for the query /// The mapping function between the document and the domain item /// A list of results for the given query [] static member inline CustomList<'TDoc>(conn, query, parameters, mapFunc: System.Func) = - WithConn.Custom.List<'TDoc>(query, parameters, mapFunc, conn) + Custom.List<'TDoc>(query, parameters, mapFunc, conn) + /// Execute a query that returns a JSON array of results + /// The SqliteConnection on which to run the query + /// The query to retrieve the results + /// Parameters to use for the query + /// The mapping function to extract the document + /// A JSON array of results for the given query + [] + static member inline CustomJsonArray(conn, query, parameters, mapFunc) = + Custom.JsonArray(query, parameters, mapFunc, conn) + + /// Execute a query, writing its results to the given StreamWriter + /// The SqliteConnection on which to run the query + /// The query to retrieve the results + /// Parameters to use for the query + /// The StreamWriter to which the results should be written + /// The mapping function to extract the document + [] + static member inline WriteCustomJsonArray(conn, query, parameters, writer, mapFunc) = + Custom.WriteJsonArray(query, parameters, writer, mapFunc, conn) + /// Execute a query that returns one or no results - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The query to retrieve the results /// Parameters to use for the query /// The mapping function between the document and the domain item - /// The first matching result, or null if not found + /// The first matching result, or null if not found [] static member inline CustomSingle<'TDoc when 'TDoc: null and 'TDoc: not struct>( conn, query, parameters, mapFunc: System.Func) = - WithConn.Custom.Single<'TDoc>(query, parameters, mapFunc, conn) + Custom.Single<'TDoc>(query, parameters, mapFunc, conn) + + /// Execute a query that returns one or no JSON documents + /// The SqliteConnection on which to run the query + /// The query to retrieve the results + /// Parameters to use for the query + /// The mapping function to extract the document + /// The JSON document with the first matching result, or an empty document if not found + [] + static member inline CustomJsonSingle(conn, query, parameters, mapFunc) = + Custom.JsonSingle(query, parameters, mapFunc, conn) /// Execute a query that returns no results - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The query to retrieve the results /// Parameters to use for the query [] static member inline CustomNonQuery(conn, query, parameters) = - WithConn.Custom.nonQuery query parameters conn + Custom.nonQuery query parameters conn /// Execute a query that returns a scalar value - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The query to retrieve the value /// Parameters to use for the query /// The mapping function to obtain the value @@ -262,118 +447,118 @@ type SqliteConnectionCSharpExtensions = [] static member inline CustomScalar<'T when 'T: struct>( conn, query, parameters, mapFunc: System.Func) = - WithConn.Custom.Scalar<'T>(query, parameters, mapFunc, conn) + Custom.Scalar<'T>(query, parameters, mapFunc, conn) /// Create a document table - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table whose existence should be ensured (may include schema) [] static member inline EnsureTable(conn, name) = - WithConn.Definition.ensureTable name conn + Definition.ensureTable name conn /// Create an index on field(s) within documents in the specified table - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table to be indexed (may include schema) /// The name of the index to create /// One or more fields to be indexed [] static member inline EnsureFieldIndex(conn, tableName, indexName, fields) = - WithConn.Definition.ensureFieldIndex tableName indexName fields conn + Definition.ensureFieldIndex tableName indexName fields conn /// Insert a new document - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table into which the document should be inserted (may include schema) /// The document to be inserted [] static member inline Insert<'TDoc>(conn, tableName, document: 'TDoc) = - WithConn.Document.insert<'TDoc> tableName document conn + insert<'TDoc> tableName document conn /// Save a document, inserting it if it does not exist and updating it if it does (AKA "upsert") - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table into which the document should be saved (may include schema) /// The document to be saved [] static member inline Save<'TDoc>(conn, tableName, document: 'TDoc) = - WithConn.Document.save<'TDoc> tableName document conn + save<'TDoc> tableName document conn /// Count all documents in a table - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which documents should be counted (may include schema) /// The count of the documents in the table [] static member inline CountAll(conn, tableName) = - WithConn.Count.all tableName conn + Count.all tableName conn - /// Count matching documents using JSON field comparisons (->> =, etc.) - /// The SqliteConnection on which to run the query + /// Count matching documents using JSON field comparisons (->> =, etc.) + /// The SqliteConnection on which to run the query /// The table in which documents should be counted (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// The count of matching documents in the table [] static member inline CountByFields(conn, tableName, howMatched, fields) = - WithConn.Count.byFields tableName howMatched fields conn + Count.byFields tableName howMatched fields conn /// Determine if a document exists for the given ID - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which existence should be checked (may include schema) /// The ID of the document whose existence should be checked /// True if a document exists, false if not [] static member inline ExistsById<'TKey>(conn, tableName, docId: 'TKey) = - WithConn.Exists.byId tableName docId conn + Exists.byId tableName docId conn - /// Determine if a document exists using JSON field comparisons (->> =, etc.) - /// The SqliteConnection on which to run the query + /// Determine if a document exists using JSON field comparisons (->> =, etc.) + /// The SqliteConnection on which to run the query /// The table in which existence should be checked (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// True if any matching documents exist, false if not [] static member inline ExistsByFields(conn, tableName, howMatched, fields) = - WithConn.Exists.byFields tableName howMatched fields conn + Exists.byFields tableName howMatched fields conn /// Retrieve all documents in the given table - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table from which documents should be retrieved (may include schema) /// All documents from the given table [] static member inline FindAll<'TDoc>(conn, tableName) = - WithConn.Find.All<'TDoc>(tableName, conn) + Find.All<'TDoc>(tableName, conn) /// Retrieve all documents in the given table ordered by the given fields in the document - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table from which documents should be retrieved (may include schema) /// Fields by which the results should be ordered /// All documents from the given table, ordered by the given fields [] static member inline FindAllOrdered<'TDoc>(conn, tableName, orderFields) = - WithConn.Find.AllOrdered<'TDoc>(tableName, orderFields, conn) + Find.AllOrdered<'TDoc>(tableName, orderFields, conn) /// Retrieve a document by its ID - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table from which a document should be retrieved (may include schema) /// The ID of the document to retrieve - /// The document if found, null otherwise + /// The document if found, null otherwise [] static member inline FindById<'TKey, 'TDoc when 'TDoc: null and 'TDoc: not struct>(conn, tableName, docId: 'TKey) = - WithConn.Find.ById<'TKey, 'TDoc>(tableName, docId, conn) + Find.ById<'TKey, 'TDoc>(tableName, docId, conn) - /// Retrieve documents matching JSON field comparisons (->> =, etc.) - /// The SqliteConnection on which to run the query + /// Retrieve documents matching JSON field comparisons (->> =, etc.) + /// The SqliteConnection on which to run the query /// The table from which documents should be retrieved (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// All documents matching the given fields [] static member inline FindByFields<'TDoc>(conn, tableName, howMatched, fields) = - WithConn.Find.ByFields<'TDoc>(tableName, howMatched, fields, conn) + Find.ByFields<'TDoc>(tableName, howMatched, fields, conn) /// - /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in + /// Retrieve documents matching JSON field comparisons (->> =, etc.) ordered by the given fields in /// the document /// - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table from which documents should be retrieved (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match @@ -381,108 +566,264 @@ type SqliteConnectionCSharpExtensions = /// All documents matching the given fields, ordered by the other given fields [] static member inline FindByFieldsOrdered<'TDoc>(conn, tableName, howMatched, queryFields, orderFields) = - WithConn.Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) + Find.ByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) - /// Retrieve the first document matching JSON field comparisons (->> =, etc.) - /// The SqliteConnection on which to run the query + /// Retrieve the first document matching JSON field comparisons (->> =, etc.) + /// The SqliteConnection on which to run the query /// The table from which a document should be retrieved (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match - /// The first document, or null if not found + /// The first document, or null if not found [] static member inline FindFirstByFields<'TDoc when 'TDoc: null and 'TDoc: not struct>( conn, tableName, howMatched, fields) = - WithConn.Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn) + Find.FirstByFields<'TDoc>(tableName, howMatched, fields, conn) /// - /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the given + /// Retrieve the first document matching JSON field comparisons (->> =, etc.) ordered by the given /// fields in the document /// - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table from which a document should be retrieved (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// Fields by which the results should be ordered - /// The first document ordered by the given fields, or null if not found + /// The first document ordered by the given fields, or null if not found [] static member inline FindFirstByFieldsOrdered<'TDoc when 'TDoc: null and 'TDoc: not struct>( conn, tableName, howMatched, queryFields, orderFields) = - WithConn.Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) + Find.FirstByFieldsOrdered<'TDoc>(tableName, howMatched, queryFields, orderFields, conn) + + /// Retrieve all JSON documents in the given table + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// All JSON documents from the given table + [] + static member inline JsonAll(conn, tableName) = + Json.all tableName conn + + /// Retrieve all JSON documents in the given table ordered by the given fields in the document + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// Fields by which the results should be ordered + /// All JSON documents from the given table, ordered by the given fields + [] + static member inline JsonAllOrdered(conn, tableName, orderFields) = + Json.allOrdered tableName orderFields conn + + /// Retrieve a JSON document by its ID + /// The SqliteConnection on which to run the query + /// The table from which a document should be retrieved (may include schema) + /// The ID of the document to retrieve + /// The JSON document if found, an empty JSON document otherwise + [] + static member inline JsonById<'TKey>(conn, tableName, docId: 'TKey) = + Json.byId tableName docId conn + + /// Retrieve JSON documents matching JSON field comparisons (->> =, etc.) + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// All JSON documents matching the given fields + [] + static member inline JsonByFields(conn, tableName, howMatched, fields) = + Json.byFields tableName howMatched fields conn + + /// + /// Retrieve JSON documents matching JSON field comparisons (->> =, etc.) ordered by the given fields + /// in the document + /// + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + /// All JSON documents matching the given fields, ordered by the other given fields + [] + static member inline JsonByFieldsOrdered(conn, tableName, howMatched, queryFields, orderFields) = + Json.byFieldsOrdered tableName howMatched queryFields orderFields conn + + /// Retrieve the first JSON document matching JSON field comparisons (->> =, etc.) + /// The SqliteConnection on which to run the query + /// The table from which a document should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// The first JSON document if found, an empty JSON document otherwise + [] + static member inline JsonFirstByFields(conn, tableName, howMatched, fields) = + Json.firstByFields tableName howMatched fields conn + + /// + /// Retrieve the first JSON document matching JSON field comparisons (->> =, etc.) ordered by the given + /// fields in the document + /// + /// The SqliteConnection on which to run the query + /// The table from which a document should be retrieved (may include schema) + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + /// The first JSON document (in order) if found, an empty JSON document otherwise + [] + static member inline JsonFirstByFieldsOrdered(conn, tableName, howMatched, queryFields, orderFields) = + Json.firstByFieldsOrdered tableName howMatched queryFields orderFields conn + + /// Write all JSON documents in the given table to the given StreamWriter + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + [] + static member inline WriteJsonAll(conn, tableName, writer) = + Json.writeAll tableName writer conn + + /// + /// Write all JSON all documents in the given table to the given StreamWriter, ordered by the given fields in + /// the document + /// + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Fields by which the results should be ordered + [] + static member inline WriteJsonAllOrdered(conn, tableName, writer, orderFields) = + Json.writeAllOrdered tableName writer orderFields conn + + /// Write a JSON document to the given StreamWriter by its ID + /// The SqliteConnection on which to run the query + /// The table from which a document should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// The ID of the document to retrieve + [] + static member inline WriteJsonById<'TKey>(conn, tableName, writer, docId: 'TKey) = + Json.writeById tableName writer docId conn + + /// + /// Write JSON documents to the given StreamWriter matching JSON field comparisons (->> =, etc.) + /// + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + [] + static member inline WriteJsonByFields(conn, tableName, writer, howMatched, fields) = + Json.writeByFields tableName writer howMatched fields conn + + /// + /// Write JSON documents to the given StreamWriter matching JSON field comparisons (->> =, etc.) + /// ordered by the given fields in the document + /// + /// The SqliteConnection on which to run the query + /// The table from which documents should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + [] + static member inline WriteJsonByFieldsOrdered(conn, tableName, writer, howMatched, queryFields, orderFields) = + Json.writeByFieldsOrdered tableName writer howMatched queryFields orderFields conn + + /// + /// Write the first JSON document to the given StreamWriter matching JSON field comparisons + /// (->> =, etc.) + /// + /// The SqliteConnection on which to run the query + /// The table from which a document should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + [] + static member inline WriteJsonFirstByFields(conn, tableName, writer, howMatched, fields) = + Json.writeFirstByFields tableName writer howMatched fields conn + + /// + /// Write the first JSON document to the given StreamWriter matching JSON field comparisons + /// (->> =, etc.) ordered by the given fields in the document + /// + /// The SqliteConnection on which to run the query + /// The table from which a document should be retrieved (may include schema) + /// The StreamWriter to which the results should be written + /// Whether to match any or all of the field conditions + /// The field conditions to match + /// Fields by which the results should be ordered + [] + static member inline WriteJsonFirstByFieldsOrdered(conn, tableName, writer, howMatched, queryFields, orderFields) = + Json.writeFirstByFieldsOrdered tableName writer howMatched queryFields orderFields conn /// Update (replace) an entire document by its ID - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which a document should be updated (may include schema) /// The ID of the document to be updated (replaced) /// The new document [] static member inline UpdateById<'TKey, 'TDoc>(conn, tableName, docId: 'TKey, document: 'TDoc) = - WithConn.Update.byId tableName docId document conn + Update.byId tableName docId document conn /// /// Update (replace) an entire document by its ID, using the provided function to obtain the ID from the document /// - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which a document should be updated (may include schema) /// The function to obtain the ID of the document /// The new document [] static member inline UpdateByFunc<'TKey, 'TDoc>( conn, tableName, idFunc: System.Func<'TDoc, 'TKey>, document: 'TDoc) = - WithConn.Update.ByFunc(tableName, idFunc, document, conn) + Update.ByFunc(tableName, idFunc, document, conn) /// Patch a document by its ID - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which a document should be patched (may include schema) /// The ID of the document to patch /// The partial document to patch the existing document [] static member inline PatchById<'TKey, 'TPatch>(conn, tableName, docId: 'TKey, patch: 'TPatch) = - WithConn.Patch.byId tableName docId patch conn + Patch.byId tableName docId patch conn /// - /// Patch documents using a JSON field comparison query in the WHERE clause (->> =, etc.) + /// Patch documents using a JSON field comparison query in the WHERE clause (->> =, etc.) /// - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which documents should be patched (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// The partial document to patch the existing document [] static member inline PatchByFields<'TPatch>(conn, tableName, howMatched, fields, patch: 'TPatch) = - WithConn.Patch.byFields tableName howMatched fields patch conn + Patch.byFields tableName howMatched fields patch conn /// Remove fields from a document by the document's ID - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which a document should be modified (may include schema) /// The ID of the document to modify /// One or more field names to remove from the document [] static member inline RemoveFieldsById<'TKey>(conn, tableName, docId: 'TKey, fieldNames) = - WithConn.RemoveFields.byId tableName docId fieldNames conn + RemoveFields.byId tableName docId fieldNames conn /// Remove fields from documents via a comparison on JSON fields in the document - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which documents should be modified (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match /// One or more field names to remove from the matching documents [] static member inline RemoveFieldsByFields(conn, tableName, howMatched, fields, fieldNames) = - WithConn.RemoveFields.byFields tableName howMatched fields fieldNames conn + RemoveFields.byFields tableName howMatched fields fieldNames conn /// Delete a document by its ID - /// The SqliteConnection on which to run the query + /// The SqliteConnection on which to run the query /// The table in which a document should be deleted (may include schema) /// The ID of the document to delete [] static member inline DeleteById<'TKey>(conn, tableName, docId: 'TKey) = - WithConn.Delete.byId tableName docId conn + Delete.byId tableName docId conn - /// Delete documents by matching a JSON field comparison query (->> =, etc.) - /// The SqliteConnection on which to run the query + /// Delete documents by matching a JSON field comparison query (->> =, etc.) + /// The SqliteConnection on which to run the query /// The table in which documents should be deleted (may include schema) /// Whether to match any or all of the field conditions /// The field conditions to match [] static member inline DeleteByFields(conn, tableName, howMatched, fields) = - WithConn.Delete.byFields tableName howMatched fields conn + Delete.byFields tableName howMatched fields conn diff --git a/src/Tests/SqliteExtensionTests.fs b/src/Tests/SqliteExtensionTests.fs index 8f0380e..b17c19e 100644 --- a/src/Tests/SqliteExtensionTests.fs +++ b/src/Tests/SqliteExtensionTests.fs @@ -1,5 +1,6 @@ module SqliteExtensionTests +open System.IO open System.Text.Json open BitBadger.Documents open BitBadger.Documents.Sqlite @@ -10,10 +11,44 @@ open Types /// Integration tests for the F# extensions on the SqliteConnection data type let integrationTests = - let loadDocs () = backgroundTask { - for doc in testDocuments do do! insert SqliteDb.TableName doc + let loadDocs (conn: SqliteConnection) = backgroundTask { + for doc in testDocuments do do! conn.insert SqliteDb.TableName doc } - testList "Sqlite.Extensions" [ + + /// Set up a stream writer for a test + let writeStream (stream: Stream) = + let writer = new StreamWriter(stream) + writer.AutoFlush <- true + writer + + /// Get the text of the given stream + let streamText (stream: Stream) = + stream.Position <- 0L + use reader = new StreamReader(stream) + reader.ReadToEnd() + + /// Verify a JSON array begins with "[" and ends with "]" + let verifyBeginEnd json = + Expect.stringStarts json "[" "The array should have started with `[`" + Expect.stringEnds json "]" "The array should have ended with `]`" + + /// Verify an empty JSON array + let verifyEmpty json = + Expect.equal json "[]" "There should be no documents returned" + + /// Verify an empty JSON document + let verifyNoDoc json = + Expect.equal json "{}" "There should be no document returned" + + /// Verify the presence of any of the given documents in the given JSON + let verifyAny (json: string) (docs: string list) = + match docs |> List.tryFind json.Contains with + | Some _ -> () + | None -> + let theDocs = docs |> String.concat " | " + Expect.isTrue false $"Could not find any of |{theDocs}| in {json}" + + ftestList "Sqlite.Extensions" [ testTask "ensureTable succeeds" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () @@ -108,7 +143,7 @@ let integrationTests = testTask "countAll succeeds" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! theCount = conn.countAll SqliteDb.TableName Expect.equal theCount 5L "There should have been 5 matching documents" @@ -116,7 +151,7 @@ let integrationTests = testTask "countByFields succeeds" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! theCount = conn.countByFields SqliteDb.TableName Any [ Field.Equal "Value" "purple" ] Expect.equal theCount 2L "There should have been 2 matching documents" @@ -125,7 +160,7 @@ let integrationTests = testTask "succeeds when a document exists" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! exists = conn.existsById SqliteDb.TableName "three" Expect.isTrue exists "There should have been an existing document" @@ -133,7 +168,7 @@ let integrationTests = testTask "succeeds when a document does not exist" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! exists = conn.existsById SqliteDb.TableName "seven" Expect.isFalse exists "There should not have been an existing document" @@ -143,7 +178,7 @@ let integrationTests = testTask "succeeds when documents exist" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 10 ] Expect.isTrue exists "There should have been existing documents" @@ -151,7 +186,7 @@ let integrationTests = testTask "succeeds when no matching documents exist" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! exists = conn.existsByFields SqliteDb.TableName Any [ Field.Equal "Nothing" "none" ] Expect.isFalse exists "There should not have been any existing documents" @@ -185,7 +220,7 @@ let integrationTests = testTask "succeeds when ordering numerically" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! results = conn.findAllOrdered SqliteDb.TableName [ Field.Named "n:NumValue" ] Expect.hasLength results 5 "There should have been 5 documents returned" @@ -197,7 +232,7 @@ let integrationTests = testTask "succeeds when ordering numerically descending" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! results = conn.findAllOrdered SqliteDb.TableName [ Field.Named "n:NumValue DESC" ] Expect.hasLength results 5 "There should have been 5 documents returned" @@ -209,7 +244,7 @@ let integrationTests = testTask "succeeds when ordering alphabetically" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! results = conn.findAllOrdered SqliteDb.TableName [ Field.Named "Id DESC" ] Expect.hasLength results 5 "There should have been 5 documents returned" @@ -223,7 +258,7 @@ let integrationTests = testTask "succeeds when a document is found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findById SqliteDb.TableName "two" Expect.isSome doc "There should have been a document returned" @@ -232,7 +267,7 @@ let integrationTests = testTask "succeeds when a document is not found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findById SqliteDb.TableName "three hundred eighty-seven" Expect.isNone doc "There should not have been a document returned" @@ -242,7 +277,7 @@ let integrationTests = testTask "succeeds when documents are found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! docs = conn.findByFields SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] Expect.hasLength docs 2 "There should have been two documents returned" @@ -250,7 +285,7 @@ let integrationTests = testTask "succeeds when documents are not found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! docs = conn.findByFields SqliteDb.TableName Any [ Field.Equal "Value" "mauve" ] Expect.isEmpty docs "There should have been no documents returned" @@ -260,7 +295,7 @@ let integrationTests = testTask "succeeds when sorting ascending" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! docs = conn.findByFieldsOrdered @@ -271,7 +306,7 @@ let integrationTests = testTask "succeeds when sorting descending" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! docs = conn.findByFieldsOrdered @@ -284,7 +319,7 @@ let integrationTests = testTask "succeeds when a document is found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findFirstByFields SqliteDb.TableName Any [ Field.Equal "Value" "another" ] Expect.isSome doc "There should have been a document returned" @@ -293,7 +328,7 @@ let integrationTests = testTask "succeeds when multiple documents are found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findFirstByFields SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] Expect.isSome doc "There should have been a document returned" @@ -302,7 +337,7 @@ let integrationTests = testTask "succeeds when a document is not found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findFirstByFields SqliteDb.TableName Any [ Field.Equal "Value" "absent" ] Expect.isNone doc "There should not have been a document returned" @@ -312,7 +347,7 @@ let integrationTests = testTask "succeeds when sorting ascending" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findFirstByFieldsOrdered @@ -323,7 +358,7 @@ let integrationTests = testTask "succeeds when sorting descending" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.findFirstByFieldsOrdered @@ -332,11 +367,500 @@ let integrationTests = Expect.equal "four" doc.Value.Id "An incorrect document was returned" } ] + testList "jsonAll" [ + testTask "succeeds when there is data" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + + do! conn.insert SqliteDb.TableName { Foo = "one"; Bar = "two" } + do! conn.insert SqliteDb.TableName { Foo = "three"; Bar = "four" } + do! conn.insert SqliteDb.TableName { Foo = "five"; Bar = "six" } + + let! json = conn.jsonAll SqliteDb.TableName + verifyBeginEnd json + Expect.stringContains json """{"Foo":"one","Bar":"two"}""" "The first document was not found" + Expect.stringContains json """{"Foo":"three","Bar":"four"}""" "The second document was not found" + Expect.stringContains json """{"Foo":"five","Bar":"six"}""" "The third document was not found" + } + testTask "succeeds when there is no data" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + let! json = conn.jsonAll SqliteDb.TableName + verifyEmpty json + } + ] + testList "jsonAllOrdered" [ + testTask "succeeds when ordering numerically" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonAllOrdered SqliteDb.TableName [ Field.Named "n:NumValue" ] + Expect.equal + json + $"[{JsonDocument.one},{JsonDocument.three},{JsonDocument.two},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering numerically descending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonAllOrdered SqliteDb.TableName [ Field.Named "n:NumValue DESC" ] + Expect.equal + json + $"[{JsonDocument.five},{JsonDocument.four},{JsonDocument.two},{JsonDocument.three},{JsonDocument.one}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering alphabetically" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonAllOrdered SqliteDb.TableName [ Field.Named "Id DESC" ] + Expect.equal + json + $"[{JsonDocument.two},{JsonDocument.three},{JsonDocument.one},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + ] + testList "jsonById" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonById SqliteDb.TableName "two" + Expect.equal json JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonById SqliteDb.TableName "three hundred eighty-seven" + verifyNoDoc json + } + ] + testList "jsonByFields" [ + testTask "succeeds when documents are found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonByFields SqliteDb.TableName Any [ Field.Greater "NumValue" 15 ] + verifyBeginEnd json + Expect.stringContains json JsonDocument.four "Document `four` should have been returned" + Expect.stringContains json JsonDocument.five "Document `five` should have been returned" + } + testTask "succeeds when documents are found using IN with numeric field" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonByFields SqliteDb.TableName All [ Field.In "NumValue" [ 2; 4; 6; 8 ] ] + Expect.equal json $"[{JsonDocument.three}]" "There should have been one document returned" + } + testTask "succeeds when documents are not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonByFields SqliteDb.TableName Any [ Field.Greater "NumValue" 100 ] + verifyEmpty json + } + testTask "succeeds for InArray when matching documents exist" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! conn.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! conn.insert SqliteDb.TableName doc + + let! json = + conn.jsonByFields SqliteDb.TableName All [ Field.InArray "Values" SqliteDb.TableName [ "c" ] ] + verifyBeginEnd json + Expect.stringContains + json """{"Id":"first","Values":["a","b","c"]}""" "Document `first` should have been returned" + Expect.stringContains + json """{"Id":"second","Values":["c","d","e"]}""" "Document `second` should have been returned" + } + testTask "succeeds for InArray when no matching documents exist" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! conn.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! conn.insert SqliteDb.TableName doc + + let! json = + conn.jsonByFields SqliteDb.TableName All [ Field.InArray "Values" SqliteDb.TableName [ "j" ] ] + verifyEmpty json + } + ] + testList "jsonByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = + conn.jsonByFieldsOrdered SqliteDb.TableName Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id" ] + Expect.equal json $"[{JsonDocument.five},{JsonDocument.four}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = + conn.jsonByFieldsOrdered + SqliteDb.TableName Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id DESC" ] + Expect.equal json $"[{JsonDocument.four},{JsonDocument.five}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting case-sensitively" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = + conn.jsonByFieldsOrdered + SqliteDb.TableName All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "Value" ] + Expect.equal + json + $"[{JsonDocument.three},{JsonDocument.one},{JsonDocument.two}]" + "Documents not ordered correctly" + } + testTask "succeeds when sorting case-insensitively" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = + conn.jsonByFieldsOrdered + SqliteDb.TableName All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "i:Value" ] + Expect.equal + json + $"[{JsonDocument.three},{JsonDocument.two},{JsonDocument.one}]" + "Documents not ordered correctly" + } + ] + testList "jsonFirstByFields" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonFirstByFields SqliteDb.TableName Any [ Field.Equal "Value" "another" ] + Expect.equal json JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when multiple documents are found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonFirstByFields SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] + Expect.notEqual json "{}" "There should have been a document returned" + verifyAny json [ JsonDocument.two; JsonDocument.four ] + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.jsonFirstByFields SqliteDb.TableName Any [ Field.Equal "Value" "absent" ] + verifyNoDoc json + } + ] + testList "jsonFirstByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = + conn.jsonFirstByFieldsOrdered + SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ] + Expect.equal json JsonDocument.two "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = + conn.jsonFirstByFieldsOrdered + SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ] + Expect.equal json JsonDocument.four "An incorrect document was returned" + } + ] + testList "writeJsonAll" [ + testTask "succeeds when there is data" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + + do! conn.insert SqliteDb.TableName { Foo = "one"; Bar = "two" } + do! conn.insert SqliteDb.TableName { Foo = "three"; Bar = "four" } + do! conn.insert SqliteDb.TableName { Foo = "five"; Bar = "six" } + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonAll SqliteDb.TableName writer + let json = streamText stream + verifyBeginEnd json + Expect.stringContains json """{"Foo":"one","Bar":"two"}""" "The first document was not found" + Expect.stringContains json """{"Foo":"three","Bar":"four"}""" "The second document was not found" + Expect.stringContains json """{"Foo":"five","Bar":"six"}""" "The third document was not found" + } + testTask "succeeds when there is no data" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonAll SqliteDb.TableName writer + verifyEmpty (streamText stream) + } + ] + testList "writeJsonAllOrdered" [ + testTask "succeeds when ordering numerically" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonAllOrdered SqliteDb.TableName writer [ Field.Named "n:NumValue" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.one},{JsonDocument.three},{JsonDocument.two},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering numerically descending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonAllOrdered SqliteDb.TableName writer [ Field.Named "n:NumValue DESC" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.five},{JsonDocument.four},{JsonDocument.two},{JsonDocument.three},{JsonDocument.one}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering alphabetically" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonAllOrdered SqliteDb.TableName writer [ Field.Named "Id DESC" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.two},{JsonDocument.three},{JsonDocument.one},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + ] + testList "writeJsonById" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonById SqliteDb.TableName writer "two" + Expect.equal (streamText stream) JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonById SqliteDb.TableName writer "three hundred eighty-seven" + verifyNoDoc (streamText stream) + } + ] + testList "writeJsonByFields" [ + testTask "succeeds when documents are found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFields SqliteDb.TableName writer Any [ Field.Greater "NumValue" 15 ] + let json = streamText stream + verifyBeginEnd json + Expect.stringContains json JsonDocument.four "Document `four` should have been returned" + Expect.stringContains json JsonDocument.five "Document `five` should have been returned" + } + testTask "succeeds when documents are found using IN with numeric field" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFields SqliteDb.TableName writer All [ Field.In "NumValue" [ 2; 4; 6; 8 ] ] + Expect.equal (streamText stream) $"[{JsonDocument.three}]" "There should have been one document returned" + } + testTask "succeeds when documents are not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFields SqliteDb.TableName writer Any [ Field.Greater "NumValue" 100 ] + verifyEmpty (streamText stream) + } + testTask "succeeds for InArray when matching documents exist" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! conn.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! conn.insert SqliteDb.TableName doc + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFields + SqliteDb.TableName writer All [ Field.InArray "Values" SqliteDb.TableName [ "c" ] ] + let json = streamText stream + verifyBeginEnd json + Expect.stringContains + json """{"Id":"first","Values":["a","b","c"]}""" "Document `first` should have been returned" + Expect.stringContains + json """{"Id":"second","Values":["c","d","e"]}""" "Document `second` should have been returned" + } + testTask "succeeds for InArray when no matching documents exist" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! conn.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! conn.insert SqliteDb.TableName doc + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFields + SqliteDb.TableName writer All [ Field.InArray "Values" SqliteDb.TableName [ "j" ] ] + verifyEmpty (streamText stream) + } + ] + testList "writeJsonByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id" ] + Expect.equal + (streamText stream) $"[{JsonDocument.five},{JsonDocument.four}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id DESC" ] + Expect.equal + (streamText stream) $"[{JsonDocument.four},{JsonDocument.five}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting case-sensitively" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFieldsOrdered + SqliteDb.TableName writer All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "Value" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.three},{JsonDocument.one},{JsonDocument.two}]" + "Documents not ordered correctly" + } + testTask "succeeds when sorting case-insensitively" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonByFieldsOrdered + SqliteDb.TableName writer All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "i:Value" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.three},{JsonDocument.two},{JsonDocument.one}]" + "Documents not ordered correctly" + } + ] + testList "writeJsonFirstByFields" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonFirstByFields SqliteDb.TableName writer Any [ Field.Equal "Value" "another" ] + Expect.equal (streamText stream) JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when multiple documents are found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonFirstByFields SqliteDb.TableName writer Any [ Field.Equal "Sub.Foo" "green" ] + let json = streamText stream + Expect.notEqual json "{}" "There should have been a document returned" + verifyAny json [ JsonDocument.two; JsonDocument.four ] + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonFirstByFields SqliteDb.TableName writer Any [ Field.Equal "Value" "absent" ] + verifyNoDoc (streamText stream) + } + ] + testList "writeJsonFirstByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonFirstByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ] + Expect.equal (streamText stream) JsonDocument.two "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeJsonFirstByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ] + Expect.equal (streamText stream) JsonDocument.four "An incorrect document was returned" + } + ] testList "updateById" [ testTask "succeeds when a document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let testDoc = { emptyDoc with Id = "one"; Sub = Some { Foo = "blue"; Bar = "red" } } do! conn.updateById SqliteDb.TableName "one" testDoc @@ -363,10 +887,10 @@ let integrationTests = testTask "succeeds when a document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.updateByFunc - 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 = conn.findById SqliteDb.TableName "one" if Option.isNone after then Expect.isTrue false "There should have been a document returned post-update" @@ -384,14 +908,14 @@ let integrationTests = // This not raising an exception is the test do! conn.updateByFunc - 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 "patchById" [ testTask "succeeds when a document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.patchById SqliteDb.TableName "one" {| NumValue = 44 |} let! after = conn.findById SqliteDb.TableName "one" @@ -414,7 +938,7 @@ let integrationTests = testTask "succeeds when a document is updated" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.patchByFields SqliteDb.TableName Any [ Field.Equal "Value" "purple" ] {| NumValue = 77 |} let! after = conn.countByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 77 ] @@ -435,7 +959,7 @@ let integrationTests = testTask "succeeds when fields are removed" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.removeFieldsById SqliteDb.TableName "two" [ "Sub"; "Value" ] try @@ -448,7 +972,7 @@ let integrationTests = testTask "succeeds when a field is not removed" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn // This not raising an exception is the test do! conn.removeFieldsById SqliteDb.TableName "two" [ "AFieldThatIsNotThere" ] @@ -465,7 +989,7 @@ let integrationTests = testTask "succeeds when a field is removed" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 17 ] [ "Sub" ] try @@ -478,7 +1002,7 @@ let integrationTests = testTask "succeeds when a field is not removed" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn // This not raising an exception is the test do! conn.removeFieldsByFields SqliteDb.TableName Any [ Field.Equal "NumValue" 17 ] [ "Nothing" ] @@ -496,7 +1020,7 @@ let integrationTests = testTask "succeeds when a document is deleted" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.deleteById SqliteDb.TableName "four" let! remaining = conn.countAll SqliteDb.TableName @@ -505,7 +1029,7 @@ let integrationTests = testTask "succeeds when a document is not deleted" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.deleteById SqliteDb.TableName "thirty" let! remaining = conn.countAll SqliteDb.TableName @@ -516,7 +1040,7 @@ let integrationTests = testTask "succeeds when documents are deleted" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.deleteByFields SqliteDb.TableName Any [ Field.NotEqual "Value" "purple" ] let! remaining = conn.countAll SqliteDb.TableName @@ -525,18 +1049,103 @@ let integrationTests = testTask "succeeds when documents are not deleted" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.deleteByFields SqliteDb.TableName Any [ Field.Equal "Value" "crimson" ] let! remaining = conn.countAll SqliteDb.TableName Expect.equal remaining 5L "There should have been 5 documents remaining" } ] + testList "customList" [ + testTask "succeeds when data is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! docs = conn.customList (Query.find SqliteDb.TableName) [] fromData + Expect.hasLength docs 5 "There should have been 5 documents returned" + } + testTask "succeeds when data is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! docs = + conn.customList + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" + [ SqliteParameter("@value", 100) ] + fromData + Expect.isEmpty docs "There should have been no documents returned" + } + ] + testList "customJsonArray" [ + testTask "succeeds when data is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! json = conn.customJsonArray (Query.find SqliteDb.TableName) [] jsonFromData + Expect.stringStarts json "[" "The JSON array should have started with `[`" + Expect.stringContains json JsonDocument.one "Document ID `one` should have been found" + Expect.stringContains json JsonDocument.two "Document ID `two` should have been found" + Expect.stringContains json JsonDocument.three "Document ID `three` should have been found" + Expect.stringContains json JsonDocument.four "Document ID `four` should have been found" + Expect.stringContains json JsonDocument.five "Document ID `five` should have been found" + Expect.stringEnds json "]" "The JSON array should have ended with `[`" + } + testTask "succeeds when data is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + let! docs = + conn.customJsonArray + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" + [ SqliteParameter("@value", 100) ] + jsonFromData + Expect.equal docs "[]" "There should have been no documents returned" + } + ] + testList "writeCustomJsonArray" [ + testTask "succeeds when data is found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeCustomJsonArray (Query.find SqliteDb.TableName) [] writer jsonFromData + + let json = streamText stream + Expect.stringStarts json "[" "The JSON array should have started with `[`" + Expect.stringContains json JsonDocument.one "Document ID `one` should have been found" + Expect.stringContains json JsonDocument.two "Document ID `two` should have been found" + Expect.stringContains json JsonDocument.three "Document ID `three` should have been found" + Expect.stringContains json JsonDocument.four "Document ID `four` should have been found" + Expect.stringContains json JsonDocument.five "Document ID `five` should have been found" + Expect.stringEnds json "]" "The JSON array should have ended with `[`" + } + testTask "succeeds when data is not found" { + use! db = SqliteDb.BuildDb() + use conn = Configuration.dbConn () + do! loadDocs conn + + use stream = new MemoryStream() + use writer = writeStream stream + do! conn.writeCustomJsonArray + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" + [ SqliteParameter("@value", 100) ] + writer + jsonFromData + + Expect.equal (streamText stream) "[]" "There should have been no documents returned" + } + ] testList "customSingle" [ testTask "succeeds when a row is found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.customSingle @@ -549,7 +1158,7 @@ let integrationTests = testTask "succeeds when a row is not found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn let! doc = conn.customSingle @@ -559,33 +1168,37 @@ let integrationTests = Expect.isNone doc "There should not have been a document returned" } ] - testList "customList" [ - testTask "succeeds when data is found" { + testList "customJsonSingle" [ + testTask "succeeds when a row is found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () - - let! docs = conn.customList (Query.find SqliteDb.TableName) [] fromData - Expect.hasLength docs 5 "There should have been 5 documents returned" + do! loadDocs conn + + let! json = + conn.customJsonSingle + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id" + [ SqliteParameter("@id", "one") ] + jsonFromData + Expect.equal json JsonDocument.one "The JSON document is incorrect" } - testTask "succeeds when data is not found" { + testTask "succeeds when a row is not found" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () - - let! docs = - conn.customList - $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" - [ SqliteParameter("@value", 100) ] - fromData - Expect.isEmpty docs "There should have been no documents returned" + do! loadDocs conn + + let! json = + conn.customJsonSingle + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id" + [ SqliteParameter("@id", "eighty") ] + jsonFromData + Expect.equal json "{}" "There should not have been a document returned" } ] testList "customNonQuery" [ testTask "succeeds when operating on data" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.customNonQuery $"DELETE FROM {SqliteDb.TableName}" [] @@ -595,7 +1208,7 @@ let integrationTests = testTask "succeeds when no data matches where clause" { use! db = SqliteDb.BuildDb() use conn = Configuration.dbConn () - do! loadDocs () + do! loadDocs conn do! conn.customNonQuery $"DELETE FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" diff --git a/src/Tests/SqliteTests.fs b/src/Tests/SqliteTests.fs index c2c3f58..1ba12a1 100644 --- a/src/Tests/SqliteTests.fs +++ b/src/Tests/SqliteTests.fs @@ -1,5 +1,6 @@ module SqliteTests +open System.IO open System.Text.Json open BitBadger.Documents open BitBadger.Documents.Sqlite @@ -135,6 +136,19 @@ let loadDocs () = backgroundTask { for doc in testDocuments do do! insert SqliteDb.TableName doc } +/// Set up a stream writer for a test +let writeStream (stream: Stream) = + let writer = new StreamWriter(stream) + writer.AutoFlush <- true + writer + +/// Get the text of the given stream +let streamText (stream: Stream) = + stream.Position <- 0L + use reader = new StreamReader(stream) + reader.ReadToEnd() + + /// Integration tests for the Configuration module of the SQLite library let configurationTests = testList "Configuration" [ test "useConnectionString / connectionString succeed" { @@ -151,6 +165,85 @@ let configurationTests = testList "Configuration" [ /// Integration tests for the Custom module of the SQLite library let customTests = testList "Custom" [ + testList "list" [ + testTask "succeeds when data is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! docs = Custom.list (Query.find SqliteDb.TableName) [] fromData + Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned" + } + testTask "succeeds when data is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! docs = + Custom.list + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" + [ SqliteParameter("@value", 100) ] + fromData + Expect.isEmpty docs "There should have been no documents returned" + } + ] + testList "jsonArray" [ + testTask "succeeds when data is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Custom.jsonArray (Query.find SqliteDb.TableName) [] jsonFromData + Expect.stringStarts json "[" "The JSON array should have started with `[`" + Expect.stringContains json JsonDocument.one "Document ID `one` should have been found" + Expect.stringContains json JsonDocument.two "Document ID `two` should have been found" + Expect.stringContains json JsonDocument.three "Document ID `three` should have been found" + Expect.stringContains json JsonDocument.four "Document ID `four` should have been found" + Expect.stringContains json JsonDocument.five "Document ID `five` should have been found" + Expect.stringEnds json "]" "The JSON array should have ended with `[`" + } + testTask "succeeds when data is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! docs = + Custom.jsonArray + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" + [ SqliteParameter("@value", 100) ] + jsonFromData + Expect.equal docs "[]" "There should have been no documents returned" + } + ] + testList "writeJsonArray" [ + testTask "succeeds when data is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Custom.writeJsonArray (Query.find SqliteDb.TableName) [] writer jsonFromData + + let json = streamText stream + Expect.stringStarts json "[" "The JSON array should have started with `[`" + Expect.stringContains json JsonDocument.one "Document ID `one` should have been found" + Expect.stringContains json JsonDocument.two "Document ID `two` should have been found" + Expect.stringContains json JsonDocument.three "Document ID `three` should have been found" + Expect.stringContains json JsonDocument.four "Document ID `four` should have been found" + Expect.stringContains json JsonDocument.five "Document ID `five` should have been found" + Expect.stringEnds json "]" "The JSON array should have ended with `[`" + } + testTask "succeeds when data is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Custom.writeJsonArray + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" + [ SqliteParameter("@value", 100) ] + writer + jsonFromData + + Expect.equal (streamText stream) "[]" "There should have been no documents returned" + } + ] testList "single" [ testTask "succeeds when a row is found" { use! db = SqliteDb.BuildDb() @@ -176,24 +269,28 @@ let customTests = testList "Custom" [ Expect.isNone doc "There should not have been a document returned" } ] - testList "list" [ - testTask "succeeds when data is found" { + testList "jsonSingle" [ + testTask "succeeds when a row is found" { use! db = SqliteDb.BuildDb() do! loadDocs () - let! docs = Custom.list (Query.find SqliteDb.TableName) [] fromData - Expect.hasCountOf docs 5u (fun _ -> true) "There should have been 5 documents returned" + let! json = + Custom.jsonSingle + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id" + [ SqliteParameter("@id", "one") ] + jsonFromData + Expect.equal json JsonDocument.one "The JSON document is incorrect" } - testTask "succeeds when data is not found" { + testTask "succeeds when a row is not found" { use! db = SqliteDb.BuildDb() do! loadDocs () - let! docs = - Custom.list - $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'NumValue' > @value" - [ SqliteParameter("@value", 100) ] - fromData - Expect.isEmpty docs "There should have been no documents returned" + let! json = + Custom.jsonSingle + $"SELECT data FROM {SqliteDb.TableName} WHERE data ->> 'Id' = @id" + [ SqliteParameter("@id", "eighty") ] + jsonFromData + Expect.equal json "{}" "There should not have been a document returned" } ] testList "nonQuery" [ @@ -653,6 +750,467 @@ let findTests = testList "Find" [ ] ] +/// Verify a JSON array begins with "[" and ends with "]" +let private verifyBeginEnd json = + Expect.stringStarts json "[" "The array should have started with `[`" + Expect.stringEnds json "]" "The array should have ended with `]`" + +/// Verify an empty JSON array +let private verifyEmpty json = + Expect.equal json "[]" "There should be no documents returned" + +/// Verify an empty JSON document +let private verifyNoDoc json = + Expect.equal json "{}" "There should be no document returned" + +/// Verify the presence of any of the given documents in the given JSON +let private verifyAny (json: string) (docs: string list) = + match docs |> List.tryFind json.Contains with + | Some _ -> () + | None -> + let theDocs = docs |> String.concat " | " + Expect.isTrue false $"Could not find any of |{theDocs}| in {json}" + +/// Integration tests for the Json module of the SQLite library +let jsonTests = testList "Json" [ + testList "all" [ + testTask "succeeds when there is data" { + use! db = SqliteDb.BuildDb() + + do! insert SqliteDb.TableName { Foo = "one"; Bar = "two" } + do! insert SqliteDb.TableName { Foo = "three"; Bar = "four" } + do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" } + + let! json = Json.all SqliteDb.TableName + verifyBeginEnd json + Expect.stringContains json """{"Foo":"one","Bar":"two"}""" "The first document was not found" + Expect.stringContains json """{"Foo":"three","Bar":"four"}""" "The second document was not found" + Expect.stringContains json """{"Foo":"five","Bar":"six"}""" "The third document was not found" + } + testTask "succeeds when there is no data" { + use! db = SqliteDb.BuildDb() + let! json = Json.all SqliteDb.TableName + verifyEmpty json + } + ] + testList "allOrdered" [ + testTask "succeeds when ordering numerically" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.allOrdered SqliteDb.TableName [ Field.Named "n:NumValue" ] + Expect.equal + json + $"[{JsonDocument.one},{JsonDocument.three},{JsonDocument.two},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering numerically descending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.allOrdered SqliteDb.TableName [ Field.Named "n:NumValue DESC" ] + Expect.equal + json + $"[{JsonDocument.five},{JsonDocument.four},{JsonDocument.two},{JsonDocument.three},{JsonDocument.one}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering alphabetically" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.allOrdered SqliteDb.TableName [ Field.Named "Id DESC" ] + Expect.equal + json + $"[{JsonDocument.two},{JsonDocument.three},{JsonDocument.one},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + ] + testList "byId" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.byId SqliteDb.TableName "two" + Expect.equal json JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.byId SqliteDb.TableName "three hundred eighty-seven" + verifyNoDoc json + } + ] + testList "byFields" [ + testTask "succeeds when documents are found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.byFields SqliteDb.TableName Any [ Field.Greater "NumValue" 15 ] + verifyBeginEnd json + Expect.stringContains json JsonDocument.four "Document `four` should have been returned" + Expect.stringContains json JsonDocument.five "Document `five` should have been returned" + } + testTask "succeeds when documents are found using IN with numeric field" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.byFields SqliteDb.TableName All [ Field.In "NumValue" [ 2; 4; 6; 8 ] ] + Expect.equal json $"[{JsonDocument.three}]" "There should have been one document returned" + } + testTask "succeeds when documents are not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.byFields SqliteDb.TableName Any [ Field.Greater "NumValue" 100 ] + verifyEmpty json + } + testTask "succeeds for InArray when matching documents exist" { + use! db = SqliteDb.BuildDb() + do! Definition.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! insert SqliteDb.TableName doc + + let! json = Json.byFields SqliteDb.TableName All [ Field.InArray "Values" SqliteDb.TableName [ "c" ] ] + verifyBeginEnd json + Expect.stringContains + json """{"Id":"first","Values":["a","b","c"]}""" "Document `first` should have been returned" + Expect.stringContains + json """{"Id":"second","Values":["c","d","e"]}""" "Document `second` should have been returned" + } + testTask "succeeds for InArray when no matching documents exist" { + use! db = SqliteDb.BuildDb() + do! Definition.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! insert SqliteDb.TableName doc + + let! json = Json.byFields SqliteDb.TableName All [ Field.InArray "Values" SqliteDb.TableName [ "j" ] ] + verifyEmpty json + } + ] + testList "byFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.byFieldsOrdered SqliteDb.TableName Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id" ] + Expect.equal json $"[{JsonDocument.five},{JsonDocument.four}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = + Json.byFieldsOrdered SqliteDb.TableName Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id DESC" ] + Expect.equal json $"[{JsonDocument.four},{JsonDocument.five}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting case-sensitively" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = + Json.byFieldsOrdered SqliteDb.TableName All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "Value" ] + Expect.equal + json $"[{JsonDocument.three},{JsonDocument.one},{JsonDocument.two}]" "Documents not ordered correctly" + } + testTask "succeeds when sorting case-insensitively" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = + Json.byFieldsOrdered + SqliteDb.TableName All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "i:Value" ] + Expect.equal + json $"[{JsonDocument.three},{JsonDocument.two},{JsonDocument.one}]" "Documents not ordered correctly" + } + ] + testList "firstByFields" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.firstByFields SqliteDb.TableName Any [ Field.Equal "Value" "another" ] + Expect.equal json JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when multiple documents are found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.firstByFields SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] + Expect.notEqual json "{}" "There should have been a document returned" + verifyAny json [ JsonDocument.two; JsonDocument.four ] + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = Json.firstByFields SqliteDb.TableName Any [ Field.Equal "Value" "absent" ] + verifyNoDoc json + } + ] + testList "firstByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = + Json.firstByFieldsOrdered + SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ] + Expect.equal json JsonDocument.two "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + let! json = + Json.firstByFieldsOrdered + SqliteDb.TableName Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ] + Expect.equal json JsonDocument.four "An incorrect document was returned" + } + ] + testList "writeAll" [ + testTask "succeeds when there is data" { + use! db = SqliteDb.BuildDb() + + do! insert SqliteDb.TableName { Foo = "one"; Bar = "two" } + do! insert SqliteDb.TableName { Foo = "three"; Bar = "four" } + do! insert SqliteDb.TableName { Foo = "five"; Bar = "six" } + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeAll SqliteDb.TableName writer + let json = streamText stream + verifyBeginEnd json + Expect.stringContains json """{"Foo":"one","Bar":"two"}""" "The first document was not found" + Expect.stringContains json """{"Foo":"three","Bar":"four"}""" "The second document was not found" + Expect.stringContains json """{"Foo":"five","Bar":"six"}""" "The third document was not found" + } + testTask "succeeds when there is no data" { + use! db = SqliteDb.BuildDb() + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeAll SqliteDb.TableName writer + verifyEmpty (streamText stream) + } + ] + testList "writeAllOrdered" [ + testTask "succeeds when ordering numerically" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeAllOrdered SqliteDb.TableName writer [ Field.Named "n:NumValue" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.one},{JsonDocument.three},{JsonDocument.two},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering numerically descending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeAllOrdered SqliteDb.TableName writer [ Field.Named "n:NumValue DESC" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.five},{JsonDocument.four},{JsonDocument.two},{JsonDocument.three},{JsonDocument.one}]" + "The documents were not ordered correctly" + } + testTask "succeeds when ordering alphabetically" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeAllOrdered SqliteDb.TableName writer [ Field.Named "Id DESC" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.two},{JsonDocument.three},{JsonDocument.one},{JsonDocument.four},{JsonDocument.five}]" + "The documents were not ordered correctly" + } + ] + testList "writeById" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeById SqliteDb.TableName writer "two" + Expect.equal (streamText stream) JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeById SqliteDb.TableName writer "three hundred eighty-seven" + verifyNoDoc (streamText stream) + } + ] + testList "writeByFields" [ + testTask "succeeds when documents are found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFields SqliteDb.TableName writer Any [ Field.Greater "NumValue" 15 ] + let json = streamText stream + verifyBeginEnd json + Expect.stringContains json JsonDocument.four "Document `four` should have been returned" + Expect.stringContains json JsonDocument.five "Document `five` should have been returned" + } + testTask "succeeds when documents are found using IN with numeric field" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFields SqliteDb.TableName writer All [ Field.In "NumValue" [ 2; 4; 6; 8 ] ] + Expect.equal (streamText stream) $"[{JsonDocument.three}]" "There should have been one document returned" + } + testTask "succeeds when documents are not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFields SqliteDb.TableName writer Any [ Field.Greater "NumValue" 100 ] + verifyEmpty (streamText stream) + } + testTask "succeeds for InArray when matching documents exist" { + use! db = SqliteDb.BuildDb() + do! Definition.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! insert SqliteDb.TableName doc + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFields SqliteDb.TableName writer All [ Field.InArray "Values" SqliteDb.TableName [ "c" ] ] + let json = streamText stream + verifyBeginEnd json + Expect.stringContains + json """{"Id":"first","Values":["a","b","c"]}""" "Document `first` should have been returned" + Expect.stringContains + json """{"Id":"second","Values":["c","d","e"]}""" "Document `second` should have been returned" + } + testTask "succeeds for InArray when no matching documents exist" { + use! db = SqliteDb.BuildDb() + do! Definition.ensureTable SqliteDb.TableName + for doc in ArrayDocument.TestDocuments do do! insert SqliteDb.TableName doc + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFields SqliteDb.TableName writer All [ Field.InArray "Values" SqliteDb.TableName [ "j" ] ] + verifyEmpty (streamText stream) + } + ] + testList "writeByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id" ] + Expect.equal + (streamText stream) $"[{JsonDocument.five},{JsonDocument.four}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Greater "NumValue" 15 ] [ Field.Named "Id DESC" ] + Expect.equal + (streamText stream) $"[{JsonDocument.four},{JsonDocument.five}]" "Incorrect documents were returned" + } + testTask "succeeds when sorting case-sensitively" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFieldsOrdered + SqliteDb.TableName writer All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "Value" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.three},{JsonDocument.one},{JsonDocument.two}]" + "Documents not ordered correctly" + } + testTask "succeeds when sorting case-insensitively" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeByFieldsOrdered + SqliteDb.TableName writer All [ Field.LessOrEqual "NumValue" 10 ] [ Field.Named "i:Value" ] + Expect.equal + (streamText stream) + $"[{JsonDocument.three},{JsonDocument.two},{JsonDocument.one}]" + "Documents not ordered correctly" + } + ] + testList "writeFirstByFields" [ + testTask "succeeds when a document is found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeFirstByFields SqliteDb.TableName writer Any [ Field.Equal "Value" "another" ] + Expect.equal (streamText stream) JsonDocument.two "The incorrect document was returned" + } + testTask "succeeds when multiple documents are found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeFirstByFields SqliteDb.TableName writer Any [ Field.Equal "Sub.Foo" "green" ] + let json = streamText stream + Expect.notEqual json "{}" "There should have been a document returned" + verifyAny json [ JsonDocument.two; JsonDocument.four ] + } + testTask "succeeds when a document is not found" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeFirstByFields SqliteDb.TableName writer Any [ Field.Equal "Value" "absent" ] + verifyNoDoc (streamText stream) + } + ] + testList "writeFirstByFieldsOrdered" [ + testTask "succeeds when sorting ascending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeFirstByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar" ] + Expect.equal (streamText stream) JsonDocument.two "An incorrect document was returned" + } + testTask "succeeds when sorting descending" { + use! db = SqliteDb.BuildDb() + do! loadDocs () + + use stream = new MemoryStream() + use writer = writeStream stream + do! Json.writeFirstByFieldsOrdered + SqliteDb.TableName writer Any [ Field.Equal "Sub.Foo" "green" ] [ Field.Named "Sub.Bar DESC" ] + Expect.equal (streamText stream) JsonDocument.four "An incorrect document was returned" + } + ] +] + /// Integration tests for the Update module of the SQLite library let updateTests = testList "Update" [ testList "byId" [ @@ -682,7 +1240,7 @@ let updateTests = testList "Update" [ use! db = SqliteDb.BuildDb() do! loadDocs () - do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None } + do! Update.byFunc SqliteDb.TableName _.Id { Id = "one"; Value = "le un"; NumValue = 1; Sub = None } let! after = Find.byId SqliteDb.TableName "one" Expect.isSome after "There should have been a document returned post-update" Expect.equal @@ -697,7 +1255,7 @@ let updateTests = testList "Update" [ Expect.isEmpty before "There should have been no documents returned" // This not raising an exception is the test - do! Update.byFunc SqliteDb.TableName (_.Id) { Id = "one"; Value = "le un"; NumValue = 1; Sub = None } + do! Update.byFunc SqliteDb.TableName _.Id { Id = "one"; Value = "le un"; NumValue = 1; Sub = None } } ] ] @@ -844,7 +1402,7 @@ let deleteTests = testList "Delete" [ ] /// All tests for the SQLite library -let all = testList "Sqlite" [ +let all = ftestList "Sqlite" [ testList "Unit" [ queryTests; parametersTests ] testSequenced <| testList "Integration" [ configurationTests @@ -854,6 +1412,7 @@ let all = testList "Sqlite" [ countTests existsTests findTests + jsonTests updateTests patchTests removeFieldsTests diff --git a/src/Tests/Types.fs b/src/Tests/Types.fs index bec9b16..16bd9a9 100644 --- a/src/Tests/Types.fs +++ b/src/Tests/Types.fs @@ -26,6 +26,22 @@ type JsonDocument = NumValue: int Sub: SubDocument option } +module JsonDocument = + /// The JSON for document ID `one` + let one = """{"Id":"one","Value":"FIRST!","NumValue":0,"Sub":null}""" + + /// The JSON for document ID `two` + let two = """{"Id":"two","Value":"another","NumValue":10,"Sub":{"Foo":"green","Bar":"blue"}}""" + + /// The JSON for document ID `three` + let three = """{"Id":"three","Value":"","NumValue":4,"Sub":null}""" + + /// The JSON for document ID `four` + let four = """{"Id":"four","Value":"purple","NumValue":17,"Sub":{"Foo":"green","Bar":"red"}}""" + + /// The JSON for document ID `five` + let five = """{"Id":"five","Value":"purple","NumValue":18,"Sub":null}""" + /// An empty JsonDocument let emptyDoc = { Id = ""; Value = ""; NumValue = 0; Sub = None }