package buildstep import ( "encoding/json" "sync" "testing" "time" "git.novox.be/novox/mesh-sdk/go/delivery" ) // 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][]delivery.Step ended chan string } func newKeys() *keys { return &keys{puts: map[string][]delivery.Step{}, ended: make(chan string, 16)} } func (k *keys) put(key string, value any) (uint64, error) { step, err := delivery.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 != delivery.StepRunning { k.ended <- key } return n, nil } func (k *keys) last(key string) delivery.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 := delivery.StepKey("ace", id); err != nil { t.Errorf("id %q: %v", id, err) } } }