diff --git a/modules/mesh-delivery-checker/internal/check/composition_test.go b/modules/mesh-delivery-checker/internal/check/composition_test.go index bafc264a..174f85be 100644 --- a/modules/mesh-delivery-checker/internal/check/composition_test.go +++ b/modules/mesh-delivery-checker/internal/check/composition_test.go @@ -79,6 +79,20 @@ func TestAPassingCheckTakesTheAssignersComposition(t *testing.T) { t.Errorf("with the list not read the composition was handed %s, want none", got) } } + // ADR 0635 §3: at a node-engine member's head a list not read fails the composition, naming why, and asks no composer + // (a composer handed none would judge no machine and the composition would pass); read, it composes. + for _, read := range []bool{false, true} { + b.ex.SeatList = fakeSeatList{read: read} + asked := len(f.asked) + hv := b.judge(t, caseOf(t, pullOf(repoA, 7, head1), pullOf(nodeEngineRepo, 3, head2))).Verdict + switch { + case !read && (hv.State != meshdeliverycheckerclient.VerdictFail || hv.Composition == nil || hv.Composition.State != meshdeliverycheckerclient.CompositionFail || + !strings.Contains(hv.Composition.Words, "seat list") || len(f.asked) != asked): + t.Errorf("at a node-engine head with the list not read: verdict %+v, composition %+v, asked %d more; want a failed composition naming the seat list, no ask", hv, hv.Composition, len(f.asked)-asked) + case read && (hv.Composition == nil || hv.Composition.State != meshdeliverycheckerclient.CompositionPass || len(f.asked) != asked+1): + t.Errorf("at a node-engine head with the list read: composition %+v, want the composer's pass", hv.Composition) + } + } b.ex.SeatList = nil f.asked, f.seats = f.asked[:1], f.seats[:1] // ADR 0548 §5: a carry whose fingerprints did not change keeps the composition it passed and asks the assigner nothing. diff --git a/modules/mesh-delivery-checker/internal/check/judge.go b/modules/mesh-delivery-checker/internal/check/judge.go index f138bddc..10680a69 100644 --- a/modules/mesh-delivery-checker/internal/check/judge.go +++ b/modules/mesh-delivery-checker/internal/check/judge.go @@ -406,9 +406,17 @@ func (st *state) composed(ctx context.Context, v *meshdeliverychecker.VerdictsBy // The composer of a node-engine member's head composes against the check's seat list, the judge's (ADR 0635 §1, ADR 0636 // §4); only a list the reader read is handed: the heads' alone are no list, and the composer then judges no machine. st.c.Seats = nil - if seats, err := st.checkSeats(ctx); err != nil { - st.c.note("the composition is given no seat list: " + err.Error()) - } else if seats.listed { + seats, serr := st.checkSeats(ctx) + switch head := composerHead(st.c); { + case serr != nil && head != "": + // A composition at a node-engine member's head that cannot read the seat list it needs fails, naming why, rather than + // passing with no machine judged (ADR 0635 §3, ADR 0636 §1). + withComposition(v, meshwants.CompositionsByGroupHeadsValue{Group: st.c.Group, Heads: st.c.Heads, State: meshdeliverycheckerclient.CompositionFail, + Sdk: head, Words: "the composer at the node-engine's head " + head + " cannot be given the seat list it composes against: " + serr.Error()}) + return + case serr != nil: + st.c.note("the composition is given no seat list: " + serr.Error()) + case seats.listed: st.c.Seats = seats.defs } comp, err := st.j.Compositions.Compose(ctx, st.c, st.tags.goPackages) diff --git a/sdk/composer/composer.go b/sdk/composer/composer.go index 685fa51d..5990fa29 100644 --- a/sdk/composer/composer.go +++ b/sdk/composer/composer.go @@ -63,9 +63,9 @@ type Request struct { // 0578 §5, ADR 0606 §4); the composer does not read them. Nil is no list at hand, and the judge judges no name; an // empty list is a list, and every name is unknown to it. Packages []string `json:"packages,omitzero"` - // Seats are the seat definitions the judge judges against, as the asker's seat list reader holds them (the definitions of the - // seat mesh-catalog, resolved), raw so that this package names no seat (ADR 0635 §4, ADR 0636 §1). The judge reads them; the - // composer does not. None is no list at hand: the judge then judges against the list its process was given. + // Seats are the seat definitions the judge judges and the composer composes against, as the asker's seat list reader holds + // them (the definitions of the seat mesh-catalog, resolved), raw so that this package names no seat (ADR 0635 §4, ADR 0636 + // §1). The judge and the composer read them. None is no list at hand: a composer handed none judges no machine (ADR 0636 §1). Seats []json.RawMessage `json:"seats,omitempty"` }