refactor(ax): remove legacy global process singleton

This commit is contained in:
Virgil 2026-03-30 00:45:45 +00:00
parent 15e4c8ddeb
commit aa3602fbb0
2 changed files with 0 additions and 397 deletions

View file

@ -1,267 +0,0 @@
package process
import (
"context"
"sync"
"testing"
framework "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestGlobal_DefaultNotInitialized(t *testing.T) {
// Reset global state for this test
old := defaultService.Swap(nil)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
assert.Nil(t, Default())
_, err := Start(context.Background(), "echo", "test")
assert.ErrorIs(t, err, ErrServiceNotInitialized)
_, err = Run(context.Background(), "echo", "test")
assert.ErrorIs(t, err, ErrServiceNotInitialized)
_, err = Get("proc-1")
assert.ErrorIs(t, err, ErrServiceNotInitialized)
assert.Nil(t, List())
assert.Nil(t, Running())
err = Kill("proc-1")
assert.ErrorIs(t, err, ErrServiceNotInitialized)
_, err = StartWithOptions(context.Background(), RunOptions{Command: "echo"})
assert.ErrorIs(t, err, ErrServiceNotInitialized)
_, err = RunWithOptions(context.Background(), RunOptions{Command: "echo"})
assert.ErrorIs(t, err, ErrServiceNotInitialized)
}
func newGlobalTestService(t *testing.T) *Service {
t.Helper()
c := framework.New()
factory := NewService(Options{})
raw, err := factory(c)
require.NoError(t, err)
return raw.(*Service)
}
func TestGlobal_SetDefault(t *testing.T) {
t.Run("sets and retrieves service", func(t *testing.T) {
old := defaultService.Swap(nil)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
svc := newGlobalTestService(t)
err := SetDefault(svc)
require.NoError(t, err)
assert.Equal(t, svc, Default())
})
t.Run("errors on nil", func(t *testing.T) {
err := SetDefault(nil)
assert.Error(t, err)
})
}
func TestGlobal_ConcurrentDefault(t *testing.T) {
old := defaultService.Swap(nil)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
svc := newGlobalTestService(t)
err := SetDefault(svc)
require.NoError(t, err)
var wg sync.WaitGroup
for i := 0; i < 100; i++ {
wg.Add(1)
go func() {
defer wg.Done()
s := Default()
assert.NotNil(t, s)
assert.Equal(t, svc, s)
}()
}
wg.Wait()
}
func TestGlobal_ConcurrentSetDefault(t *testing.T) {
old := defaultService.Swap(nil)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
var services []*Service
for i := 0; i < 10; i++ {
svc := newGlobalTestService(t)
services = append(services, svc)
}
var wg sync.WaitGroup
for _, svc := range services {
wg.Add(1)
go func(s *Service) {
defer wg.Done()
_ = SetDefault(s)
}(svc)
}
wg.Wait()
final := Default()
assert.NotNil(t, final)
found := false
for _, svc := range services {
if svc == final {
found = true
break
}
}
assert.True(t, found, "Default should be one of the set services")
}
func TestGlobal_ConcurrentOperations(t *testing.T) {
old := defaultService.Swap(nil)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
svc := newGlobalTestService(t)
err := SetDefault(svc)
require.NoError(t, err)
var wg sync.WaitGroup
var processes []*Process
var procMu sync.Mutex
for i := 0; i < 20; i++ {
wg.Add(1)
go func() {
defer wg.Done()
proc, err := Start(context.Background(), "echo", "concurrent")
if err == nil {
procMu.Lock()
processes = append(processes, proc)
procMu.Unlock()
}
}()
}
for i := 0; i < 10; i++ {
wg.Add(1)
go func() {
defer wg.Done()
_ = List()
_ = Running()
}()
}
wg.Wait()
procMu.Lock()
for _, p := range processes {
<-p.Done()
}
procMu.Unlock()
assert.Len(t, processes, 20)
var wg2 sync.WaitGroup
for _, p := range processes {
wg2.Add(1)
go func(id string) {
defer wg2.Done()
got, err := Get(id)
assert.NoError(t, err)
assert.NotNil(t, got)
}(p.ID)
}
wg2.Wait()
}
func TestGlobal_StartWithOptions(t *testing.T) {
svc, _ := newTestService(t)
old := defaultService.Swap(svc)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
proc, err := StartWithOptions(context.Background(), RunOptions{
Command: "echo",
Args: []string{"with", "options"},
})
require.NoError(t, err)
<-proc.Done()
assert.Equal(t, 0, proc.ExitCode)
assert.Contains(t, proc.Output(), "with options")
}
func TestGlobal_RunWithOptions(t *testing.T) {
svc, _ := newTestService(t)
old := defaultService.Swap(svc)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
output, err := RunWithOptions(context.Background(), RunOptions{
Command: "echo",
Args: []string{"run", "options"},
})
require.NoError(t, err)
assert.Contains(t, output, "run options")
}
func TestGlobal_Running(t *testing.T) {
svc, _ := newTestService(t)
old := defaultService.Swap(svc)
defer func() {
if old != nil {
defaultService.Store(old)
}
}()
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
proc, err := Start(ctx, "sleep", "60")
require.NoError(t, err)
running := Running()
assert.Len(t, running, 1)
assert.Equal(t, proc.ID, running[0].ID)
cancel()
<-proc.Done()
running = Running()
assert.Len(t, running, 0)
}

View file

@ -1,130 +0,0 @@
package process
import (
"context"
"sync"
"sync/atomic"
"dappco.re/go/core"
coreerr "dappco.re/go/core/log"
)
// Global default service (follows i18n pattern).
var (
defaultService atomic.Pointer[Service]
defaultOnce sync.Once
defaultErr error
)
// Default returns the global process service.
// Returns nil if not initialized.
func Default() *Service {
return defaultService.Load()
}
// SetDefault sets the global process service.
// Thread-safe: can be called concurrently with Default().
func SetDefault(s *Service) error {
if s == nil {
return ErrSetDefaultNil
}
defaultService.Store(s)
return nil
}
// Init initializes the default global service with a Core instance.
// This is typically called during application startup.
func Init(c *core.Core) error {
defaultOnce.Do(func() {
factory := NewService(Options{})
svc, err := factory(c)
if err != nil {
defaultErr = err
return
}
defaultService.Store(svc.(*Service))
})
return defaultErr
}
// --- Global convenience functions ---
// Start spawns a new process using the default service.
func Start(ctx context.Context, command string, args ...string) (*Process, error) {
svc := Default()
if svc == nil {
return nil, ErrServiceNotInitialized
}
return svc.Start(ctx, command, args...)
}
// Run executes a command and waits for completion using the default service.
func Run(ctx context.Context, command string, args ...string) (string, error) {
svc := Default()
if svc == nil {
return "", ErrServiceNotInitialized
}
return svc.Run(ctx, command, args...)
}
// Get returns a process by ID from the default service.
func Get(id string) (*Process, error) {
svc := Default()
if svc == nil {
return nil, ErrServiceNotInitialized
}
return svc.Get(id)
}
// List returns all processes from the default service.
func List() []*Process {
svc := Default()
if svc == nil {
return nil
}
return svc.List()
}
// Kill terminates a process by ID using the default service.
func Kill(id string) error {
svc := Default()
if svc == nil {
return ErrServiceNotInitialized
}
return svc.Kill(id)
}
// StartWithOptions spawns a process with full configuration using the default service.
func StartWithOptions(ctx context.Context, opts RunOptions) (*Process, error) {
svc := Default()
if svc == nil {
return nil, ErrServiceNotInitialized
}
return svc.StartWithOptions(ctx, opts)
}
// RunWithOptions executes a command with options and waits using the default service.
func RunWithOptions(ctx context.Context, opts RunOptions) (string, error) {
svc := Default()
if svc == nil {
return "", ErrServiceNotInitialized
}
return svc.RunWithOptions(ctx, opts)
}
// Running returns all currently running processes from the default service.
func Running() []*Process {
svc := Default()
if svc == nil {
return nil
}
return svc.Running()
}
// Errors
var (
// ErrServiceNotInitialized is returned when the service is not initialized.
ErrServiceNotInitialized = coreerr.E("", "process: service not initialized; call process.Init(core) first", nil)
// ErrSetDefaultNil is returned when SetDefault is called with nil.
ErrSetDefaultNil = coreerr.E("", "process: SetDefault called with nil service", nil)
)