Files
mesh-delivery/modules/mesh-delivery-sdk/buildstep/step_test.go
T
jschoubben 8af822e3fd SDK (A-324, A-325): one PackedOrFetched for the seed and the runner
The rule of an artifact the builder packs or fetches itself was written twice,
in the indexer's seed and in the runner's placing; both now read the one in
mesh-delivery-sdk's buildstep, the contract of a build step every delivery
part reads.

Builds: design 47 G A-324, A-325

Claude-Session: https://claude.ai/code/session_01NcbgfGKaxw984XyAPfLm4h
2026-11-13 02:33:38 +01:00

104 lines
2.9 KiB
Go

package buildstep
import (
"encoding/json"
"sync"
"testing"
"time"
)
// keys is a steps state in memory: every value put, in order, per key, and a signal for each end.
type keys struct {
mu sync.Mutex
puts map[string][]Step
ended chan string
}
func newKeys() *keys { return &keys{puts: map[string][]Step{}, ended: make(chan string, 16)} }
func (k *keys) put(key string, value any) (uint64, error) {
step, err := DecodeStep(value.(json.RawMessage))
if err != nil {
return 0, err
}
k.mu.Lock()
k.puts[key] = append(k.puts[key], step)
n := uint64(len(k.puts[key]))
k.mu.Unlock()
if step.State != StepRunning {
k.ended <- key
}
return n, nil
}
func (k *keys) last(key string) Step {
k.mu.Lock()
defer k.mu.Unlock()
s := k.puts[key]
return s[len(s)-1]
}
func (k *keys) waitEnd(t *testing.T) string {
t.Helper()
select {
case key := <-k.ended:
return key
case <-time.After(3 * time.Second):
t.Fatal("the step never ended on its key")
return ""
}
}
// 1471/AC5: ids made in one and the same millisecond differ, and each is one key token. The clock is fixed
// while no step runs, so no goroutine reads it meanwhile.
func TestTwoIDsInOneMillisecondDiffer(t *testing.T) {
was := now
defer func() { now = was }()
at := time.Date(2026, 10, 20, 12, 0, 0, 0, time.UTC)
now = func() time.Time { return at }
seen := map[string]bool{}
for i := 0; i < 50; i++ {
id := NewID()
if seen[id] {
t.Fatalf("id %q handed out twice", id)
}
seen[id] = true
if _, err := StepKey("ace", id); err != nil {
t.Errorf("id %q: %v", id, err)
}
}
}
// 1966 (G §484, §649): ParseStepKey reads both forms of a key, main's answering no asker, and refuses a third.
func TestParseStepKeyReadsBothFormsAndRefusesAThird(t *testing.T) {
for _, tc := range []struct{ key, asker, node, id string }{
{"ace.abc123", "", "ace", "abc123"},
{"runner.ace.abc123", "runner", "ace", "abc123"},
{"builder.g14.x1", "builder", "g14", "x1"},
} {
asker, node, id, err := ParseStepKey(tc.key)
if err != nil || asker != tc.asker || node != tc.node || id != tc.id {
t.Errorf("%s: %q %q %q %v", tc.key, asker, node, id, err)
}
}
for _, key := range []string{"ace", "a.b.c.d", "Runner.ace.abc123", "runner.ace.ABC", ".ace.abc123", "ace."} {
if _, _, _, err := ParseStepKey(key); err == nil {
t.Errorf("%q was read as a step key", key)
}
}
}
// Design 47 G A-324, A-325: only an artifact of kind archive or upstream with no language is packed or fetched by the builder.
func TestPackedOrFetchedIsAnArchiveOrUpstreamWithNoLanguage(t *testing.T) {
for _, c := range []struct {
language, kind string
want bool
}{
{"", "archive", true}, {"", "upstream", true}, {"go", "archive", false}, {"", "image", false}, {"go", "", false}, {"", "", false},
} {
if got := PackedOrFetched(c.language, c.kind); got != c.want {
t.Errorf("PackedOrFetched(%q, %q) = %v, want %v", c.language, c.kind, got, c.want)
}
}
}