183 lines
4.6 KiB
C#
183 lines
4.6 KiB
C#
using Xunit;
|
|
using FluentAssertions;
|
|
using NSubstitute;
|
|
using System.Collections.Generic;
|
|
using FreeCode.Core.Models;
|
|
|
|
namespace FreeCode.Tests.Unit.Models;
|
|
|
|
public sealed class OperationResultAndTokenUsageTests
|
|
{
|
|
[Fact]
|
|
public void PermissionResult_Allowed_ReturnsAllowedWithNullReason()
|
|
{
|
|
var result = PermissionResult.Allowed();
|
|
|
|
result.IsAllowed.Should().BeTrue();
|
|
result.Reason.Should().BeNull();
|
|
}
|
|
|
|
[Fact]
|
|
public void PermissionResult_Denied_ReturnsDeniedWithReason()
|
|
{
|
|
var result = PermissionResult.Denied("not allowed");
|
|
|
|
result.IsAllowed.Should().BeFalse();
|
|
result.Reason.Should().Be("not allowed");
|
|
}
|
|
|
|
[Fact]
|
|
public void PermissionResult_Constructor_CanCreateAllowedWithReason()
|
|
{
|
|
var result = new PermissionResult(true, "manual");
|
|
|
|
result.IsAllowed.Should().BeTrue();
|
|
result.Reason.Should().Be("manual");
|
|
}
|
|
|
|
[Fact]
|
|
public void PermissionResult_UsesValueEquality()
|
|
{
|
|
var left = PermissionResult.Denied("reason");
|
|
var right = PermissionResult.Denied("reason");
|
|
|
|
left.Should().Be(right);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationResult_Success_ReturnsValidWithoutErrors()
|
|
{
|
|
var result = ValidationResult.Success();
|
|
|
|
result.IsValid.Should().BeTrue();
|
|
result.Errors.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationResult_Failure_ReturnsInvalidWithSingleError()
|
|
{
|
|
var result = ValidationResult.Failure(["missing field"]);
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
result.Errors.Should().ContainSingle().Which.Should().Be("missing field");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationResult_Failure_PreservesErrorOrder()
|
|
{
|
|
var result = ValidationResult.Failure(["first", "second", "third"]);
|
|
|
|
result.Errors.Should().ContainInOrder("first", "second", "third");
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationResult_Failure_AllowsEmptyEnumerable()
|
|
{
|
|
var result = ValidationResult.Failure([]);
|
|
|
|
result.IsValid.Should().BeFalse();
|
|
result.Errors.Should().BeEmpty();
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationResult_Success_UsesValueEquality()
|
|
{
|
|
var left = ValidationResult.Success();
|
|
var right = ValidationResult.Success();
|
|
|
|
left.Should().Be(right);
|
|
}
|
|
|
|
[Fact]
|
|
public void ValidationResult_Failure_CopiesInputSequence()
|
|
{
|
|
var errors = new List<string> { "first" };
|
|
|
|
var result = ValidationResult.Failure(errors);
|
|
errors.Add("second");
|
|
|
|
result.Errors.Should().ContainSingle().Which.Should().Be("first");
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_HoldsAllCounts()
|
|
{
|
|
var usage = new TokenUsage(10, 20, 30, 40);
|
|
|
|
usage.InputTokens.Should().Be(10);
|
|
usage.OutputTokens.Should().Be(20);
|
|
usage.CacheCreationTokens.Should().Be(30);
|
|
usage.CacheReadTokens.Should().Be(40);
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_UsesValueEquality()
|
|
{
|
|
var left = new TokenUsage(1, 2, 3, 4);
|
|
var right = new TokenUsage(1, 2, 3, 4);
|
|
|
|
left.Should().Be(right);
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_DeconstructsIntoOriginalValues()
|
|
{
|
|
var usage = new TokenUsage(1, 2, 3, 4);
|
|
|
|
var (input, output, cacheCreation, cacheRead) = usage;
|
|
|
|
input.Should().Be(1);
|
|
output.Should().Be(2);
|
|
cacheCreation.Should().Be(3);
|
|
cacheRead.Should().Be(4);
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_AllowsZeroCounts()
|
|
{
|
|
var usage = new TokenUsage(0, 0, 0, 0);
|
|
|
|
usage.Should().Be(new TokenUsage(0, 0, 0, 0));
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_PassivelyStoresNegativeCounts()
|
|
{
|
|
var usage = new TokenUsage(-1, -2, -3, -4);
|
|
|
|
usage.InputTokens.Should().Be(-1);
|
|
usage.OutputTokens.Should().Be(-2);
|
|
usage.CacheCreationTokens.Should().Be(-3);
|
|
usage.CacheReadTokens.Should().Be(-4);
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_CanBeSummedByConsumerCode()
|
|
{
|
|
var usage = new TokenUsage(10, 20, 30, 40);
|
|
|
|
var total = usage.InputTokens + usage.OutputTokens + usage.CacheCreationTokens + usage.CacheReadTokens;
|
|
|
|
total.Should().Be(100);
|
|
}
|
|
|
|
[Fact]
|
|
public void TokenUsage_CanBeStoredInsideASubstituteConsumer()
|
|
{
|
|
var usage = new TokenUsage(2, 4, 6, 8);
|
|
var holder = new TokenUsageHolder(usage);
|
|
|
|
holder.Usage.Should().Be(usage);
|
|
}
|
|
|
|
private sealed class TokenUsageHolder(TokenUsage usage) : ITokenUsageHolder
|
|
{
|
|
public TokenUsage Usage { get; } = usage;
|
|
}
|
|
|
|
public interface ITokenUsageHolder
|
|
{
|
|
TokenUsage Usage { get; }
|
|
}
|
|
}
|