Files
mesh-delivery/modules/mesh-delivery-builder/cmd/mesh-delivery-builder/stepsread_test.go
T
jschoubben dc024ae552 BL17e: the read of an ask's steps is a kept call: a fault ends no key, the watch is taken up again from its last revision, past five minutes unanswered it is call-unanswered on a bound key
JetStreamAsks.WatchAsk opens the watch as a kept call and takes a closed watch up after the last revision heard; the first
fault arms a key of the state bounds with its own time to live (StepsBoundKey), whose end while the read is unanswered raises
call-unanswered about the ask, and the answer ends it.

Builds: design 47 G §568, A-77
Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-11 23:33:20 +01:00

44 lines
2.1 KiB
Go

package main
import (
"context"
"errors"
"strings"
"testing"
"time"
"github.com/nats-io/nats.go/jetstream"
"git.novox.be/novox/mesh-contracts/health-and-repair/go/clients/meshconditionsclient"
"mesh-delivery-builder/internal/versions"
)
// Design 47 G §568, A-77: the first fault of the read of an ask's steps arms a bound key with its own time to live; its end
// while the read is unanswered raises call-unanswered about the ask; the answer deletes the bound and clears the finding.
func TestTheStepsReadIsCallUnansweredAfterItsBound(t *testing.T) {
st := &memFindings{vals: map[string][]byte{}}
f := &findings{w: st, logf: t.Logf, now: func() time.Time { return time.Unix(0, 0) }}
marks := &memBounds{keys: map[string]bool{}}
s := &stepsRead{bounds: func() jetstream.KeyValue { return marks }, findings: f, bound: meshconditionsclient.CallUnansweredAfter,
now: func() time.Time { return time.Unix(600, 0) }, logf: t.Logf}
ask := "mvl0frv18d1958"
callKey := meshconditionsclient.Key(meshconditionsclient.ScopeCall, ask, meshconditionsclient.KindCallUnanswered)
s.unanswered("mesh-build-go", ask, errors.New("nats: no responders available for request"))
if !marks.keys[versions.StepsBoundKey("mesh-build-go", ask)] || st.vals[callKey] != nil {
t.Fatalf("a first fault: bound %v, finding %v; want the bound armed, no finding yet", marks.keys, st.vals[callKey] != nil)
}
s.expired(context.Background(), purged{key: versions.StepsBoundKey("mesh-build-go", ask)})
if got, err := decodeFinding(st.vals[callKey]); err != nil || got.Kind != meshconditionsclient.KindCallUnanswered || !strings.Contains(got.Summary, "the steps of mesh-build-go") || !strings.Contains(got.Summary, ask) {
t.Fatalf("the bound's end: the finding = %+v, %v", got, err)
}
s.answered("mesh-build-go", ask)
if marks.keys[versions.StepsBoundKey("mesh-build-go", ask)] || st.vals[callKey] != nil {
t.Errorf("the read answered: bound %v, finding left %v", marks.keys, st.vals[callKey] != nil)
}
s.expired(context.Background(), purged{key: versions.StepsBoundKey("mesh-build-go", ask)})
if st.vals[callKey] != nil {
t.Error("a bound's end after the answer raised call-unanswered")
}
}