template/pkg/result/panic_test.go
2026-05-05 00:12:54 +00:00

67 lines
1.9 KiB
Go

package result_test
import (
"os"
"os/exec"
"strings"
"testing"
"gitea.djmil.dev/go/template/pkg/result"
)
// TestMustCollected verifies that Must() failures are collected by Run as
// normal errors, not left as unhandled panics. By contrast, genuine runtime
// panics (nil-deref, index out of bounds, etc.) are NOT collected — they
// propagate and crash the program. That behavior cannot be unit-tested
// without a subprocess, so it is only documented here.
func TestMustCollected(t *testing.T) {
err := result.Run(func() {
result.Errf[int]("unrecoverable").Must()
})
if err == nil {
t.Fatal("expected error, got nil")
}
if err.Error() != "unrecoverable" {
t.Fatalf("unexpected error: %v", err)
}
}
// TestGenuineRuntimePanicPropagates verifies that genuine runtime panics
// (nil-deref, index out of bounds, etc.) are NOT collected by Go/Run — they
// crash the program. Tested via subprocess so the crash doesn't kill the suite.
func TestGenuineRuntimePanicPropagates(t *testing.T) {
if os.Getenv("RESULT_TEST_CRASH") == "1" {
_ = result.Run(func() {
panic("genuine runtime panic") // not a *stackError, so Run must not collect it
})
return
}
cmd := exec.Command(os.Args[0], "-test.run=TestGenuineRuntimePanicPropagates")
cmd.Env = append(os.Environ(), "RESULT_TEST_CRASH=1")
if cmd.Run() == nil {
t.Fatal("expected subprocess to crash, but it exited cleanly")
}
}
// TestStackTrace verifies that StackTrace returns a non-empty string containing
// the call site of the failing Expect call.
func TestStackTrace(t *testing.T) {
err := result.Run(func() {
parsePort("not-a-number").Expect("parse port")
})
if err == nil {
t.Fatal("expected error, got nil")
}
trace := result.StackTrace(err)
if trace == "" {
t.Fatal("expected non-empty stack trace")
}
if !strings.Contains(trace, "panic_test.go") {
t.Fatalf("expected trace to reference panic_test.go, got:\n%s", trace)
}
}