feat: add command domain invariants
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
package domain
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
rvboxv1 "github.com/rvbox/rvbox/gen/go/rvbox/v1"
|
||||
)
|
||||
|
||||
func TestLifecycleTransitionMatrix_HP_CMD_01(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
states := []rvboxv1.CommandLifecycle{
|
||||
rvboxv1.CommandLifecycle_COMMAND_LIFECYCLE_UNSPECIFIED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_QUEUED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_DISPATCHED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_ACCEPTED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_RUNNING,
|
||||
rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_FAILED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_TERMINATED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_CANCELLED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_EXPIRED,
|
||||
rvboxv1.CommandLifecycle_COMMAND_REJECTED,
|
||||
}
|
||||
legal := map[[2]rvboxv1.CommandLifecycle]bool{
|
||||
{rvboxv1.CommandLifecycle_COMMAND_QUEUED, rvboxv1.CommandLifecycle_COMMAND_DISPATCHED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_QUEUED, rvboxv1.CommandLifecycle_COMMAND_CANCELLED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_QUEUED, rvboxv1.CommandLifecycle_COMMAND_EXPIRED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_DISPATCHED, rvboxv1.CommandLifecycle_COMMAND_QUEUED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_DISPATCHED, rvboxv1.CommandLifecycle_COMMAND_ACCEPTED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_DISPATCHED, rvboxv1.CommandLifecycle_COMMAND_REJECTED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_DISPATCHED, rvboxv1.CommandLifecycle_COMMAND_CANCELLED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_DISPATCHED, rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_ACCEPTED, rvboxv1.CommandLifecycle_COMMAND_RUNNING}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_ACCEPTED, rvboxv1.CommandLifecycle_COMMAND_REJECTED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_ACCEPTED, rvboxv1.CommandLifecycle_COMMAND_CANCELLED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_ACCEPTED, rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_RUNNING, rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_RUNNING, rvboxv1.CommandLifecycle_COMMAND_FAILED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_RUNNING, rvboxv1.CommandLifecycle_COMMAND_TERMINATED}: true,
|
||||
{rvboxv1.CommandLifecycle_COMMAND_RUNNING, rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED}: true,
|
||||
}
|
||||
|
||||
for _, from := range states {
|
||||
for _, to := range states {
|
||||
want := legal[[2]rvboxv1.CommandLifecycle{from, to}]
|
||||
if got := CanTransition(from, to); got != want {
|
||||
t.Errorf("CanTransition(%s, %s) = %t, want %t", from, to, got, want)
|
||||
}
|
||||
if err := ValidateTransition(from, to); (err == nil) != want {
|
||||
t.Errorf("ValidateTransition(%s, %s) error = %v, legal = %t", from, to, err, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestTerminalPredicateExhaustive_HP_CMD_01(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
terminal := map[rvboxv1.CommandLifecycle]bool{
|
||||
rvboxv1.CommandLifecycle_COMMAND_SUCCEEDED: true,
|
||||
rvboxv1.CommandLifecycle_COMMAND_FAILED: true,
|
||||
rvboxv1.CommandLifecycle_COMMAND_TERMINATED: true,
|
||||
rvboxv1.CommandLifecycle_COMMAND_CANCELLED: true,
|
||||
rvboxv1.CommandLifecycle_COMMAND_INTERRUPTED: true,
|
||||
rvboxv1.CommandLifecycle_COMMAND_EXPIRED: true,
|
||||
rvboxv1.CommandLifecycle_COMMAND_REJECTED: true,
|
||||
}
|
||||
for value := rvboxv1.CommandLifecycle(0); value <= rvboxv1.CommandLifecycle_COMMAND_REJECTED; value++ {
|
||||
if got := IsTerminal(value); got != terminal[value] {
|
||||
t.Errorf("IsTerminal(%s) = %t, want %t", value, got, terminal[value])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestLaunchBarrier_HP_LAUNCH_01(t *testing.T) {
|
||||
t.Parallel()
|
||||
|
||||
if !CanAdvanceLaunchPhase(LaunchPhaseNone, LaunchPhasePrepared) ||
|
||||
!CanAdvanceLaunchPhase(LaunchPhasePrepared, LaunchPhaseAuthorized) {
|
||||
t.Fatal("required launch barrier transitions rejected")
|
||||
}
|
||||
for from := LaunchPhaseNone; from <= LaunchPhaseAuthorized; from++ {
|
||||
for to := LaunchPhaseNone; to <= LaunchPhaseAuthorized; to++ {
|
||||
want := (from == LaunchPhaseNone && to == LaunchPhasePrepared) ||
|
||||
(from == LaunchPhasePrepared && to == LaunchPhaseAuthorized)
|
||||
if CanAdvanceLaunchPhase(from, to) != want {
|
||||
t.Errorf("CanAdvanceLaunchPhase(%d, %d) mismatch", from, to)
|
||||
}
|
||||
}
|
||||
}
|
||||
if MayRedispatch(LaunchPhaseAuthorized) {
|
||||
t.Fatal("authorized launch may be redispatched")
|
||||
}
|
||||
if !MayRedispatch(LaunchPhaseNone) || !MayRedispatch(LaunchPhasePrepared) {
|
||||
t.Fatal("pre-authorization phase unexpectedly forbids redispatch")
|
||||
}
|
||||
if MayRedispatch(LaunchPhase(255)) {
|
||||
t.Fatal("unknown launch phase may be redispatched")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user