diff --git a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main_test.go b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main_test.go index 7e8ab4b3..a3f306da 100644 --- a/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main_test.go +++ b/modules/mesh-delivery-watchman/cmd/mesh-delivery-watchman/main_test.go @@ -13,6 +13,8 @@ import ( "git.novox.be/novox/mesh-sdk/go/process" "git.novox.be/novox/mesh-sdk/go/views" + "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-indexer/takein" + "mesh-delivery-watchman/internal/watch" "github.com/nats-io/nats.go/jetstream" @@ -77,6 +79,34 @@ func TestRunNeedsItsCredential(t *testing.T) { // 1135/AC6 if err == nil || !strings.Contains(err.Error(), "MESH_BROKER_FILE") { t.Fatalf("run = %v, want a refusal naming MESH_BROKER_FILE", err) } + t.Run("1966/AC195-5: a seed indexer_pending could not answer, or answered unreadably, ends nothing", func(t *testing.T) { + for name, call := range map[string]func(string, any) (json.RawMessage, error){ + "cannot be asked": func(string, any) (json.RawMessage, error) { return nil, errors.New("no answer") }, + "unreadable": func(string, any) (json.RawMessage, error) { return json.RawMessage(`{"waits":`), nil }, + } { + out := &findingsOut{findings: map[string]bool{}} + w := watch.New(out, time.Now) + const key = "m.00000000000000000042" + old := watch.Finding{Scope: watch.ScopeDelivery, ID: key, Kind: watch.KindTakeInWaits, Severity: watch.Warning, Source: "take-in", + Since: time.Now().Add(-3 * time.Hour), Summary: "held", Said: "held", Headline: "held", Explanation: "held", Needs: "a take-in"} + value, err := watch.EncodeFinding(old) + if err != nil { + t.Fatal(err) + } + out.findings[old.Key()] = true + w.SeedFinding(old.Key(), value) + body, err := takein.EncodeTakeIn(takein.TakeIn{Kind: takein.TakeInPending, Key: "m.00000000000000000043", What: takein.TakeInRevision, Since: time.Now(), WaitsFor: "src2:x", + Merges: []takein.Merge{{Repository: "novox/a", Number: 1, Merge: "c0ffee"}}}) + if err != nil { + t.Fatal(err) + } + w.TakeIn(takein.TakeInPendingEvent, body) + seedTakeIn(w, call, func(string, ...any) {}) + if !out.findings[old.Key()] || out.pulseDeletes != 0 { + t.Errorf("%s: finding stands %v, signals ended %d: want the previous run's finding and the held wait kept", name, out.findings[old.Key()], out.pulseDeletes) + } + } + }) t.Run("A2j: a want stood since the assigner's bus wrote it, through the first view", func(t *testing.T) { // The watchman's want wait starts at the entry's time (createdOr): through mesh-sdk's views.ProcessReader it is // the time the owner's bus wrote the key, never the watchman's start. @@ -113,11 +143,14 @@ type refusingBus struct{ err error } func (r refusingBus) KeyValue(context.Context, string) (jetstream.KeyValue, error) { return nil, r.err } // findingsOut keeps which findings stand. -type findingsOut struct{ findings map[string]bool } +type findingsOut struct { + findings map[string]bool + pulseDeletes int +} func (o *findingsOut) PutPresence(string, bool) error { return nil } func (o *findingsOut) PutPulse(string, []byte, time.Duration) error { return nil } -func (o *findingsOut) DeletePulse(string) error { return nil } +func (o *findingsOut) DeletePulse(string) error { o.pulseDeletes++; return nil } func (o *findingsOut) PutStuck(string, []byte) error { return nil } func (o *findingsOut) PutFinding(key string, _ []byte) error { o.findings[key] = true; return nil } func (o *findingsOut) DeleteFinding(key string) error { delete(o.findings, key); return nil }