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.
64 lines
2.9 KiB
Go
64 lines
2.9 KiB
Go
package frombus
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/catalogue"
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
// aNode is a bus with n1 the hub, each of manifests placed on it and held at the indexer, in the order given.
|
|
func aNode(t *testing.T, manifests ...string) (*bus, map[string]string) {
|
|
t.Helper()
|
|
p := delivery.Placement{Node: "n1", Decided: delivery.NodeDecided{Hub: true, Address: "10.77.0.1"}}
|
|
b := &bus{records: map[string]delivery.NodeRecord{"n1": {Node: "n1", TunnelKey: publicKey(t), SealingKey: "sk", Capabilities: []string{"systemd"}}},
|
|
modules: map[string]delivery.Module{}, manifests: map[string][]byte{}, builds: map[string]*delivery.BuilderBuild{}}
|
|
fps := map[string]string{}
|
|
for i, raw := range manifests {
|
|
module := strings.SplitN(strings.TrimPrefix(raw, `{"module":"`), `"`, 2)[0]
|
|
p.Modules = append(p.Modules, delivery.PlacedModule{Module: module})
|
|
b.modules[module] = delivery.Module{Manifest: delivery.ManifestDigest([]byte(raw))}
|
|
b.manifests[delivery.ManifestDigest([]byte(raw))] = []byte(raw)
|
|
fps[module] = "src2:" + strings.Repeat(string(rune('1'+i)), 64)
|
|
}
|
|
b.placements = map[string]delivery.Placement{"n1": p}
|
|
return b, fps
|
|
}
|
|
|
|
// Issue 774, 2026-10-30 19:30: a node refused whole after holding a module says what it held and why, since the last
|
|
// refusal can be of the held module's stub (node-tools held, then "its build produced 0 bundle(s)", its own why unsaid).
|
|
func aNodeRefusedWholeSaysWhatItHeld(t *testing.T) {
|
|
refused := `{"module":"power","version":"1","items":[{"collection":"contribution:node-bar:block","attributes":{"shows":"state"},"template":"files/a"}]}`
|
|
needs := `{"module":"lamp","version":"1","requires":["nothing-provides-this"]}`
|
|
b, fps := aNode(t, refused, needs)
|
|
_, _, held, err := Compose(context.Background(), b, Mesh{}, Scope{Node: "n1"}, fps)
|
|
if err == nil || !strings.Contains(err.Error(), "nothing provides") || !strings.Contains(err.Error(), `held before: power: `) ||
|
|
!strings.Contains(err.Error(), `"shows" is none of them`) {
|
|
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)
|
|
}
|
|
}
|
|
}
|