Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 47 additions & 35 deletions cmd/mecatui/client/schedule_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -222,42 +222,54 @@ func TestGetScheduleMapping(t *testing.T) {
}

func TestCreateScheduleBuildsProto(t *testing.T) {
spec := ScheduleSpec{
Name: "daily",
Prompt: "ship it",
Trigger: ScheduleTrigger{Cron: "*/5 * * * *"},
Mode: "plan",
Misfire: "skip",
MaxFires: 7,
Selector: ScheduleSelector{ProviderID: "anthropic"},
}
fake := &fakeScheduleClient{createResp: &mecatlv1.CreateScheduleResponse{Schedule: sampleProtoSchedule("daily")}}
cl := newScheduleFakeClient(fake)
for _, tc := range []struct {
mode string
mutating bool
want mecatlv1.PermissionMode
}{
{"plan", false, mecatlv1.PermissionMode_PERMISSION_MODE_PLAN},
{ModeDefaultString, true, mecatlv1.PermissionMode_PERMISSION_MODE_DEFAULT},
} {
t.Run(tc.mode, func(t *testing.T) {
spec := ScheduleSpec{
Name: "daily",
Prompt: "ship it",
Trigger: ScheduleTrigger{Cron: "*/5 * * * *"},
Mode: tc.mode,
Mutating: tc.mutating,
Misfire: "skip",
MaxFires: 7,
Selector: ScheduleSelector{ProviderID: "anthropic"},
}
fake := &fakeScheduleClient{createResp: &mecatlv1.CreateScheduleResponse{Schedule: sampleProtoSchedule("daily")}}
cl := newScheduleFakeClient(fake)

if _, err := cl.CreateSchedule(context.Background(), spec); err != nil {
t.Fatalf("CreateSchedule: %v", err)
}
if fake.lastCreate == nil || fake.lastCreate.GetSpec() == nil {
t.Fatal("create request not sent")
}
got := fake.lastCreate.GetSpec()
if got.GetName() != "daily" || got.GetPrompt() != "ship it" {
t.Fatalf("spec proto mismapped: %+v", got)
}
if got.GetTrigger().GetCron() != "*/5 * * * *" || got.GetTrigger().GetOneShot() != nil {
t.Fatalf("trigger proto mismapped: %+v", got.GetTrigger())
}
if got.GetMode() != mecatlv1.PermissionMode_PERMISSION_MODE_PLAN {
t.Fatalf("mode proto = %v, want PLAN", got.GetMode())
}
if got.GetMisfire() != mecatlv1.MisfirePolicy_MISFIRE_SKIP {
t.Fatalf("misfire proto = %v, want SKIP", got.GetMisfire())
}
if got.GetMaxFires() != 7 {
t.Fatalf("max_fires proto = %d, want 7", got.GetMaxFires())
}
if got.GetSelector().GetProviderId() != "anthropic" {
t.Fatalf("selector proto = %+v", got.GetSelector())
if _, err := cl.CreateSchedule(context.Background(), spec); err != nil {
t.Fatalf("CreateSchedule: %v", err)
}
if fake.lastCreate == nil || fake.lastCreate.GetSpec() == nil {
t.Fatal("create request not sent")
}
got := fake.lastCreate.GetSpec()
if got.GetName() != "daily" || got.GetPrompt() != "ship it" {
t.Fatalf("spec proto mismapped: %+v", got)
}
if got.GetTrigger().GetCron() != "*/5 * * * *" || got.GetTrigger().GetOneShot() != nil {
t.Fatalf("trigger proto mismapped: %+v", got.GetTrigger())
}
if got.GetMode() != tc.want || got.GetMutating() != tc.mutating {
t.Fatalf("mode/mutating proto = %v/%t, want %v/%t", got.GetMode(), got.GetMutating(), tc.want, tc.mutating)
}
if got.GetMisfire() != mecatlv1.MisfirePolicy_MISFIRE_SKIP {
t.Fatalf("misfire proto = %v, want SKIP", got.GetMisfire())
}
if got.GetMaxFires() != 7 {
t.Fatalf("max_fires proto = %d, want 7", got.GetMaxFires())
}
if got.GetSelector().GetProviderId() != "anthropic" {
t.Fatalf("selector proto = %+v", got.GetSelector())
}
})
}
}

Expand Down
2 changes: 1 addition & 1 deletion cmd/mecatui/ui/double_escape_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ func TestDoubleEscapeAllEscapeOwnersSuppressGesture(t *testing.T) {
{"connect", func(m Model) Model { m.connect.open = true; return m }},
{"effort", func(m Model) Model { m.caps.ModelSelection = true; mm, _ := m.runEffort(); return mm.(Model) }},
{"worktrees", func(m Model) Model { m.modal = &worktreesState{deps: (&m).surfaceDeps()}; return m }},
{"schedule", func(m Model) Model { m.schedule.view = schedulePanel; return m }},
{"schedule", func(m Model) Model { m.modal = &scheduleState{deps: (&m).surfaceDeps()}; return m }},
{"running turn", func(m Model) Model { m.phase = phaseRunning; return m }},
}
for _, tc := range owners {
Expand Down
12 changes: 7 additions & 5 deletions cmd/mecatui/ui/keymark_liveness_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -687,18 +687,20 @@ func TestScheduleOverlayHintsReflectKeyOverride(t *testing.T) {
hk := liveHK()
th := theme.New("aztec", theme.AztecPalette())
t.Run("panel", func(t *testing.T) {
st := scheduleState{view: schedulePanel, filtered: []client.Schedule{{Spec: client.ScheduleSpec{Name: "n1"}}}}
got := stripANSIstr(renderScheduleOverlay(th, st, client.Capabilities{}, false, hk, 100, 30))
if !strings.Contains(got, "ctrl+f17: inspect c: create p: pause r: resume f: fire-now d: delete /: filter ctrl+f16: close") {
st := scheduleState{deps: surfaceDeps{theme: th, keys: defaultKeys(), marks: hk}, view: schedulePanel}
body, _ := st.Render(200, 30)
got := stripANSIstr(body)
if !strings.Contains(got, "ctrl+f17: runs c: create p: pause r: resume f: fire-now d: delete /: filter ctrl+f16: close") {
t.Errorf("schedule panel hint should carry live inspect/close + literal c/p/r/f/d: %q", got)
}
if strings.Contains(got, "enter: inspect") || strings.Contains(got, "esc: close") {
t.Errorf("schedule panel hint still shows a default: %q", got)
}
})
t.Run("confirm", func(t *testing.T) {
st := scheduleState{view: scheduleConfirm, confirm: client.Schedule{Spec: client.ScheduleSpec{Name: "n1"}}}
got := stripANSIstr(renderScheduleOverlay(th, st, client.Capabilities{}, false, hk, 100, 30))
st := scheduleState{deps: surfaceDeps{theme: th, keys: defaultKeys(), marks: hk}, view: scheduleConfirm, confirm: client.Schedule{Spec: client.ScheduleSpec{Name: "n1"}}}
body, _ := st.Render(100, 30)
got := stripANSIstr(body)
if !strings.Contains(got, "ctrl+f17: delete ctrl+f16: back") {
t.Errorf("schedule confirm hint should carry live delete/back: %q", got)
}
Expand Down
1 change: 0 additions & 1 deletion cmd/mecatui/ui/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -684,7 +684,6 @@ type Model struct {
// overwrite root state or a reopened picker.
modelCatalogRequestToken uint64
modelCatalog modelCatalog // root-owned inventory, statuses, defaults, and selection reconciliation
schedule scheduleState // /schedule overlay state (view==scheduleNone when closed) — issue #234
connect connectState // /connect saved-target picker (tombstone overlay)
connectLoadGeneration uint64 // monotonic across overlay close/reopen; stale async loads are ignored
connectIntent *ConnectRestartIntent // set only immediately before tea.Quit
Expand Down
4 changes: 2 additions & 2 deletions cmd/mecatui/ui/prompt_input_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ func TestPromptMousePressRespectsBodyOwners(t *testing.T) {
name string
setup func(*Model)
}{
{"schedule", func(m *Model) { m.schedule.view = schedulePanel }},
{"schedule", func(m *Model) { m.modal = &scheduleState{deps: m.surfaceDeps()} }},
{"session details", func(m *Model) { m.sessionDetailsOpen = true }},
} {
t.Run(tc.name, func(t *testing.T) {
Expand Down Expand Up @@ -193,7 +193,7 @@ func TestSelectableMatchesAllBodyOwners(t *testing.T) {
{"dream", func(m *Model) { m.modal = &dreamState{view: dreamTargets} }, false},
{"effort", func(m *Model) { m.caps.ModelSelection = true; mm, _ := m.runEffort(); *m = mm.(Model) }, false},
{"worktrees", func(m *Model) { m.modal = &worktreesState{deps: m.surfaceDeps()} }, false},
{"schedule", func(m *Model) { m.schedule.view = schedulePanel }, false},
{"schedule", func(m *Model) { m.modal = &scheduleState{deps: m.surfaceDeps()} }, false},
} {
t.Run(tc.name, func(t *testing.T) {
got := m
Expand Down
Loading
Loading