Modernizes several `internal/*` packages under the Go proxy, replaces
third-party forks with standard-library primitives, and brings the
test suite from near-zero to high coverage across the touched packages.
Package changes
- **`internal/errors`** — Rewrites the `pkg/errors` fork as a thin
wrapper
over stdlib `errors`. A single `withStack` struct captures stack
traces via `runtime.Callers`; `fmt.Errorf("%w", ...)` handles all
message wrapping. Restores `errors.Is`/`As`/`Unwrap` chain traversal
(silently broken in the fork) and deletes ~190 lines of stack/frame
formatting. `Is`, `As`, `Unwrap`, and `Join` are re-exported so
callers need a single import.
- **`internal/logger`** — Swaps stdlib `log.Logger` for `log/slog` JSON
handlers with UTC timestamps and custom level labels (`verb`, `debug`,
`warn`, `error`). Hides `withContextID` (no external callers).
- **`internal/sync`** — Converts `Map[K, V]` from a concrete struct to
an interface with a `NewMap` constructor for testability.
- **`internal/signal`** — Adds `signalNotify` / `osExit` indirections so
`InstallSignals` and `InstallForceQuit` can be exercised without real
OS signals or process termination.
- **`internal/utils`** — Drops deprecated `io/ioutil` and the stdlib
`errors` alias (the internal `errors` package re-exports what's
needed).
- **`internal/version`** — No code changes; fully covered by new tests.
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
234 lines
5.8 KiB
Go
234 lines
5.8 KiB
Go
// Copyright (c) 2026 Winlin
|
|
//
|
|
// SPDX-License-Identifier: MIT
|
|
package errors
|
|
|
|
import (
|
|
stderrors "errors"
|
|
"fmt"
|
|
"strings"
|
|
"testing"
|
|
)
|
|
|
|
func TestNew_MessageAndStack(t *testing.T) {
|
|
err := New("boom")
|
|
if err == nil {
|
|
t.Fatal("New returned nil")
|
|
}
|
|
if err.Error() != "boom" {
|
|
t.Fatalf("Error() = %q, want %q", err.Error(), "boom")
|
|
}
|
|
ws, ok := err.(*withStack)
|
|
if !ok {
|
|
t.Fatalf("New did not return *withStack, got %T", err)
|
|
}
|
|
if len(ws.StackTrace()) == 0 {
|
|
t.Fatal("StackTrace is empty")
|
|
}
|
|
}
|
|
|
|
func TestErrorf_FormatsMessage(t *testing.T) {
|
|
err := Errorf("code=%d reason=%s", 42, "oops")
|
|
if err.Error() != "code=42 reason=oops" {
|
|
t.Fatalf("Error() = %q", err.Error())
|
|
}
|
|
}
|
|
|
|
func TestErrorf_SupportsWrapVerb(t *testing.T) {
|
|
root := stderrors.New("root")
|
|
err := Errorf("ctx: %w", root)
|
|
if !stderrors.Is(err, root) {
|
|
t.Fatal("errors.Is did not find root through Errorf(%w)")
|
|
}
|
|
}
|
|
|
|
func TestWithStack_NilReturnsNil(t *testing.T) {
|
|
if got := WithStack(nil); got != nil {
|
|
t.Fatalf("WithStack(nil) = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWithStack_PreservesMessage(t *testing.T) {
|
|
inner := stderrors.New("plain")
|
|
err := WithStack(inner)
|
|
if err.Error() != "plain" {
|
|
t.Fatalf("Error() = %q, want %q", err.Error(), "plain")
|
|
}
|
|
if !stderrors.Is(err, inner) {
|
|
t.Fatal("errors.Is did not find inner through WithStack")
|
|
}
|
|
}
|
|
|
|
func TestWithMessage_NilReturnsNil(t *testing.T) {
|
|
if got := WithMessage(nil, "ignored"); got != nil {
|
|
t.Fatalf("WithMessage(nil) = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWithMessage_PrependsAndWraps(t *testing.T) {
|
|
inner := stderrors.New("root")
|
|
err := WithMessage(inner, "ctx")
|
|
if err.Error() != "ctx: root" {
|
|
t.Fatalf("Error() = %q", err.Error())
|
|
}
|
|
if !stderrors.Is(err, inner) {
|
|
t.Fatal("errors.Is did not traverse WithMessage")
|
|
}
|
|
// WithMessage must not capture a stack — verify the result is not a *withStack.
|
|
if _, ok := err.(*withStack); ok {
|
|
t.Fatal("WithMessage should not attach a stack")
|
|
}
|
|
}
|
|
|
|
func TestWrap_NilReturnsNil(t *testing.T) {
|
|
if got := Wrap(nil, "ignored"); got != nil {
|
|
t.Fatalf("Wrap(nil) = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWrap_MessageAndStackAndChain(t *testing.T) {
|
|
inner := stderrors.New("root")
|
|
err := Wrap(inner, "ctx")
|
|
if err.Error() != "ctx: root" {
|
|
t.Fatalf("Error() = %q", err.Error())
|
|
}
|
|
ws, ok := err.(*withStack)
|
|
if !ok {
|
|
t.Fatalf("Wrap did not return *withStack, got %T", err)
|
|
}
|
|
if len(ws.StackTrace()) == 0 {
|
|
t.Fatal("StackTrace is empty")
|
|
}
|
|
if !stderrors.Is(err, inner) {
|
|
t.Fatal("errors.Is did not traverse Wrap")
|
|
}
|
|
}
|
|
|
|
func TestWrapf_NilReturnsNil(t *testing.T) {
|
|
if got := Wrapf(nil, "ignored %d", 1); got != nil {
|
|
t.Fatalf("Wrapf(nil) = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestWrapf_FormatsAndChains(t *testing.T) {
|
|
inner := stderrors.New("root")
|
|
err := Wrapf(inner, "ctx=%d op=%s", 7, "read")
|
|
if err.Error() != "ctx=7 op=read: root" {
|
|
t.Fatalf("Error() = %q", err.Error())
|
|
}
|
|
if !stderrors.Is(err, inner) {
|
|
t.Fatal("errors.Is did not traverse Wrapf")
|
|
}
|
|
}
|
|
|
|
func TestCause_NilReturnsNil(t *testing.T) {
|
|
if got := Cause(nil); got != nil {
|
|
t.Fatalf("Cause(nil) = %v, want nil", got)
|
|
}
|
|
}
|
|
|
|
func TestCause_NoUnwrapReturnsSelf(t *testing.T) {
|
|
root := stderrors.New("root")
|
|
if got := Cause(root); got != root {
|
|
t.Fatalf("Cause(root) = %v, want root", got)
|
|
}
|
|
}
|
|
|
|
func TestCause_WalksToRoot(t *testing.T) {
|
|
root := stderrors.New("root")
|
|
err := Wrap(Wrap(WithMessage(root, "a"), "b"), "c")
|
|
if got := Cause(err); got != root {
|
|
t.Fatalf("Cause = %v, want root", got)
|
|
}
|
|
}
|
|
|
|
func TestUnwrap_ReturnsInner(t *testing.T) {
|
|
inner := stderrors.New("inner")
|
|
err := WithStack(inner)
|
|
if got := stderrors.Unwrap(err); got != inner {
|
|
t.Fatalf("Unwrap = %v, want inner", got)
|
|
}
|
|
}
|
|
|
|
func TestFormat_S(t *testing.T) {
|
|
err := New("msg")
|
|
got := fmt.Sprintf("%s", err)
|
|
if got != "msg" {
|
|
t.Fatalf("%%s = %q, want %q", got, "msg")
|
|
}
|
|
}
|
|
|
|
func TestFormat_VFallsThroughToS(t *testing.T) {
|
|
err := New("msg")
|
|
got := fmt.Sprintf("%v", err)
|
|
if got != "msg" {
|
|
t.Fatalf("%%v = %q, want %q", got, "msg")
|
|
}
|
|
}
|
|
|
|
func TestFormat_VPlusIncludesStack(t *testing.T) {
|
|
err := New("msg")
|
|
got := fmt.Sprintf("%+v", err)
|
|
if !strings.HasPrefix(got, "msg") {
|
|
t.Fatalf("%%+v output does not start with message: %q", got)
|
|
}
|
|
// Must include this test function in the captured stack.
|
|
if !strings.Contains(got, "TestFormat_VPlusIncludesStack") {
|
|
t.Fatalf("%%+v output missing caller frame:\n%s", got)
|
|
}
|
|
// Must include a file:line reference.
|
|
if !strings.Contains(got, "errors_test.go:") {
|
|
t.Fatalf("%%+v output missing file:line:\n%s", got)
|
|
}
|
|
}
|
|
|
|
func TestFormat_Q(t *testing.T) {
|
|
err := New("msg")
|
|
got := fmt.Sprintf("%q", err)
|
|
if got != `"msg"` {
|
|
t.Fatalf("%%q = %q, want %q", got, `"msg"`)
|
|
}
|
|
}
|
|
|
|
func TestIs_ThroughWrapChain(t *testing.T) {
|
|
sentinel := stderrors.New("sentinel")
|
|
err := Wrap(WithMessage(WithStack(sentinel), "mid"), "outer")
|
|
if !stderrors.Is(err, sentinel) {
|
|
t.Fatal("errors.Is failed to traverse Wrap/WithMessage/WithStack chain")
|
|
}
|
|
}
|
|
|
|
type typedErr struct{ code int }
|
|
|
|
func (t *typedErr) Error() string { return fmt.Sprintf("typed(%d)", t.code) }
|
|
|
|
func TestAs_ThroughWrapChain(t *testing.T) {
|
|
target := &typedErr{code: 7}
|
|
err := Wrap(WithStack(target), "ctx")
|
|
var got *typedErr
|
|
if !stderrors.As(err, &got) {
|
|
t.Fatal("errors.As failed to find *typedErr in chain")
|
|
}
|
|
if got.code != 7 {
|
|
t.Fatalf("As returned code=%d, want 7", got.code)
|
|
}
|
|
}
|
|
|
|
func TestReExports_AreStdlib(t *testing.T) {
|
|
// Sanity: the re-exports must actually be the stdlib functions.
|
|
a := stderrors.New("a")
|
|
b := stderrors.New("b")
|
|
joined := Join(a, b)
|
|
if !Is(joined, a) || !Is(joined, b) {
|
|
t.Fatal("Join/Is re-exports do not match stdlib behavior")
|
|
}
|
|
if Unwrap(WithStack(a)) != a {
|
|
t.Fatal("Unwrap re-export does not match stdlib behavior")
|
|
}
|
|
var target *typedErr
|
|
te := &typedErr{code: 1}
|
|
if !As(WithStack(te), &target) {
|
|
t.Fatal("As re-export does not match stdlib behavior")
|
|
}
|
|
}
|