feat: inline tests + Fs zero-value fix + coverage 76.9% → 82.3%

Move all tests from tests/ to package root for proper coverage.
Fix Fs zero-value: path() and validatePath() default empty root
to "/" so &Fs{} works without New().

New tests: PathGlob, PathIsAbs, CleanPath, Cli.SetOutput,
ServiceShutdown, Core.Context, Fs zero-value, Fs protected
delete, Command lifecycle with implementation, error formatting
branches, PerformAsync completion/no-handler/after-shutdown,
Extract with templates, Embed path traversal.

Coverage: 76.9% → 82.3% (23 test files).

Co-Authored-By: Virgil <virgil@lethean.io>
This commit is contained in:
Snider 2026-03-22 13:30:01 +00:00
parent a06b779e3c
commit e0c190ca8f
26 changed files with 547 additions and 9 deletions

View file

@ -1,6 +1,7 @@
package core_test package core_test
import ( import (
"bytes"
"testing" "testing"
. "dappco.re/go/core" . "dappco.re/go/core"
@ -74,3 +75,11 @@ func TestCli_PrintHelp_Good(t *testing.T) {
c.Command("serve", Command{Action: func(_ Options) Result { return Result{OK: true} }}) c.Command("serve", Command{Action: func(_ Options) Result { return Result{OK: true} }})
c.Cli().PrintHelp() c.Cli().PrintHelp()
} }
func TestCli_SetOutput_Good(t *testing.T) {
c := New()
var buf bytes.Buffer
c.Cli().SetOutput(&buf)
c.Cli().Print("hello %s", "world")
assert.Contains(t, buf.String(), "hello world")
}

View file

@ -121,6 +121,93 @@ func TestCommand_Lifecycle_NoImpl_Good(t *testing.T) {
assert.False(t, cmd.Signal("HUP").OK) assert.False(t, cmd.Signal("HUP").OK)
} }
// --- Lifecycle with Implementation ---
type testLifecycle struct {
started bool
stopped bool
restarted bool
reloaded bool
signalled string
}
func (l *testLifecycle) Start(opts Options) Result {
l.started = true
return Result{Value: "started", OK: true}
}
func (l *testLifecycle) Stop() Result {
l.stopped = true
return Result{OK: true}
}
func (l *testLifecycle) Restart() Result {
l.restarted = true
return Result{OK: true}
}
func (l *testLifecycle) Reload() Result {
l.reloaded = true
return Result{OK: true}
}
func (l *testLifecycle) Signal(sig string) Result {
l.signalled = sig
return Result{Value: sig, OK: true}
}
func TestCommand_Lifecycle_WithImpl_Good(t *testing.T) {
c := New()
lc := &testLifecycle{}
c.Command("daemon", Command{Lifecycle: lc})
cmd := c.Command("daemon").Value.(*Command)
r := cmd.Start(Options{})
assert.True(t, r.OK)
assert.True(t, lc.started)
assert.True(t, cmd.Stop().OK)
assert.True(t, lc.stopped)
assert.True(t, cmd.Restart().OK)
assert.True(t, lc.restarted)
assert.True(t, cmd.Reload().OK)
assert.True(t, lc.reloaded)
r = cmd.Signal("HUP")
assert.True(t, r.OK)
assert.Equal(t, "HUP", lc.signalled)
}
func TestCommand_Duplicate_Bad(t *testing.T) {
c := New()
c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
r := c.Command("deploy", Command{Action: func(_ Options) Result { return Result{OK: true} }})
assert.False(t, r.OK)
}
func TestCommand_InvalidPath_Bad(t *testing.T) {
c := New()
assert.False(t, c.Command("/leading", Command{}).OK)
assert.False(t, c.Command("trailing/", Command{}).OK)
assert.False(t, c.Command("double//slash", Command{}).OK)
}
// --- Cli Run with Lifecycle ---
func TestCli_Run_Lifecycle_Good(t *testing.T) {
c := New()
lc := &testLifecycle{}
c.Command("serve", Command{Lifecycle: lc})
r := c.Cli().Run("serve")
assert.True(t, r.OK)
assert.True(t, lc.started)
}
func TestCli_Run_NoActionNoLifecycle_Bad(t *testing.T) {
c := New()
c.Command("empty", Command{})
r := c.Cli().Run("empty")
assert.False(t, r.OK)
}
// --- Empty path --- // --- Empty path ---
func TestCommand_EmptyPath_Bad(t *testing.T) { func TestCommand_EmptyPath_Bad(t *testing.T) {

View file

@ -157,6 +157,94 @@ func TestGeneratePack_WithFiles_Good(t *testing.T) {
assert.Contains(t, r.Value.(string), "core.AddAsset") assert.Contains(t, r.Value.(string), "core.AddAsset")
} }
// --- Extract (template + nested) ---
func TestExtract_WithTemplate_Good(t *testing.T) {
dir := t.TempDir()
// Create an in-memory FS with a template file and a plain file
tmplDir := os.DirFS(t.TempDir())
// Use a real temp dir with files
srcDir := t.TempDir()
os.WriteFile(srcDir+"/plain.txt", []byte("static content"), 0644)
os.WriteFile(srcDir+"/greeting.tmpl", []byte("Hello {{.Name}}!"), 0644)
os.MkdirAll(srcDir+"/sub", 0755)
os.WriteFile(srcDir+"/sub/nested.txt", []byte("nested"), 0644)
_ = tmplDir
fsys := os.DirFS(srcDir)
data := map[string]string{"Name": "World"}
r := Extract(fsys, dir, data)
assert.True(t, r.OK)
// Plain file copied
content, err := os.ReadFile(dir + "/plain.txt")
assert.NoError(t, err)
assert.Equal(t, "static content", string(content))
// Template processed and .tmpl stripped
greeting, err := os.ReadFile(dir + "/greeting")
assert.NoError(t, err)
assert.Equal(t, "Hello World!", string(greeting))
// Nested directory preserved
nested, err := os.ReadFile(dir + "/sub/nested.txt")
assert.NoError(t, err)
assert.Equal(t, "nested", string(nested))
}
func TestExtract_BadTargetDir_Ugly(t *testing.T) {
srcDir := t.TempDir()
os.WriteFile(srcDir+"/f.txt", []byte("x"), 0644)
r := Extract(os.DirFS(srcDir), "/nonexistent/deeply/nested/impossible", nil)
// Should fail gracefully, not panic
_ = r
}
func TestEmbed_PathTraversal_Ugly(t *testing.T) {
emb := Mount(testFS, "testdata").Value.(*Embed)
r := emb.ReadFile("../../etc/passwd")
assert.False(t, r.OK)
}
func TestEmbed_Sub_BaseDir_Good(t *testing.T) {
emb := Mount(testFS, "testdata").Value.(*Embed)
r := emb.Sub("scantest")
assert.True(t, r.OK)
sub := r.Value.(*Embed)
assert.Equal(t, ".", sub.BaseDirectory())
}
func TestEmbed_Open_Bad(t *testing.T) {
emb := Mount(testFS, "testdata").Value.(*Embed)
r := emb.Open("nonexistent.txt")
assert.False(t, r.OK)
}
func TestEmbed_ReadDir_Bad(t *testing.T) {
emb := Mount(testFS, "testdata").Value.(*Embed)
r := emb.ReadDir("nonexistent")
assert.False(t, r.OK)
}
func TestEmbed_EmbedFS_Original_Good(t *testing.T) {
emb := Mount(testFS, "testdata").Value.(*Embed)
efs := emb.EmbedFS()
_, err := efs.ReadFile("testdata/test.txt")
assert.NoError(t, err)
}
func TestExtract_NilData_Good(t *testing.T) {
dir := t.TempDir()
srcDir := t.TempDir()
os.WriteFile(srcDir+"/file.txt", []byte("no template"), 0644)
r := Extract(os.DirFS(srcDir), dir, nil)
assert.True(t, r.OK)
}
func mustCompress(input string) string { func mustCompress(input string) string {
var buf bytes.Buffer var buf bytes.Buffer
b64 := base64.NewEncoder(base64.StdEncoding, &buf) b64 := base64.NewEncoder(base64.StdEncoding, &buf)

View file

@ -227,3 +227,46 @@ func TestErrorPanic_CrashFile_Good(t *testing.T) {
assert.Nil(t, r.Value) assert.Nil(t, r.Value)
_ = path _ = path
} }
// --- Error formatting branches ---
func TestErr_Error_WithCode_Good(t *testing.T) {
err := WrapCode(errors.New("bad"), "INVALID", "validate", "input failed")
assert.Contains(t, err.Error(), "[INVALID]")
assert.Contains(t, err.Error(), "validate")
assert.Contains(t, err.Error(), "bad")
}
func TestErr_Error_CodeNoCause_Good(t *testing.T) {
err := NewCode("NOT_FOUND", "resource missing")
assert.Contains(t, err.Error(), "[NOT_FOUND]")
assert.Contains(t, err.Error(), "resource missing")
}
func TestErr_Error_NoOp_Good(t *testing.T) {
err := &Err{Message: "bare error"}
assert.Equal(t, "bare error", err.Error())
}
func TestWrapCode_NilErr_EmptyCode_Good(t *testing.T) {
err := WrapCode(nil, "", "op", "msg")
assert.Nil(t, err)
}
func TestWrap_PreservesCode_Good(t *testing.T) {
inner := WrapCode(errors.New("root"), "AUTH_FAIL", "auth", "denied")
outer := Wrap(inner, "handler", "request failed")
assert.Equal(t, "AUTH_FAIL", ErrorCode(outer))
}
func TestErrorLog_Warn_Nil_Good(t *testing.T) {
c := New()
r := c.LogWarn(nil, "op", "msg")
assert.True(t, r.OK)
}
func TestErrorLog_Error_Nil_Good(t *testing.T) {
c := New()
r := c.LogError(nil, "op", "msg")
assert.True(t, r.OK)
}

25
fs.go
View file

@ -15,15 +15,20 @@ type Fs struct {
// path sanitises and returns the full path. // path sanitises and returns the full path.
// Absolute paths are sandboxed under root (unless root is "/"). // Absolute paths are sandboxed under root (unless root is "/").
// Empty root defaults to "/" — the zero value of Fs is usable.
func (m *Fs) path(p string) string { func (m *Fs) path(p string) string {
root := m.root
if root == "" {
root = "/"
}
if p == "" { if p == "" {
return m.root return root
} }
// If the path is relative and the medium is rooted at "/", // If the path is relative and the medium is rooted at "/",
// treat it as relative to the current working directory. // treat it as relative to the current working directory.
// This makes io.Local behave more like the standard 'os' package. // This makes io.Local behave more like the standard 'os' package.
if m.root == "/" && !filepath.IsAbs(p) { if root == "/" && !filepath.IsAbs(p) {
cwd, _ := os.Getwd() cwd, _ := os.Getwd()
return filepath.Join(cwd, p) return filepath.Join(cwd, p)
} }
@ -33,23 +38,27 @@ func (m *Fs) path(p string) string {
clean := filepath.Clean("/" + p) clean := filepath.Clean("/" + p)
// If root is "/", allow absolute paths through // If root is "/", allow absolute paths through
if m.root == "/" { if root == "/" {
return clean return clean
} }
// Strip leading "/" so Join works correctly with root // Strip leading "/" so Join works correctly with root
return filepath.Join(m.root, clean[1:]) return filepath.Join(root, clean[1:])
} }
// validatePath ensures the path is within the sandbox, following symlinks if they exist. // validatePath ensures the path is within the sandbox, following symlinks if they exist.
func (m *Fs) validatePath(p string) Result { func (m *Fs) validatePath(p string) Result {
if m.root == "/" { root := m.root
if root == "" {
root = "/"
}
if root == "/" {
return Result{m.path(p), true} return Result{m.path(p), true}
} }
// Split the cleaned path into components // Split the cleaned path into components
parts := Split(filepath.Clean("/"+p), string(os.PathSeparator)) parts := Split(filepath.Clean("/"+p), string(os.PathSeparator))
current := m.root current := root
for _, part := range parts { for _, part := range parts {
if part == "" { if part == "" {
@ -70,7 +79,7 @@ func (m *Fs) validatePath(p string) Result {
} }
// Verify the resolved part is still within the root // Verify the resolved part is still within the root
rel, err := filepath.Rel(m.root, realNext) rel, err := filepath.Rel(root, realNext)
if err != nil || HasPrefix(rel, "..") { if err != nil || HasPrefix(rel, "..") {
// Security event: sandbox escape attempt // Security event: sandbox escape attempt
username := "unknown" username := "unknown"
@ -78,7 +87,7 @@ func (m *Fs) validatePath(p string) Result {
username = u.Username username = u.Username
} }
Print(os.Stderr, "[%s] SECURITY sandbox escape detected root=%s path=%s attempted=%s user=%s", Print(os.Stderr, "[%s] SECURITY sandbox escape detected root=%s path=%s attempted=%s user=%s",
time.Now().Format(time.RFC3339), m.root, p, realNext, username) time.Now().Format(time.RFC3339), root, p, realNext, username)
if err == nil { if err == nil {
err = E("fs.validatePath", Concat("sandbox escape: ", p, " resolves outside ", m.root), nil) err = E("fs.validatePath", Concat("sandbox escape: ", p, " resolves outside ", m.root), nil)
} }

View file

@ -184,3 +184,74 @@ func TestFs_WriteMode_Good(t *testing.T) {
assert.True(t, r.OK) assert.True(t, r.OK)
assert.Equal(t, "secret.txt", r.Value.(os.FileInfo).Name()) assert.Equal(t, "secret.txt", r.Value.(os.FileInfo).Name())
} }
// --- Zero Value ---
func TestFs_ZeroValue_Good(t *testing.T) {
dir := t.TempDir()
zeroFs := &Fs{}
path := filepath.Join(dir, "zero.txt")
assert.True(t, zeroFs.Write(path, "zero value works").OK)
r := zeroFs.Read(path)
assert.True(t, r.OK)
assert.Equal(t, "zero value works", r.Value.(string))
assert.True(t, zeroFs.IsFile(path))
assert.True(t, zeroFs.Exists(path))
assert.True(t, zeroFs.IsDir(dir))
}
func TestFs_ZeroValue_List_Good(t *testing.T) {
dir := t.TempDir()
zeroFs := &Fs{}
os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a"), 0644)
r := zeroFs.List(dir)
assert.True(t, r.OK)
entries := r.Value.([]fs.DirEntry)
assert.Len(t, entries, 1)
}
func TestFs_Exists_NotFound_Bad(t *testing.T) {
c := New()
assert.False(t, c.Fs().Exists("/nonexistent/path/xyz"))
}
// --- Fs path/validatePath edge cases ---
func TestFs_Read_EmptyPath_Ugly(t *testing.T) {
c := New()
r := c.Fs().Read("")
assert.False(t, r.OK)
}
func TestFs_Write_EmptyPath_Ugly(t *testing.T) {
c := New()
r := c.Fs().Write("", "data")
assert.False(t, r.OK)
}
func TestFs_Delete_Protected_Ugly(t *testing.T) {
c := New()
r := c.Fs().Delete("/")
assert.False(t, r.OK)
}
func TestFs_DeleteAll_Protected_Ugly(t *testing.T) {
c := New()
r := c.Fs().DeleteAll("/")
assert.False(t, r.OK)
}
func TestFs_ReadStream_WriteStream_Good(t *testing.T) {
dir := t.TempDir()
c := New()
path := filepath.Join(dir, "stream.txt")
c.Fs().Write(path, "streamed")
r := c.Fs().ReadStream(path)
assert.True(t, r.OK)
w := c.Fs().WriteStream(path)
assert.True(t, w.OK)
}

View file

@ -142,6 +142,24 @@ func TestLogPanic_Recover_Good(t *testing.T) {
func TestLog_SetOutput_Good(t *testing.T) { func TestLog_SetOutput_Good(t *testing.T) {
l := NewLog(LogOptions{Level: LevelInfo}) l := NewLog(LogOptions{Level: LevelInfo})
l.SetOutput(os.Stderr) l.SetOutput(os.Stderr)
// Should not panic — just changes where logs go
l.Info("redirected") l.Info("redirected")
} }
// --- Log suppression by level ---
func TestLog_Quiet_Suppresses_Ugly(t *testing.T) {
l := NewLog(LogOptions{Level: LevelQuiet})
// These should not panic even though nothing is logged
l.Debug("suppressed")
l.Info("suppressed")
l.Warn("suppressed")
l.Error("suppressed")
}
func TestLog_ErrorLevel_Suppresses_Ugly(t *testing.T) {
l := NewLog(LogOptions{Level: LevelError})
l.Debug("suppressed") // below threshold
l.Info("suppressed") // below threshold
l.Warn("suppressed") // below threshold
l.Error("visible") // at threshold
}

112
path_test.go Normal file
View file

@ -0,0 +1,112 @@
// SPDX-License-Identifier: EUPL-1.2
package core_test
import (
"os"
"path/filepath"
"testing"
core "dappco.re/go/core"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
func TestPath_Relative(t *testing.T) {
home, err := os.UserHomeDir()
require.NoError(t, err)
ds := core.Env("DS")
assert.Equal(t, home+ds+"Code"+ds+".core", core.Path("Code", ".core"))
}
func TestPath_Absolute(t *testing.T) {
ds := core.Env("DS")
assert.Equal(t, "/tmp"+ds+"workspace", core.Path("/tmp", "workspace"))
}
func TestPath_Empty(t *testing.T) {
home, err := os.UserHomeDir()
require.NoError(t, err)
assert.Equal(t, home, core.Path())
}
func TestPath_Cleans(t *testing.T) {
home, err := os.UserHomeDir()
require.NoError(t, err)
assert.Equal(t, home+core.Env("DS")+"Code", core.Path("Code", "sub", ".."))
}
func TestPath_CleanDoubleSlash(t *testing.T) {
ds := core.Env("DS")
assert.Equal(t, ds+"tmp"+ds+"file", core.Path("/tmp//file"))
}
func TestPathBase(t *testing.T) {
assert.Equal(t, "core", core.PathBase("/Users/snider/Code/core"))
assert.Equal(t, "homelab", core.PathBase("deploy/to/homelab"))
}
func TestPathBase_Root(t *testing.T) {
assert.Equal(t, "/", core.PathBase("/"))
}
func TestPathBase_Empty(t *testing.T) {
assert.Equal(t, ".", core.PathBase(""))
}
func TestPathDir(t *testing.T) {
assert.Equal(t, "/Users/snider/Code", core.PathDir("/Users/snider/Code/core"))
}
func TestPathDir_Root(t *testing.T) {
assert.Equal(t, "/", core.PathDir("/file"))
}
func TestPathDir_NoDir(t *testing.T) {
assert.Equal(t, ".", core.PathDir("file.go"))
}
func TestPathExt(t *testing.T) {
assert.Equal(t, ".go", core.PathExt("main.go"))
assert.Equal(t, "", core.PathExt("Makefile"))
assert.Equal(t, ".gz", core.PathExt("archive.tar.gz"))
}
func TestPath_EnvConsistency(t *testing.T) {
assert.Equal(t, core.Env("DIR_HOME"), core.Path())
}
func TestPathGlob_Good(t *testing.T) {
dir := t.TempDir()
os.WriteFile(filepath.Join(dir, "a.txt"), []byte("a"), 0644)
os.WriteFile(filepath.Join(dir, "b.txt"), []byte("b"), 0644)
os.WriteFile(filepath.Join(dir, "c.log"), []byte("c"), 0644)
matches := core.PathGlob(filepath.Join(dir, "*.txt"))
assert.Len(t, matches, 2)
}
func TestPathGlob_NoMatch(t *testing.T) {
matches := core.PathGlob("/nonexistent/pattern-*.xyz")
assert.Empty(t, matches)
}
func TestPathIsAbs_Good(t *testing.T) {
assert.True(t, core.PathIsAbs("/tmp"))
assert.True(t, core.PathIsAbs("/"))
assert.False(t, core.PathIsAbs("relative"))
assert.False(t, core.PathIsAbs(""))
}
func TestCleanPath_Good(t *testing.T) {
assert.Equal(t, "/a/b", core.CleanPath("/a//b", "/"))
assert.Equal(t, "/a/c", core.CleanPath("/a/b/../c", "/"))
assert.Equal(t, "/", core.CleanPath("/", "/"))
assert.Equal(t, ".", core.CleanPath("", "/"))
}
func TestPathDir_TrailingSlash(t *testing.T) {
// Trailing slash is stripped, then dir of /Users/snider/Code = /Users/snider
result := core.PathDir("/Users/snider/Code/")
assert.Equal(t, "/Users/snider/Code", result)
}

View file

@ -74,3 +74,48 @@ func TestRuntime_Lifecycle_Good(t *testing.T) {
assert.True(t, result.OK) assert.True(t, result.OK)
assert.True(t, started) assert.True(t, started)
} }
func TestRuntime_ServiceShutdown_Good(t *testing.T) {
stopped := false
r := NewWithFactories(nil, map[string]ServiceFactory{
"test": func() Result {
return Result{Value: Service{
OnStart: func() Result { return Result{OK: true} },
OnStop: func() Result { stopped = true; return Result{OK: true} },
}, OK: true}
},
})
assert.True(t, r.OK)
rt := r.Value.(*Runtime)
rt.ServiceStartup(context.Background(), nil)
result := rt.ServiceShutdown(context.Background())
assert.True(t, result.OK)
assert.True(t, stopped)
}
func TestRuntime_ServiceShutdown_NilCore_Good(t *testing.T) {
rt := &Runtime{}
result := rt.ServiceShutdown(context.Background())
assert.True(t, result.OK)
}
func TestCore_ServiceShutdown_Good(t *testing.T) {
stopped := false
c := New()
c.Service("test", Service{
OnStart: func() Result { return Result{OK: true} },
OnStop: func() Result { stopped = true; return Result{OK: true} },
})
c.ServiceStartup(context.Background(), nil)
result := c.ServiceShutdown(context.Background())
assert.True(t, result.OK)
assert.True(t, stopped)
}
func TestCore_Context_Good(t *testing.T) {
c := New()
c.ServiceStartup(context.Background(), nil)
assert.NotNil(t, c.Context())
c.ServiceShutdown(context.Background())
}

View file

@ -1,6 +1,7 @@
package core_test package core_test
import ( import (
"context"
"sync" "sync"
"testing" "testing"
"time" "time"
@ -46,6 +47,61 @@ func TestPerformAsync_Progress_Good(t *testing.T) {
c.Progress(taskID, 0.5, "halfway", "work") c.Progress(taskID, 0.5, "halfway", "work")
} }
func TestPerformAsync_Completion_Good(t *testing.T) {
c := New()
completed := make(chan ActionTaskCompleted, 1)
c.RegisterTask(func(_ *Core, task Task) Result {
return Result{Value: "result", OK: true}
})
c.RegisterAction(func(_ *Core, msg Message) Result {
if evt, ok := msg.(ActionTaskCompleted); ok {
completed <- evt
}
return Result{OK: true}
})
c.PerformAsync("work")
select {
case evt := <-completed:
assert.Nil(t, evt.Error)
assert.Equal(t, "result", evt.Result)
case <-time.After(2 * time.Second):
t.Fatal("timed out waiting for completion")
}
}
func TestPerformAsync_NoHandler_Good(t *testing.T) {
c := New()
completed := make(chan ActionTaskCompleted, 1)
c.RegisterAction(func(_ *Core, msg Message) Result {
if evt, ok := msg.(ActionTaskCompleted); ok {
completed <- evt
}
return Result{OK: true}
})
c.PerformAsync("unhandled")
select {
case evt := <-completed:
assert.NotNil(t, evt.Error)
case <-time.After(2 * time.Second):
t.Fatal("timed out")
}
}
func TestPerformAsync_AfterShutdown_Bad(t *testing.T) {
c := New()
c.ServiceStartup(context.Background(), nil)
c.ServiceShutdown(context.Background())
r := c.PerformAsync("should not run")
assert.False(t, r.OK)
}
// --- RegisterAction + RegisterActions --- // --- RegisterAction + RegisterActions ---
func TestRegisterAction_Good(t *testing.T) { func TestRegisterAction_Good(t *testing.T) {