Files
mesh-delivery/modules/mesh-delivery-sdk/steprun/askers_test.go
T
jschoubben 43f6b57ab2 Main's subject takes the checker's check and recheck again, as A-184 says until it goes
The asker rename (d29e1426) let main's subject, which names no asker, take only a build and a run, so a holder refused the
checker's check asks ("the kind check is not taken on main's subject"), and seven of the checker's eight integration tests
failed. Until main's subject goes it also takes the checker's check and recheck; a step there is still refused.

Builds: design 47 G A-184, §543

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-12 20:30:13 +01:00

98 lines
4.9 KiB
Go

package steprun
import (
"context"
"strings"
"testing"
"time"
sdkstep "git.novox.be/novox/mesh-delivery/modules/mesh-delivery-sdk/buildstep"
"git.novox.be/novox/mesh-sdk/go/seatdef"
)
func callersFor(table map[string][]string) func(string) ([]string, bool) {
return func(kind string) ([]string, bool) { c, ok := table[kind]; return c, ok }
}
// Design 47 G §543 (BG2, BG3, A-163, A-170): an ask whose kind its asker, the last token of its subject, is no caller of is
// answered error naming the kind and the asker and ended failed; a step on main's subject is refused as well (its check and recheck
// taken, A-184); one the
// seat list does not judge yet is given back.
func TestPullRefusesAKindItsAskerMayNotAsk(t *testing.T) {
step := testAsk(sdkstep.AskStep)
step.Check = nil
step.Step = &sdkstep.StepRun{Pipeline: "p", Step: sdkstep.StepUnit, Module: "m", Artifact: "m:a", Commit: "0123456789abcdef0123456789abcdef01234567",
Fingerprint: "fp", Fetch: sdkstep.Fetch{Repository: "novox/x", Commit: "0123456789abcdef0123456789abcdef01234567"}}
build := testAsk(sdkstep.AskDelivery)
table := map[string][]string{"step": {"mesh-pipeline-runner"}, "delivery": {"mesh-builds"}}
for _, c := range []struct {
name, subject string
ask sdkstep.StepAsk
callers func(string) ([]string, bool)
want string // "" runs it, "refused" refuses it, "back" gives it back
}{
{"the indexer's step", "mesh.seat.node-build-go.accept.check.mesh-catalog", step, callersFor(table), "refused"},
{"the runner's step", "mesh.seat.node-build-go.accept.check.mesh-pipeline-runner", step, callersFor(table), ""},
{"a step on main's subject", "mesh.seat.node-build-go.accept.check", step, callersFor(table), "refused"},
{"a build on main's subject", "mesh.seat.node-build-go.accept.delivery", build, callersFor(table), ""},
// A-184: until main's subject goes, it also takes the checker's check and recheck asks.
{"the checker's check on main's subject", "mesh.seat.node-build-go.accept.check", testAsk(sdkstep.AskCheck), callersFor(table), ""},
{"the checker's recheck on main's subject", "mesh.seat.node-build-go.accept.recheck", testAsk(sdkstep.AskRecheck), callersFor(table), ""},
{"a kind the seat list does not hold", "mesh.seat.node-build-go.accept.check.mesh-pipeline-runner", step, callersFor(nil), "back"},
} {
t.Run(c.name, func(t *testing.T) {
src, st, sy := NewFakeAsks(), newSteps(), &sayer{}
raw, _ := sdkstep.EncodeStepAsk(c.ask)
queue := c.ask.Kind
if queue == sdkstep.AskStep {
queue = sdkstep.AskCheck // a step rides the queue check (A-170, A-184)
}
src.PublishWithSubject(queue, raw, c.subject)
_, _, asker, _ := sdkstep.ParseAskSubjectBy(c.subject)
ran := make(chan struct{}, 1)
p := &Puller{Source: src, Language: sdkstep.AskGo, Node: "shanks", Put: st.put, Capacity: func() int { return 1 }, Callers: c.callers,
PullWait: 5 * time.Millisecond, Heartbeat: time.Hour, Say: sy.say, ProgressEvery: time.Hour,
Run: func(context.Context, sdkstep.StepAsk) (any, error) { ran <- struct{}{}; return nil, nil }}
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
go p.Pull(ctx)
switch c.want {
case "":
<-ran
// §481: the step is keyed under the asker its subject names, main's under its node alone.
key, _ := sdkstep.AskStepKey(asker, "shanks", c.ask.ID)
waitFor(t, "the step keyed "+key, func() bool { _, ok := st.get(key); return ok })
case "refused":
waitFor(t, "the ask ended", func() bool { _, termed, _ := src.Fates(); return len(termed) == 1 })
key, _ := sdkstep.AskStepKey(asker, "shanks", c.ask.ID)
s, _ := st.get(key)
if s.State != sdkstep.StepFailed || !strings.Contains(s.Reason, KindOf(c.ask)) || (strings.Contains(c.subject, "mesh-catalog") && !strings.Contains(s.Reason, asker)) {
t.Fatalf("the step = %+v, want failed naming the kind %s and the asker", s, KindOf(c.ask))
}
if end := sy.of("step-ended"); len(end) != 1 || end[0].body["outcome"] != sdkstep.OutcomeFailed {
t.Fatalf("step-ended = %+v, want failed", end)
}
select {
case <-ran:
t.Fatal("a refused ask ran")
default:
}
case "back":
waitFor(t, "the ask given back", func() bool { src.mu.Lock(); defer src.mu.Unlock(); return len(src.Delayed) > 0 })
}
})
}
}
func TestCallersFromDefinitionsReadsTheSeatsCallers(t *testing.T) {
def := seatdef.Definition{Seat: "node-build-go", Accepts: []seatdef.Message{
{Name: "step", Callers: &seatdef.Callers{List: []string{"seat:mesh-pipeline-runner"}}}}}
callers := CallersFromDefinitions(sdkstep.AskGo, func() map[string]seatdef.Definition { return map[string]seatdef.Definition{"node-build-go": def} })
if got, ok := callers("step"); !ok || len(got) != 1 || got[0] != "mesh-pipeline-runner" {
t.Fatalf("callers of step = %v, %v", got, ok)
}
if _, ok := callers("run"); ok {
t.Fatal("a kind the definition does not list is found")
}
}