Files
executus/lane/sampler_test.go
T
steve ca243a2d50
executus CI / test (push) Failing after 24s
P0: stand up executus harness module above majordomo
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>
2026-06-26 19:18:37 -04:00

114 lines
3.0 KiB
Go

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
}