// Package testutil provides testing utilities for rdev-api. package testutil import ( "context" "sync" "github.com/orchard9/rdev/internal/domain" "github.com/orchard9/rdev/internal/port" ) // MockExecutor is a mock implementation of the Executor for testing. type MockExecutor struct { mu sync.Mutex ExecCalls []ExecCall ExecResult *domain.CommandResult ExecOutputs []domain.OutputLine PodExistsMap map[string]bool ConnectionError error } // ExecCall records the parameters of an Execute call. type ExecCall struct { Cmd *domain.Command PodName string } // Ensure MockExecutor implements port.CommandExecutor at compile time. var _ port.CommandExecutor = (*MockExecutor)(nil) // NewMockExecutor creates a new mock executor. func NewMockExecutor() *MockExecutor { return &MockExecutor{ PodExistsMap: make(map[string]bool), ExecResult: &domain.CommandResult{}, } } // Execute mocks command execution. func (m *MockExecutor) Execute(ctx context.Context, cmd *domain.Command, podName string, handler domain.OutputHandler) (*domain.CommandResult, error) { m.mu.Lock() m.ExecCalls = append(m.ExecCalls, ExecCall{Cmd: cmd, PodName: podName}) outputs := m.ExecOutputs result := m.ExecResult m.mu.Unlock() // Send mock outputs for _, o := range outputs { select { case <-ctx.Done(): return &domain.CommandResult{CommandID: cmd.ID, ExitCode: 130, Error: ctx.Err()}, nil default: handler(o) } } return result, nil } // Cancel mocks command cancellation. func (m *MockExecutor) Cancel(ctx context.Context, cmdID domain.CommandID) error { return nil } // SetExecResult sets the result to return from Execute. func (m *MockExecutor) SetExecResult(result *domain.CommandResult) { m.mu.Lock() defer m.mu.Unlock() m.ExecResult = result } // SetExecOutputs sets the outputs to send during Execute. func (m *MockExecutor) SetExecOutputs(outputs []domain.OutputLine) { m.mu.Lock() defer m.mu.Unlock() m.ExecOutputs = outputs } // PodExists mocks pod existence check. func (m *MockExecutor) PodExists(ctx context.Context, podName string) (bool, error) { m.mu.Lock() defer m.mu.Unlock() exists, ok := m.PodExistsMap[podName] if !ok { return false, nil } return exists, nil } // CheckConnection mocks cluster connection check. func (m *MockExecutor) CheckConnection(ctx context.Context) error { m.mu.Lock() defer m.mu.Unlock() return m.ConnectionError } // SetConnectionError sets the error to return from CheckConnection. func (m *MockExecutor) SetConnectionError(err error) { m.mu.Lock() defer m.mu.Unlock() m.ConnectionError = err } // SetPodExists sets whether a pod exists. func (m *MockExecutor) SetPodExists(podName string, exists bool) { m.mu.Lock() defer m.mu.Unlock() m.PodExistsMap[podName] = exists } // GetExecCalls returns all recorded Execute calls. func (m *MockExecutor) GetExecCalls() []ExecCall { m.mu.Lock() defer m.mu.Unlock() return append([]ExecCall{}, m.ExecCalls...) } // Reset clears all recorded calls and resets the mock. func (m *MockExecutor) Reset() { m.mu.Lock() defer m.mu.Unlock() m.ExecCalls = nil m.ExecOutputs = nil m.ExecResult = &domain.CommandResult{} m.PodExistsMap = make(map[string]bool) }