Files
mesh-catalog/modules/distribution/cmd/distribution-tools/publishing_test.go
T
jschoubben e34fcbeda3 CAT-D5: the publishing builds are written with compare-and-set on the key's revision, so two first publishes at once both count
A blind write after a read could lose the build of the publish that wrote first until a bundle restart learned it again.

Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig
2026-11-04 13:16:55 +01:00

67 lines
2.1 KiB
Go

package main
import (
"encoding/json"
"reflect"
"testing"
stdio "git.novox.be/novox/mesh-sdk/go"
"distribution/internal/holder"
)
// casMem is a kept state with revisions; before writes it lets another writer write first, once.
type casMem struct {
value []byte
rev uint64
before func()
}
func (m *casMem) Get(string) (*stdio.StateEntry, error) {
if m.value == nil {
return nil, nil
}
return &stdio.StateEntry{Value: m.value, Revision: m.rev}, nil
}
func (m *casMem) write(v any, at uint64) (uint64, error) {
if f := m.before; f != nil {
m.before = nil
f()
}
if m.rev != at {
return 0, stdio.ErrChanged
}
m.value, _ = json.Marshal(v)
m.rev++
return m.rev, nil
}
func (m *casMem) Create(_ string, v any) (uint64, error) { return m.write(v, 0) }
func (m *casMem) Update(_ string, v any, rev uint64) (uint64, error) { return m.write(v, rev) }
// TestTwoFirstPublishesAtOnceAreBothKept: the publishing builds are written with compare-and-set, so a writer that lost
// the race reads again and neither build is lost (A3f finding 2).
func TestTwoFirstPublishesAtOnceAreBothKept(t *testing.T) {
m := &casMem{}
m.before = func() {
// Another tools process learns mesh-build-go's build first.
m.value, _ = json.Marshal(holder.Publishing{Name: holder.PublishingKey, Modules: map[string][]string{"mesh-build-go": {"fp-go"}}})
m.rev = 1
}
if learnt, err := learnInto(m, "mesh-build-node", "fp-node", 3); err != nil || !learnt {
t.Fatalf("learnInto = %v, %v", learnt, err)
}
var got holder.Publishing
_ = json.Unmarshal(m.value, &got)
want := map[string][]string{"mesh-build-go": {"fp-go"}, "mesh-build-node": {"fp-node"}}
if !reflect.DeepEqual(got.Modules, want) {
t.Errorf("kept %v, want both builds %v", got.Modules, want)
}
if learnt, err := learnInto(m, "mesh-build-node", "fp-node", 3); err != nil || learnt {
t.Errorf("learning a kept build again = %v, %v, want not new and no write", learnt, err)
}
m.before = func() { m.rev++ }
if _, err := learnInto(m, "mesh-build-oci", "fp-oci", 1); err == nil {
t.Error("a write lost on every attempt answered no error")
}
}