go/core_test.go

222 lines
5.1 KiB
Go
Raw Normal View History

package core_test
import (
"context"
"os"
"os/exec"
"path/filepath"
"testing"
2026-03-20 21:00:48 +00:00
. "dappco.re/go/core"
"github.com/stretchr/testify/assert"
)
// --- New ---
func TestNew_Good(t *testing.T) {
c := New()
assert.NotNil(t, c)
}
func TestNew_WithOptions_Good(t *testing.T) {
c := New(WithOptions(NewOptions(Option{Key: "name", Value: "myapp"})))
assert.NotNil(t, c)
assert.Equal(t, "myapp", c.App().Name)
}
func TestNew_WithOptions_Bad(t *testing.T) {
// Empty options — should still create a valid Core
c := New(WithOptions(NewOptions()))
assert.NotNil(t, c)
}
func TestNew_WithService_Good(t *testing.T) {
started := false
c := New(
WithOptions(NewOptions(Option{Key: "name", Value: "myapp"})),
WithService(func(c *Core) Result {
c.Service("test", Service{
OnStart: func() Result { started = true; return Result{OK: true} },
})
return Result{OK: true}
}),
)
svc := c.Service("test")
assert.True(t, svc.OK)
c.ServiceStartup(context.Background(), nil)
assert.True(t, started)
}
func TestNew_WithServiceLock_Good(t *testing.T) {
c := New(
WithService(func(c *Core) Result {
c.Service("allowed", Service{})
return Result{OK: true}
}),
WithServiceLock(),
)
// Registration after lock should fail
reg := c.Service("blocked", Service{})
assert.False(t, reg.OK)
}
func TestNew_WithService_Bad_FailingOption(t *testing.T) {
secondCalled := false
_ = New(
WithService(func(c *Core) Result {
return Result{Value: E("test", "intentional failure", nil), OK: false}
}),
WithService(func(c *Core) Result {
secondCalled = true
return Result{OK: true}
}),
)
assert.False(t, secondCalled, "second option should not run after first fails")
}
// --- Accessors ---
func TestAccessors_Good(t *testing.T) {
c := New()
assert.NotNil(t, c.App())
assert.NotNil(t, c.Data())
assert.NotNil(t, c.Drive())
assert.NotNil(t, c.Fs())
assert.NotNil(t, c.Config())
assert.NotNil(t, c.Error())
assert.NotNil(t, c.Log())
assert.NotNil(t, c.Cli())
assert.NotNil(t, c.IPC())
assert.NotNil(t, c.I18n())
assert.Equal(t, c, c.Core())
}
func TestOptions_Accessor_Good(t *testing.T) {
c := New(WithOptions(NewOptions(
Option{Key: "name", Value: "testapp"},
Option{Key: "port", Value: 8080},
Option{Key: "debug", Value: true},
)))
opts := c.Options()
assert.NotNil(t, opts)
assert.Equal(t, "testapp", opts.String("name"))
assert.Equal(t, 8080, opts.Int("port"))
assert.True(t, opts.Bool("debug"))
}
func TestOptions_Accessor_Nil(t *testing.T) {
c := New()
// No options passed — Options() returns nil
assert.Nil(t, c.Options())
}
// --- Core Error/Log Helpers ---
func TestCore_LogError_Good(t *testing.T) {
c := New()
cause := assert.AnError
r := c.LogError(cause, "test.Operation", "something broke")
err, ok := r.Value.(error)
assert.True(t, ok)
assert.ErrorIs(t, err, cause)
}
func TestCore_LogWarn_Good(t *testing.T) {
c := New()
r := c.LogWarn(assert.AnError, "test.Operation", "heads up")
_, ok := r.Value.(error)
assert.True(t, ok)
}
func TestCore_Must_Ugly(t *testing.T) {
c := New()
assert.Panics(t, func() {
c.Must(assert.AnError, "test.Operation", "fatal")
})
}
func TestCore_Must_Nil_Good(t *testing.T) {
c := New()
assert.NotPanics(t, func() {
c.Must(nil, "test.Operation", "no error")
})
}
func TestCore_Run_HelperProcess(t *testing.T) {
if os.Getenv("GO_WANT_HELPER_PROCESS") != "1" {
return
}
switch os.Getenv("CORE_RUN_MODE") {
case "startup-fail":
c := New(
WithService(func(c *Core) Result {
return c.Service("broken", Service{
OnStart: func() Result {
return Result{Value: NewError("startup failed"), OK: false}
},
})
}),
)
c.Run()
case "cli-fail":
shutdownFile := os.Getenv("CORE_RUN_SHUTDOWN_FILE")
c := New(
WithService(func(c *Core) Result {
return c.Service("cleanup", Service{
OnStop: func() Result {
if err := os.WriteFile(shutdownFile, []byte("stopped"), 0o600); err != nil {
return Result{Value: err, OK: false}
}
return Result{OK: true}
},
})
}),
)
c.Command("explode", Command{
Action: func(_ Options) Result {
return Result{Value: NewError("cli failed"), OK: false}
},
})
os.Args = []string{"core-test", "explode"}
c.Run()
default:
os.Exit(2)
}
}
func TestCore_Run_Bad(t *testing.T) {
err := runCoreRunHelper(t, "startup-fail")
var exitErr *exec.ExitError
if assert.ErrorAs(t, err, &exitErr) {
assert.Equal(t, 1, exitErr.ExitCode())
}
}
func TestCore_Run_Ugly(t *testing.T) {
shutdownFile := filepath.Join(t.TempDir(), "shutdown.txt")
err := runCoreRunHelper(t, "cli-fail", "CORE_RUN_SHUTDOWN_FILE="+shutdownFile)
var exitErr *exec.ExitError
if assert.ErrorAs(t, err, &exitErr) {
assert.Equal(t, 1, exitErr.ExitCode())
}
data, readErr := os.ReadFile(shutdownFile)
assert.NoError(t, readErr)
assert.Equal(t, "stopped", string(data))
}
func runCoreRunHelper(t *testing.T, mode string, extraEnv ...string) error {
t.Helper()
cmd := exec.Command(os.Args[0], "-test.run=^TestCore_Run_HelperProcess$")
cmd.Env = append(os.Environ(), "GO_WANT_HELPER_PROCESS=1", "CORE_RUN_MODE="+mode)
cmd.Env = append(cmd.Env, extraEnv...)
return cmd.Run()
}