Note
This page is generated from src/devops/CONTRIBUTING.md. Edit the repository file, not this copy.
Contributing to DevOps and tests
This guide is for adding tests, benchmarks and repository tooling. For everyday commands, start with the DevOps README.
Add a test
1. Find the production contract
Choose the area that owns the behaviour, such as Search, Index, CodecKit, Store, Mapping or TextIntegration.
2. Choose the test role
| Category | Use it when |
|---|---|
Unit |
A focused component contract has a direct expected result |
Integration |
Behaviour crosses meaningful production, file or process boundaries |
Chaos |
Generated data, corruption, hostile conditions or operation histories expose the risk |
Techniques such as property-based, state-machine and metamorphic testing describe how a test works. They do not require separate projects.
3. Put the test under the owning area
src/devops/Rowles.LeanCorpus.Tests.Core/
Search/
Unit/
Integration/
Chaos/
Do not create a new test project merely because a test uses a different technique.
4. Add metadata
[Category(TestCategory.Unit)]
[Area(TestArea.Search)]
public sealed class MyQueryTests
{
}
Use multiple areas only for a genuinely cross-cutting contract:
[Category(TestCategory.Integration)]
[Area(TestArea.Index)]
[Area(TestArea.Store)]
public sealed class IndexPersistenceTests
{
}
5. Run the focused selection
./devops test -Suite core -Area Search -Category Unit
6. Connect new production paths
Affected testing follows this flow:
flowchart LR
Change["Changed production path"] --> Mapping["code-areas.psd1"]
Mapping --> Targets["suite:area targets"]
Targets --> Traits["Tests with matching Area traits"]
Traits --> Run["Focused test projects"]
Update scripts/devops/config/code-areas.psd1 when adding or moving a production area.
7. Run affected tests
./devops test -Suite affected
If the test accompanies a user-visible feature or fix, make sure the underlying change is represented in the current release changelog.
Choose a useful oracle
| Technique | Useful oracle |
|---|---|
| Unit | Direct value, boundary or error assertion |
| Integration | Observable API, index or filesystem result |
| Property-based | Invariant, reference model or round trip |
| State machine | Model transition plus observable postcondition |
| Metamorphic | Defined relation between equivalent executions |
| Corruption | Explicit rejection or bounded fallback |
| Native AOT | Successful publish and execution |
Prefer stable logical IDs and stored fields over internal document IDs, segment names or incidental timing.
Warning
“Nothing threw” can be a robustness property, but it is rarely a complete correctness oracle.
Add a property test
Use FsCheck when a rule matters across many inputs:
decode(encode(x)) == x
normalise(normalise(x)) == normalise(x)
forceMerge(index(x)) preserves logical results
generated offsets remain inside the source text
Metadata normally includes:
[Category(TestCategory.Chaos)]
[Area(TestArea.Index)]
[Technique(TestTechnique.PropertyBased)]
Keep generators bounded and meaningful. Retain explicit examples where they communicate the contract more clearly.
Add a state machine
State machines are useful when failure depends on a history such as add, delete, commit, refresh, merge, reopen and rollback.
Place them under:
<Area>/Chaos/StateMachine/
Each machine should have:
- a simple reference model;
- an isolated system-under-test harness;
- explicit operations and model transitions;
- postconditions over observable behaviour;
- readable operation descriptions for shrinking.
Important
Each machine owns its filesystem, writer and readers. Shared mutable fixtures make shrinking and reproduction unreliable.
Add a metamorphic test
Use a metamorphic test when two executions should have a defined relationship:
sequential indexing is set-equivalent to concurrent indexing
unmerged results exactly match force-merged results
serialise then deserialise preserves the observation
Place tests under <Area>/Chaos/Metamorphic/ and use the shared relations in Rowles.LeanCorpus.Tests.Shared/Metamorphic/ where appropriate.
Compare logical results, not incidental segment layout or timing.
Add DevOps tooling
Choose the destination by responsibility:
| Change | Location |
|---|---|
| Top-level command orchestration | scripts/devops/commands/ |
| Small reusable helper | scripts/devops/common/ |
| Declarative suites, mappings or strategies | scripts/devops/config/ |
| Larger reusable subsystem | scripts/devops/support/ |
For a new top-level command:
- Add an
Invoke-DevOps...entry point undercommands/. - Import it from
DevOps.psm1. - Add it to the dispatcher.
- Add concise top-level help and one useful example.
- Put generic helpers and declarative data in their owning directories.
- Exercise the command directly.
- Run
./devops test -Suite affected. - Update contributor documentation and the changelog when behaviour is visible.
Keep the root devops and devops.ps1 wrappers small.
Add or change a benchmark
- Select the suite and area that own the workload.
- Keep corpus and setup equivalent to the comparison target.
- Add a fast or dry route for smoke validation.
- Record source commit, framework, strategy, host and corpus provenance.
- Treat BenchmarkDotNet artefacts as evidence and generated pages as presentation.
List registered suites with:
./devops benchmark -List
Validation by change
| Change | Validation |
|---|---|
| Normal test addition | Focused selection, then -Suite affected |
| Test infrastructure | Affected tests plus every directly impacted suite |
| Architecture boundary | ./devops test -Suite architecture |
| AOT smoke path | ./devops aot |
| Benchmark registration | ./devops benchmark -List and a bounded smoke run |
| Coverage tooling | ./devops coverage -Clean |
| Documentation tooling | ./devops docs build -SkipBenchmarks |
Warning
Generated coverage, benchmark and DocFX output must not be hand-edited. Change the inputs or generator and regenerate it through ./devops.
Repository guides copied into the site are mapped by Copy-RepositoryDocumentation. Add the canonical source and its site destination there, then update docs/toc.yml. Do not introduce a second metadata file when the repository README already contains the required catalogue or workflow.
Before submitting
- [ ] The test or command has one clear owner.
- [ ] Metadata and affected mappings describe the real contract.
- [ ] The oracle is independent enough to catch the intended failure.
- [ ] Focused and affected validation were run.
- [ ] Platform, AOT and performance claims name their environment.
- [ ] Help, examples and contributor documentation match behaviour.