Unit tests for the SboxSuperuser.Contracts project. They validate construction and behavior of permission keys, player references, command descriptors/arguments, durations, store read/write semantics, module descriptors, and related contract types.
using Xunit;
namespace SboxSuperuser.Contracts.Tests;
public sealed class ContractTests
{
[Theory]
[InlineData( "admin.*" )]
[InlineData( "Admin.kick" )]
[InlineData( "admin..kick" )]
[InlineData( "admin." )]
[InlineData( "../owners" )]
[InlineData( "" )]
public void PermissionKeysRejectWildcardsAndMalformedNames( string key )
{
Assert.Throws<ArgumentException>( () => new PermissionDescriptor( key, "Help" ) );
}
[Theory]
[InlineData( "0" )]
[InlineData( "0123" )]
[InlineData( "-1" )]
[InlineData( " 123" )]
[InlineData( "18446744073709551616" )]
public void PlayerIdsRejectAmbiguousOrInvalidValues( string value )
{
Assert.Throws<ArgumentException>( () => new PlayerReference( value ) );
}
[Fact]
public void MutableRegistrationInputsCannotChangeDescriptors()
{
var choices = new[] { "one", "two" };
var argument = new CommandArgumentDescriptor( "choice", "Choice", CommandArgumentKind.Enum, choices: choices );
var arguments = new[] { argument };
var aliases = new[] { "alternate" };
var descriptor = new CommandDescriptor( "sample", "Help", "sample.run", "sample", arguments, aliases );
choices[ 0 ] = "changed";
arguments[ 0 ] = null;
aliases[ 0 ] = "changed";
Assert.Equal( "one", descriptor.Arguments[ 0 ].Choices[ 0 ] );
Assert.Equal( "alternate", descriptor.Aliases[ 0 ] );
Assert.Throws<NotSupportedException>( () => ((IList<string>)descriptor.Aliases)[ 0 ] = "changed" );
}
[Fact]
public void RequestPayloadCannotCarryAuthorityAndHostContextHasNoPublicConstructor()
{
Assert.Equal( new[] { "Arguments", "Command", "RequestId" },
typeof( CommandRequest ).GetProperties().Select( property => property.Name ).OrderBy( name => name ) );
Assert.Empty( typeof( CommandContext ).GetConstructors() );
Assert.Empty( typeof( ActorIdentity ).GetConstructors() );
Assert.Throws<ArgumentException>( () => new ActorIdentity( ActorKind.Unknown, "anything" ) );
Assert.Throws<ArgumentException>( () => new ActorIdentity( ActorKind.DedicatedConsole, "0" ) );
}
[Fact]
public void TypedArgumentsSnapshotAndRejectDefaultsAndMutableValues()
{
var source = new Dictionary<string, object> { [ "count" ] = 3L };
var arguments = new CommandArguments( source );
source[ "count" ] = 4L;
Assert.Equal( 3L, arguments.Get<long>( "count" ) );
Assert.False( arguments.Contains( "missing" ) );
Assert.Throws<ArgumentException>( () => arguments.Get<long>( "missing" ) );
Assert.Throws<ArgumentException>( () => arguments.Get<string>( "count" ) );
Assert.Throws<ArgumentException>( () => new CommandArguments( new Dictionary<string, object> { [ "bad" ] = new List<int>() } ) );
Assert.Throws<ArgumentException>( () => new CommandArguments( new Dictionary<string, object> { [ "bad" ] = double.NaN } ) );
}
[Fact]
public void PermanentDurationIsExplicitAndFiniteDurationMustBePositive()
{
Assert.True( CommandDuration.Permanent.IsPermanent );
Assert.Null( CommandDuration.Permanent.Value );
Assert.False( new CommandDuration( TimeSpan.FromMinutes( 5 ) ).IsPermanent );
Assert.Throws<ArgumentOutOfRangeException>( () => new CommandDuration( TimeSpan.Zero ) );
Assert.Throws<ArgumentOutOfRangeException>( () => new CommandDuration( TimeSpan.FromTicks( -1 ) ) );
}
[Fact]
public void UnknownResultsAndStorageFailuresCannotClaimConfirmedSuccess()
{
Assert.Equal( CommandStatus.Unknown, default );
Assert.Equal( StoreReadStatus.Unknown, default );
Assert.Equal( StoreWriteStatus.Unknown, default );
Assert.Throws<ArgumentException>( () => new StoreWriteResult( StoreWriteStatus.Committed ) );
Assert.Throws<ArgumentException>( () => new StoreWriteResult( StoreWriteStatus.Failed, 1 ) );
Assert.Throws<ArgumentException>( () => new StoreWriteResult( StoreWriteStatus.Committed, 0 ) );
Assert.Null( new StoreWriteResult( StoreWriteStatus.Unknown ).CommittedRevision );
Assert.NotEqual( StoreReadStatus.Missing, StoreReadStatus.Corrupt );
Assert.NotEqual( StoreReadStatus.Missing, StoreReadStatus.UnsupportedSchema );
}
[Fact]
public void ReadOutcomesRequireDocumentOnlyForFound()
{
var document = new VersionedStoreDocument( 1, 1, "{}" );
Assert.Throws<ArgumentException>( () => new StoreReadResult( StoreReadStatus.Found ) );
Assert.Throws<ArgumentException>( () => new StoreReadResult( StoreReadStatus.Corrupt, document ) );
Assert.Same( document, new StoreReadResult( StoreReadStatus.Found, document ).Document );
}
[Fact]
public void StoreWritesRequireNextRevisionAndLogicalKeys()
{
var key = new StoreKey( "security", "policy" );
var document = new VersionedStoreDocument( 2, 1, "{}" );
Assert.Equal( 0, new StoreWriteRequest( key, document, 0 ).ExpectedRevision );
Assert.Throws<ArgumentException>( () => new StoreWriteRequest( key, document, 1 ) );
Assert.Throws<ArgumentException>( () => new StoreWriteRequest( key, document, long.MaxValue ) );
Assert.Throws<ArgumentException>( () => new StoreKey( "security", "../owners" ) );
Assert.Throws<ArgumentException>( () => new VersionedStoreDocument( 0, 1, "{}" ) );
}
[Fact]
public void ModuleDependenciesAreSnapshotsAndApiVersionsAreExplicit()
{
var dependencies = new[] { new ModuleDependency( "security", 1 ) };
var module = new ModuleDescriptor( "moderation", "Help", 1, dependencies );
dependencies[ 0 ] = new ModuleDependency( "other", 2 );
Assert.Equal( "security", module.Dependencies[ 0 ].ModuleId );
Assert.Throws<ArgumentOutOfRangeException>( () => new ModuleDependency( "security", 0 ) );
}
[Fact]
public void ResultDataCannotBeChangedAfterConstruction()
{
var fields = new Dictionary<string, string> { [ "code" ] = "safe" };
var result = new CommandResult( CommandStatus.Unsupported, "Unavailable", fields );
fields[ "code" ] = "changed";
Assert.Equal( "safe", result.Data[ "code" ] );
}
}