Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
e76bbbf
test: port the framework-neutral Hedgehog adapter sources
xperiandri Oct 3, 2026
e26fe40
test: add the Hedgehog MSTest property attribute
xperiandri Oct 3, 2026
2cc6691
feat: add `FSharp.Azure.Cosmos.Sql` with `EquatableArray` and the `Im…
xperiandri Oct 10, 2026
c6fd556
feat: add the FSharp.Azure.Cosmos.Sql syntax tree
xperiandri Oct 3, 2026
8786bbf
feat: print Cosmos SQL deterministically
xperiandri Oct 3, 2026
40dff58
feat: add the Cosmos SQL function catalog
xperiandri Oct 3, 2026
2464182
feat: validate Cosmos SQL beyond the grammar
xperiandri Oct 3, 2026
56e98c7
chore(build): package FSharp.Azure.Cosmos.Sql
xperiandri Oct 3, 2026
82913d4
docs: list the files of FSharp.Azure.Cosmos.Sql in the agent instruct…
xperiandri Oct 10, 2026
0d04071
test: apply the repository's conventions to the Hedgehog MSTest adapter
xperiandri Oct 10, 2026
e300bc4
test: add the Hedgehog MSTest adapter suite
xperiandri Oct 3, 2026
943b24e
test: add `DateOnly` and `TimeOnly` generator attributes to the Hedge…
xperiandri Oct 10, 2026
9736d79
test: generate `DateOnly` and `TimeOnly` for a property without an at…
xperiandri Oct 11, 2026
240fd46
chore(build): list the tests of every test application before running…
xperiandri Oct 3, 2026
ed8c41e
docs: document the Hedgehog MSTest adapter in the agent instructions
xperiandri Oct 3, 2026
323165e
Merge the Hedgehog MSTest adapter (#43) into the Cosmos SQL model (#42)
xperiandri Oct 11, 2026
8d9327f
test: add `Cosmos.Sql.Hedgehog` with the generator helpers
xperiandri Oct 11, 2026
be3e581
test: cover the generator helpers
xperiandri Oct 11, 2026
017a7b2
docs: describe the generator helpers in the agent instructions
xperiandri Oct 11, 2026
2a29040
test: add Hedgehog generators of the Cosmos SQL syntax tree
xperiandri Oct 11, 2026
e3acf84
test: check the laws of the Cosmos SQL printer on generated trees
xperiandri Oct 11, 2026
d0bc2d4
docs: say how a property gets a generated syntax tree
xperiandri Oct 11, 2026
d6d2088
test: add the parser of Cosmos DB SQL that the SDK generated
xperiandri Oct 11, 2026
2eeacf5
test: read query text into the syntax tree with the SDK's parser
xperiandri Oct 11, 2026
e184d72
test: check that the SDK's parser reads what the printer writes
xperiandri Oct 11, 2026
78885f0
docs: describe the grammar oracle in the agent instructions
xperiandri Oct 11, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion .github/copilot-instructions.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,18 @@

```text
/
├── src/Cosmos.Sql/ – Cosmos DB SQL model (FSharp.Azure.Cosmos.Sql)
│ ├── EquatableArray.fs – immutable array with structural equality
│ ├── ImmutableArrayPatterns.fs – active patterns that match an `ImmutableArray` by its length
│ ├── Syntax.fs – syntax tree of the query language
│ ├── Keywords.fs – reserved words and the identifier rule
│ ├── FunctionSpec.fs – specification of a built-in function
│ ├── CatalogData.fs – one row per built-in function
│ ├── Catalog.fs – lookup of built-in functions
│ ├── ValidationError.fs – validation errors and options
│ ├── Inline.fs – replacement of parameters by literals
│ ├── Printer.fs – printer of the query text
│ └── Validator.fs – rules of the query language that the grammar cannot express
├── src/Cosmos/ – main library (FSharp.Azure.Cosmos)
│ ├── SubStatusCodes.fs – documented Cosmos DB sub-status codes
│ ├── Cosmos.fs – core types and container extensions
Expand All @@ -28,7 +40,13 @@
├── src/Shared/ – source files that every F# project under src and tests compiles
│ └── ValueCollections.fs – `Seq`, `List` and `Array` functions that return `voption` and struct tuples
├── tests/Cosmos.Tests/ – MSTest integration test project
├── tests/Cosmos.Sql.Tests/ – MSTest unit tests of FSharp.Azure.Cosmos.Sql, no emulator needed
├── tests/Cosmos.Sql.Hedgehog/ – Hedgehog generators that the tests share: helpers (`Sample`, `Gen`, `CollectionGen`, `TextGen`) and the SQL syntax tree (`SqlGenerators`, `SqlAutoGenConfig`)
├── tests/Cosmos.Sql.Grammar/ – the SDK's parser of Cosmos DB SQL as a test oracle: `SdkGrammar.parse` reads query text into the syntax tree
├── tests/Cosmos.Sql.Grammar.Generated/ – C#: the SDK's grammar `sql.g4` and the parser ANTLR generated from it, copied unchanged
├── tests/Cosmos.Tests.Infrastructure/ – shared test fixtures, emulator settings and assertion helpers
├── tests/Hedgehog.MSTest/ – Hedgehog MSTest adapter for [<Property>] test methods (from hedgehogqa/fsharp-hedgehog)
├── tests/Hedgehog.MSTest.Tests/ – the adapter's own test suite
├── build/ – FAKE build scripts
└── docsSrc/ – FSharp.Formatting documentation source
```
Expand All @@ -40,6 +58,7 @@
* [`FSharp.Control.Reactive`](https://github.com/fsprojects/FSharp.Reactive) – Rx extensions for F#
* [`FsToolkit.ErrorHandling`](https://github.com/demystifyfp/FsToolkit.ErrorHandling) – `taskResult`, `Result`, `voption` CEs
* [`Unquote`](https://github.com/SwensenSoftware/unquote) – test assertions
* [`Hedgehog`](https://github.com/hedgehogqa/fsharp-hedgehog) – property-based testing through the `tests/Hedgehog.MSTest` adapter
* [`MSTest`](https://github.com/microsoft/testfx) – test framework

Use GitHub MCP tools for code search in these repositories when needed.
Expand Down Expand Up @@ -238,7 +257,12 @@ module MyTypeExtensions =
* `COSMOS_EMULATOR_PARTITION_COUNT` is the number of partitions the emulator offers (default `25`, the Windows emulator's default `/PartitionCount`); the fixtures keep at most that many test containers at a time, one partition each.
* `COSMOS_TEST_LEFTOVER_AGE_MINUTES` is how many minutes a `fsac-test-` database must stay unmodified before the leftover sweep of `[<AssemblyInitialize>]` and `[<AssemblyCleanup>]` deletes it (default `60`), so that the live databases of another test process on the same emulator are kept; `0` deletes every test database and is safe only while no other test process uses the emulator.
* `COSMOS_REWRITE_BASELINES` set to `1` or `true` makes a test that compares SQL with its golden baseline (`Baseline.assertMatches`, files under `Baselines/<suite>/` of the test project) write what it produced into the baseline instead of comparing; the test then reports itself inconclusive, the rewritten files are reviewed in the diff, and CI refuses to rewrite.
* Every test project under `tests/` references `tests/Cosmos.Tests.Infrastructure` through `tests/Directory.Build.props`; a new test project gets it without a `ProjectReference` of its own.
* Every test project under `tests/` references `tests/Cosmos.Tests.Infrastructure` through `tests/Directory.Build.props`; a new test project gets it without a `ProjectReference` of its own. The Hedgehog MSTest adapter and its suite are the exception: they stay free of the Cosmos projects, and the suite references the adapter alone.
* Property tests are methods of `[<TestClass>]` types marked `[<Property>]` (from `tests/Hedgehog.MSTest`), whose parameters Hedgehog generates; never call `Property.check` inside a `[<TestMethod>]`. A property is an instance method that returns `unit`, `Task` or `ValueTask` and fails by throwing: MSTest 4 rejects static methods and other return types, and one such method fails the discovery of the whole assembly, which `--list-tests` shows before a run. MSTest creates the test class and runs `[<TestInitialize>]` and `[<TestCleanup>]` for every generated case and every shrink step. A failed property reports a `[<Recheck("…")>]` to add next to its `Property` attribute, which then replays the counterexample. A parameter of type `DateOnly` or `TimeOnly`, or of a type that holds one, is generated like any other, and `[<DateOnly>]` and `[<TimeOnly>]` bound its value: Hedgehog 2.0.4 has no generator of the two types, so the adapter registers the ones that [hedgehogqa/fsharp-hedgehog#488](https://github.com/hedgehogqa/fsharp-hedgehog/pull/488) adds to Hedgehog, until a release has them. `Gen.auto` outside a property does not know the two types before that release.
* `tests/Hedgehog.MSTest` and `tests/Hedgehog.MSTest.Tests` are ported from hedgehogqa/fsharp-hedgehog and proposed there too ([hedgehogqa/fsharp-hedgehog#487](https://github.com/hedgehogqa/fsharp-hedgehog/pull/487)). Their code follows the rules of this file, formatting with Fantomas and nullness checking included; the copy in the upstream pull request keeps upstream's style, so a change of behaviour goes into both. Copied files keep their Apache-2.0 header, which records every change made to them, and `THIRD-PARTY-NOTICES.md` lists them.
* Generators are in `tests/Cosmos.Sql.Hedgehog`, namespace `FSharp.Azure.Cosmos.Tests.Models`. `Sample` draws values outside of a property; data that a failure must be able to replay comes from `Sample.singleWith` and `Sample.multipleWith`, which take a seed. `Gen` and `CollectionGen` generate optional values and collections; a function with the suffix `Balanced` gives the absent value or the empty collection in one draw of two at every size, the others make it rarer as the size grows. A generator file opens `Hedgehog` and `Hedgehog.FSharp`, never `Hedgehog.Linq`, and names a parameter that holds a generator `source` or `element`, never `gen`, which hides the `gen { }` builder. A generator makes valid values by construction: no `Gen.filter`, no `Gen.sample` inside it, no clock and no current culture.
* A property over the SQL syntax tree names `SqlAutoGenConfig` on its class or method (`[<Properties(typeof<SqlAutoGenConfig>)>]`): every `SqlQuery` and `ScalarExpression` parameter is then a tree that `SqlQuery.validate` accepts. A parameter that needs another generator of `SqlGenerators` – any tree, valid or not (`arbitrarySqlQueryGen`), a constant that JSON can carry (`constantGen`) – takes it through an attribute derived from `GenAttribute<'T>`, declared in the test file. A law never asks the code it checks for the expected answer: the printer laws find the parameters of a tree with a traversal of their own.
* `SdkGrammar.parse` (`tests/Cosmos.Sql.Grammar`) reads query text with the parser the SDK runs and gives the `SqlQuery` it means, or the first syntax error. Use it to check that a text is a query the SDK reads, and that it reads it as the tree the text was written from. The generated parser is in `tests/Cosmos.Sql.Grammar.Generated`; only `tests/Cosmos.Sql.Grammar` names its types `sqlParser` and `sqlLexer`, and that project must never reference `Microsoft.Azure.Cosmos`: the SDK assembly has internal types of the same names, and F# then resolves the names to those. Three distinctions of a tree are not in its text, so a valid generated tree has them the way the parser reads them: a chain of joins is left-deep, a number without a fraction is an integer literal, and under `PropertyStyle.DotWhenSafe` a string step of an input path is written like an identifier step.
* `CollectionAssert` cannot work with F# lists – use F# array syntax (`[| ... |]`) instead.
* `StringAssert` has overloads with `StringComparison`.
* Use `Assert.Contains` instead of `Assert.IsTrue (str.Contains ..., "message")`, and do not put the actual value into the message.
Expand Down
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

## [Unreleased]

### Added
* `FSharp.Azure.Cosmos.Sql` package: an immutable syntax tree of the Azure Cosmos DB NoSQL query language that mirrors the grammar of the .NET SDK, a deterministic printer with a parameterized and an inline mode, a validator for the rules the grammar cannot express and the catalog of the built-in functions as data, without a dependency on the Azure Cosmos DB SDK

### Changed
* BREAKING: the package targets `net10.0` only and no longer supports `net8.0`, which makes this a major release
* BREAKING: `Microsoft.Azure.Cosmos` updated from 3.60.0 to 3.62.0, which becomes the minimum version for consumers. Its release notes list a breaking change of their own: thin client mode is enabled by default and does not support resource-token authentication (opt out with `AZURE_COSMOS_THIN_CLIENT_ENABLED=false`)
Expand Down
3 changes: 3 additions & 0 deletions Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@
<PackageVersion Include="FSharp.Control.Reactive" Version="6.1.2" />
<PackageVersion Include="FSharp.Control.TaskSeq" Version="1.0.0" />
<PackageVersion Include="FSharp.Core" Version="10.0.100" />
<PackageVersion Include="FSharp.SystemTextJson" Version="1.4.36" />
<PackageVersion Include="FsToolkit.ErrorHandling" Version="5.0.0" />
<PackageVersion Include="IcedTasks" Version="0.11.10-beta001" />
</ItemGroup>
<ItemGroup Label="Build and Test">
<PackageVersion Include="Antlr4.Runtime.Standard" Version="4.9.3" />
<PackageVersion Include="Argu" Version="6.2.5" />
<PackageVersion Include="Fake.Api.GitHub" Version="6.1.5" />
<PackageVersion Include="Fake.BuildServer.GitHubActions" Version="6.1.5" />
Expand All @@ -24,6 +26,7 @@
<PackageVersion Include="Fake.DotNet.Paket" Version="6.1.5" />
<PackageVersion Include="Fake.IO.FileSystem" Version="6.1.5" />
<PackageVersion Include="Fake.Tools.Git" Version="6.1.5" />
<PackageVersion Include="Hedgehog" Version="2.0.4" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.5.1" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="10.0.401" />
<PackageVersion Include="MSTest" Version="4.2.3" />
Expand Down
9 changes: 8 additions & 1 deletion FSharp.Azure.Cosmos.slnf
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,16 @@
"solution": {
"path": "FSharp.Azure.Cosmos.slnx",
"projects": [
"src\\Cosmos.Sql\\FSharp.Azure.Cosmos.Sql.fsproj",
"src\\Cosmos\\FSharp.Azure.Cosmos.fsproj",
"tests\\Cosmos.Sql.Grammar.Generated\\FSharp.Azure.Cosmos.Sql.Grammar.Generated.csproj",
"tests\\Cosmos.Sql.Grammar\\FSharp.Azure.Cosmos.Sql.Grammar.fsproj",
"tests\\Cosmos.Sql.Hedgehog\\FSharp.Azure.Cosmos.Sql.Hedgehog.fsproj",
"tests\\Cosmos.Sql.Tests\\FSharp.Azure.Cosmos.Sql.Tests.fsproj",
"tests\\Cosmos.Tests.Infrastructure\\FSharp.Azure.Cosmos.Tests.Infrastructure.fsproj",
"tests\\Cosmos.Tests\\FSharp.Azure.Cosmos.Tests.fsproj"
"tests\\Cosmos.Tests\\FSharp.Azure.Cosmos.Tests.fsproj",
"tests\\Hedgehog.MSTest\\Hedgehog.MSTest.fsproj",
"tests\\Hedgehog.MSTest.Tests\\Hedgehog.MSTest.Tests.fsproj"
]
}
}
8 changes: 8 additions & 0 deletions FSharp.Azure.Cosmos.slnx
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,17 @@
<File Path="Directory.Packages.props" />
<File Path="global.json" />
<File Path="README.md" />
<File Path="THIRD-PARTY-NOTICES.md" />
</Folder>
<Project Path="build/build.fsproj" />
<Project Path="src/Cosmos.Sql/FSharp.Azure.Cosmos.Sql.fsproj" />
<Project Path="src/Cosmos/FSharp.Azure.Cosmos.fsproj" />
<Project Path="tests/Cosmos.Sql.Grammar.Generated/FSharp.Azure.Cosmos.Sql.Grammar.Generated.csproj" />
<Project Path="tests/Cosmos.Sql.Grammar/FSharp.Azure.Cosmos.Sql.Grammar.fsproj" />
<Project Path="tests/Cosmos.Sql.Hedgehog/FSharp.Azure.Cosmos.Sql.Hedgehog.fsproj" />
<Project Path="tests/Cosmos.Sql.Tests/FSharp.Azure.Cosmos.Sql.Tests.fsproj" />
<Project Path="tests/Cosmos.Tests.Infrastructure/FSharp.Azure.Cosmos.Tests.Infrastructure.fsproj" />
<Project Path="tests/Cosmos.Tests/FSharp.Azure.Cosmos.Tests.fsproj" />
<Project Path="tests/Hedgehog.MSTest/Hedgehog.MSTest.fsproj" />
<Project Path="tests/Hedgehog.MSTest.Tests/Hedgehog.MSTest.Tests.fsproj" />
</Solution>
79 changes: 79 additions & 0 deletions THIRD-PARTY-NOTICES.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# Third-party notices

This repository and the packages built from it include material from the third-party projects listed below. The
original copyright notices and licences are reproduced here.

## Azure Cosmos DB .NET SDK v3

* Source: <https://github.com/Azure/azure-cosmos-dotnet-v3>, tag `3.62.0` (commit `fe16fe00617e962dbab246244813d605871c086d`)
* Licence: MIT
* Used in `FSharp.Azure.Cosmos.Sql` (`src/Cosmos.Sql`) and its tests (`tests/Cosmos.Sql.Tests`, `tests/Cosmos.Sql.Grammar`,
`tests/Cosmos.Sql.Grammar.Generated`):
* the node inventory of the syntax tree, after the classes in `Microsoft.Azure.Cosmos/src/SqlObjects` and the
grammar `Microsoft.Azure.Cosmos/src/Query/Core/Parser/sql.g4`;
* the printing rules, after `Microsoft.Azure.Cosmos/src/SqlObjects/Visitors/SqlObjectTextSerializer.cs`;
* the names of the built-in functions in the catalog, from `SqlFunctionCallScalarExpression.Names` in
`Microsoft.Azure.Cosmos/src/SqlObjects/SqlFunctionCallScalarExpression.cs`;
* expected query texts in the tests, from the baselines in
`Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.Tests/BaselineTest/TestBaseline` and
`Microsoft.Azure.Cosmos/tests/Microsoft.Azure.Cosmos.EmulatorTests/BaselineTest/TestBaseline`;
* the grammar `sql.g4` and the lexer and parser that ANTLR generated from it (`sqlLexer.cs`, `sqlParser.cs`,
`IsqlVisitor.cs`, `sqlBaseVisitor.cs`, `IsqlListener.cs`, `sqlBaseListener.cs`), copied unchanged from
`Microsoft.Azure.Cosmos/src/Query/Core/Parser` into `tests/Cosmos.Sql.Grammar.Generated`;
* the conversion of a parse tree into the syntax tree in `tests/Cosmos.Sql.Grammar`, after `CstToAstVisitor.cs` in
the same folder.

```text
MIT License

Copyright (c) Microsoft Corporation. All rights reserved.

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
```

## fsharp-hedgehog

* Source: <https://github.com/hedgehogqa/fsharp-hedgehog>, release commit
[`a46977278db9a60542e3df3fe0fcd74b90f38ee3`](https://github.com/hedgehogqa/fsharp-hedgehog/tree/a46977278db9a60542e3df3fe0fcd74b90f38ee3)
(Hedgehog 2.0.4; the repository has no tag for this release)
* Copied into: `tests/Hedgehog.MSTest` (the Hedgehog MSTest adapter), from `src/Hedgehog.NUnit`: `Prelude.fs`,
`ReflectionHelpers.fs`, `AutoGenConfig.fs`, `IPropertyAttribute.fs`, `RecheckAttribute.fs`, `GenAttribute.fs`,
`GenAttribute.Prelude.fs`, `PropertyContext.fs`, `InternalLogic.fs`, `PropertiesAttribute.fs` and, adapted from
NUnit to MSTest, `PropertyAttribute.fs`
* Ported into: `tests/Hedgehog.MSTest.Tests` (the adapter's suite), rewritten for MSTest, from
`tests/Hedgehog.Xunit.Tests.FSharp/PropertyTests.fs`, `tests/Hedgehog.NUnit.Tests.FSharp/PropertyTests.fs` and
`tests/Hedgehog.NUnit.Tests.FSharp/GenAttributePreludeTests.fs`: `Common.fs`, `PropertyTests.fs` and
`GenAttributePreludeTests.fs`
* License: Apache License, Version 2.0

```text
Copyright (c) 2016 Jacob Stanley, Nikos Baxevanis

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
```
Loading
Loading