diff --git a/modules/mesh-delivery-checker/internal/check/engines.go b/modules/mesh-delivery-checker/internal/check/engines.go index 64c8579b..2e077789 100644 --- a/modules/mesh-delivery-checker/internal/check/engines.go +++ b/modules/mesh-delivery-checker/internal/check/engines.go @@ -147,6 +147,12 @@ func JudgeEngines(group, heads, composerName string, nodes []string, answers map v.Warnings = append(v.Warnings, node+": "+delivery.ComposeNoAnswer) continue } + if a.Verdict == delivery.ComposeCandidateNotJudged { + // Composed in the sandbox and not judged (ADR 0593 §9): named as no answer, with the composer's words; decided by the + // verdict, never by the words (ADR 0601 §4's kind). + v.Warnings = append(v.Warnings, node+": "+delivery.ComposeNoAnswer+" ("+a.Refused+")") + continue + } v.Machines = append(v.Machines, node) if a.Verdict != delivery.ComposeCandidateRefused { continue @@ -221,8 +227,11 @@ func (e EngineCompositions) inSandbox(ctx context.Context, c *Case, head, node s return a, fmt.Errorf("the composer at the node-engine's head %s did not answer for %s: %w", head, node, err) } out := delivery.ComposeCandidateAnswer{V: a.V, Node: node, Candidate: a.Candidate, Verdict: delivery.ComposeCandidateComposed} + notJudged := "" for _, cv := range verdict.Cases { - if m, ok := cv.Machines[node]; ok && !m.Composes { + if m, ok := cv.Machines[node]; ok && m.NotJudged != "" { + notJudged = m.NotJudged // the composer did not judge the node: neither composed nor refused (ADR 0593 §9) + } else if ok && !m.Composes { out.Verdict = delivery.ComposeCandidateRefused out.Refused = strings.Join(append(append([]string{m.Reason}, m.Problems...), m.Refusals...), "; ") } @@ -234,5 +243,8 @@ func (e EngineCompositions) inSandbox(ctx context.Context, c *Case, head, node s } } sort.Slice(out.Modules, func(i, j int) bool { return out.Modules[i].Module < out.Modules[j].Module }) + if notJudged != "" && out.Verdict == delivery.ComposeCandidateComposed { + out.Verdict, out.Refused = delivery.ComposeCandidateNotJudged, notJudged + } return out, nil } diff --git a/modules/mesh-delivery-checker/internal/check/engines_test.go b/modules/mesh-delivery-checker/internal/check/engines_test.go index 52e98c68..fed2774f 100644 --- a/modules/mesh-delivery-checker/internal/check/engines_test.go +++ b/modules/mesh-delivery-checker/internal/check/engines_test.go @@ -168,6 +168,25 @@ func TestTheCompositionIsMadeFromTheEnginesAnswers(t *testing.T) { if v.State != delivery.CompositionFail || v.SDK != head2 || !strings.Contains(v.Words, "the new composer refuses it") { t.Errorf("verdict = %+v", v) } + // ADR 0593 §9 (ADR 0601 §4's kind): a node the composer did not judge is named as no answer with its words, by the + // contract's field, and the check passes; words in Reason alone are no such mark. + sb.err = nil + for _, row := range []struct { + m composer.Machine + pass bool + }{ + {composer.Machine{NotJudged: "its node-engine sends no running state for web"}, true}, + {composer.Machine{Reason: "not judged: its node-engine sends no running state for web"}, false}, + } { + sb.v = func(node string) composer.Verdict { + return composer.Verdict{V: composer.Version, Cases: []composer.CaseVerdict{{Name: "change", Machines: map[string]composer.Machine{node: row.m}}}} + } + v, err = ec.Compose(context.Background(), host) + named := len(v.Warnings) == 1 && strings.Contains(v.Warnings[0], "anchor: "+delivery.ComposeNoAnswer+" (its node-engine sends no running state for web)") + if err != nil || (v.State == delivery.CompositionPass) != row.pass || named != row.pass { + t.Errorf("%+v: verdict %+v, %v", row.m, v, err) + } + } // Review finding 3 (ADR 0593 §9 lets only a node that does not answer pass): a composer at the head that cannot // run fails the composition, naming the head, never passing as a silent node. sb.err = errors.New("the build of mesh-compose failed")