81 lines
3.0 KiB
Go
81 lines
3.0 KiB
Go
package main
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
)
|
|
|
|
// §23, §24, A-12, A-17, A-18: a hand act writes its key and starts the run's unit with {scope, unit}; the tool
|
|
// answers from the act's event when it comes within the wait, else "started", never "done"; a failed start
|
|
// leaves no key; pause writes `paused`, resume deletes it.
|
|
func TestAHandActWritesItsKeyStartsTheRunAndAnswersFromItsEvent(t *testing.T) {
|
|
cases := []struct {
|
|
name, act, why string
|
|
event map[string]any
|
|
callErr error
|
|
outcome string
|
|
err string
|
|
keyLeft bool
|
|
waited []string
|
|
}{
|
|
{name: "event seen", act: ActSnapshots, why: "before the move", event: map[string]any{"snapshot": "tank@mesh-hourly"}, outcome: "seen", keyLeft: true,
|
|
waited: handEnds[ActSnapshots]},
|
|
{name: "event not seen in time", act: ActPrune, why: "free space", outcome: "started", keyLeft: true, waited: handEnds[ActPrune]},
|
|
{name: "apply", act: ActApply, why: "now", event: map[string]any{"key": "hostid"}, outcome: "seen", keyLeft: true, waited: handEnds[ActApply]},
|
|
{name: "start refused", act: ActSnapshots, why: "x", callErr: errors.New("refused"), err: "did not start"},
|
|
{name: "no why", act: ActSnapshots, why: "", err: "say why"},
|
|
{name: "unknown act", act: "destroy", why: "x", err: "not an act"},
|
|
}
|
|
for _, c := range cases {
|
|
f := newFake(t)
|
|
m := f.machine()
|
|
st := memStates{}
|
|
m.States, m.Node = st, "ace"
|
|
var key string
|
|
var body any
|
|
m.Call = func(k string, b any) (json.RawMessage, error) { key, body = k, b; return nil, c.callErr }
|
|
var bound time.Duration
|
|
var kinds []string
|
|
m.Await = func(k []string, d time.Duration) (map[string]any, bool) {
|
|
kinds, bound = k, d
|
|
return c.event, c.event != nil
|
|
}
|
|
a, err := m.HandAct(context.Background(), c.act, "caller@node", c.why)
|
|
if c.err != "" {
|
|
if err == nil || !strings.Contains(err.Error(), c.err) {
|
|
t.Errorf("%s: %v", c.name, err)
|
|
}
|
|
} else if err != nil || a.Outcome != c.outcome {
|
|
t.Errorf("%s: %+v %v", c.name, a, err)
|
|
}
|
|
if _, left := st["schedule/"+c.act]; left != c.keyLeft {
|
|
t.Errorf("%s: key left %v, want %v", c.name, left, c.keyLeft)
|
|
}
|
|
if c.err == "" || c.callErr != nil {
|
|
args, _ := body.(map[string]any)
|
|
if key != "seat:node-service-manager.start@ace" || args["unit"] != "zfs-mesh-run.service" || args["scope"] != "system" || len(args) != 2 {
|
|
t.Errorf("%s: called %s with %v", c.name, key, body)
|
|
}
|
|
}
|
|
if c.err == "" && (bound != 30*time.Second || strings.Join(kinds, ",") != strings.Join(c.waited, ",")) {
|
|
t.Errorf("%s: waited %v for %v", c.name, bound, kinds)
|
|
}
|
|
if c.outcome == "started" && strings.Contains(a.Said, "done") {
|
|
t.Errorf("%s: said done for what it did not see: %s", c.name, a.Said)
|
|
}
|
|
}
|
|
f := newFake(t)
|
|
m := f.machine()
|
|
m.States = memStates{}
|
|
if err := m.Pause("p@n", "maintenance"); err != nil || !m.Paused() {
|
|
t.Fatal(err, m.Paused())
|
|
}
|
|
if err := m.Resume(); err != nil || m.Paused() {
|
|
t.Fatal(err, m.Paused())
|
|
}
|
|
}
|