chore: establish local platform baseline
This commit is contained in:
@@ -0,0 +1,52 @@
|
||||
package conversation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
)
|
||||
|
||||
type Status string
|
||||
|
||||
const (
|
||||
StatusPreparing Status = "PREPARING"
|
||||
StatusRoomIssued Status = "ROOM_ISSUED"
|
||||
StatusStartingAgent Status = "STARTING_AGENT"
|
||||
StatusActive Status = "ACTIVE"
|
||||
StatusStopping Status = "STOPPING"
|
||||
StatusCompleted Status = "COMPLETED"
|
||||
StatusFailed Status = "FAILED"
|
||||
StatusExpired Status = "EXPIRED"
|
||||
)
|
||||
|
||||
var ErrInvalidTransition = errors.New("invalid conversation state transition")
|
||||
|
||||
var transitions = map[Status]map[Status]struct{}{
|
||||
StatusPreparing: {StatusRoomIssued: {}, StatusFailed: {}},
|
||||
StatusRoomIssued: {StatusStartingAgent: {}, StatusExpired: {}},
|
||||
StatusStartingAgent: {StatusActive: {}, StatusStopping: {}, StatusFailed: {}},
|
||||
StatusActive: {StatusStopping: {}},
|
||||
StatusStopping: {StatusCompleted: {}},
|
||||
}
|
||||
|
||||
func ValidateTransition(from, to Status) error {
|
||||
if from == to && IsKnown(from) {
|
||||
return nil
|
||||
}
|
||||
if _, allowed := transitions[from][to]; !allowed {
|
||||
return fmt.Errorf("%w: %s -> %s", ErrInvalidTransition, from, to)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func IsTerminal(status Status) bool {
|
||||
return status == StatusCompleted || status == StatusFailed || status == StatusExpired
|
||||
}
|
||||
|
||||
func IsKnown(status Status) bool {
|
||||
switch status {
|
||||
case StatusPreparing, StatusRoomIssued, StatusStartingAgent, StatusActive, StatusStopping, StatusCompleted, StatusFailed, StatusExpired:
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
package conversation
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestDocumentedConversationPath(t *testing.T) {
|
||||
path := []Status{StatusPreparing, StatusRoomIssued, StatusStartingAgent, StatusActive, StatusStopping, StatusCompleted}
|
||||
for index := 1; index < len(path); index++ {
|
||||
if err := ValidateTransition(path[index-1], path[index]); err != nil {
|
||||
t.Fatalf("transition %s -> %s: %v", path[index-1], path[index], err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestDuplicateAndTerminalCallbacksCannotRegress(t *testing.T) {
|
||||
if err := ValidateTransition(StatusActive, StatusActive); err != nil {
|
||||
t.Fatalf("duplicate callback should be idempotent: %v", err)
|
||||
}
|
||||
for _, terminal := range []Status{StatusCompleted, StatusFailed, StatusExpired} {
|
||||
if !IsTerminal(terminal) {
|
||||
t.Fatalf("%s should be terminal", terminal)
|
||||
}
|
||||
if err := ValidateTransition(terminal, StatusActive); !errors.Is(err, ErrInvalidTransition) {
|
||||
t.Fatalf("terminal regression %s -> ACTIVE error = %v", terminal, err)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestFailureAndExpiryBranches(t *testing.T) {
|
||||
for _, transition := range [][2]Status{
|
||||
{StatusPreparing, StatusFailed},
|
||||
{StatusRoomIssued, StatusExpired},
|
||||
{StatusStartingAgent, StatusFailed},
|
||||
} {
|
||||
if err := ValidateTransition(transition[0], transition[1]); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user