feat(process): validate runner dependencies

This commit is contained in:
Virgil 2026-04-04 07:37:50 +00:00
parent bc2cb6ae9d
commit 720104babc
2 changed files with 41 additions and 0 deletions

View file

@ -19,6 +19,9 @@ var ErrRunnerNoService = coreerr.E("", "runner service is nil", nil)
// ErrRunnerInvalidSpecName is returned when a RunSpec name is empty or duplicated.
var ErrRunnerInvalidSpecName = coreerr.E("", "runner spec names must be non-empty and unique", nil)
// ErrRunnerInvalidDependencyName is returned when a RunSpec dependency name is empty, duplicated, or self-referential.
var ErrRunnerInvalidDependencyName = coreerr.E("", "runner dependency names must be non-empty, unique, and not self-referential", nil)
// ErrRunnerContextRequired is returned when a runner method is called without a context.
var ErrRunnerContextRequired = coreerr.E("", "runner context is required", nil)
@ -399,6 +402,20 @@ func validateSpecs(specs []RunSpec) error {
return coreerr.E("Runner.validateSpecs", "runner spec name is duplicated", ErrRunnerInvalidSpecName)
}
seen[spec.Name] = struct{}{}
deps := make(map[string]struct{}, len(spec.After))
for _, dep := range spec.After {
if dep == "" {
return coreerr.E("Runner.validateSpecs", "runner dependency name is required", ErrRunnerInvalidDependencyName)
}
if dep == spec.Name {
return coreerr.E("Runner.validateSpecs", "runner dependency cannot reference itself", ErrRunnerInvalidDependencyName)
}
if _, ok := deps[dep]; ok {
return coreerr.E("Runner.validateSpecs", "runner dependency name is duplicated", ErrRunnerInvalidDependencyName)
}
deps[dep] = struct{}{}
}
}
return nil
}

View file

@ -327,6 +327,30 @@ func TestRunner_InvalidSpecNames(t *testing.T) {
assert.ErrorIs(t, err, ErrRunnerInvalidSpecName)
})
t.Run("rejects empty dependency names", func(t *testing.T) {
_, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "one", Command: "echo", Args: []string{"a"}, After: []string{""}},
})
require.Error(t, err)
assert.ErrorIs(t, err, ErrRunnerInvalidDependencyName)
})
t.Run("rejects duplicated dependency names", func(t *testing.T) {
_, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "one", Command: "echo", Args: []string{"a"}, After: []string{"two", "two"}},
})
require.Error(t, err)
assert.ErrorIs(t, err, ErrRunnerInvalidDependencyName)
})
t.Run("rejects self dependency", func(t *testing.T) {
_, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "one", Command: "echo", Args: []string{"a"}, After: []string{"one"}},
})
require.Error(t, err)
assert.ErrorIs(t, err, ErrRunnerInvalidDependencyName)
})
t.Run("rejects duplicate names", func(t *testing.T) {
_, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "same", Command: "echo", Args: []string{"a"}},