Batteries-included agent-harness base, extracted from mort's agent layer. This first cut establishes the module + the zero-coupling core primitives: - lane, dispatchguard, pendingattach, run/progress.go: moved verbatim from mort - config: host config Source seam + env-var default (nil-safe helpers) - deliver: output-egress seam + Discard/Stdout defaults - identity: AdminPolicy + MemberResolver seams (nil-safe) - fanout: programmatic N×M swarm (bounded global + per-key concurrency) - README/CLAUDE.md with the vibe-coded banner; CI with Go gates + the "core stays majordomo+stdlib only" invariant Core builds with stdlib only today; majordomo enters at P1 (model/structured). go build/vet/test -race all green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,113 @@
|
||||
package lane
|
||||
|
||||
import (
|
||||
"context"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
)
|
||||
|
||||
// recordingSink captures every RecordLaneSample call.
|
||||
type recordingSink struct {
|
||||
mu sync.Mutex
|
||||
samples []sampleRow
|
||||
}
|
||||
|
||||
type sampleRow struct {
|
||||
lane string
|
||||
running int
|
||||
queued int
|
||||
sampledAt time.Time
|
||||
}
|
||||
|
||||
func (r *recordingSink) RecordLaneSample(_ context.Context, lane string, running, queued int, sampledAt time.Time) error {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.samples = append(r.samples, sampleRow{lane, running, queued, sampledAt})
|
||||
return nil
|
||||
}
|
||||
|
||||
// recordingPurger captures PurgeLaneSamples cutoffs.
|
||||
type recordingPurger struct {
|
||||
mu sync.Mutex
|
||||
cutoffs []time.Time
|
||||
}
|
||||
|
||||
func (r *recordingPurger) PurgeLaneSamples(_ context.Context, olderThan time.Time) (int64, error) {
|
||||
r.mu.Lock()
|
||||
defer r.mu.Unlock()
|
||||
r.cutoffs = append(r.cutoffs, olderThan)
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func TestSampler_SamplesAllLanes(t *testing.T) {
|
||||
reg := NewRegistry(nil)
|
||||
reg.GetOrCreate(context.Background(), "ollama")
|
||||
reg.GetOrCreate(context.Background(), "anthropic-default")
|
||||
|
||||
sink := &recordingSink{}
|
||||
now := time.Date(2026, 5, 4, 12, 0, 0, 0, time.UTC)
|
||||
clock := func() time.Time { return now }
|
||||
s := NewSampler(reg, sink, nil, 30*time.Second, 7*24*time.Hour, clock)
|
||||
|
||||
s.Sample(context.Background())
|
||||
|
||||
sink.mu.Lock()
|
||||
defer sink.mu.Unlock()
|
||||
if len(sink.samples) != 2 {
|
||||
t.Fatalf("expected 2 samples (one per lane), got %d", len(sink.samples))
|
||||
}
|
||||
seen := map[string]bool{}
|
||||
for _, sm := range sink.samples {
|
||||
seen[sm.lane] = true
|
||||
if !sm.sampledAt.Equal(now) {
|
||||
t.Errorf("expected sampledAt=%v, got %v", now, sm.sampledAt)
|
||||
}
|
||||
}
|
||||
if !seen["ollama"] || !seen["anthropic-default"] {
|
||||
t.Fatalf("missing lane: %+v", seen)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSampler_PurgeOnceUsesRetentionWindow(t *testing.T) {
|
||||
reg := NewRegistry(nil)
|
||||
purger := &recordingPurger{}
|
||||
now := time.Date(2026, 5, 4, 12, 0, 0, 0, time.UTC)
|
||||
clock := func() time.Time { return now }
|
||||
s := NewSampler(reg, nil, purger, 30*time.Second, 7*24*time.Hour, clock)
|
||||
|
||||
s.PurgeOnce(context.Background())
|
||||
|
||||
purger.mu.Lock()
|
||||
defer purger.mu.Unlock()
|
||||
if len(purger.cutoffs) != 1 {
|
||||
t.Fatalf("expected 1 purge call, got %d", len(purger.cutoffs))
|
||||
}
|
||||
want := now.Add(-7 * 24 * time.Hour)
|
||||
if !purger.cutoffs[0].Equal(want) {
|
||||
t.Fatalf("cutoff: want %v, got %v", want, purger.cutoffs[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestSampler_NilSinkOrRegistryIsSafe(t *testing.T) {
|
||||
// nil registry — no-op, no panic.
|
||||
s := NewSampler(nil, &recordingSink{}, nil, 30*time.Second, 7*24*time.Hour, nil)
|
||||
s.Sample(context.Background())
|
||||
|
||||
// nil sink — no-op.
|
||||
reg := NewRegistry(nil)
|
||||
reg.GetOrCreate(context.Background(), "ollama")
|
||||
s2 := NewSampler(reg, nil, nil, 30*time.Second, 7*24*time.Hour, nil)
|
||||
s2.Sample(context.Background())
|
||||
}
|
||||
|
||||
func TestSampler_StartStopIdempotent(t *testing.T) {
|
||||
reg := NewRegistry(nil)
|
||||
sink := &recordingSink{}
|
||||
s := NewSampler(reg, sink, nil, 30*time.Second, 7*24*time.Hour, nil)
|
||||
ctx := context.Background()
|
||||
s.Start(ctx)
|
||||
s.Start(ctx) // second Start is a no-op
|
||||
s.Stop()
|
||||
s.Stop() // second Stop is a no-op
|
||||
}
|
||||
Reference in New Issue
Block a user