diff --git a/internal/greeter/greeter_test.go b/internal/greeter/greeter_test.go index d2d0340..71b6905 100644 --- a/internal/greeter/greeter_test.go +++ b/internal/greeter/greeter_test.go @@ -4,9 +4,9 @@ import ( "testing" "gitea.djmil.dev/go/template/internal/greeter" + "gitea.djmil.dev/go/template/pkg/check" "gitea.djmil.dev/go/template/pkg/logger" "gitea.djmil.dev/go/template/pkg/result" - "gitea.djmil.dev/go/template/pkg/testutil" ) // ── Service (unit tests) ────────────────────────────────────────────────────── @@ -16,12 +16,12 @@ func TestGreet(t *testing.T) { t.Run("returns personalized greeting", func(t *testing.T) { msg, err := svc.Greet("World").Unwrap() - testutil.NoError(t, err) - testutil.Equal(t, msg, "Hello, World!") + check.NoError(t, err) + check.Equal(t, msg, "Hello, World!") }) t.Run("rejects empty name", func(t *testing.T) { - testutil.ErrorContains(t, svc.Greet("").Err(), "name must not be empty") + check.ErrorContains(t, svc.Greet(""), "name must not be empty") }) } @@ -45,6 +45,6 @@ func TestFakeUsageExample(t *testing.T) { } msg, err := fake.Greet("Alice").Unwrap() - testutil.NoError(t, err) - testutil.Equal(t, msg, "Hello, Alice!") + check.NoError(t, err) + check.Equal(t, msg, "Hello, Alice!") } diff --git a/pkg/check/check.go b/pkg/check/check.go new file mode 100644 index 0000000..0f4bdbb --- /dev/null +++ b/pkg/check/check.go @@ -0,0 +1,93 @@ +// Package check provides lightweight test helpers to reduce boilerplate in +// table-driven tests. +// +// Error-family functions (NoError, Error, ErrorContains) accept either a plain +// error or a result.Expect[T] value — the package extracts the error from +// whichever type it receives. +// +// Every helper calls t.Helper() so failures are reported at the call site. +package check + +import ( + "strings" + "testing" + + "gitea.djmil.dev/go/template/pkg/result" +) + +// extractErr pulls an error out of v, which must be error or result.Expect[T]. +// Fatally fails the test for any other type. +func extractErr(t *testing.T, v any) error { + t.Helper() + if v == nil { + return nil + } + switch x := v.(type) { + case error: + return x + case interface{ Err() error }: + return x.Err() + default: + t.Fatalf("check: unsupported type %T (want error or result.Expect[T])", v) + return nil + } +} + +// NoError fails the test if v contains a non-nil error. +func NoError(t *testing.T, v any) { + t.Helper() + if err := extractErr(t, v); err != nil { + t.Fatalf("unexpected error: %v", err) + } +} + +// Error fails the test if v contains no error. +func Error(t *testing.T, v any) { + t.Helper() + if extractErr(t, v) == nil { + t.Fatal("expected error, got nil") + } +} + +// ErrorContains fails the test if v contains no error or its message does not +// contain substr. +func ErrorContains(t *testing.T, v any, substr string) { + t.Helper() + err := extractErr(t, v) + if err == nil { + t.Fatal("expected error, got nil") + return + } + if !strings.Contains(err.Error(), substr) { + t.Errorf("error %q does not contain %q", err.Error(), substr) + } +} + +// Equal fails the test if got != want. +func Equal[T comparable](t *testing.T, got, want T) { + t.Helper() + if got != want { + t.Errorf("got %v, want %v", got, want) + } +} + +// Ok fails the test if r holds an error, then returns the value. +func Ok[T any](t *testing.T, r result.Expect[T]) T { + t.Helper() + if r.Err() != nil { + t.Fatalf("unexpected error: %v", r.Err()) + } + return r.Value() +} + +// OkNotNil fails the test if r holds an error or its value is the zero value. +// T must be a pointer or comparable type where zero means absent. +func OkNotNil[T comparable](t *testing.T, r result.Expect[T]) T { + t.Helper() + v := Ok(t, r) + var zero T + if v == zero { + t.Fatal("expected non-nil value, got zero") + } + return v +} diff --git a/pkg/logger/logger_test.go b/pkg/logger/logger_test.go index 3ca6e2d..c981255 100644 --- a/pkg/logger/logger_test.go +++ b/pkg/logger/logger_test.go @@ -3,8 +3,8 @@ package logger_test import ( "testing" + "gitea.djmil.dev/go/template/pkg/check" "gitea.djmil.dev/go/template/pkg/logger" - "gitea.djmil.dev/go/template/pkg/testutil" ) func TestNewCLI(t *testing.T) { @@ -24,10 +24,10 @@ func TestNewCLI(t *testing.T) { // In tests stderr is not a terminal — NewCLI uses JSON path. r := logger.NewCLI(tc.level, nil) if tc.wantErr { - testutil.ResultErr(t, r) + check.Error(t, r) return } - testutil.ResultOkNotNil(t, r) + check.OkNotNil(t, r) }) } } @@ -49,10 +49,10 @@ func TestNew(t *testing.T) { t.Run(tc.level, func(t *testing.T) { r := logger.New(tc.level) if tc.wantErr { - testutil.ResultErr(t, r) + check.Error(t, r) return } - testutil.ResultOkNotNil(t, r) + check.OkNotNil(t, r) }) } } diff --git a/pkg/testutil/testutil.go b/pkg/testutil/testutil.go deleted file mode 100644 index 4aa4071..0000000 --- a/pkg/testutil/testutil.go +++ /dev/null @@ -1,75 +0,0 @@ -// Package testutil provides lightweight test helpers to reduce boilerplate in -// table-driven tests. -// -// Every helper calls t.Helper() so failures are reported at the call site, not -// inside this package. -package testutil - -import ( - "strings" - "testing" - - "gitea.djmil.dev/go/template/pkg/result" -) - -// NoError fails the test immediately if err is not nil. -func NoError(t *testing.T, err error) { - t.Helper() - if err != nil { - t.Fatalf("unexpected error: %v", err) - } -} - -// Error fails the test if err is nil. -func Error(t *testing.T, err error) { - t.Helper() - if err == nil { - t.Fatal("expected error, got nil") - } -} - -// ErrorContains fails the test if err is nil or its message does not contain substr. -func ErrorContains(t *testing.T, err error, substr string) { - t.Helper() - Error(t, err) - if !strings.Contains(err.Error(), substr) { - t.Errorf("error %q does not contain %q", err.Error(), substr) - } -} - -// Equal fails the test if got != want. -func Equal[T comparable](t *testing.T, got, want T) { - t.Helper() - if got != want { - t.Errorf("got %v, want %v", got, want) - } -} - -// ResultOk fails the test if r holds an error, then returns the value. -func ResultOk[T any](t *testing.T, r result.Expect[T]) T { - t.Helper() - if r.Err() != nil { - t.Fatalf("unexpected error: %v", r.Err()) - } - return r.Value() -} - -// ResultOkNotNil fails the test if r holds an error or its value is nil. -// T must be a pointer type. -func ResultOkNotNil[T comparable](t *testing.T, r result.Expect[T]) T { - t.Helper() - v := ResultOk(t, r) - var zero T - if v == zero { - t.Fatal("expected non-nil value, got nil") - } - return v -} - -// ResultErr fails the test if r does not hold an error. -func ResultErr[T any](t *testing.T, r result.Expect[T]) { - t.Helper() - if r.Err() == nil { - t.Fatal("expected error, got nil") - } -}