Recovery of photos and link2pay on novox, 2026-10-29: their composition and grant were lost and no tool wrote a done key again. Quick fix: rewrite the done key unchanged as a new revision. Thorough fix: ADR 0547, the node agent composes its own and the assigner stops waiting on controller compositions (design 47).
50 lines
1.9 KiB
Go
50 lines
1.9 KiB
Go
package build
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"strings"
|
|
"testing"
|
|
|
|
"git.novox.be/novox/mesh-sdk/go/delivery"
|
|
)
|
|
|
|
func TestAnnounceAgainRewritesOnlyADoneKeyUnchanged(t *testing.T) { // recovery of photos and link2pay, 2026-10-29
|
|
for _, c := range []struct {
|
|
name string
|
|
key *delivery.BuilderBuild
|
|
fp string
|
|
refuse string
|
|
}{
|
|
{name: "a done key is written again, unchanged, as a new revision", key: &delivery.BuilderBuild{State: delivery.BuildDone, Step: "built"}},
|
|
{name: "a failed key is refused, builder_build_again is for it", refuse: "builder_build_again", key: &delivery.BuilderBuild{State: delivery.BuildFailed, Error: "x"}},
|
|
{name: "a building key is refused, builder_ask_again is for it", refuse: "builder_ask_again", key: &delivery.BuilderBuild{State: delivery.BuildBuilding, By: "novox.builder", Step: "asked"}},
|
|
{name: "a key with no build is refused", refuse: "has no build key"},
|
|
{name: "a fingerprint that is not one is refused", refuse: "fingerprint", fp: "nope"},
|
|
} {
|
|
t.Run(c.name, func(t *testing.T) {
|
|
r := newRig(t, goBundle)
|
|
var rev uint64
|
|
if c.key != nil {
|
|
c.key.Manifest = manifestD
|
|
rev = putBuild(t, r.st, r.key, *c.key)
|
|
}
|
|
before := bytes.Clone(r.st.vals[r.key])
|
|
fp := fp1
|
|
if c.fp != "" {
|
|
fp = c.fp
|
|
}
|
|
res := r.b.AnnounceAgain(context.Background(), "m", fp, "grant lost", "shanks.lead")
|
|
if c.refuse != "" {
|
|
if !strings.Contains(res.Refused, c.refuse) || res.Revision != 0 || r.st.revs[r.key] != rev || !bytes.Equal(r.st.vals[r.key], before) {
|
|
t.Fatalf("answer %+v, key at revision %d (was %d); want refused with %q and nothing written", res, r.st.revs[r.key], rev, c.refuse)
|
|
}
|
|
return
|
|
}
|
|
if res.Refused != "" || res.Revision <= rev || r.st.revs[r.key] != res.Revision || !bytes.Equal(r.st.vals[r.key], before) {
|
|
t.Fatalf("answer %+v, key at revision %d (was %d); want a new revision and the same bytes", res, r.st.revs[r.key], rev)
|
|
}
|
|
})
|
|
}
|
|
}
|