mesh-delivery-view passes a node's placement answer through as written, so its bodies reach readers (issue 1126, group fix/1126-shadow-bodies) #92
@@ -154,7 +154,10 @@ func TestModulesToolAnswersEveryKeyAsStored(t *testing.T) {
|
||||
return raw, revs[key], nil
|
||||
}}
|
||||
p.HavesState = fake{
|
||||
"ace.node": `{"v":1,"composed":"x","candidates":{"feat/p":{"head":"` + h2 + `","verdict":"same","same":["plex"]}}}`,
|
||||
// ace's answer as the node wrote it, with what this view's mesh-sdk may not know (issue 1126, probe #532: the
|
||||
// bodies of each difference were dropped on the way): shown as stored.
|
||||
"ace.node": `{"v":1,"composed":"x","candidates":{"feat/p":{"head":"` + h2 + `","verdict":"same","same":["plex"],` +
|
||||
`"differs":[],"note":"took over a minute","future":{"bodies":[{"id":"x","composed":"<masked>"}]}}}}`,
|
||||
"g14.node": `{"v":1,"composed":"x","candidates":{"feat/p":{"head":"` + h1 + `","revision":2,"verdict":"same"}}}`,
|
||||
}.reader().State
|
||||
out, err = run(t, p, view.PlacementReadTool, map[string]any{})
|
||||
@@ -164,6 +167,9 @@ func TestModulesToolAnswersEveryKeyAsStored(t *testing.T) {
|
||||
answered := map[string]string{}
|
||||
for _, c := range out.(map[string]any)["candidates"].([]view.CandidateRow) {
|
||||
answered[c.Node] = c.Answered
|
||||
if c.Node == "ace" && (!strings.Contains(string(c.Answer), `"future":{"bodies"`) || !strings.Contains(string(c.Answer), `"note":"took over a minute"`)) {
|
||||
t.Errorf("ace's answer is not shown as stored: %s", c.Answer)
|
||||
}
|
||||
}
|
||||
if want := map[string]string{"ace": "same", "g14": view.AnswerStale, "novox": view.AnswerNone}; fmt.Sprint(answered) != fmt.Sprint(want) {
|
||||
t.Errorf("answers %v, want %v", answered, want)
|
||||
|
||||
@@ -3,7 +3,7 @@ module mesh-delivery-view
|
||||
go 1.26.0
|
||||
|
||||
require (
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027193820-8ad9372b1952
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028003907-362a3f1378aa
|
||||
github.com/nats-io/nats-server/v2 v2.11.17
|
||||
github.com/nats-io/nats.go v1.54.0
|
||||
)
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027193820-8ad9372b1952 h1:TmveT3Bija643RGWkilvkzP/h8UWlVvHFZZtQfJK5oU=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261027193820-8ad9372b1952/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028003907-362a3f1378aa h1:DFYX7pIluzeeEyM+YXGeB6GvEnOYyRhz/1Ke/LFa8+8=
|
||||
git.novox.be/novox/mesh-sdk/go v0.1.14-0.20261028003907-362a3f1378aa/go.mod h1:ouERNpZizzwZ0HLEtAypsY9FR20FyUgUYRni6J7kVCY=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op h1:Z/MZK75wC/NSrkgqeNIa7jexam9uWzhLmFTSCPI/kn0=
|
||||
github.com/antithesishq/antithesis-sdk-go v0.7.0-default-no-op/go.mod h1:FQyySiasQQM8735Ddel3MRojmy4dA1IqCeyJ5jmPMbI=
|
||||
github.com/google/go-tpm v0.9.8 h1:slArAR9Ft+1ybZu0lBwpSmpwhRXaa85hWtMinMyRAWo=
|
||||
|
||||
@@ -726,14 +726,16 @@ const (
|
||||
|
||||
// CandidateRow is one placement candidate and the node's answer to it.
|
||||
type CandidateRow struct {
|
||||
Group string `json:"group"`
|
||||
Node string `json:"node"`
|
||||
Pull delivery.Head `json:"pull"`
|
||||
Revision uint64 `json:"revision"`
|
||||
Placement json.RawMessage `json:"placement,omitempty"` // the candidate value as stored
|
||||
Removes bool `json:"removes,omitempty"` // the head removes the node's file
|
||||
Answered string `json:"answered"` // same, differs, refused, stale or none
|
||||
Answer *delivery.CandidateAnswer `json:"answer,omitempty"`
|
||||
Group string `json:"group"`
|
||||
Node string `json:"node"`
|
||||
Pull delivery.Head `json:"pull"`
|
||||
Revision uint64 `json:"revision"`
|
||||
Placement json.RawMessage `json:"placement,omitempty"` // the candidate value as stored
|
||||
Removes bool `json:"removes,omitempty"` // the head removes the node's file
|
||||
Answered string `json:"answered"` // same, differs, refused, stale or none
|
||||
// Answer is the node's answer as its node wrote it, whole: what this view's mesh-sdk does not know rides through (issue
|
||||
// 1126, probe #532: each difference's bodies were dropped when the answer was decoded and encoded again here).
|
||||
Answer json.RawMessage `json:"answer,omitempty"`
|
||||
}
|
||||
|
||||
// PlacementCandidates is every placement candidate, narrowed by node and by a module its placement names, with each
|
||||
@@ -773,8 +775,8 @@ func (r Reader) PlacementCandidates(node, module string) ([]CandidateRow, error)
|
||||
placed map[string]bool
|
||||
}
|
||||
groups := map[string]*group{}
|
||||
answers := map[string]map[string]delivery.CandidateAnswer{} // node, then group
|
||||
answerOf := func(n, g string) (delivery.CandidateAnswer, bool) {
|
||||
answers := map[string]map[string]storedAnswer{} // node, then group
|
||||
answerOf := func(n, g string) (storedAnswer, bool) {
|
||||
if _, read := answers[n]; !read {
|
||||
answers[n] = r.candidateAnswers(n)
|
||||
}
|
||||
@@ -818,8 +820,8 @@ func (r Reader) PlacementCandidates(node, module string) ([]CandidateRow, error)
|
||||
continue
|
||||
}
|
||||
if a, ok := answerOf(n, gname); ok && err == nil {
|
||||
row.Answer = &a
|
||||
row.Answered = AnswerOf(c.Pull.Head, rev, true, a)
|
||||
row.Answer = a.raw
|
||||
row.Answered = AnswerOf(c.Pull.Head, rev, true, a.a)
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
@@ -841,8 +843,8 @@ func (r Reader) PlacementCandidates(node, module string) ([]CandidateRow, error)
|
||||
}
|
||||
row := CandidateRow{Group: gname, Node: n, Pull: g.pull, Answered: AnswerNone}
|
||||
if a, ok := answerOf(n, gname); ok {
|
||||
row.Answer = &a
|
||||
row.Answered = AnswerOf(g.pull.Head, 0, false, a)
|
||||
row.Answer = a.raw
|
||||
row.Answered = AnswerOf(g.pull.Head, 0, false, a.a)
|
||||
}
|
||||
rows = append(rows, row)
|
||||
}
|
||||
@@ -859,8 +861,15 @@ func AnswerOf(head string, rev uint64, keyed bool, a delivery.CandidateAnswer) s
|
||||
return a.Verdict
|
||||
}
|
||||
|
||||
// candidateAnswers is a node's answers to its candidates, by group, from its `<node>.node` have; none when it has none.
|
||||
func (r Reader) candidateAnswers(node string) map[string]delivery.CandidateAnswer {
|
||||
// storedAnswer is a node's answer to one group: as stored, and read for its head, revision and verdict.
|
||||
type storedAnswer struct {
|
||||
raw json.RawMessage
|
||||
a delivery.CandidateAnswer
|
||||
}
|
||||
|
||||
// candidateAnswers is a node's answers to its candidates, by group, from its `<node>.node` have, each as stored; none
|
||||
// when it has none.
|
||||
func (r Reader) candidateAnswers(node string) map[string]storedAnswer {
|
||||
if r.HavesState.Get == nil {
|
||||
return nil
|
||||
}
|
||||
@@ -872,9 +881,21 @@ func (r Reader) candidateAnswers(node string) map[string]delivery.CandidateAnswe
|
||||
if err != nil || len(raw) == 0 {
|
||||
return nil
|
||||
}
|
||||
h, err := delivery.DecodeHaveNode(raw)
|
||||
if err != nil {
|
||||
if _, err := delivery.DecodeHaveNode(raw); err != nil {
|
||||
return nil
|
||||
}
|
||||
return h.Candidates
|
||||
var stored struct {
|
||||
Candidates map[string]json.RawMessage `json:"candidates"`
|
||||
}
|
||||
if json.Unmarshal(raw, &stored) != nil {
|
||||
return nil
|
||||
}
|
||||
out := make(map[string]storedAnswer, len(stored.Candidates))
|
||||
for g, ans := range stored.Candidates {
|
||||
var a delivery.CandidateAnswer
|
||||
if json.Unmarshal(ans, &a) == nil {
|
||||
out[g] = storedAnswer{raw: ans, a: a}
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user