go-process/runner_test.go
Claude 861c88b8e8
fix(ax): AX compliance sweep — banned imports, naming, test coverage
- pkg/api/provider.go: remove banned os/syscall imports; delegate to
  new process.KillPID and process.IsPIDAlive exported helpers
- service.go: rename `sr` → `startResult`; add KillPID/IsPIDAlive exports
- runner.go: rename `aggResult` → `aggregate` in all three RunXxx methods;
  add usage-example comments on all exported functions
- process.go: replace prose doc-comments with usage-example comments
- buffer.go, registry.go, health.go: replace prose comments with examples
- buffer_test.go: rename TestRingBuffer_Basics_Good → TestBuffer_{Write,String,Reset}_{Good,Bad,Ugly}
- All test files: add missing _Bad and _Ugly variants for all functions
  (daemon, health, pidfile, registry, runner, process, program, exec, pkg/api)

Co-Authored-By: Virgil <virgil@lethean.io>
2026-03-31 08:15:47 +01:00

268 lines
7.7 KiB
Go

package process
import (
"context"
"testing"
framework "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func newTestRunner(t *testing.T) *Runner {
t.Helper()
c := framework.New()
r := Register(c)
require.True(t, r.OK)
return NewRunner(r.Value.(*Service))
}
func TestRunner_RunSequential_Good(t *testing.T) {
t.Run("all pass", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunSequential(context.Background(), []RunSpec{
{Name: "first", Command: "echo", Args: []string{"1"}},
{Name: "second", Command: "echo", Args: []string{"2"}},
{Name: "third", Command: "echo", Args: []string{"3"}},
})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Equal(t, 3, result.Passed)
assert.Equal(t, 0, result.Failed)
assert.Equal(t, 0, result.Skipped)
})
t.Run("stops on failure", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunSequential(context.Background(), []RunSpec{
{Name: "first", Command: "echo", Args: []string{"1"}},
{Name: "fails", Command: "sh", Args: []string{"-c", "exit 1"}},
{Name: "third", Command: "echo", Args: []string{"3"}},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 1, result.Passed)
assert.Equal(t, 1, result.Failed)
assert.Equal(t, 1, result.Skipped)
})
t.Run("allow failure continues", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunSequential(context.Background(), []RunSpec{
{Name: "first", Command: "echo", Args: []string{"1"}},
{Name: "fails", Command: "sh", Args: []string{"-c", "exit 1"}, AllowFailure: true},
{Name: "third", Command: "echo", Args: []string{"3"}},
})
require.NoError(t, err)
// Still counts as failed but pipeline continues
assert.Equal(t, 2, result.Passed)
assert.Equal(t, 1, result.Failed)
assert.Equal(t, 0, result.Skipped)
})
}
func TestRunner_RunParallel_Good(t *testing.T) {
t.Run("all run concurrently", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunParallel(context.Background(), []RunSpec{
{Name: "first", Command: "echo", Args: []string{"1"}},
{Name: "second", Command: "echo", Args: []string{"2"}},
{Name: "third", Command: "echo", Args: []string{"3"}},
})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Equal(t, 3, result.Passed)
assert.Len(t, result.Results, 3)
})
t.Run("failure doesnt stop others", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunParallel(context.Background(), []RunSpec{
{Name: "first", Command: "echo", Args: []string{"1"}},
{Name: "fails", Command: "sh", Args: []string{"-c", "exit 1"}},
{Name: "third", Command: "echo", Args: []string{"3"}},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 2, result.Passed)
assert.Equal(t, 1, result.Failed)
})
}
func TestRunner_RunAll_Good(t *testing.T) {
t.Run("respects dependencies", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "third", Command: "echo", Args: []string{"3"}, After: []string{"second"}},
{Name: "first", Command: "echo", Args: []string{"1"}},
{Name: "second", Command: "echo", Args: []string{"2"}, After: []string{"first"}},
})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Equal(t, 3, result.Passed)
})
t.Run("skips dependents on failure", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "first", Command: "sh", Args: []string{"-c", "exit 1"}},
{Name: "second", Command: "echo", Args: []string{"2"}, After: []string{"first"}},
{Name: "third", Command: "echo", Args: []string{"3"}, After: []string{"second"}},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 0, result.Passed)
assert.Equal(t, 1, result.Failed)
assert.Equal(t, 2, result.Skipped)
})
t.Run("parallel independent specs", func(t *testing.T) {
runner := newTestRunner(t)
// These should run in parallel since they have no dependencies
result, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "a", Command: "echo", Args: []string{"a"}},
{Name: "b", Command: "echo", Args: []string{"b"}},
{Name: "c", Command: "echo", Args: []string{"c"}},
{Name: "final", Command: "echo", Args: []string{"done"}, After: []string{"a", "b", "c"}},
})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Equal(t, 4, result.Passed)
})
}
func TestRunner_CircularDeps_Bad(t *testing.T) {
t.Run("circular dependency counts as failed", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "a", Command: "echo", Args: []string{"a"}, After: []string{"b"}},
{Name: "b", Command: "echo", Args: []string{"b"}, After: []string{"a"}},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 2, result.Failed)
assert.Equal(t, 0, result.Skipped)
})
}
func TestRunResult_Passed_Good(t *testing.T) {
t.Run("success", func(t *testing.T) {
r := RunResult{ExitCode: 0}
assert.True(t, r.Passed())
})
t.Run("non-zero exit", func(t *testing.T) {
r := RunResult{ExitCode: 1}
assert.False(t, r.Passed())
})
t.Run("skipped", func(t *testing.T) {
r := RunResult{ExitCode: 0, Skipped: true}
assert.False(t, r.Passed())
})
t.Run("error", func(t *testing.T) {
r := RunResult{ExitCode: 0, Error: assert.AnError}
assert.False(t, r.Passed())
})
}
func TestRunner_RunSequential_Bad(t *testing.T) {
t.Run("invalid command fails", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunSequential(context.Background(), []RunSpec{
{Name: "bad", Command: "nonexistent_command_xyz"},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 1, result.Failed)
})
}
func TestRunner_RunSequential_Ugly(t *testing.T) {
t.Run("empty spec list succeeds with no results", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunSequential(context.Background(), []RunSpec{})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Equal(t, 0, result.Passed)
assert.Len(t, result.Results, 0)
})
}
func TestRunner_RunParallel_Bad(t *testing.T) {
t.Run("invalid command fails without stopping others", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunParallel(context.Background(), []RunSpec{
{Name: "ok", Command: "echo", Args: []string{"1"}},
{Name: "bad", Command: "nonexistent_command_xyz"},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 1, result.Passed)
assert.Equal(t, 1, result.Failed)
})
}
func TestRunner_RunParallel_Ugly(t *testing.T) {
t.Run("empty spec list succeeds", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunParallel(context.Background(), []RunSpec{})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Len(t, result.Results, 0)
})
}
func TestRunner_RunAll_Bad(t *testing.T) {
t.Run("missing dependency name counts as deadlock", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunAll(context.Background(), []RunSpec{
{Name: "first", Command: "echo", Args: []string{"1"}, After: []string{"missing"}},
})
require.NoError(t, err)
assert.False(t, result.Success())
assert.Equal(t, 1, result.Failed)
})
}
func TestRunner_RunAll_Ugly(t *testing.T) {
t.Run("empty spec list succeeds", func(t *testing.T) {
runner := newTestRunner(t)
result, err := runner.RunAll(context.Background(), []RunSpec{})
require.NoError(t, err)
assert.True(t, result.Success())
assert.Len(t, result.Results, 0)
})
}