package result_test import ( "errors" "strings" "testing" "gitea.djmil.dev/go/template/pkg/result" ) var errRoot = errors.New("root cause") // Intended usage of Wrap is demonstrated in example_test.go (parsePort). These // tests pin the behavioral guarantees that an example's Output cannot assert: // chain preservation, file:line stamping, stack survival, and the panic // contract on misuse. // TestWrapRetypesVerbatim verifies that Wrap with no message carries a failure // from one value type to another, preserving the error chain unchanged. func TestWrapRetypesVerbatim(t *testing.T) { src := result.Err[int](errRoot) // Expect[int] dst := result.Wrap[string](src) // Expect[string] if !errors.Is(dst.Err(), errRoot) { t.Fatalf("errors.Is chain not preserved: %v", dst.Err()) } if dst.Err().Error() != errRoot.Error() { t.Fatalf("verbatim wrap changed the message: got %q want %q", dst.Err(), errRoot) } } // TestWrapAnnotates verifies that a context message is prepended with the // caller's file:line while the underlying chain stays intact and leads the // trace (outermost context first, root cause last). func TestWrapAnnotates(t *testing.T) { src := result.Failf[int]("parse %d", 7) dst := result.Wrap[string](src, "load config id=%d", 42) msg := dst.Err().Error() if !strings.Contains(msg, "load config id=42") { t.Fatalf("formatted context missing: %q", msg) } if !strings.Contains(msg, "wrap_test.go:") { t.Fatalf("caller file:line not prepended: %q", msg) } if !strings.Contains(msg, "parse 7") { t.Fatalf("underlying cause lost: %q", msg) } if first, _, _ := strings.Cut(msg, "\n"); !strings.Contains(first, "load config id=42") { t.Fatalf("context should head the trace, got first line %q", first) } } // TestWrapPreservesStackError verifies that a failure captured by Expect (a // *stackError) survives the retype, so StackTrace still resolves it. func TestWrapPreservesStackError(t *testing.T) { captured := result.Run(func() { result.Err[int](errRoot).Expect("inner") }) if captured == nil { t.Fatal("setup: expected Run to collect an error") } dst := result.Wrap[string](result.Err[int](captured), "outer") if result.StackTrace(dst.Err()) == "" { t.Fatal("stack trace lost through Wrap") } } // TestWrapOnSuccessPanics locks in the documented contract: Wrap is only valid // on a failed result; calling it on a success is a programmer error. func TestWrapOnSuccessPanics(t *testing.T) { defer func() { if recover() == nil { t.Fatal("expected Wrap on a successful Expect to panic") } }() _ = result.Wrap[string](result.Ok(1)) }