diff --git a/internal/driver/hermes/driver.go b/internal/driver/hermes/driver.go index f8dae23..8c40d44 100644 --- a/internal/driver/hermes/driver.go +++ b/internal/driver/hermes/driver.go @@ -61,7 +61,14 @@ func (d *Driver) Validate(rc *driver.ResolvedClaw) error { } } if supported == 0 { - return fmt.Errorf("hermes driver: no supported HANDLE platforms enabled (add at least one of: %s)", strings.Join(supportedPlatforms, ", ")) + if len(rc.Invocations) == 0 { + return fmt.Errorf("hermes driver: no supported HANDLE platforms enabled (add at least one of: %s, or configure a local-only INVOKE)", strings.Join(supportedPlatforms, ", ")) + } + for i, inv := range rc.Invocations { + if strings.TrimSpace(inv.To) != "" { + return fmt.Errorf("hermes driver: handle-less INVOKE %d cannot route to %q; omit to for local-only delivery", i+1, inv.To) + } + } } for i, inv := range rc.Invocations { diff --git a/internal/driver/hermes/driver_test.go b/internal/driver/hermes/driver_test.go index 20440c2..ab018f9 100644 --- a/internal/driver/hermes/driver_test.go +++ b/internal/driver/hermes/driver_test.go @@ -70,6 +70,30 @@ func TestValidateRequiresSupportedHandle(t *testing.T) { } } +func TestValidateAllowsScheduledLocalOnlyServiceWithoutHandle(t *testing.T) { + rc, _ := newTestRC(t) + rc.Handles = map[string]*driver.HandleInfo{} + rc.Invocations = []driver.Invocation{{Schedule: "0 12 * * 1-5", Message: "Run local task"}} + + if err := (&Driver{}).Validate(rc); err != nil { + t.Fatalf("expected local-only scheduled service to validate: %v", err) + } +} + +func TestValidateRejectsHandlelessRoutedInvocation(t *testing.T) { + rc, _ := newTestRC(t) + rc.Handles = map[string]*driver.HandleInfo{} + rc.Invocations = []driver.Invocation{{Schedule: "0 12 * * 1-5", Message: "Route task", To: "alerts"}} + + err := (&Driver{}).Validate(rc) + if err == nil { + t.Fatal("expected routed invocation without a handle to fail") + } + if !strings.Contains(err.Error(), "handle-less INVOKE") { + t.Fatalf("unexpected error: %v", err) + } +} + func TestValidateAcceptsComposeEnvTokenReference(t *testing.T) { t.Setenv("ALLEN_BOT_TOKEN", "") diff --git a/internal/driver/hermes/jobs_test.go b/internal/driver/hermes/jobs_test.go index bd86a4f..64e58c2 100644 --- a/internal/driver/hermes/jobs_test.go +++ b/internal/driver/hermes/jobs_test.go @@ -13,9 +13,7 @@ func TestGenerateJobsJSONUsesWrapperAndLocalDelivery(t *testing.T) { Invocations: []driver.Invocation{ {Schedule: "*/5 * * * *", Message: "Check status", Name: "status"}, }, - Handles: map[string]*driver.HandleInfo{ - "discord": {}, - }, + Handles: map[string]*driver.HandleInfo{}, } data, err := GenerateJobsJSON(rc)