OneAbove.Arena.GameContract.Testing 6.5.0

dotnet add package OneAbove.Arena.GameContract.Testing --version 6.5.0
                    
NuGet\Install-Package OneAbove.Arena.GameContract.Testing -Version 6.5.0
                    
This command is intended to be used within the Package Manager Console in Visual Studio, as it uses the NuGet module's version of Install-Package.
<PackageReference Include="OneAbove.Arena.GameContract.Testing" Version="6.5.0" />
                    
For projects that support PackageReference, copy this XML node into the project file to reference the package.
<PackageVersion Include="OneAbove.Arena.GameContract.Testing" Version="6.5.0" />
                    
Directory.Packages.props
<PackageReference Include="OneAbove.Arena.GameContract.Testing" />
                    
Project file
For projects that support Central Package Management (CPM), copy this XML node into the solution Directory.Packages.props file to version the package.
paket add OneAbove.Arena.GameContract.Testing --version 6.5.0
                    
#r "nuget: OneAbove.Arena.GameContract.Testing, 6.5.0"
                    
#r directive can be used in F# Interactive and Polyglot Notebooks. Copy this into the interactive tool or source code of the script to reference the package.
#:package OneAbove.Arena.GameContract.Testing@6.5.0
                    
#:package directive can be used in C# file-based apps starting in .NET 10 preview 4. Copy this into a .cs file before any lines of code to reference the package.
#addin nuget:?package=OneAbove.Arena.GameContract.Testing&version=6.5.0
                    
Install as a Cake Addin
#tool nuget:?package=OneAbove.Arena.GameContract.Testing&version=6.5.0
                    
Install as a Cake Tool

OneAbove.Arena.GameContract.Testing

The check every rule module of the One-Above Arena has to pass.

It is no recommendation: the platform takes on no module version whose suite has not run green. It stands in place of the code review that does not exist between the repos.

Use

Inherit, fill one member, done:

using OneAbove.Arena.GameContract.Testing;

public sealed class MyGameContractTests
    : GameEngineContract<MyState, MyMove, MyView>
{
    protected override IGameEngine<MyState, MyMove, MyView> Engine { get; }
        = new MyGame();
}

That checks purity, determinism, the serialisation round trip, totality, leaks and the end of the match — and plays a few moves by itself in the process, so that not only the initial state is checked.

The suite derives its arrangements from your descriptor: every mode you carry (online, hot seat, solo), every declared seat count, and every option sampled one at a time. You cannot narrow it; you can only declare less — and that stands out.

Since contract 6.1 every (mode, option, value) is sampled uncapped, at the first seat count where the option and its ancestors apply, with the path of parents set that makes it visible (GameDescriptor.OptionSamples()). Two checks hold the sampler itself: every sample must be accepted by MatchSetup.Problems and show its option, and every declared value must have been sampled. The bound is in the descriptor: more than 128 samples and Problems() refuses it. ⚠️ Until 6.0 the list was cut at 24 arrangements — if your own tests pinned that number, they change.

PlayDepth says how many moves deep it plays. The default is 12; override it when a game needs more or fewer.

⚠️ What you have to say: what is hidden

Since contract 4.0 that stands in the rule module, no longer in the test project.

Two statements, and they have to match one another:

1. In the descriptor, for every game:

OpenInformation = true,   // chess, Vier gewinnt: everyone sees everything

2. On the module, when OpenInformation = false:

public sealed class MyGame
    : IGameEngine<MyState, MyMove, MyView>, ISecretDeclaration<MyState>
{
    public IReadOnlyList<string> SecretsFor(MyState state, Seat? seat) =>
        seat is Seat owner
            ? [.. state.Hands[owner.Index].Select(card => card.Id)]
            : [.. state.DrawPile.Select(card => card.Id)];
}

The suite then checks that none of these strings occurs in a foreign view.

⚠️ Seat?, not Seat — and null is the interesting case. It means „nobody may see this": the unshuffled draw pile, Minesweeper's mines, a hidden solution. Whoever attributes such a thing to a seat is claiming that this seat may see it — and then precisely that seat's view is not checked.

Why this moved

Your rule module runs in the arena in a container of its own. The platform checks a new release over a socket — there it has neither your state type nor your test project. A check running only at your place is none.

Concretely the migration reads: your secrets method moves out of rules.tests into rules, is called SecretsFor and takes a Seat? instead of a Seat. The old overridable hook is gone — OpenInformation in the descriptor now says it.

For a game with completely open information you do not need the interface. There the canary bites: the match's seed may stand in no view.

Your open view (contract 6.4)

⚠️ Built in the platform's repo, not yet published — the chain rc → template → number waits for the product owner.

If your module implements IRevealedView<TState, TView>, four checks look at it — and none of them runs the leak checks over it, because showing the hands after the end is its point:

Check What it wants
The_revealed_view_shows_what_spectators_do_not_see For every seat that has a secret in a match, something of it stands in the open view somewhere in that match while the spectator view of the same state lacks it. What belongs to nobody (the mines) may stand there and need not
The_revealed_view_answers_in_every_state No exception, readable JSON — in every state the suite plays, not only at the end
The_revealed_view_is_deterministic Two calls, the same bytes and badges
Open_information_offers_no_revealed_view Not together with OpenInformation = true

And No_view_reveals_the_seed now looks into the open view too: it may show every hand, never the seed.

A game without the interface passes the four trivially — it simply gets no switch in the arena's replay.

Golden matches

Recorded matches, replayed bit-identically. The only defence against a change nobody took for a rule change.

GoldenGameRunner.Verify(GoldenGameFile.Load("2026-09-06-first-match.json"), engine);

When a golden test goes red, the first question is not „what is wrong with the recording" but: did we just change the rules?

  • Deliberately ⇒ raise GameVersion and create a new recording. The old one stays and has to remain green under its old version — it is the proof that running matches stay replayable.
  • Unintentionally ⇒ you have just found a bug.

Your media (contract 4.4)

If your repo has a media/ directory, the suite runs the platform's own check over it — the same method the admission runs, out of the contract package. So a refused manual costs you a test run and not a v tag, which is the one step you cannot take back.

Since contract 6.2 that includes the pictures themselves: bytes that match the extension, a readable header, at most 4096 × 4096 — and, for a screenshot your manual's Overview shows, no EXIF or XMP. A cover exported with its editor's metadata still passes; the same file as an overview screenshot does not.

It looks for media/ by walking up from the test assembly, because that is the only way to reach it: a test runs in bin/<config>/<tfm>/, the media lie at your repo root beside golden/ and renderer/. The path is the same one game-publish.yml copies into your image — override MediaDirectory only if you move it in both, because otherwise „the suite found nothing" and „the image ships nothing" become two states instead of one.

⚠️ No media is a pass, and it passes silently. A game does not have to bring any, and xUnit 2.9 shows no message for a passing test. So if you expected your cover to be checked and nothing happened, the directory is not where the suite looked — and it is not where the pipeline will look either.

What this suite does not check

Your game's rules. That a rook does not move diagonally is noticed by nobody here. That is what your own tests are for.

Product Compatible and additional computed target framework versions.
.NET net10.0 is compatible.  net10.0-android was computed.  net10.0-browser was computed.  net10.0-ios was computed.  net10.0-maccatalyst was computed.  net10.0-macos was computed.  net10.0-tvos was computed.  net10.0-windows was computed. 
Compatible target framework(s)
Included target framework(s) (in package)
Learn more about Target Frameworks and .NET Standard.

NuGet packages

This package is not used by any NuGet packages.

GitHub repositories

This package is not used by any popular GitHub repositories.

Version Downloads Last Updated
6.5.0 0 9/27/2026
6.5.0-rc.1 0 9/27/2026
6.3.0 45 9/26/2026
6.3.0-rc.1 35 9/25/2026
6.2.1 40 9/25/2026
6.2.1-rc.1 40 9/25/2026
6.2.0 60 9/24/2026
6.2.0-rc.1 41 9/24/2026
6.1.0 54 9/24/2026
6.1.0-rc.1 34 9/24/2026
6.0.0 56 9/24/2026
6.0.0-rc.1 39 9/24/2026
5.0.0 86 9/23/2026
5.0.0-rc.1 41 9/23/2026
4.7.0 100 9/21/2026
4.7.0-rc.1 47 9/21/2026
4.6.0 86 9/21/2026
4.6.0-rc.1 43 9/21/2026
4.5.0 103 9/20/2026
4.5.0-rc.1 45 9/20/2026
Loading failed