Tests: Run, RegisterService, ServiceFor, MustServiceFor _Bad/_Ugly variants. Fix: Lock map is now per-Core instance, not package-level global. This prevents deadlocks when multiple Core instances exist (e.g. tests). Coverage: 82.4% → 83.6% Co-Authored-By: Virgil <virgil@lethean.io>
190 lines
4.3 KiB
Go
190 lines
4.3 KiB
Go
package core_test
|
|
|
|
import (
|
|
"context"
|
|
"testing"
|
|
|
|
. "dappco.re/go/core"
|
|
"github.com/stretchr/testify/assert"
|
|
)
|
|
|
|
// --- Service Registration ---
|
|
|
|
func TestService_Register_Good(t *testing.T) {
|
|
c := New()
|
|
r := c.Service("auth", Service{})
|
|
assert.True(t, r.OK)
|
|
}
|
|
|
|
func TestService_Register_Duplicate_Bad(t *testing.T) {
|
|
c := New()
|
|
c.Service("auth", Service{})
|
|
r := c.Service("auth", Service{})
|
|
assert.False(t, r.OK)
|
|
}
|
|
|
|
func TestService_Register_Empty_Bad(t *testing.T) {
|
|
c := New()
|
|
r := c.Service("", Service{})
|
|
assert.False(t, r.OK)
|
|
}
|
|
|
|
func TestService_Get_Good(t *testing.T) {
|
|
c := New()
|
|
c.Service("brain", Service{OnStart: func() Result { return Result{OK: true} }})
|
|
r := c.Service("brain")
|
|
assert.True(t, r.OK)
|
|
assert.NotNil(t, r.Value)
|
|
}
|
|
|
|
func TestService_Get_Bad(t *testing.T) {
|
|
c := New()
|
|
r := c.Service("nonexistent")
|
|
assert.False(t, r.OK)
|
|
}
|
|
|
|
func TestService_Names_Good(t *testing.T) {
|
|
c := New()
|
|
c.Service("a", Service{})
|
|
c.Service("b", Service{})
|
|
names := c.Services()
|
|
assert.Contains(t, names, "a")
|
|
assert.Contains(t, names, "b")
|
|
assert.Contains(t, names, "cli") // auto-registered by CliRegister in New()
|
|
}
|
|
|
|
// --- Service Lifecycle ---
|
|
|
|
func TestService_Lifecycle_Good(t *testing.T) {
|
|
c := New()
|
|
started := false
|
|
stopped := false
|
|
c.Service("lifecycle", Service{
|
|
OnStart: func() Result { started = true; return Result{OK: true} },
|
|
OnStop: func() Result { stopped = true; return Result{OK: true} },
|
|
})
|
|
|
|
sr := c.Startables()
|
|
assert.True(t, sr.OK)
|
|
startables := sr.Value.([]*Service)
|
|
assert.Len(t, startables, 1)
|
|
startables[0].OnStart()
|
|
assert.True(t, started)
|
|
|
|
tr := c.Stoppables()
|
|
assert.True(t, tr.OK)
|
|
stoppables := tr.Value.([]*Service)
|
|
assert.Len(t, stoppables, 1)
|
|
stoppables[0].OnStop()
|
|
assert.True(t, stopped)
|
|
}
|
|
|
|
type autoLifecycleService struct {
|
|
started bool
|
|
stopped bool
|
|
messages []Message
|
|
}
|
|
|
|
func (s *autoLifecycleService) OnStartup(_ context.Context) error {
|
|
s.started = true
|
|
return nil
|
|
}
|
|
|
|
func (s *autoLifecycleService) OnShutdown(_ context.Context) error {
|
|
s.stopped = true
|
|
return nil
|
|
}
|
|
|
|
func (s *autoLifecycleService) HandleIPCEvents(_ *Core, msg Message) Result {
|
|
s.messages = append(s.messages, msg)
|
|
return Result{OK: true}
|
|
}
|
|
|
|
func TestService_RegisterService_Bad(t *testing.T) {
|
|
t.Run("EmptyName", func(t *testing.T) {
|
|
c := New()
|
|
r := c.RegisterService("", "value")
|
|
assert.False(t, r.OK)
|
|
|
|
err, ok := r.Value.(error)
|
|
if assert.True(t, ok) {
|
|
assert.Equal(t, "core.RegisterService", Operation(err))
|
|
}
|
|
})
|
|
|
|
t.Run("DuplicateName", func(t *testing.T) {
|
|
c := New()
|
|
assert.True(t, c.RegisterService("svc", "first").OK)
|
|
|
|
r := c.RegisterService("svc", "second")
|
|
assert.False(t, r.OK)
|
|
})
|
|
|
|
t.Run("LockedRegistry", func(t *testing.T) {
|
|
c := New()
|
|
c.LockEnable()
|
|
c.LockApply()
|
|
|
|
r := c.RegisterService("blocked", "value")
|
|
assert.False(t, r.OK)
|
|
})
|
|
}
|
|
|
|
func TestService_RegisterService_Ugly(t *testing.T) {
|
|
t.Run("AutoDiscoversLifecycleAndIPCHandlers", func(t *testing.T) {
|
|
c := New()
|
|
svc := &autoLifecycleService{}
|
|
|
|
r := c.RegisterService("auto", svc)
|
|
assert.True(t, r.OK)
|
|
assert.True(t, c.ServiceStartup(context.Background(), nil).OK)
|
|
assert.True(t, c.ACTION("ping").OK)
|
|
assert.True(t, c.ServiceShutdown(context.Background()).OK)
|
|
assert.True(t, svc.started)
|
|
assert.True(t, svc.stopped)
|
|
assert.Contains(t, svc.messages, Message("ping"))
|
|
})
|
|
|
|
t.Run("NilInstanceReturnsServiceDTO", func(t *testing.T) {
|
|
c := New()
|
|
assert.True(t, c.RegisterService("nil", nil).OK)
|
|
|
|
r := c.Service("nil")
|
|
if assert.True(t, r.OK) {
|
|
svc, ok := r.Value.(*Service)
|
|
if assert.True(t, ok) {
|
|
assert.Equal(t, "nil", svc.Name)
|
|
assert.Nil(t, svc.Instance)
|
|
}
|
|
}
|
|
})
|
|
}
|
|
|
|
func TestService_ServiceFor_Bad(t *testing.T) {
|
|
typed, ok := ServiceFor[string](New(), "missing")
|
|
assert.False(t, ok)
|
|
assert.Equal(t, "", typed)
|
|
}
|
|
|
|
func TestService_ServiceFor_Ugly(t *testing.T) {
|
|
c := New()
|
|
assert.True(t, c.RegisterService("value", "hello").OK)
|
|
|
|
typed, ok := ServiceFor[int](c, "value")
|
|
assert.False(t, ok)
|
|
assert.Equal(t, 0, typed)
|
|
}
|
|
|
|
func TestService_MustServiceFor_Bad(t *testing.T) {
|
|
c := New()
|
|
assert.PanicsWithError(t, `core.MustServiceFor: service "missing" not found or wrong type`, func() {
|
|
_ = MustServiceFor[string](c, "missing")
|
|
})
|
|
}
|
|
|
|
func TestService_MustServiceFor_Ugly(t *testing.T) {
|
|
var c *Core
|
|
assert.Panics(t, func() {
|
|
_ = MustServiceFor[string](c, "missing")
|
|
})
|
|
}
|