98 lines
4.2 KiB
Go
98 lines
4.2 KiB
Go
package recovery
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"strings"
|
|
"testing"
|
|
|
|
core "git.yuntuo.hk/k2safe/orbit/model"
|
|
"git.yuntuo.hk/k2safe/orbit/platform/auth"
|
|
)
|
|
|
|
type captureStore struct{ calls int }
|
|
|
|
func (s *captureStore) ReviewTask(context.Context, auth.Token, core.ID, string, ReviewTaskInput) (Result, error) {
|
|
s.calls++
|
|
return Result{}, nil
|
|
}
|
|
func (s *captureStore) ResolveStep(context.Context, auth.Token, core.ID, string, ResolveStepInput) (Result, error) {
|
|
s.calls++
|
|
return Result{}, nil
|
|
}
|
|
func (s *captureStore) Continue(context.Context, auth.Token, core.ID, string, ContinueInput) (Result, error) {
|
|
s.calls++
|
|
return Result{}, nil
|
|
}
|
|
func (s *captureStore) RetryStep(context.Context, auth.Token, core.ID, string, RetryStepInput) (Result, error) {
|
|
s.calls++
|
|
return Result{}, nil
|
|
}
|
|
func (s *captureStore) ContinuePoll(context.Context, auth.Token, core.ID, string, ContinuePollInput) (Result, error) {
|
|
s.calls++
|
|
return Result{}, nil
|
|
}
|
|
func (s *captureStore) RearmWait(context.Context, auth.Token, core.ID, string, RearmWaitInput) (Result, error) {
|
|
s.calls++
|
|
return Result{}, nil
|
|
}
|
|
func TestRecoveryValidation(t *testing.T) {
|
|
id := core.ID(strings.Repeat("a", 32))
|
|
d := Decision{ExpectedRevision: 2, Reason: "核实外部结果", EvidenceRef: "evidence://case/1"}
|
|
step := StepDecision{Decision: d, StepKey: "stage-1.v2", ActivationID: id, ExpectedStepRevision: 3}
|
|
store := &captureStore{}
|
|
s := New(store)
|
|
ctx := context.Background()
|
|
token := auth.Token{}
|
|
if _, err := s.ReviewTask(ctx, token, id, "operation", ReviewTaskInput{Decision: d, TaskID: id, ExpectedTaskRevision: 4, ExecutionSettled: true, Outcome: "SUCCEEDED", Result: json.RawMessage(`{"n":9007199254740993}`)}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.ResolveStep(ctx, token, id, "operation", ResolveStepInput{StepDecision: step, ExecutionSettled: true, Outcome: "SUCCEEDED", Result: json.RawMessage(`null`)}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.Continue(ctx, token, id, "operation", ContinueInput{Decision: d, ReviewResolved: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.RetryStep(ctx, token, id, "operation", RetryStepInput{StepDecision: step, SafeToRetry: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
for _, change := range []func(*Decision){func(v *Decision) { v.ExpectedRevision = 0 }, func(v *Decision) { v.Reason = " " }, func(v *Decision) { v.Reason = string([]byte{255}) }, func(v *Decision) { v.EvidenceRef = "secret bad" }, func(v *Decision) { v.EvidenceRef = "" }} {
|
|
bad := d
|
|
change(&bad)
|
|
if _, err := s.Continue(ctx, token, id, "operation", ContinueInput{Decision: bad, ReviewResolved: true}); err == nil {
|
|
t.Fatal("invalid decision accepted")
|
|
}
|
|
}
|
|
for _, bad := range []ReviewTaskInput{
|
|
{Decision: d, TaskID: id, ExpectedTaskRevision: 4, Outcome: "SUCCEEDED", Result: json.RawMessage(`{}`)},
|
|
{Decision: d, TaskID: id, ExpectedTaskRevision: 4, ExecutionSettled: true, Outcome: "UNKNOWN", Result: json.RawMessage(`{}`)},
|
|
{Decision: d, TaskID: id, ExpectedTaskRevision: 4, ExecutionSettled: true, Outcome: "SUCCEEDED", Result: json.RawMessage(`null`)},
|
|
{Decision: d, TaskID: id, ExpectedTaskRevision: 4, ExecutionSettled: true, Outcome: "SUCCEEDED", Result: json.RawMessage(`{"n":1,"n":2}`)},
|
|
} {
|
|
if _, err := s.ReviewTask(ctx, token, id, "operation", bad); err == nil {
|
|
t.Fatal("unsafe execution review accepted")
|
|
}
|
|
}
|
|
if _, err := s.Continue(ctx, token, id, "operation", ContinueInput{Decision: d}); err == nil {
|
|
t.Fatal("unresolved review continued")
|
|
}
|
|
if _, err := s.RetryStep(ctx, token, id, "operation", RetryStepInput{StepDecision: step}); err == nil {
|
|
t.Fatal("unsafe retry accepted")
|
|
}
|
|
if _, err := s.ContinuePoll(ctx, token, id, "operation", ContinuePollInput{StepDecision: step}); err == nil {
|
|
t.Fatal("unconfirmed pending accepted")
|
|
}
|
|
if _, err := s.RearmWait(ctx, token, id, "operation", RearmWaitInput{StepDecision: step}); err == nil {
|
|
t.Fatal("unverified wait accepted")
|
|
}
|
|
if _, err := s.ContinuePoll(ctx, token, id, "operation", ContinuePollInput{StepDecision: step, PendingConfirmed: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if _, err := s.RearmWait(ctx, token, id, "operation", RearmWaitInput{StepDecision: step, WaitVerified: true}); err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
if store.calls != 6 {
|
|
t.Fatal("invalid inputs reached storage", store.calls)
|
|
}
|
|
}
|