package main import ( "encoding/json" "sort" "strings" "testing" "time" ) type memStates map[string]json.RawMessage func (s memStates) Put(state, key string, v any) error { b, _ := json.Marshal(v) s[state+"/"+key] = b return nil } func (s memStates) Delete(state, key string) error { delete(s, state+"/"+key); return nil } func (s memStates) Keys(state string) ([]string, error) { var out []string for k := range s { if rest, ok := strings.CutPrefix(k, state+"/"); ok { out = append(out, rest) } } sort.Strings(out) return out, nil } func (s memStates) Get(state, key string) (json.RawMessage, bool, error) { b, ok := s[state+"/"+key] return b, ok, nil } // §25, §26: each change emits its event with before and after and keeps its key of the state `applied`, a // refusal and a wait too, a wait keeping its first since, and a value back to not managed leaves the state. func TestEachChangeIsSaidAndKeptAndANotManagedValueLeavesTheState(t *testing.T) { f := newFake(t) m := f.machine() st := memStates{} m.States = st if err := m.Changed("pool", "pool-properties.autotrim", "off", "on", "on"); err != nil { t.Fatal(err) } e := f.emitted[0] if e["_type"] != "config.applied" || e["target"] != "pool" || e["key"] != "pool-properties.autotrim" || e["before"] != "off" || e["after"] != "on" || e["read-back"] != "on" { t.Errorf("event: %v", e) } var got AppliedEntry json.Unmarshal(st["applied/pool-properties.autotrim"], &got) if got.Before != "off" || got.After != "on" || got.Outcome != OutcomeApplied || !got.At.Equal(f.now.UTC()) { t.Errorf("state: %+v", got) } if err := m.Refused(Refusal{Key: "hostid", Value: "zzz", Reason: "is not eight hexadecimal digits"}); err != nil { t.Fatal(err) } if f.emitted[1]["_type"] != "config.refused" || f.emitted[1]["reason"] == "" || !strings.Contains(string(st["applied/hostid"]), `"refused"`) { t.Errorf("refusal: %v %s", f.emitted[1], st["applied/hostid"]) } m.Waiting("log-devices", "the pool's scrub") first := f.now f.now = f.now.Add(time.Hour) m.Waiting("log-devices", "the pool's scrub") var w AppliedEntry json.Unmarshal(st["applied/log-devices"], &w) if f.emitted[3]["_type"] != "config.waiting" || !w.Since.Equal(first.UTC()) || !w.At.Equal(f.now.UTC()) || w.Outcome != OutcomeWaiting { t.Errorf("wait: %v %+v", f.emitted[3], w) } m.NotManaged("log-devices") if _, ok := st["applied/log-devices"]; ok { t.Error("the key stays after the value went back to not managed") } m.Emit = nil if err := m.Changed("pool", "k", "a", "b", "b"); err == nil { t.Error("an event that cannot be said is said as done") } }