<asker>.<node>.<id> and main's <node>.<id> (no asker); a third form is refused. sdk/buildrun follows. Builds: design 47 G §484, §649 Claude-Session: https://claude.ai/code/session_01JG73KEq4zoDfM9WtYpcQgZ
90 lines
2.3 KiB
Go
90 lines
2.3 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)
|
|
}
|
|
}
|
|
}
|