Skip to content

Running Integration Tests

Integration tests run from the Integration/Client project and can execute in either in-process or out-of-process mode. They require Docker to be running on your machine.

When you provide no runtime arguments, the suite defaults to:

  • mode: outofprocess
  • database: mongodb

Pull-request pushes affecting the build paths run the Release build, specs, and a local Docker image build from Release binaries without pushing it, not the Docker integration suites. Run the relevant integration namespace locally before requesting CI integration. The hot-core gate reports which hot-core paths a PR touches; its ordinary PR check does not run the matrix.

Apply the run-integration label to a pull request to start the separate Requested Integration workflow, even for a documentation-only PR. It builds the commit and runs specs, API and MongoDB integration, and one client integration matrix: MongoDB and SQLite normally, every backend when the PR touches the hot core. The separately named requested hot-core gate requires that full matrix to pass. Remove and re-add the label to request another run after changes; pushing new commits does not rerun integration automatically. Other labels do not start integration or replace the ordinary PR check results.

The existing nightly schedule on main and manual workflow_dispatch run the full client backend matrix. Main-branch pushes do not run integration; nightly coverage handles main. A fork PR cannot push the integration image with its read-only token; labeling it fails the requested gate with an “unavailable” notice instead of running integration, so a fork PR can never satisfy that gate. Ask a maintainer to dispatch the workflow on a trusted branch containing those changes.

For a labeled event, the image-push guard checks github.actor—the account that applied the label, not the PR author. A maintainer-labeled Dependabot PR therefore attempts the image push; if GitHub supplies a read-only token, the requested run fails rather than silently skipping the client matrix.

  • Docker running locally
  • .NET SDK (version matches global.json)
  • Built binaries — run dotnet build from the repository root before running tests for the first time, or after any source change
SuiteProjectDescription
ClientIntegration/ClientRuns the shared .NET integration specifications in either inprocess or outofprocess mode, with runtime storage selected through command-line arguments.
API (out-of-process)Integration/ApiRuns a full Chronicle server via Docker and tests the HTTP/gRPC client against it.

Before publishing locally packed packages, check startup from outside the repository:

Terminal window
bash .github/scripts/verify-consumer-smoke.sh "$CHRONICLE_VERSION" "$LOCAL_PACKAGE_FEED"

Set CHRONICLE_VERSION to the candidate version and LOCAL_PACKAGE_FEED to the directory containing its NuGet packages. The check requires the .NET SDK and NuGet access, but not Docker. It starts two separate applications: one using Chronicle.Testing with Arc.Chronicle.Testing, and one using Chronicle.XUnit.Integration without either Testing package. Both must complete AddCratisArcCore() type discovery.

Each test package carries its own embedded kernel runtime assemblies, including Compatibility. Do not add a separate Compatibility package to the consumer. Testing one package alongside the other can hide a missing runtime assembly, so keep the two consumer graphs separate. This startup check does not replace the kernel/storage integration suites below.

Run the suite with dotnet test from the repository root. --configuration Release matches CI; use Debug for faster local iteration:

Terminal window
dotnet test Integration/Client/Client.csproj \
--logger "console;verbosity=normal" \
--configuration Release \
--framework net10.0 \
-- inprocess mongodb

The first positional argument after -- is the mode, and the second is the database:

Terminal window
dotnet test Integration/Client/Client.csproj -- outofprocess mongodb
dotnet test Integration/Client/Client.csproj -- outofprocess postgresql
dotnet test Integration/Client/Client.csproj -- outofprocess mssql
dotnet test Integration/Client/Client.csproj -- outofprocess sqlite
dotnet test Integration/Client/Client.csproj -- inprocess mongodb

Named arguments are also supported:

Terminal window
dotnet test Integration/Client/Client.csproj -- --mode=outofprocess --database=postgresql
dotnet test Integration/Client/Client.csproj -- --mode=inprocess --db=mongodb

Alternatively, configure via environment variables or --environment flags:

Terminal window
dotnet test Integration/Client/Client.csproj --environment CHRONICLE_RUNTIME_MODE=inprocess
CHRONICLE_RUNTIME_MODE=inprocess dotnet test Integration/Client/Client.csproj

For PostgreSQL and MsSql, set connection details before running tests. The values below are examples only — replace credentials and hosts with values from your local environment:

Terminal window
export CHRONICLE_POSTGRESQL_CONNECTION_DETAILS="Host=localhost;Port=5432;Database=chronicle;Username=postgres;Password=postgres"
export CHRONICLE_MSSQL_CONNECTION_DETAILS="Server=localhost,1433;Database=chronicle;User Id=sa;Password=Your_password123;TrustServerCertificate=true"

For SQLite, you can optionally set:

Terminal window
export CHRONICLE_SQLITE_CONNECTION_DETAILS="Data Source=/tmp/chronicle.db"

Set CHRONICLE_MONGODB_CONNECTION_DETAILS to a MongoDB connection string to run Storage.MongoDB.Specs against an external service instead of starting MongoDB containers. The client, kernel, API, MongoDB integration, and clustering suites do not support this external mode.

Terminal window
export CHRONICLE_MONGODB_CONNECTION_DETAILS="mongodb://mongo.example.test:27017/?replicaSet=myReplicaSet"
dotnet test Source/Kernel/Storage.MongoDB.Specs/Storage.MongoDB.Specs.csproj --configuration Release

Use the service’s connection string unchanged, including authentication, TLS, replica-set, or SRV options. Storage specs create unique, run-prefixed databases and remove only the databases they create. The prefix reduces the available database-name budget by its length. External mode does not start or restart containers or initiate a replica set.

The external service must support the MongoDB features used by the Storage specs, including multi-document transactions and change streams for event-sequence and observation specs. Grant permissions to create and drop databases, create indexes, read/write collections, and enumerate database names. Providing a connection string does not imply compatibility with a managed service; a failing spec can reveal an unsupported feature.

Unset CHRONICLE_MONGODB_CONNECTION_DETAILS to return to the default container-backed Storage spec fixtures. CHRONICLE_SPECS_MONGODB_IMAGE applies only when these fixtures start MongoDB themselves.

Use --filter with the fully qualified type name or a substring of it:

Terminal window
dotnet test Integration/Client/Client.csproj \
--filter "FullyQualifiedName~for_EventSequence.when_appending.an_event" \
--no-build \
-- outofprocess mongodb

You can run the same configurations from VS Code by creating a dedicated test launch configuration in .vscode/launch.json:

{
"name": ".NET Test (Client integration - outofprocess mongodb)",
"type": "coreclr",
"request": "launch",
"program": "dotnet",
"args": [
"test",
"${workspaceFolder}/Integration/Client/Client.csproj",
"--framework",
"net10.0",
"--configuration",
"Debug",
"--",
"outofprocess",
"mongodb"
],
"cwd": "${workspaceFolder}",
"console": "integratedTerminal"
}

For in-process mode, change the last two arguments to "inprocess" and "mongodb".

For database changes in out-of-process mode, set the second argument to "postgresql", "mssql", or "sqlite".

The in-process MongoDB lane starts a fresh host for each test context and gives its databases a unique prefix. A previous host can still flush writes after shutdown (#4790); those writes now land in databases that the next context never reads. Context teardown awaits host disposal without wiping its databases in place. The collection fixture drops only the databases belonging to prefixes it issued when the collection ends.

If you use Cratis.Chronicle.XUnit.Integration in your own suite, override ChronicleFixture.UsesPerContextMongoDBHost to return true for an in-process MongoDB fixture. It defaults to false, so package consumers keep the existing shared-host behavior unless they opt in. The prefix is applied by the default MongoDB storage configuration; if your fixture overrides GetStorageConfigurator, pass the fixture’s DatabaseNamePrefix to WithMongoDB as databaseNamePrefix.

Starting and stopping a host per context costs time: measured Projections shards took about 1.8–3.1 times as long as the shared-host baseline. The performance follow-up (#4792) tracks that overhead. Out-of-process and remote-kernel lanes are not covered by this isolation; they still use the kernel reset.

The Integration/Client fixtures publish every container port on a random host port, so consecutive runs — or two runs at once — of that suite do not compete for a port. Docker’s Ryuk reaper removes the containers when a run ends.

The Integration/Api suite, and the reusable out-of-process fixture in Cratis.Chronicle.XUnit.Integration, bind fixed host ports (27018 and 35001). Two of those runs at once collide, and a run started immediately after another can wait for Docker to release the ports.

Each test collection automatically takes a MongoDB backup at the end of a run when the backup feature is enabled. Backups are useful for inspecting the state left by a failing test.

Set the CHRONICLE_BACKUP_ENABLED environment variable to true before running tests:

Terminal window
CHRONICLE_BACKUP_ENABLED=true dotnet test Integration/Client/Client.csproj \
--environment CHRONICLE_RUNTIME_MODE=inprocess \
--environment CHRONICLE_STORAGE_PROVIDER=mongodb

Backups are written to a backups/ directory that sits alongside the compiled test binary. For a Debug build targeting net10.0, that path is:

Terminal window
Integration/Client/bin/Debug/net10.0/backups/

The directory is created automatically during fixture initialization — you do not need to create it yourself.

Each backup file is a gzip-compressed MongoDB archive dump with the following naming pattern:

Terminal window
{prefix}-yyyyMMdd-HHmmss.tgz

The prefix corresponds to the xUnit collection name registered for the test suite. When no prefix is set the timestamp is used alone:

Terminal window
20260412-143025.tgz

Restore a backup to a local mongod instance to inspect the data:

Terminal window
mongorestore --gzip --archive=backups/20260412-143025.tgz