diff --git a/src/Tests.CSharp/PostgresCSharpTests.cs b/src/Tests.CSharp/PostgresCSharpTests.cs
index 5e38dc6..8977851 100644
--- a/src/Tests.CSharp/PostgresCSharpTests.cs
+++ b/src/Tests.CSharp/PostgresCSharpTests.cs
@@ -1288,6 +1288,423 @@ public static class PostgresCSharpTests
])
]);
+ /// Verify a JSON array begins with "[" and ends with "]"
+ private static void VerifyBeginEnd(string json)
+ {
+ Expect.stringStarts(json, "[", "The array should have started with `[`");
+ Expect.stringEnds(json, "]", "The array should have ended with `]`");
+ }
+
+ /// Verify the presence of a document by its ID
+ private static void VerifyDocById(string json, string docId)
+ {
+ Expect.stringContains(json, $"{{\"Id\": \"{docId}\",", $"Document `{docId}` not present");
+ }
+
+ /// Verify the presence of a document by its ID
+ private static void VerifySingleById(string json, string docId)
+ {
+ VerifyBeginEnd(json);
+ Expect.stringContains(json, $"{{\"Id\": \"{docId}\",", $"Document `{docId}` not present");
+ }
+
+ /// Verify the presence of any of the given document IDs in the given JSON
+ private static void VerifyAnyById(string json, IEnumerable docIds)
+ {
+ var theIds = docIds.ToList();
+ if (theIds.Any(it => json.Contains($"{{\"Id\": \"{it}\""))) return;
+ var ids = string.Join(", ", theIds);
+ Expect.isTrue(false, $"Could not find any of IDs {ids} in {json}");
+ }
+
+ /// Verify the JSON for `all` returning data
+ private static void VerifyAllData(string json)
+ {
+ VerifyBeginEnd(json);
+ IEnumerable ids = ["one", "two", "three", "four", "five"];
+ foreach (var docId in ids) VerifyDocById(json, docId);
+ }
+
+ /// Verify an empty JSON array
+ private static void VerifyEmpty(string json)
+ {
+ Expect.equal(json, "[]", "There should be no documents returned");
+ }
+
+ /// Verify an empty JSON document
+ private static void VerifyNoDoc(string json)
+ {
+ Expect.equal(json, "{}", "There should be no document returned");
+ }
+
+ /// Verify the JSON for an ordered query
+ private static void VerifyExpectedOrder(string json, string idFirst, string idSecond, string? idThird = null,
+ string? idFourth = null, string? idFifth = null)
+ {
+ var firstIdx = json.IndexOf($"{{\"Id\": \"{idFirst}\",", StringComparison.Ordinal);
+ var secondIdx = json.IndexOf($"{{\"Id\": \"{idSecond}\",", StringComparison.Ordinal);
+ VerifyBeginEnd(json);
+ Expect.isGreaterThan(secondIdx, firstIdx, $"`{idSecond}` should have been after `{idFirst}`");
+ if (idThird is null) return;
+
+ var thirdIdx = json.IndexOf($"{{\"Id\": \"{idThird}\",", StringComparison.Ordinal);
+ Expect.isGreaterThan(thirdIdx, secondIdx, $"`{idThird}` should have been after `{idSecond}`");
+ if (idFourth is null) return;
+
+ var fourthIdx = json.IndexOf($"{{\"Id\": \"{idFourth}\",", StringComparison.Ordinal);
+ Expect.isGreaterThan(fourthIdx, thirdIdx, $"`{idFourth}` should have been after `{idThird}`");
+ if (idFifth is null) return;
+
+ var fifthIdx = json.IndexOf($"{{\"Id\": \"{idFifth}\",", StringComparison.Ordinal);
+ Expect.isGreaterThan(fifthIdx, fourthIdx, $"`{idFifth}` should have been after `{idFourth}`");
+ }
+
+ ///
+ /// Integration tests for the Json module of the PostgreSQL library
+ ///
+ private static readonly Test JsonTests = TestList("Json",
+ [
+ TestList("All",
+ [
+ TestCase("succeeds when there is data", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyAllData(Json.All(PostgresDb.TableName));
+ }),
+ TestCase("succeeds when there is no data", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ VerifyEmpty(Json.All(PostgresDb.TableName));
+ })
+ ]),
+ TestList("AllOrdered",
+ [
+ TestCase("succeeds when ordering numerically", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(Json.AllOrdered(PostgresDb.TableName, [Field.Named("n:NumValue")]),
+ "one", "three", "two", "four", "five");
+ }),
+ TestCase("succeeds when ordering numerically descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(Json.AllOrdered(PostgresDb.TableName, [Field.Named("n:NumValue DESC")]),
+ "five", "four", "two", "three", "one");
+ }),
+ TestCase("succeeds when ordering alphabetically", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(Json.AllOrdered(PostgresDb.TableName, [Field.Named("Id DESC")]),
+ "two", "three", "one", "four", "five");
+ })
+ ]),
+ TestList("ById",
+ [
+ TestCase("succeeds when a document is found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+
+ var json = Json.ById(PostgresDb.TableName, "two");
+ Expect.stringStarts(json, """{"Id": "two",""", "An incorrect document was returned");
+ Expect.stringEnds(json, "}", "JSON should have ended with this document");
+ }),
+ TestCase("succeeds when a document is not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyNoDoc(Json.ById(PostgresDb.TableName, "three hundred eighty-seven"));
+ })
+ ]),
+ TestList("ByFields",
+ [
+ TestCase("succeeds when documents are found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifySingleById(
+ Json.ByFields(PostgresDb.TableName, FieldMatch.All,
+ [Field.In("Value", ["purple", "blue"]), Field.Exists("Sub")]),
+ "four");
+ }),
+ TestCase("succeeds when documents are found using IN with numeric field", async() =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifySingleById(
+ Json.ByFields(PostgresDb.TableName, FieldMatch.All, [Field.In("NumValue", [2, 4, 6, 8])]),
+ "three");
+ }),
+ TestCase("succeeds when documents are not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyEmpty(Json.ByFields(PostgresDb.TableName, FieldMatch.All,
+ [Field.Equal("Value", "mauve"), Field.NotEqual("NumValue", 40)]));
+ }),
+ TestCase("succeeds for InArray when matching documents exist", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await Definition.EnsureTable(PostgresDb.TableName);
+ foreach (var doc in ArrayDocument.TestDocuments) await Document.Insert(PostgresDb.TableName, doc);
+
+ var json = Json.ByFields(PostgresDb.TableName, FieldMatch.All,
+ [Field.InArray("Values", PostgresDb.TableName, ["c"])]);
+ VerifyBeginEnd(json);
+ VerifyDocById(json, "first");
+ VerifyDocById(json, "second");
+ }),
+ TestCase("succeeds for InArray when no matching documents exist", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await Definition.EnsureTable(PostgresDb.TableName);
+ foreach (var doc in ArrayDocument.TestDocuments) await Document.Insert(PostgresDb.TableName, doc);
+ VerifyEmpty(Json.ByFields(PostgresDb.TableName, FieldMatch.All,
+ [Field.InArray("Values", PostgresDb.TableName, ["j"])]));
+ })
+ ]),
+ TestList("ByFieldsOrdered",
+ [
+ TestCase("succeeds when sorting ascending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(
+ Json.ByFieldsOrdered(PostgresDb.TableName, FieldMatch.All, [Field.Equal("Value", "purple")],
+ [Field.Named("Id")]),
+ "five", "four");
+ }),
+ TestCase("succeeds when sorting descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(
+ Json.ByFieldsOrdered(PostgresDb.TableName, FieldMatch.All, [Field.Equal("Value", "purple")],
+ [Field.Named("Id DESC")]),
+ "four", "five");
+ })
+ ]),
+ TestList("ByContains",
+ [
+ TestCase("succeeds when documents are found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+
+ var json = Json.ByContains(PostgresDb.TableName, new { Sub = new { Foo = "green" } });
+ VerifyBeginEnd(json);
+ VerifyDocById(json, "two");
+ VerifyDocById(json, "four");
+ }),
+ TestCase("succeeds when documents are not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyEmpty(Json.ByContains(PostgresDb.TableName, new { Value = "mauve" }));
+ })
+ ]),
+ TestList("ByContainsOrdered",
+ [
+ // Id = two, Sub.Bar = blue; Id = four, Sub.Bar = red
+ TestCase("succeeds when sorting ascending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(
+ Json.ByContainsOrdered(PostgresDb.TableName, new { Sub = new { Foo = "green" } },
+ [Field.Named("Sub.Bar")]),
+ "two", "four");
+ }),
+ TestCase("succeeds when sorting descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(
+ Json.ByContainsOrdered(PostgresDb.TableName, new { Sub = new { Foo = "green" } },
+ [Field.Named("Sub.Bar DESC")]),
+ "four", "two");
+ })
+ ]),
+ TestList("ByJsonPath",
+ [
+ TestCase("succeeds when documents are found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+
+ var json = Json.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 15)");
+ VerifyBeginEnd(json);
+ VerifyDocById(json, "one");
+ VerifyDocById(json, "two");
+ VerifyDocById(json, "three");
+ }),
+ TestCase("succeeds when documents are not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyEmpty(Json.ByJsonPath(PostgresDb.TableName, "$.NumValue ? (@ < 0)"));
+ })
+ ]),
+ TestList("ByJsonPathOrdered",
+ [
+ // Id = one, NumValue = 0; Id = two, NumValue = 10; Id = three, NumValue = 4
+ TestCase("succeeds when sorting ascending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(
+ Json.ByJsonPathOrdered(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
+ [Field.Named("n:NumValue")]),
+ "one", "three", "two");
+ }),
+ TestCase("succeeds when sorting descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyExpectedOrder(
+ Json.ByJsonPathOrdered(PostgresDb.TableName, "$.NumValue ? (@ < 15)",
+ [Field.Named("n:NumValue DESC")]),
+ "two", "three", "one");
+ })
+ ]),
+ TestList("FirstByFields",
+ [
+ TestCase("succeeds when a document is found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByFields(PostgresDb.TableName, FieldMatch.Any, [Field.Equal("Value", "another")]),
+ "two");
+ }),
+ TestCase("succeeds when multiple documents are found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyAnyById(
+ Json.FirstByFields(PostgresDb.TableName, FieldMatch.Any, [Field.Equal("Value", "purple")]),
+ ["five", "four"]);
+ }),
+ TestCase("succeeds when a document is not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyNoDoc(Json.FirstByFields(PostgresDb.TableName, FieldMatch.Any, [Field.Equal("Value", "absent")]));
+ })
+ ]),
+ TestList("FirstByFieldsOrdered",
+ [
+ TestCase("succeeds when sorting ascending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByFieldsOrdered(PostgresDb.TableName, FieldMatch.Any, [Field.Equal("Value", "purple")],
+ [Field.Named("Id")]),
+ "five");
+ }),
+ TestCase("succeeds when sorting descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByFieldsOrdered(PostgresDb.TableName, FieldMatch.Any, [Field.Equal("Value", "purple")],
+ [Field.Named("Id DESC")]),
+ "four");
+ })
+ ]),
+ TestList("FirstByContains",
+ [
+ TestCase("succeeds when a document is found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(Json.FirstByContains(PostgresDb.TableName, new { Value = "another" }), "two");
+ }),
+ TestCase("succeeds when multiple documents are found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyAnyById(Json.FirstByContains(PostgresDb.TableName, new { Sub = new { Foo = "green" } }),
+ ["two", "four"]);
+ }),
+ TestCase("succeeds when a document is not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyNoDoc(Json.FirstByContains(PostgresDb.TableName, new { Value = "absent" }));
+ })
+ ]),
+ TestList("FirstByContainsOrdered",
+ [
+ TestCase("succeeds when sorting ascending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByContainsOrdered(PostgresDb.TableName, new { Sub = new { Foo = "green" } },
+ [Field.Named("Value")]),
+ "two");
+ }),
+ TestCase("succeeds when sorting descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByContainsOrdered(PostgresDb.TableName, new { Sub = new { Foo = "green" } },
+ [Field.Named("Value DESC")]),
+ "four");
+ })
+ ]),
+ TestList("FirstByJsonPath",
+ [
+ TestCase("succeeds when a document is found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(Json.FirstByJsonPath(PostgresDb.TableName, """$.Value ? (@ == "FIRST!")"""), "one");
+ }),
+ TestCase("succeeds when multiple documents are found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyAnyById(Json.FirstByJsonPath(PostgresDb.TableName, """$.Sub.Foo ? (@ == "green")"""),
+ ["two", "four"]);
+ }),
+ TestCase("succeeds when a document is not found", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyNoDoc(Json.FirstByJsonPath(PostgresDb.TableName, """$.Id ? (@ == "nope")"""));
+ })
+ ]),
+ TestList("FirstByJsonPathOrdered",
+ [
+ TestCase("succeeds when sorting ascending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByJsonPathOrdered(PostgresDb.TableName, """$.Sub.Foo ? (@ == "green")""",
+ [Field.Named("Sub.Bar")]),
+ "two");
+ }),
+ TestCase("succeeds when sorting descending", async () =>
+ {
+ await using var db = PostgresDb.BuildDb();
+ await LoadDocs();
+ VerifyDocById(
+ Json.FirstByJsonPathOrdered(PostgresDb.TableName, """$.Sub.Foo ? (@ == "green")""",
+ [Field.Named("Sub.Bar DESC")]),
+ "four");
+ })
+ ])
+ ]);
+
///
/// Integration tests for the Update module of the PostgreSQL library
///
@@ -1729,6 +2146,7 @@ public static class PostgresCSharpTests
CountTests,
ExistsTests,
FindTests,
+ JsonTests,
UpdateTests,
PatchTests,
RemoveFieldsTests,
diff --git a/src/Tests/PostgresTests.fs b/src/Tests/PostgresTests.fs
index 46d9a2d..a5fd012 100644
--- a/src/Tests/PostgresTests.fs
+++ b/src/Tests/PostgresTests.fs
@@ -1163,7 +1163,6 @@ let private verifyExpectedOrder idFirst idSecond idThird idFourth idFifth (json:
| None -> ()
| None -> ()
| None -> ()
- Expect.stringEnds json "]" "The array should have ended with `]`"
/// Integration tests for the Json module of the PostgreSQL library
let jsonTests = testList "Json" [