composer: a request carries its composition input as raw JSON, so the shape stays in mesh-sdk while the composer moves to the node-engine (ADR 0593 §7)
Claude-Session: https://claude.ai/code/session_01NcbgfJ9Gum47zkFKWRaovL
This commit is contained in:
@@ -46,7 +46,7 @@ func meshOf() compose.Input {
|
||||
|
||||
func request(t *testing.T, depth string) composer.Request {
|
||||
t.Helper()
|
||||
return composer.Request{V: composer.Version, Input: meshOf(), Depth: depth, HoldingsFromClaims: true,
|
||||
return composer.Request{V: composer.Version, Input: rawInput(meshOf()), Depth: depth, HoldingsFromClaims: true,
|
||||
Machines: []string{"anchor", "laptop", "ace"},
|
||||
Cases: []composer.Case{{Name: "trunk", Manifests: captured(t)}}}
|
||||
}
|
||||
@@ -180,7 +180,7 @@ func TestACatalogueCheckoutIsComposedForANode(t *testing.T) {
|
||||
if err := os.WriteFile(filepath.Join(dir, "modules", "demo", "module.json"), []byte(manifest), 0o644); err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
in := encode(t, composer.Request{V: composer.Version, Input: compose.Input{Nodes: []compose.Node{{Name: "laptop", Assigned: []string{"demo"}}}}})
|
||||
in := encode(t, composer.Request{V: composer.Version, Input: rawInput(compose.Input{Nodes: []compose.Node{{Name: "laptop", Assigned: []string{"demo"}}}})})
|
||||
code, v, stderr := runJSON(t, []string{"-catalogue", dir, "-node", "laptop", "-declarations"}, in)
|
||||
if code != 0 {
|
||||
t.Fatalf("exit %d: %s", code, stderr)
|
||||
@@ -318,3 +318,12 @@ func TestTheCommandRunsWithNoNetwork(t *testing.T) {
|
||||
sort.Strings(names)
|
||||
t.Logf("composed with no network: %v", names)
|
||||
}
|
||||
|
||||
// rawInput is a composition input as a request carries it.
|
||||
func rawInput(in compose.Input) json.RawMessage {
|
||||
raw, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
+10
-3
@@ -44,8 +44,9 @@ const (
|
||||
type Request struct {
|
||||
V int `json:"v"`
|
||||
// Input is the composition's input without its catalogue (and without each node's kept manifests): the cases give
|
||||
// those. A Catalogue or Kept it holds is ignored.
|
||||
Input compose.Input `json:"input"`
|
||||
// those. A Catalogue or Kept it holds is ignored. It is the node-engine's composition input as JSON, which only the
|
||||
// composer decodes (novox/hq ADR 0593 §7), so this shape imports no composition.
|
||||
Input json.RawMessage `json:"input"`
|
||||
// Cases are composed one after the other over Input, each with its own manifests; their names are unique.
|
||||
Cases []Case `json:"cases"`
|
||||
// Machines are the nodes composed in every case; none means the manifests are only read.
|
||||
@@ -267,7 +268,13 @@ func runCase(r Request, c Case, depth string) (CaseVerdict, error) {
|
||||
if err != nil {
|
||||
return CaseVerdict{}, err
|
||||
}
|
||||
in, err := frombus.Placed(r.Input, c.Placements)
|
||||
var input compose.Input
|
||||
if len(r.Input) > 0 {
|
||||
if err := json.Unmarshal(r.Input, &input); err != nil {
|
||||
return CaseVerdict{}, fmt.Errorf("composer: the request's input is not a composition input: %w", err)
|
||||
}
|
||||
}
|
||||
in, err := frombus.Placed(input, c.Placements)
|
||||
if err != nil {
|
||||
return CaseVerdict{}, fmt.Errorf("composer: the case %q: %w", c.Name, err)
|
||||
}
|
||||
|
||||
@@ -64,7 +64,7 @@ func TestTheInputTravelsAsJSON(t *testing.T) {
|
||||
Holdings: []catalogue.Held{{Claim: "mesh-broker", Scope: "mesh", Node: "anchor", Module: "nats"}},
|
||||
Issued: []compose.Issued{{Provision: "bus", Consumer: "laptop", ConsumerModule: "x", Provider: "anchor"}},
|
||||
}
|
||||
raw, err := json.Marshal(Request{V: Version, Input: in})
|
||||
raw, err := json.Marshal(Request{V: Version, Input: rawInput(in)})
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
@@ -72,8 +72,9 @@ func TestTheInputTravelsAsJSON(t *testing.T) {
|
||||
if err != nil {
|
||||
t.Fatal(err)
|
||||
}
|
||||
if !reflect.DeepEqual(back.Input, in) {
|
||||
t.Fatalf("the input came back as\n%+v\nwant\n%+v", back.Input, in)
|
||||
var got compose.Input
|
||||
if err := json.Unmarshal(back.Input, &got); err != nil || !reflect.DeepEqual(got, in) {
|
||||
t.Fatalf("the input came back as\n%+v (%v)\nwant\n%+v", got, err, in)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -102,7 +103,7 @@ func TestARequestItCannotRunIsRefused(t *testing.T) {
|
||||
t.Error("an unknown depth runs")
|
||||
}
|
||||
// A case's own placement (issue 1126) composes over a node the input holds, and is refused for one it does not.
|
||||
placed := Request{V: Version, Input: compose.Input{Nodes: []compose.Node{{Name: "a"}}}, Machines: []string{"a"}, Depth: DepthResolve,
|
||||
placed := Request{V: Version, Input: rawInput(compose.Input{Nodes: []compose.Node{{Name: "a"}}}), Machines: []string{"a"}, Depth: DepthResolve,
|
||||
Cases: []Case{{Name: "trunk", Manifests: map[string]Manifest{"demo": {Raw: json.RawMessage(demo)}}},
|
||||
{Name: "change", Manifests: map[string]Manifest{"demo": {Raw: json.RawMessage(demo)}},
|
||||
Placements: map[string]*delivery.Placement{"a": {Node: "a", Modules: []delivery.PlacedModule{{Module: "ghost"}}}}}}}
|
||||
@@ -152,3 +153,12 @@ func TestTheComposerTakesTheVerdictsOfItsInput(t *testing.T) {
|
||||
t.Fatalf("xterm refused by its verdict: %+v, want refused in the verdict's words", got)
|
||||
}
|
||||
}
|
||||
|
||||
// rawInput is a composition input as a request carries it.
|
||||
func rawInput(in compose.Input) json.RawMessage {
|
||||
raw, err := json.Marshal(in)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return raw
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user