Baselines
A baseline saves current query counts. Later runs show what changed:
GET /api/companies
3 -> 51 queries
Use a baseline when you do not know the right query budget yet. You can also use it alongside fixed budgets.
Record and verify with the CLI
Install the tool:
dotnet tool install -g QueryGuard.Cli
Have your tests write JSON reports:
await new QueryGuardJsonReporter().WriteAsync(result, "artifacts/queryguard/companies.json");
Run the tests, then record the baseline:
queryguard baseline record
Commit queryguard-baseline.json. After later test runs, compare the new reports:
queryguard verify --summary artifacts/queryguard/summary.md
Add --fail-on-regression to return a non-zero exit code when counts increase.
Without it, regressions are reported but do not fail the command.
The CLI reads reports; it does not run tests. Recording updates measured scopes and keeps other baseline entries. Files that are not QueryGuard reports are skipped.
The file
{
"schemaVersion": "1.0",
"scopes": [
{
"scope": "GET /api/companies",
"readCommands": 3,
"distinctQueries": 2,
"topFingerprintOccurrences": 2
}
]
}
The file stores counts, not SQL or timings. Entries are sorted by scope name.
Recording one in code
var baseline = QueryGuardBaseline.Empty;
foreach (var result in measuredResults)
{
baseline = baseline.Record(result);
}
File.WriteAllText("queryguard-baseline.json", baseline.ToJson());
Record returns a new baseline. Keep its return value.
Comparing against one
var baseline = QueryGuardBaseline.FromJson(File.ReadAllText("queryguard-baseline.json"));
var comparison = QueryGuardBaselineComparison.Compare(baseline, measuredResults);
if (comparison.HasRegressions)
{
// Fail the test, log a warning, or publish a report.
}
Compare returns results without throwing for regressions.
In a pull request
Render a Markdown summary:
var markdown = new QueryGuardBaselineMarkdownReporter().Render(comparison);
var summary = Environment.GetEnvironmentVariable("GITHUB_STEP_SUMMARY");
if (summary is not null)
{
File.AppendAllText(summary, markdown);
}
| Scope | Before | Now | Change |
|---|---|---|---|
GET /api/companies |
3 | 51 | +48, most-repeated query +48 |
GET /api/orders |
8 | 8 | most-repeated query +7 |
GET /api/users |
4 | 4 | unchanged |
GET /api/reports |
12 | 3 | -9 (improved) |
A stable total can still hide more repetition, as the orders row shows.
The baseline tracks the highest occurrence count separately.
For a pull request comment, see the GitHub Action setup.
Comparison rules
- New scopes are shown as new, not as regressions.
- Scopes missing from a run are ignored.
- Lower counts are reported as improvements.
- To accept an intended increase, record again and commit the diff.
- Renaming a scope makes it a new entry; names are the comparison key.
- Unsupported future schema versions are rejected.
- To resolve a baseline merge conflict, rerun the relevant tests and record their results.
See baseline design for details.