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
When CI runs integration
Section titled “When CI runs integration”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.
Prerequisites
Section titled “Prerequisites”- Docker running locally
- .NET SDK (version matches
global.json) - Built binaries — run
dotnet buildfrom the repository root before running tests for the first time, or after any source change
Test Suites
Section titled “Test Suites”| Suite | Project | Description |
|---|---|---|
| Client | Integration/Client | Runs the shared .NET integration specifications in either inprocess or outofprocess mode, with runtime storage selected through command-line arguments. |
| API (out-of-process) | Integration/Api | Runs a full Chronicle server via Docker and tests the HTTP/gRPC client against it. |
Checking packed test-package startup
Section titled “Checking packed test-package startup”Before publishing locally packed packages, check startup from outside the repository:
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.
Running the Tests
Section titled “Running the Tests”From the command line
Section titled “From the command line”Run the suite with dotnet test from the repository root. --configuration Release matches CI; use Debug for faster local iteration:
dotnet test Integration/Client/Client.csproj \ --logger "console;verbosity=normal" \ --configuration Release \ --framework net10.0 \ -- inprocess mongodbRuntime and database configurations (CLI)
Section titled “Runtime and database configurations (CLI)”The first positional argument after -- is the mode, and the second is the database:
dotnet test Integration/Client/Client.csproj -- outofprocess mongodbdotnet test Integration/Client/Client.csproj -- outofprocess postgresqldotnet test Integration/Client/Client.csproj -- outofprocess mssqldotnet test Integration/Client/Client.csproj -- outofprocess sqlitedotnet test Integration/Client/Client.csproj -- inprocess mongodbNamed arguments are also supported:
dotnet test Integration/Client/Client.csproj -- --mode=outofprocess --database=postgresqldotnet test Integration/Client/Client.csproj -- --mode=inprocess --db=mongodbAlternatively, configure via environment variables or --environment flags:
dotnet test Integration/Client/Client.csproj --environment CHRONICLE_RUNTIME_MODE=inprocessCHRONICLE_RUNTIME_MODE=inprocess dotnet test Integration/Client/Client.csprojFor 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:
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:
export CHRONICLE_SQLITE_CONNECTION_DETAILS="Data Source=/tmp/chronicle.db"Running against an external MongoDB
Section titled “Running against an external MongoDB”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.
export CHRONICLE_MONGODB_CONNECTION_DETAILS="mongodb://mongo.example.test:27017/?replicaSet=myReplicaSet"dotnet test Source/Kernel/Storage.MongoDB.Specs/Storage.MongoDB.Specs.csproj --configuration ReleaseUse 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.
Running a single test
Section titled “Running a single test”Use --filter with the fully qualified type name or a substring of it:
dotnet test Integration/Client/Client.csproj \ --filter "FullyQualifiedName~for_EventSequence.when_appending.an_event" \ --no-build \ -- outofprocess mongodbFrom VS Code
Section titled “From VS Code”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".
Per-context storage isolation
Section titled “Per-context storage isolation”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.
Consecutive runs
Section titled “Consecutive runs”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.
Test Backups
Section titled “Test Backups”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.
Enabling backups
Section titled “Enabling backups”Set the CHRONICLE_BACKUP_ENABLED environment variable to true before running tests:
CHRONICLE_BACKUP_ENABLED=true dotnet test Integration/Client/Client.csproj \ --environment CHRONICLE_RUNTIME_MODE=inprocess \ --environment CHRONICLE_STORAGE_PROVIDER=mongodbWhere backups are stored
Section titled “Where backups are stored”Backups are written to a backups/ directory that sits alongside the compiled test binary. For a Debug build targeting net10.0, that path is:
Integration/Client/bin/Debug/net10.0/backups/The directory is created automatically during fixture initialization — you do not need to create it yourself.
Backup file naming
Section titled “Backup file naming”Each backup file is a gzip-compressed MongoDB archive dump with the following naming pattern:
{prefix}-yyyyMMdd-HHmmss.tgzThe prefix corresponds to the xUnit collection name registered for the test suite. When no prefix is set the timestamp is used alone:
20260412-143025.tgzInspecting a backup
Section titled “Inspecting a backup”Restore a backup to a local mongod instance to inspect the data:
mongorestore --gzip --archive=backups/20260412-143025.tgz