Files
KArtSell.Aegis/tests/KArtSell.ModelOperations.UnitTests/ModelOperationExecutionTests.cs
T
kjh2064 d38dc32e7a feat: require explicit model-operation holds (AEG-V15-037)
Separates business holds from technical failures in the pure execution state machine. Evidence: targeted Release tests 3/3 passed; TRX SHA256 2F2CD06B1DFD3F76F336A0636598553599CD05CF7FA82E477DB160425975085F.
2026-08-09 02:14:55 +09:00

44 lines
2.2 KiB
C#

using KArtSell.Modules.ModelOperations.Domain;
namespace KArtSell.ModelOperations.UnitTests;
public sealed class ModelOperationExecutionTests
{
[Fact] public void Business_hold_can_resume_but_success_is_terminal()
{
var now = DateTimeOffset.UtcNow;
var execution = new ModelOperationExecution(Guid.NewGuid(), "J32:KR:1", now);
execution.MoveTo(ModelOperationExecutionState.BusinessHold, "PIT_NOT_READY", "h1", now, now.AddMinutes(5));
execution.MoveTo(ModelOperationExecutionState.Running, "DATA_READY", "h2", now.AddMinutes(1));
execution.MoveTo(ModelOperationExecutionState.Succeeded, "OUTPUT_FROZEN", "h3", now.AddMinutes(2));
Assert.Throws<InvalidOperationException>(() => execution.MoveTo(ModelOperationExecutionState.Running, "REOPEN", "h4", now.AddMinutes(3)));
}
[Fact]
public void Business_hold_requires_a_future_expiry_but_technical_failure_cannot_set_one()
{
var now = new DateTimeOffset(2026, 8, 9, 0, 0, 0, TimeSpan.Zero);
var execution = new ModelOperationExecution(Guid.NewGuid(), "key", now);
Assert.Throws<InvalidOperationException>(() => execution.MoveTo(ModelOperationExecutionState.BusinessHold, "PIT_GAP", "h1", now));
execution.MoveTo(ModelOperationExecutionState.Running, "STARTED", "h2", now.AddMinutes(1));
Assert.Throws<InvalidOperationException>(() => execution.MoveTo(ModelOperationExecutionState.Failed, "NETWORK_TIMEOUT", "h3", now.AddMinutes(2), now.AddMinutes(3)));
}
[Fact]
public void Business_hold_persists_reason_and_expiry_until_an_explicit_resume()
{
var now = new DateTimeOffset(2026, 8, 9, 0, 0, 0, TimeSpan.Zero);
var holdUntil = now.AddHours(1);
var execution = new ModelOperationExecution(Guid.NewGuid(), "key", now);
var transition = execution.MoveTo(ModelOperationExecutionState.BusinessHold, "CALENDAR_NOT_APPROVED", "h1", now.AddMinutes(1), holdUntil);
Assert.Equal("CALENDAR_NOT_APPROVED", transition.ReasonCode);
Assert.Equal(holdUntil, transition.HoldUntil);
Assert.Equal(holdUntil, execution.HoldUntil);
execution.MoveTo(ModelOperationExecutionState.Running, "CALENDAR_APPROVED", "h2", holdUntil);
Assert.Null(execution.HoldUntil);
}
}