diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index 6624d4055..da520c3b0 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -440,6 +440,40 @@ func ErrorAs(t T, err error, target any, msgAndArgs ...any) bool { return assertions.ErrorAs(t, err, target, msgAndArgs...) } +// ErrorAsType asserts that at least one of the errors in err's chain is of type E. +// +// It is the type-safe counterpart of [ErrorAs], built on the go1.26 [errors.AsType]: +// the expected type is the type parameter E (checked at compile time, no reflection), +// rather than the untyped any target used by [ErrorAs]. +// +// target receives the matched error when the assertion succeeds. It may be nil, for +// callers that only want to know whether the chain holds an error of type E: in that +// case E cannot be inferred and must be supplied explicitly. +// +// This assertion requires go1.26 or newer; it is unavailable on older toolchains. +// +// # Usage +// +// // capture the matched error (E is inferred from target): +// var target *MyError +// assertions.ErrorAsType(t, err, &target) +// +// // only check, discarding the value (E given explicitly): +// assertions.ErrorAsType[*MyError](t, err, nil) +// +// # Examples +// +// success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) +// failure: ErrTest, new(*dummyError) +// +// Upon failure, the test [T] is marked as failed and continues execution. +func ErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.ErrorAsType[E](t, err, target, msgAndArgs...) +} + // ErrorContains asserts that a function returned a non-nil error (i.e. an // error) and that the error contains the specified substring. // @@ -2471,6 +2505,36 @@ func NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool { return assertions.NotErrorAs(t, err, target, msgAndArgs...) } +// NotErrorAsType asserts that none of the errors in err's chain is of type E. +// +// It is the type-safe counterpart of [NotErrorAs], built on the go1.26 [errors.AsType]. +// +// target is only used to infer the type parameter E and is never assigned; it may be nil, +// in which case E must be supplied explicitly. +// +// This assertion requires go1.26 or newer; it is unavailable on older toolchains. +// +// # Usage +// +// var target *MyError +// assertions.NotErrorAsType(t, err, &target) +// +// // or, supplying E explicitly: +// assertions.NotErrorAsType[*MyError](t, err, nil) +// +// # Examples +// +// success: ErrTest, new(*dummyError) +// failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotErrorAsType[E](t, err, target, msgAndArgs...) +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // // This is a wrapper for [errors.Is]. diff --git a/assert/assert_assertions_go126.go b/assert/assert_assertions_go126.go deleted file mode 100644 index 2541109cc..000000000 --- a/assert/assert_assertions_go126.go +++ /dev/null @@ -1,76 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package assert - -import ( - "github.com/go-openapi/testify/v2/internal/assertions" -) - -// ErrorAsType asserts that at least one of the errors in err's chain is of type E. -// -// It is the type-safe counterpart of [ErrorAs], built on the go1.26 [errors.AsType]: -// the expected type is the type parameter E (checked at compile time, no reflection), -// rather than the untyped any target used by [ErrorAs]. -// -// target receives the matched error when the assertion succeeds. It may be nil, for -// callers that only want to know whether the chain holds an error of type E: in that -// case E cannot be inferred and must be supplied explicitly. -// -// This assertion requires go1.26 or newer; it is unavailable on older toolchains. -// -// # Usage -// -// // capture the matched error (E is inferred from target): -// var target *MyError -// assertions.ErrorAsType(t, err, &target) -// -// // only check, discarding the value (E given explicitly): -// assertions.ErrorAsType[*MyError](t, err, nil) -// -// # Examples -// -// success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) -// failure: ErrTest, new(*dummyError) -// -// Upon failure, the test [T] is marked as failed and continues execution. -func ErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) bool { - if h, ok := t.(H); ok { - h.Helper() - } - return assertions.ErrorAsType[E](t, err, target, msgAndArgs...) -} - -// NotErrorAsType asserts that none of the errors in err's chain is of type E. -// -// It is the type-safe counterpart of [NotErrorAs], built on the go1.26 [errors.AsType]. -// -// target is only used to infer the type parameter E and is never assigned; it may be nil, -// in which case E must be supplied explicitly. -// -// This assertion requires go1.26 or newer; it is unavailable on older toolchains. -// -// # Usage -// -// var target *MyError -// assertions.NotErrorAsType(t, err, &target) -// -// // or, supplying E explicitly: -// assertions.NotErrorAsType[*MyError](t, err, nil) -// -// # Examples -// -// success: ErrTest, new(*dummyError) -// failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) -// -// Upon failure, the test [T] is marked as failed and continues execution. -func NotErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) bool { - if h, ok := t.(H); ok { - h.Helper() - } - return assertions.NotErrorAsType[E](t, err, target, msgAndArgs...) -} diff --git a/assert/assert_assertions_go126_test.go b/assert/assert_assertions_go126_test.go deleted file mode 100644 index 481b98f22..000000000 --- a/assert/assert_assertions_go126_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package assert - -import ( - "fmt" - "testing" -) - -func TestErrorAsType(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := ErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) - if !result { - t.Error("ErrorAsType should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := ErrorAsType(mock, ErrTest, new(*dummyError)) - if result { - t.Error("ErrorAsType should return false on failure") - } - if !mock.failed { - t.Error("ErrorAsType should mark test as failed") - } - }) -} - -func TestNotErrorAsType(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := NotErrorAsType(mock, ErrTest, new(*dummyError)) - if !result { - t.Error("NotErrorAsType should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := NotErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) - if result { - t.Error("NotErrorAsType should return false on failure") - } - if !mock.failed { - t.Error("NotErrorAsType should mark test as failed") - } - }) -} diff --git a/assert/assert_assertions_test.go b/assert/assert_assertions_test.go index 553e79db4..95db68c7f 100644 --- a/assert/assert_assertions_test.go +++ b/assert/assert_assertions_test.go @@ -476,6 +476,33 @@ func TestErrorAs(t *testing.T) { }) } +func TestErrorAsType(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := ErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + if !result { + t.Error("ErrorAsType should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := ErrorAsType(mock, ErrTest, new(*dummyError)) + if result { + t.Error("ErrorAsType should return false on failure") + } + if !mock.failed { + t.Error("ErrorAsType should mark test as failed") + } + }) +} + func TestErrorContains(t *testing.T) { t.Parallel() @@ -2549,6 +2576,33 @@ func TestNotErrorAs(t *testing.T) { }) } +func TestNotErrorAsType(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotErrorAsType(mock, ErrTest, new(*dummyError)) + if !result { + t.Error("NotErrorAsType should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + if result { + t.Error("NotErrorAsType should return false on failure") + } + if !mock.failed { + t.Error("NotErrorAsType should mark test as failed") + } + }) +} + func TestNotErrorIs(t *testing.T) { t.Parallel() diff --git a/assert/assert_examples_go126_test.go b/assert/assert_examples_go126_test.go deleted file mode 100644 index 4198f3b8a..000000000 --- a/assert/assert_examples_go126_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package assert_test - -import ( - "fmt" - "testing" - - "github.com/go-openapi/testify/v2/assert" -) - -func ExampleErrorAsType() { - t := new(testing.T) // should come from testing, e.g. func TestErrorAsType(t *testing.T) - success := assert.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) - fmt.Printf("success: %t\n", success) - - // Output: success: true -} - -func ExampleNotErrorAsType() { - t := new(testing.T) // should come from testing, e.g. func TestNotErrorAsType(t *testing.T) - success := assert.NotErrorAsType(t, assert.ErrTest, new(*dummyError)) - fmt.Printf("success: %t\n", success) - - // Output: success: true -} diff --git a/assert/assert_examples_test.go b/assert/assert_examples_test.go index 1760ddd7e..f6c513b64 100644 --- a/assert/assert_examples_test.go +++ b/assert/assert_examples_test.go @@ -160,6 +160,14 @@ func ExampleErrorAs() { // Output: success: true } +func ExampleErrorAsType() { + t := new(testing.T) // should come from testing, e.g. func TestErrorAsType(t *testing.T) + success := assert.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleErrorContains() { t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) success := assert.ErrorContains(t, assert.ErrTest, "general error") @@ -792,6 +800,14 @@ func ExampleNotErrorAs() { // Output: success: true } +func ExampleNotErrorAsType() { + t := new(testing.T) // should come from testing, e.g. func TestNotErrorAsType(t *testing.T) + success := assert.NotErrorAsType(t, assert.ErrTest, new(*dummyError)) + fmt.Printf("success: %t\n", success) + + // Output: success: true +} + func ExampleNotErrorIs() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) success := assert.NotErrorIs(t, assert.ErrTest, io.EOF) diff --git a/assert/assert_format.go b/assert/assert_format.go index 531f549e3..5f4caa9a8 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -185,6 +185,16 @@ func ErrorAsf(t T, err error, target any, msg string, args ...any) bool { return assertions.ErrorAs(t, err, target, forwardArgs(msg, args)...) } +// ErrorAsTypef is the same as [ErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func ErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.ErrorAsType[E](t, err, target, forwardArgs(msg, args)...) +} + // ErrorContainsf is the same as [ErrorContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. @@ -965,6 +975,16 @@ func NotErrorAsf(t T, err error, target any, msg string, args ...any) bool { return assertions.NotErrorAs(t, err, target, forwardArgs(msg, args)...) } +// NotErrorAsTypef is the same as [NotErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and continues execution. +func NotErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) bool { + if h, ok := t.(H); ok { + h.Helper() + } + return assertions.NotErrorAsType[E](t, err, target, forwardArgs(msg, args)...) +} + // NotErrorIsf is the same as [NotErrorIs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. diff --git a/assert/assert_format_go126.go b/assert/assert_format_go126.go deleted file mode 100644 index 8520eb6e8..000000000 --- a/assert/assert_format_go126.go +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package assert - -import ( - "github.com/go-openapi/testify/v2/internal/assertions" -) - -// ErrorAsTypef is the same as [ErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. -// -// Upon failure, the test [T] is marked as failed and continues execution. -func ErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) bool { - if h, ok := t.(H); ok { - h.Helper() - } - return assertions.ErrorAsType[E](t, err, target, forwardArgs(msg, args)...) -} - -// NotErrorAsTypef is the same as [NotErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. -// -// Upon failure, the test [T] is marked as failed and continues execution. -func NotErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) bool { - if h, ok := t.(H); ok { - h.Helper() - } - return assertions.NotErrorAsType[E](t, err, target, forwardArgs(msg, args)...) -} diff --git a/assert/assert_format_go126_test.go b/assert/assert_format_go126_test.go deleted file mode 100644 index d46262deb..000000000 --- a/assert/assert_format_go126_test.go +++ /dev/null @@ -1,67 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package assert - -import ( - "fmt" - "testing" -) - -func TestErrorAsTypef(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := ErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") - if !result { - t.Error("ErrorAsTypef should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := ErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") - if result { - t.Error("ErrorAsTypef should return false on failure") - } - if !mock.failed { - t.Error("ErrorAsTypef should mark test as failed") - } - }) -} - -func TestNotErrorAsTypef(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := NotErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") - if !result { - t.Error("NotErrorAsTypef should return true on success") - } - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockT) - result := NotErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") - if result { - t.Error("NotErrorAsTypef should return false on failure") - } - if !mock.failed { - t.Error("NotErrorAsTypef should mark test as failed") - } - }) -} diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index feb809f26..7becdc24f 100644 --- a/assert/assert_format_test.go +++ b/assert/assert_format_test.go @@ -476,6 +476,33 @@ func TestErrorAsf(t *testing.T) { }) } +func TestErrorAsTypef(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := ErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") + if !result { + t.Error("ErrorAsTypef should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := ErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") + if result { + t.Error("ErrorAsTypef should return false on failure") + } + if !mock.failed { + t.Error("ErrorAsTypef should mark test as failed") + } + }) +} + func TestErrorContainsf(t *testing.T) { t.Parallel() @@ -2549,6 +2576,33 @@ func TestNotErrorAsf(t *testing.T) { }) } +func TestNotErrorAsTypef(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") + if !result { + t.Error("NotErrorAsTypef should return true on success") + } + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockT) + result := NotErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") + if result { + t.Error("NotErrorAsTypef should return false on failure") + } + if !mock.failed { + t.Error("NotErrorAsTypef should mark test as failed") + } + }) +} + func TestNotErrorIsf(t *testing.T) { t.Parallel() diff --git a/codegen/go.mod b/codegen/go.mod index 4f66b2eff..9f128a843 100644 --- a/codegen/go.mod +++ b/codegen/go.mod @@ -1,6 +1,6 @@ module github.com/go-openapi/testify/codegen/v2 -go 1.25.0 +go 1.26.0 toolchain go1.27.0 diff --git a/codegen/internal/generator/doc_generator.go b/codegen/internal/generator/doc_generator.go index 2f0d7e6de..face2093a 100644 --- a/codegen/internal/generator/doc_generator.go +++ b/codegen/internal/generator/doc_generator.go @@ -108,62 +108,62 @@ func (d *DocGenerator) reorganizeByDomain() (iter.Seq2[string, model.Document], pkggodevURL := "https://pkg.go.dev/" + discoveredDomains.RootPackage() return func(yield func(string, model.Document) bool) { - weight := 1 - for domain, entry := range discoveredDomains.Entries() { - doc := model.Document{ - Title: funcmaps.Titleize(domain), - Domain: domain, - Description: entry.Description(), - Kind: model.KindPage, - File: domain + ".md", - Package: &model.AssertionPackage{ - Package: assertions, // package that is the single source of truth - Tool: discoveredDomains.Tool(), - Copyright: discoveredDomains.Copyright(), - Receiver: discoveredDomains.Receiver(), - Header: discoveredDomains.Header(), - EnableFormat: d.ctx.enableFormat, - EnableForward: d.ctx.enableForward, - EnableGenerics: d.ctx.enableGenerics, - EnableExamples: d.ctx.generateExamples, - RunnableExamples: d.ctx.runnableExamples, - // skip package-level docstring - // skip other package-level extra comments - // filtered functions and types for this domain across all packages - Functions: entry.Functions(), - Types: entry.Types(), - Vars: entry.Vars(), - Consts: entry.Consts(), - }, - ExtraPackages: entry.ExtraPackages(), - GitHubURL: githubURL, - PkgGoDevURL: pkggodevURL, - RefCount: entry.Len(), - Weight: weight, - } - weight++ - - // populate document context in all children: at doc generation time, - // we need the full context to be available when iterating over functions. - doc.Package.Context = &doc - for i, fn := range doc.Package.Functions { - fn.Context = &doc - doc.Package.Functions[i] = fn - } + weight := 1 + for domain, entry := range discoveredDomains.Entries() { + doc := model.Document{ + Title: funcmaps.Titleize(domain), + Domain: domain, + Description: entry.Description(), + Kind: model.KindPage, + File: domain + ".md", + Package: &model.AssertionPackage{ + Package: assertions, // package that is the single source of truth + Tool: discoveredDomains.Tool(), + Copyright: discoveredDomains.Copyright(), + Receiver: discoveredDomains.Receiver(), + Header: discoveredDomains.Header(), + EnableFormat: d.ctx.enableFormat, + EnableForward: d.ctx.enableForward, + EnableGenerics: d.ctx.enableGenerics, + EnableExamples: d.ctx.generateExamples, + RunnableExamples: d.ctx.runnableExamples, + // skip package-level docstring + // skip other package-level extra comments + // filtered functions and types for this domain across all packages + Functions: entry.Functions(), + Types: entry.Types(), + Vars: entry.Vars(), + Consts: entry.Consts(), + }, + ExtraPackages: entry.ExtraPackages(), + GitHubURL: githubURL, + PkgGoDevURL: pkggodevURL, + RefCount: entry.Len(), + Weight: weight, + } + weight++ + + // populate document context in all children: at doc generation time, + // we need the full context to be available when iterating over functions. + doc.Package.Context = &doc + for i, fn := range doc.Package.Functions { + fn.Context = &doc + doc.Package.Functions[i] = fn + } - if !yield(doc.Domain, doc) { - return - } + if !yield(doc.Domain, doc) { + return } - }, uniqueValues{ - // metadata that are unique - tool: discoveredDomains.Tool(), - receiver: discoveredDomains.Receiver(), - copyright: discoveredDomains.Copyright(), - header: discoveredDomains.Header(), - githubURL: githubURL, - pkggodevURL: pkggodevURL, } + }, uniqueValues{ + // metadata that are unique + tool: discoveredDomains.Tool(), + receiver: discoveredDomains.Receiver(), + copyright: discoveredDomains.Copyright(), + header: discoveredDomains.Header(), + githubURL: githubURL, + pkggodevURL: pkggodevURL, + } } func (d *DocGenerator) buildIndexDocument(docsByDomain iter.Seq2[string, model.Document], extras uniqueValues) model.Document { diff --git a/codegen/internal/generator/funcmaps/markdown.go b/codegen/internal/generator/funcmaps/markdown.go index 4dc8239cc..34cabee67 100644 --- a/codegen/internal/generator/funcmaps/markdown.go +++ b/codegen/internal/generator/funcmaps/markdown.go @@ -157,7 +157,7 @@ func stripSections(in string, object any) (result, trailer []string) { perPkgExamples []packageExamples funcName string ) - if function, ok := (object).(model.Function); ok { + if function, ok := object.(model.Function); ok { funcName = function.Name // Testable examples live in the generated packages (assert, require), diff --git a/codegen/internal/scanner/buildtags_test.go b/codegen/internal/scanner/buildtags_test.go index 54010a688..ea871809c 100644 --- a/codegen/internal/scanner/buildtags_test.go +++ b/codegen/internal/scanner/buildtags_test.go @@ -15,6 +15,7 @@ import ( // error_go126.go), and the unguarded ErrorAs in the same domain. // NOTE: this requires running codegen on a toolchain >= go1.26 (latest stable). func TestBuildConstraintDetection(t *testing.T) { + t.Skipf("temporarily disabled: we don't currently have guarded assertions") s := New() pkg, err := s.Scan() diff --git a/docs/doc-site/api/error.md b/docs/doc-site/api/error.md index 841ea1db5..684290ff0 100644 --- a/docs/doc-site/api/error.md +++ b/docs/doc-site/api/error.md @@ -43,19 +43,18 @@ Generic assertions are marked with a {{% icon icon="star" color=orange %}}. Their method variants carry a {{% goversion "go1.27" %}} badge: methods take type parameters only from go1.27 onwards, so on an older toolchain a generic assertion is available as a package-level function alone. -Assertions requiring a newer Go toolchain are marked with a version badge, e.g. {{% goversion "go1.26" %}} (the assertion is unavailable on older toolchains). ```tree - [EqualError](#equalerror) | angles-right - [Error](#error) | angles-right - [ErrorAs](#erroras) | angles-right -- [ErrorAsType[E error]](#errorastypee-error) (go1.26+) | star | orange +- [ErrorAsType[E error]](#errorastypee-error) | star | orange - [ErrorContains](#errorcontains) | angles-right - [ErrorIs](#erroris) | angles-right - [ErrorNotContains](#errornotcontains) | angles-right - [NoError](#noerror) | angles-right - [NotErrorAs](#noterroras) | angles-right -- [NotErrorAsType[E error]](#noterrorastypee-error) (go1.26+) | star | orange +- [NotErrorAsType[E error]](#noterrorastypee-error) | star | orange - [NotErrorIs](#noterroris) | angles-right ``` @@ -411,7 +410,7 @@ func (d *dummyError) Error() string { {{% /tab %}} {{< /tabs >}} -### ErrorAsType[E error] {{% icon icon="star" color=orange %}} {{% goversion "go1.26" %}}{#errorastypee-error} +### ErrorAsType[E error] {{% icon icon="star" color=orange %}}{#errorastypee-error} ErrorAsType asserts that at least one of the errors in err's chain is of type E. It is the type-safe counterpart of [ErrorAs](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorAs), built on the go1.26 [errors.AsType](https://pkg.go.dev/errors#AsType): @@ -447,9 +446,29 @@ This assertion requires go1.26 or newer; it is unavailable on older toolchains. ```go // real-world test would inject *testing.T from TestErrorAsType(t *testing.T) -t := new(testing.T) -success := assert.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) -fmt.Printf("success: %t\n", success) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestErrorAsType(t *testing.T) + success := assert.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + fmt.Printf("success: %t\n", success) + +} + +type dummyError struct { +} + +func (d *dummyError) Error() string { + return "dummy error" +} + ``` {{% /card %}} @@ -468,9 +487,29 @@ fmt.Printf("success: %t\n", success) ```go // real-world test would inject *testing.T from TestErrorAsType(t *testing.T) -t := new(testing.T) -require.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) -fmt.Println("passed") +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestErrorAsType(t *testing.T) + require.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + fmt.Println("passed") + +} + +type dummyError struct { +} + +func (d *dummyError) Error() string { + return "dummy error" +} + ``` {{% /card %}} @@ -506,7 +545,7 @@ fmt.Println("passed") |--|--| | [`assertions.ErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorAsType) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorAsType](https://github.com/go-openapi/testify/blob/master/internal/assertions/error_go126.go#L39) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorAsType](https://github.com/go-openapi/testify/blob/master/internal/assertions/error_go126.go#L37) {{% /tab %}} {{< /tabs >}} @@ -1094,7 +1133,7 @@ func (d *dummyError) Error() string { {{% /tab %}} {{< /tabs >}} -### NotErrorAsType[E error] {{% icon icon="star" color=orange %}} {{% goversion "go1.26" %}}{#noterrorastypee-error} +### NotErrorAsType[E error] {{% icon icon="star" color=orange %}}{#noterrorastypee-error} NotErrorAsType asserts that none of the errors in err's chain is of type E. It is the type-safe counterpart of [NotErrorAs](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotErrorAs), built on the go1.26 [errors.AsType](https://pkg.go.dev/errors#AsType). @@ -1126,9 +1165,29 @@ This assertion requires go1.26 or newer; it is unavailable on older toolchains. ```go // real-world test would inject *testing.T from TestNotErrorAsType(t *testing.T) -t := new(testing.T) -success := assert.NotErrorAsType(t, assert.ErrTest, new(*dummyError)) -fmt.Printf("success: %t\n", success) +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/assert" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestNotErrorAsType(t *testing.T) + success := assert.NotErrorAsType(t, assert.ErrTest, new(*dummyError)) + fmt.Printf("success: %t\n", success) + +} + +type dummyError struct { +} + +func (d *dummyError) Error() string { + return "dummy error" +} + ``` {{% /card %}} @@ -1147,9 +1206,29 @@ fmt.Printf("success: %t\n", success) ```go // real-world test would inject *testing.T from TestNotErrorAsType(t *testing.T) -t := new(testing.T) -require.NotErrorAsType(t, require.ErrTest, new(*dummyError)) -fmt.Println("passed") +package main + +import ( + "fmt" + "testing" + + "github.com/go-openapi/testify/v2/require" +) + +func main() { + t := new(testing.T) // should come from testing, e.g. func TestNotErrorAsType(t *testing.T) + require.NotErrorAsType(t, require.ErrTest, new(*dummyError)) + fmt.Println("passed") + +} + +type dummyError struct { +} + +func (d *dummyError) Error() string { + return "dummy error" +} + ``` {{% /card %}} @@ -1185,7 +1264,7 @@ fmt.Println("passed") |--|--| | [`assertions.NotErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotErrorAsType) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorAsType](https://github.com/go-openapi/testify/blob/master/internal/assertions/error_go126.go#L87) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorAsType](https://github.com/go-openapi/testify/blob/master/internal/assertions/error_go126.go#L85) {{% /tab %}} {{< /tabs >}} diff --git a/docs/doc-site/api/metrics.md b/docs/doc-site/api/metrics.md index 64095f3e1..e2b7d4d41 100644 --- a/docs/doc-site/api/metrics.md +++ b/docs/doc-site/api/metrics.md @@ -51,7 +51,7 @@ Table of core assertions, excluding variants. Each function is side by side with | [EqualValues](equality/#equalvalues) | [NotEqualValues](equality/#notequalvalues) | equality | | | [Error](error/#error) | [NoError](error/#noerror) | error | | | [ErrorAs](error/#erroras) | [NotErrorAs](error/#noterroras) | error | | -| [ErrorAsType[E error]](error/#errorastypee-error) {{% icon icon="star" color=orange %}} {{% goversion "go1.26" %}} | [NotErrorAsType](error/#noterrorastypee-error) | error | | +| [ErrorAsType[E error]](error/#errorastypee-error) {{% icon icon="star" color=orange %}} | [NotErrorAsType](error/#noterrorastypee-error) | error | | | [ErrorContains](error/#errorcontains) | [ErrorNotContains](error/#errornotcontains) | error | | | [ErrorIs](error/#erroris) | [NotErrorIs](error/#noterroris) | error | | | [EventuallyWith[C CollectibleConditioner]](condition/#eventuallywithc-collectibleconditioner) {{% icon icon="star" color=orange %}} | | condition | | diff --git a/enable/colors/go.mod b/enable/colors/go.mod index 561211127..28277e2a2 100644 --- a/enable/colors/go.mod +++ b/enable/colors/go.mod @@ -9,4 +9,4 @@ require golang.org/x/sys v0.47.0 // indirect replace github.com/go-openapi/testify/v2 => ../.. -go 1.25.0 +go 1.26.0 diff --git a/enable/yaml/go.mod b/enable/yaml/go.mod index edb83db73..90d532735 100644 --- a/enable/yaml/go.mod +++ b/enable/yaml/go.mod @@ -7,4 +7,4 @@ require ( replace github.com/go-openapi/testify/v2 => ../.. -go 1.25.0 +go 1.26.0 diff --git a/go.mod b/go.mod index 1224f0212..1a8995b67 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,4 @@ module github.com/go-openapi/testify/v2 retract v2.0.0 -go 1.25.0 +go 1.26.0 diff --git a/go.work b/go.work index 7cb3e9216..54e5f6374 100644 --- a/go.work +++ b/go.work @@ -7,14 +7,15 @@ use ( ./internal/testintegration ) -go 1.25.0 +go 1.26.0 // Dev/codegen toolchain floor. Must be >= the highest //go:build go1.N guard in play: // the guards written by hand in internal/assertions (enforced by TestToolchainFloorCoversGuards), // and the go1.27 guard codegen stamps on the forward methods of the generic assertions. // Below that floor, guarded files are dropped before the compiler sees them: codegen would // emit incomplete output, and `go test ./...` would exercise none of the generic methods. +// // This line is NOT published to consumers: a dependency's toolchain directive is ignored -// downstream — only our go.mod `go 1.25.0` floor reaches them, so go1.25 users still build +// downstream — only our go.mod `go 1.26.0` floor reaches them, so go1.26 users still build // (guarded files excluded). toolchain go1.27.0 diff --git a/hack/migrate-testify/go.mod b/hack/migrate-testify/go.mod index beb39b92e..09a4c7056 100644 --- a/hack/migrate-testify/go.mod +++ b/hack/migrate-testify/go.mod @@ -1,6 +1,6 @@ module github.com/go-openapi/testify/hack/migrate-testify/v2 -go 1.25.0 +go 1.26.0 require ( golang.org/x/mod v0.40.0 diff --git a/internal/assertions/collection_test.go b/internal/assertions/collection_test.go index 6c594d0b5..2f9806e12 100644 --- a/internal/assertions/collection_test.go +++ b/internal/assertions/collection_test.go @@ -257,7 +257,7 @@ func collectionLenCases() iter.Seq[collectionLenCase] { {"invalid type/rune", 'A', 0, "", false}, {"invalid type/struct", struct{}{}, 0, "", false}, {"invalid type/ptr-not-array", &longSlice, 1_000_000, `<... truncated>" could not be applied builtin len()`, false}, - {"invalid type/ptr-anything", ptr(1), 0, `" could not be applied builtin len()`, false}, + {"invalid type/ptr-anything", new(1), 0, `" could not be applied builtin len()`, false}, // Truncated message {"truncated message/long slice", longSlice, 1_000_000, `<... truncated>" should have 1000001 item(s), but has 1000000`, true}, diff --git a/internal/assertions/compare.go b/internal/assertions/compare.go index 0b354e120..8c9a88722 100644 --- a/internal/assertions/compare.go +++ b/internal/assertions/compare.go @@ -510,16 +510,17 @@ func compareSlice(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareRe func convertReflectValue[V any](obj any, value reflect.Value) V { //nolint:ireturn // false positive // we try and avoid calling [reflect.Value.Convert()] whenever possible, // as this has a pretty big performance impact - converted, ok := obj.(V) + asserted, ok := obj.(V) if !ok { - converted, ok = value.Convert(reflect.TypeFor[V]()).Interface().(V) + converted := value.Convert(reflect.TypeFor[V]()) + asserted, ok = reflect.TypeAssert[V](converted) if !ok { // should never get there - panic("internal error: expected that reflect.Value.Convert yields its target type") + panic("internal error: expected that reflect.Value.TypeAssert matches the type it just converted to") } } - return converted + return asserted } // compareOrderedWithAny compares two [Ordered] values. diff --git a/internal/assertions/equal_test.go b/internal/assertions/equal_test.go index b8049932e..6a9bc0c79 100644 --- a/internal/assertions/equal_test.go +++ b/internal/assertions/equal_test.go @@ -294,7 +294,7 @@ func unifiedEqualityCases() iter.Seq[equalityTestCase] { return slices.Values([]equalityTestCase{ // Both nil {"both-nil/ptr", func() (any, any) { return (*int)(nil), (*int)(nil) }, eqBothNil, false}, - {"both-nil/interface", func() (any, any) { return (any)(nil), (any)(nil) }, eqBothNil, true}, + {"both-nil/interface", func() (any, any) { return any(nil), any(nil) }, eqBothNil, true}, // One nil (reflection only - type mismatch) {"one-nil/first", func() (any, any) { v := 42; return nil, &v }, eqOneNil, true}, @@ -701,8 +701,8 @@ func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { }, { name: "equal-values/slice-of-pointers", - expected: []*int{ptr(1), nil, ptr(2)}, - actual: []*int{ptr(1), nil, ptr(2)}, + expected: []*int{new(1), nil, new(2)}, + actual: []*int{new(1), nil, new(2)}, expectedEqual: true, }, { diff --git a/internal/assertions/equal_unary_test.go b/internal/assertions/equal_unary_test.go index 71cd6e9b7..80b5c0864 100644 --- a/internal/assertions/equal_unary_test.go +++ b/internal/assertions/equal_unary_test.go @@ -64,7 +64,7 @@ func unifiedUnaryCases() iter.Seq[unaryTestCase] { // Nil category {"nil/nil-ptr", (*int)(nil), nilCategory}, {"nil/nil-slice", []int(nil), nilCategory}, - {"nil/nil-interface", (any)(nil), nilCategory}, + {"nil/nil-interface", any(nil), nilCategory}, {"nil/nil-struct-ptr", (*struct{})(nil), nilCategory}, // Empty non-nil category diff --git a/internal/assertions/error_go126.go b/internal/assertions/error_go126.go index 7bf48b115..81242a3be 100644 --- a/internal/assertions/error_go126.go +++ b/internal/assertions/error_go126.go @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -//go:build go1.26 - package assertions import ( diff --git a/internal/assertions/error_go126_test.go b/internal/assertions/error_go126_test.go index a8e0a2d4f..4da9843c2 100644 --- a/internal/assertions/error_go126_test.go +++ b/internal/assertions/error_go126_test.go @@ -1,8 +1,6 @@ // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 -//go:build go1.26 - package assertions import ( diff --git a/internal/assertions/mock_test.go b/internal/assertions/mock_test.go index dbc1ad5f2..6ecdadc74 100644 --- a/internal/assertions/mock_test.go +++ b/internal/assertions/mock_test.go @@ -114,10 +114,6 @@ func shouldPassOrFail(t *testing.T, mock *mockT, result, shouldPass bool) { } } -func ptr(i int) *int { - return &i -} - // failCase defines a test case for verifying assertion error messages. // // Only one of wantError, wantMatch, or wantContains should be set per case. diff --git a/internal/assertions/type_test.go b/internal/assertions/type_test.go index fa100b362..3006cefff 100644 --- a/internal/assertions/type_test.go +++ b/internal/assertions/type_test.go @@ -266,10 +266,10 @@ func typeNonZeros() iter.Seq[any] { []any{}, struct{ x int }{1}, (&i), - (func() {}), + func() {}, any(1), map[any]any{}, - (make(chan any)), + make(chan any), (<-chan any)(make(chan any)), (chan<- any)(make(chan any)), }) @@ -304,7 +304,7 @@ func kindCases() iter.Seq[kindCase] { // True {reflect.Invalid, any(nil), true, "legitimate expectation of reflect.Invalid (any)"}, {reflect.Pointer, (*any)(nil), true, "legitimate expectation of reflect.Pointer (*any)"}, - {reflect.Invalid, (error)(nil), true, "legitimate expectation of reflect.Invalid (error)"}, + {reflect.Invalid, error(nil), true, "legitimate expectation of reflect.Invalid (error)"}, {reflect.Invalid, nil, true, "legitimate nil input"}, // False {reflect.Interface, iface, false, "interface returns concrete type (any)"}, diff --git a/internal/spew/common.go b/internal/spew/common.go index cdf5c52f7..3d44cd065 100644 --- a/internal/spew/common.go +++ b/internal/spew/common.go @@ -544,8 +544,8 @@ func timeLess(a, b reflect.Value) bool { return a.String() < b.String() } - tA, okTimeA := convertedA.Interface().(time.Time) - tB, okTimeB := convertedB.Interface().(time.Time) + tA, okTimeA := reflect.TypeAssert[time.Time](convertedA) + tB, okTimeB := reflect.TypeAssert[time.Time](convertedB) if !okTimeA || !okTimeB { // defensive safeguard (should never get there, since we have successfully indirected and converted) diff --git a/internal/spew/common_test.go b/internal/spew/common_test.go index 917a4c3f5..3629b3b9c 100644 --- a/internal/spew/common_test.go +++ b/internal/spew/common_test.go @@ -128,7 +128,7 @@ func TestSortValues(t *testing.T) { }, // indirection pointers: **time.Time { - []reflect.Value{v(pt0), v(ppt2), v((nilTimePtrPtr)), v(t1)}, + []reflect.Value{v(pt0), v(ppt2), v(nilTimePtrPtr), v(t1)}, []reflect.Value{v(nilTimePtrPtr), v(pt0), v(t1), v(ppt2)}, }, // invalid **time.Time (nil) diff --git a/internal/testintegration/assertions/assertions_test.go b/internal/testintegration/assertions/assertions_test.go index c34004311..47a814406 100644 --- a/internal/testintegration/assertions/assertions_test.go +++ b/internal/testintegration/assertions/assertions_test.go @@ -214,9 +214,9 @@ func genNilValue(t *rapid.T) any { case 9: return (*struct{})(nil) case 10: - return (error)(nil) + return error(nil) default: - return (fmt.Stringer)(nil) + return fmt.Stringer(nil) } } diff --git a/internal/testintegration/go.mod b/internal/testintegration/go.mod index 1762a12ac..3934f6f52 100644 --- a/internal/testintegration/go.mod +++ b/internal/testintegration/go.mod @@ -1,6 +1,6 @@ module github.com/go-openapi/testify/v2/internal/testintegration/v2 -go 1.25.0 +go 1.26.0 require ( github.com/go-openapi/testify/enable/colors/v2 v2.4.0 diff --git a/require/require_assertions.go b/require/require_assertions.go index e4d219727..f9a2b5b51 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -508,6 +508,44 @@ func ErrorAs(t T, err error, target any, msgAndArgs ...any) { t.FailNow() } +// ErrorAsType asserts that at least one of the errors in err's chain is of type E. +// +// It is the type-safe counterpart of [ErrorAs], built on the go1.26 [errors.AsType]: +// the expected type is the type parameter E (checked at compile time, no reflection), +// rather than the untyped any target used by [ErrorAs]. +// +// target receives the matched error when the assertion succeeds. It may be nil, for +// callers that only want to know whether the chain holds an error of type E: in that +// case E cannot be inferred and must be supplied explicitly. +// +// This assertion requires go1.26 or newer; it is unavailable on older toolchains. +// +// # Usage +// +// // capture the matched error (E is inferred from target): +// var target *MyError +// assertions.ErrorAsType(t, err, &target) +// +// // only check, discarding the value (E given explicitly): +// assertions.ErrorAsType[*MyError](t, err, nil) +// +// # Examples +// +// success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) +// failure: ErrTest, new(*dummyError) +// +// Upon failure, the test [T] is marked as failed and stops execution. +func ErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.ErrorAsType[E](t, err, target, msgAndArgs...) { + return + } + + t.FailNow() +} + // ErrorContains asserts that a function returned a non-nil error (i.e. an // error) and that the error contains the specified substring. // @@ -2847,6 +2885,40 @@ func NotErrorAs(t T, err error, target any, msgAndArgs ...any) { t.FailNow() } +// NotErrorAsType asserts that none of the errors in err's chain is of type E. +// +// It is the type-safe counterpart of [NotErrorAs], built on the go1.26 [errors.AsType]. +// +// target is only used to infer the type parameter E and is never assigned; it may be nil, +// in which case E must be supplied explicitly. +// +// This assertion requires go1.26 or newer; it is unavailable on older toolchains. +// +// # Usage +// +// var target *MyError +// assertions.NotErrorAsType(t, err, &target) +// +// // or, supplying E explicitly: +// assertions.NotErrorAsType[*MyError](t, err, nil) +// +// # Examples +// +// success: ErrTest, new(*dummyError) +// failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotErrorAsType[E](t, err, target, msgAndArgs...) { + return + } + + t.FailNow() +} + // NotErrorIs asserts that none of the errors in err's chain matches target. // // This is a wrapper for [errors.Is]. diff --git a/require/require_assertions_go126.go b/require/require_assertions_go126.go deleted file mode 100644 index 5d3ef9b63..000000000 --- a/require/require_assertions_go126.go +++ /dev/null @@ -1,84 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package require - -import ( - "github.com/go-openapi/testify/v2/internal/assertions" -) - -// ErrorAsType asserts that at least one of the errors in err's chain is of type E. -// -// It is the type-safe counterpart of [ErrorAs], built on the go1.26 [errors.AsType]: -// the expected type is the type parameter E (checked at compile time, no reflection), -// rather than the untyped any target used by [ErrorAs]. -// -// target receives the matched error when the assertion succeeds. It may be nil, for -// callers that only want to know whether the chain holds an error of type E: in that -// case E cannot be inferred and must be supplied explicitly. -// -// This assertion requires go1.26 or newer; it is unavailable on older toolchains. -// -// # Usage -// -// // capture the matched error (E is inferred from target): -// var target *MyError -// assertions.ErrorAsType(t, err, &target) -// -// // only check, discarding the value (E given explicitly): -// assertions.ErrorAsType[*MyError](t, err, nil) -// -// # Examples -// -// success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) -// failure: ErrTest, new(*dummyError) -// -// Upon failure, the test [T] is marked as failed and stops execution. -func ErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) { - if h, ok := t.(H); ok { - h.Helper() - } - if assertions.ErrorAsType[E](t, err, target, msgAndArgs...) { - return - } - - t.FailNow() -} - -// NotErrorAsType asserts that none of the errors in err's chain is of type E. -// -// It is the type-safe counterpart of [NotErrorAs], built on the go1.26 [errors.AsType]. -// -// target is only used to infer the type parameter E and is never assigned; it may be nil, -// in which case E must be supplied explicitly. -// -// This assertion requires go1.26 or newer; it is unavailable on older toolchains. -// -// # Usage -// -// var target *MyError -// assertions.NotErrorAsType(t, err, &target) -// -// // or, supplying E explicitly: -// assertions.NotErrorAsType[*MyError](t, err, nil) -// -// # Examples -// -// success: ErrTest, new(*dummyError) -// failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) -// -// Upon failure, the test [T] is marked as failed and stops execution. -func NotErrorAsType[E error](t T, err error, target *E, msgAndArgs ...any) { - if h, ok := t.(H); ok { - h.Helper() - } - if assertions.NotErrorAsType[E](t, err, target, msgAndArgs...) { - return - } - - t.FailNow() -} diff --git a/require/require_assertions_go126_test.go b/require/require_assertions_go126_test.go deleted file mode 100644 index 65e2c2e79..000000000 --- a/require/require_assertions_go126_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package require - -import ( - "fmt" - "testing" -) - -func TestErrorAsType(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - ErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - ErrorAsType(mock, ErrTest, new(*dummyError)) - // require functions don't return a value - if !mock.failed { - t.Error("ErrorAsType should call FailNow()") - } - }) -} - -func TestNotErrorAsType(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - NotErrorAsType(mock, ErrTest, new(*dummyError)) - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - NotErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) - // require functions don't return a value - if !mock.failed { - t.Error("NotErrorAsType should call FailNow()") - } - }) -} diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index 167a6530f..0f1dae50e 100644 --- a/require/require_assertions_test.go +++ b/require/require_assertions_test.go @@ -408,6 +408,29 @@ func TestErrorAs(t *testing.T) { }) } +func TestErrorAsType(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + ErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + ErrorAsType(mock, ErrTest, new(*dummyError)) + // require functions don't return a value + if !mock.failed { + t.Error("ErrorAsType should call FailNow()") + } + }) +} + func TestErrorContains(t *testing.T) { t.Parallel() @@ -2175,6 +2198,29 @@ func TestNotErrorAs(t *testing.T) { }) } +func TestNotErrorAsType(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotErrorAsType(mock, ErrTest, new(*dummyError)) + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotErrorAsType(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + // require functions don't return a value + if !mock.failed { + t.Error("NotErrorAsType should call FailNow()") + } + }) +} + func TestNotErrorIs(t *testing.T) { t.Parallel() diff --git a/require/require_examples_go126_test.go b/require/require_examples_go126_test.go deleted file mode 100644 index 6c0ea6740..000000000 --- a/require/require_examples_go126_test.go +++ /dev/null @@ -1,31 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package require_test - -import ( - "fmt" - "testing" - - "github.com/go-openapi/testify/v2/require" -) - -func ExampleErrorAsType() { - t := new(testing.T) // should come from testing, e.g. func TestErrorAsType(t *testing.T) - require.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) - fmt.Println("passed") - - // Output: passed -} - -func ExampleNotErrorAsType() { - t := new(testing.T) // should come from testing, e.g. func TestNotErrorAsType(t *testing.T) - require.NotErrorAsType(t, require.ErrTest, new(*dummyError)) - fmt.Println("passed") - - // Output: passed -} diff --git a/require/require_examples_test.go b/require/require_examples_test.go index 31861d3f0..46f71fe35 100644 --- a/require/require_examples_test.go +++ b/require/require_examples_test.go @@ -161,6 +161,14 @@ func ExampleErrorAs() { // Output: passed } +func ExampleErrorAsType() { + t := new(testing.T) // should come from testing, e.g. func TestErrorAsType(t *testing.T) + require.ErrorAsType(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) + fmt.Println("passed") + + // Output: passed +} + func ExampleErrorContains() { t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) require.ErrorContains(t, require.ErrTest, "general error") @@ -793,6 +801,14 @@ func ExampleNotErrorAs() { // Output: passed } +func ExampleNotErrorAsType() { + t := new(testing.T) // should come from testing, e.g. func TestNotErrorAsType(t *testing.T) + require.NotErrorAsType(t, require.ErrTest, new(*dummyError)) + fmt.Println("passed") + + // Output: passed +} + func ExampleNotErrorIs() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) require.NotErrorIs(t, require.ErrTest, io.EOF) diff --git a/require/require_format.go b/require/require_format.go index 266242082..3e0ba22c2 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -253,6 +253,20 @@ func ErrorAsf(t T, err error, target any, msg string, args ...any) { t.FailNow() } +// ErrorAsTypef is the same as [ErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func ErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.ErrorAsType[E](t, err, target, forwardArgs(msg, args)...) { + return + } + + t.FailNow() +} + // ErrorContainsf is the same as [ErrorContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. @@ -1341,6 +1355,20 @@ func NotErrorAsf(t T, err error, target any, msg string, args ...any) { t.FailNow() } +// NotErrorAsTypef is the same as [NotErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. +// +// Upon failure, the test [T] is marked as failed and stops execution. +func NotErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) { + if h, ok := t.(H); ok { + h.Helper() + } + if assertions.NotErrorAsType[E](t, err, target, forwardArgs(msg, args)...) { + return + } + + t.FailNow() +} + // NotErrorIsf is the same as [NotErrorIs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and stops execution. diff --git a/require/require_format_go126.go b/require/require_format_go126.go deleted file mode 100644 index 60265144e..000000000 --- a/require/require_format_go126.go +++ /dev/null @@ -1,40 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package require - -import ( - "github.com/go-openapi/testify/v2/internal/assertions" -) - -// ErrorAsTypef is the same as [ErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. -// -// Upon failure, the test [T] is marked as failed and stops execution. -func ErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) { - if h, ok := t.(H); ok { - h.Helper() - } - if assertions.ErrorAsType[E](t, err, target, forwardArgs(msg, args)...) { - return - } - - t.FailNow() -} - -// NotErrorAsTypef is the same as [NotErrorAsType], but it accepts a format string to format arguments like [fmt.Printf]. -// -// Upon failure, the test [T] is marked as failed and stops execution. -func NotErrorAsTypef[E error](t T, err error, target *E, msg string, args ...any) { - if h, ok := t.(H); ok { - h.Helper() - } - if assertions.NotErrorAsType[E](t, err, target, forwardArgs(msg, args)...) { - return - } - - t.FailNow() -} diff --git a/require/require_format_go126_test.go b/require/require_format_go126_test.go deleted file mode 100644 index fc63ba32c..000000000 --- a/require/require_format_go126_test.go +++ /dev/null @@ -1,59 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -// Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. - -//go:build go1.26 - -package require - -import ( - "fmt" - "testing" -) - -func TestErrorAsTypef(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - ErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - ErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") - // require functions don't return a value - if !mock.failed { - t.Error("ErrorAsTypef should call FailNow()") - } - }) -} - -func TestNotErrorAsTypef(t *testing.T) { - t.Parallel() - - t.Run("success", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - NotErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") - // require functions don't return a value - }) - - t.Run("failure", func(t *testing.T) { - t.Parallel() - - mock := new(mockFailNowT) - NotErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") - // require functions don't return a value - if !mock.failed { - t.Error("NotErrorAsTypef should call FailNow()") - } - }) -} diff --git a/require/require_format_test.go b/require/require_format_test.go index d6ec4a4aa..6752ed8f9 100644 --- a/require/require_format_test.go +++ b/require/require_format_test.go @@ -408,6 +408,29 @@ func TestErrorAsf(t *testing.T) { }) } +func TestErrorAsTypef(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + ErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + ErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("ErrorAsTypef should call FailNow()") + } + }) +} + func TestErrorContainsf(t *testing.T) { t.Parallel() @@ -2175,6 +2198,29 @@ func TestNotErrorAsf(t *testing.T) { }) } +func TestNotErrorAsTypef(t *testing.T) { + t.Parallel() + + t.Run("success", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotErrorAsTypef(mock, ErrTest, new(*dummyError), "test message") + // require functions don't return a value + }) + + t.Run("failure", func(t *testing.T) { + t.Parallel() + + mock := new(mockFailNowT) + NotErrorAsTypef(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") + // require functions don't return a value + if !mock.failed { + t.Error("NotErrorAsTypef should call FailNow()") + } + }) +} + func TestNotErrorIsf(t *testing.T) { t.Parallel()