Take the runtime as a culprit only where its stub composes, so node-tools is never held for an error that names it (issue 774)
The culprit rule held a module whose stub made the composition fail otherwise. The runtime's stub carries no bundle, so it always fails otherwise: any refusal naming node-tools (or a module whose name holds it) held the runtime, and the node was then refused whole for the runtime's "0 bundle(s)". Quick fix: never take node-tools as a culprit. Thorough fix: blames, the rule as one function, takes the runtime only where its stub composes. Chose thorough: a runtime whose stub composes may still be held alone.
This commit is contained in:
@@ -373,6 +373,16 @@ func composeEach(in compose.Input, node string, fps map[string]string, applied b
|
||||
return out, skipped, stood, nil
|
||||
}
|
||||
|
||||
// blames says the composition with m's stub (again) makes m the culprit of err: it composes, or fails otherwise. The
|
||||
// runtime's stub is never a runtime (no build is taken in, so it carries no bundle) and always fails otherwise: it is
|
||||
// the culprit only where its stub composes (issue 774, 2026-10-30 19:30: node-tools held for any error naming it).
|
||||
func blames(m string, again, err error) bool {
|
||||
if m == catalogue.RuntimeModule {
|
||||
return again == nil
|
||||
}
|
||||
return again == nil || again.Error() != err.Error()
|
||||
}
|
||||
|
||||
// leaveAll leaves out the whole of a held module's offer and its own secrets (a bare stub); any other word leaves out
|
||||
// what it contributes to that provision alone.
|
||||
const leaveAll = "*"
|
||||
@@ -419,7 +429,7 @@ func culprit(in compose.Input, node string, fps map[string]string, mode compose.
|
||||
}
|
||||
changes := func(m, leave string) bool {
|
||||
_, _, _, again := compose.CompositionsIn(stubbed(in, node, m, leave), node, fps, mode)
|
||||
return again == nil || again.Error() != err.Error()
|
||||
return blames(m, again, err)
|
||||
}
|
||||
for _, m := range order {
|
||||
if _, done := failed[m]; done {
|
||||
|
||||
@@ -140,6 +140,7 @@ func TestInput(t *testing.T) {
|
||||
t.Run("no store reached says why", noStoreReachedSaysWhy)
|
||||
// Issue 774: a node refused whole says what it held on the way.
|
||||
t.Run("a node refused whole says what it held", aNodeRefusedWholeSaysWhatItHeld)
|
||||
t.Run("the runtime is blamed only where its stub composes", theRuntimeIsBlamedOnlyWhereItsStubComposes)
|
||||
t.Run("another node not resolved is named", otherNodeNotResolvedIsNamed)
|
||||
ctx := context.Background()
|
||||
xterm, err := os.ReadFile("../../catalogue/testdata/catalogue-835bf22b-xterm.module.json")
|
||||
|
||||
@@ -2,9 +2,11 @@ package frombus
|
||||
|
||||
import (
|
||||
"context"
|
||||
"errors"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
||||
"git.novox.be/novox/mesh-sdk/go/delivery"
|
||||
)
|
||||
|
||||
@@ -38,3 +40,24 @@ func aNodeRefusedWholeSaysWhatItHeld(t *testing.T) {
|
||||
t.Errorf("the node refused whole with %v (held %v), want lamp's refusal and power held before, with its why", err, held)
|
||||
}
|
||||
}
|
||||
|
||||
// Issue 774: the culprit rule takes a module whose stub composes or fails otherwise; the runtime only where its stub
|
||||
// composes, since its stub always fails otherwise.
|
||||
func theRuntimeIsBlamedOnlyWhereItsStubComposes(t *testing.T) {
|
||||
err := errors.New("lamp refuses")
|
||||
for _, row := range []struct {
|
||||
module string
|
||||
again error
|
||||
blamed bool
|
||||
}{
|
||||
{"lamp", nil, true},
|
||||
{"lamp", errors.New("other"), true},
|
||||
{"lamp", errors.New("lamp refuses"), false},
|
||||
{catalogue.RuntimeModule, nil, true},
|
||||
{catalogue.RuntimeModule, errors.New("node-tools is assigned to n1 and its build produced 0 bundle(s)"), false},
|
||||
} {
|
||||
if got := blames(row.module, row.again, err); got != row.blamed {
|
||||
t.Errorf("%s with %v: blamed %v, want %v", row.module, row.again, got, row.blamed)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user