diff --git a/.claude/CLAUDE.md b/.claude/CLAUDE.md index 9ae028f72..374c68847 100644 --- a/.claude/CLAUDE.md +++ b/.claude/CLAUDE.md @@ -97,10 +97,12 @@ With 76 assertion functions, this generates 608 functions automatically. ### Dependency Isolation Strategy - **internal/spew**: Internalized copy of go-spew for pretty-printing values - **internal/difflib**: Internalized copy of go-difflib for generating diffs -- **assert/yaml**: Stub package that panics by default if YAML assertions are used +- **internal/assertions/enable**: Internal stubs that panic by default if YAML/color assertions are used +- **enable/stubs**: Public API for enabling optional features (yaml, colors) - **enable/yaml**: Optional module that activates YAML support via init() when imported +- **enable/colors**: Optional module that activates colorized output via init() when imported -The "enable" pattern allows YAML functionality to be opt-in: import `_ "github.com/go-openapi/testify/v2/enable/yaml"` to activate YAML assertions without forcing a dependency on all users. +The "enable" pattern allows optional functionality to be opt-in: import `_ "github.com/go-openapi/testify/v2/enable/yaml"` to activate YAML assertions, or `_ "github.com/go-openapi/testify/v2/enable/colors"` to enable colorized output, without forcing dependencies on all users. ## Development Commands diff --git a/.github/workflows/auto-merge.yml b/.github/workflows/auto-merge.yml index 34ceb36cc..99be99128 100644 --- a/.github/workflows/auto-merge.yml +++ b/.github/workflows/auto-merge.yml @@ -11,5 +11,5 @@ jobs: permissions: contents: write pull-requests: write - uses: go-openapi/ci-workflows/.github/workflows/auto-merge.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # v0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/auto-merge.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 secrets: inherit diff --git a/.github/workflows/bump-release.yml b/.github/workflows/bump-release.yml index 09728a2c4..4446cf3d4 100644 --- a/.github/workflows/bump-release.yml +++ b/.github/workflows/bump-release.yml @@ -32,7 +32,7 @@ jobs: permissions: contents: write pull-requests: write - uses: go-openapi/ci-workflows/.github/workflows/bump-release-monorepo.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # v0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/bump-release-monorepo.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 with: bump-type: ${{ inputs.bump-type }} tag-message-title: ${{ inputs.tag-message-title }} diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c0a01eb67..73da00599 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -18,5 +18,5 @@ jobs: permissions: contents: read security-events: write - uses: go-openapi/ci-workflows/.github/workflows/codeql.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # v0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/codeql.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 secrets: inherit diff --git a/.github/workflows/contributors.yml b/.github/workflows/contributors.yml index eb6a65bdf..65d6f46f5 100644 --- a/.github/workflows/contributors.yml +++ b/.github/workflows/contributors.yml @@ -14,5 +14,5 @@ jobs: permissions: pull-requests: write contents: write - uses: go-openapi/ci-workflows/.github/workflows/contributors.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # v0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/contributors.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 secrets: inherit diff --git a/.github/workflows/go-test.yml b/.github/workflows/go-test.yml index 155fd06ba..d1baad85b 100644 --- a/.github/workflows/go-test.yml +++ b/.github/workflows/go-test.yml @@ -13,5 +13,7 @@ on: jobs: test: - uses: go-openapi/ci-workflows/.github/workflows/go-test-monorepo.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # v0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/go-test-monorepo.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 + with: + extra-flags: '-tags testcgo' # this is to trigger extra tests in spew secrets: inherit diff --git a/.github/workflows/scanner.yml b/.github/workflows/scanner.yml index e168330d8..c7550a9af 100644 --- a/.github/workflows/scanner.yml +++ b/.github/workflows/scanner.yml @@ -15,5 +15,5 @@ jobs: permissions: contents: read security-events: write - uses: go-openapi/ci-workflows/.github/workflows/scanner.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # V0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/scanner.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # V0.2.4 secrets: inherit diff --git a/.github/workflows/tag-release.yml b/.github/workflows/tag-release.yml index a968eebc8..fd527bd83 100644 --- a/.github/workflows/tag-release.yml +++ b/.github/workflows/tag-release.yml @@ -13,7 +13,7 @@ jobs: name: Create release permissions: contents: write - uses: go-openapi/ci-workflows/.github/workflows/release.yml@50a35d7a3dec2ab631ddc39b1307a8394fcc44c7 # v0.2.4 + uses: go-openapi/ci-workflows/.github/workflows/release.yml@ea0dfc9a8f78335f47355d842b76bcf95d29c846 # v0.2.5 with: tag: ${{ github.ref_name }} is-monorepo: true diff --git a/assert/assert_assertions.go b/assert/assert_assertions.go index dc9f45d70..37dfaff76 100644 --- a/assert/assert_assertions.go +++ b/assert/assert_assertions.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_assertions_test.go b/assert/assert_assertions_test.go index 454a6a35f..732ef80fe 100644 --- a/assert/assert_assertions_test.go +++ b/assert/assert_assertions_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_examples_test.go b/assert/assert_examples_test.go index 3c592e9ef..8a82b030a 100644 --- a/assert/assert_examples_test.go +++ b/assert/assert_examples_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert_test diff --git a/assert/assert_format.go b/assert/assert_format.go index 0685172de..43a430475 100644 --- a/assert/assert_format.go +++ b/assert/assert_format.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_format_test.go b/assert/assert_format_test.go index e0f979de6..d917114d0 100644 --- a/assert/assert_format_test.go +++ b/assert/assert_format_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_forward.go b/assert/assert_forward.go index 1f1ef7eeb..12586b47c 100644 --- a/assert/assert_forward.go +++ b/assert/assert_forward.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_forward_test.go b/assert/assert_forward_test.go index b5d5b4e95..545710836 100644 --- a/assert/assert_forward_test.go +++ b/assert/assert_forward_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_helpers.go b/assert/assert_helpers.go index 331e1139f..c9f848042 100644 --- a/assert/assert_helpers.go +++ b/assert/assert_helpers.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_helpers_test.go b/assert/assert_helpers_test.go index e07386f26..7bf508455 100644 --- a/assert/assert_helpers_test.go +++ b/assert/assert_helpers_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert diff --git a/assert/assert_types.go b/assert/assert_types.go index 612ae657f..9991493cb 100644 --- a/assert/assert_types.go +++ b/assert/assert_types.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package assert @@ -10,11 +10,11 @@ import ( "github.com/go-openapi/testify/v2/internal/assertions" ) -var ( +const ( // ErrTest is an error instance useful for testing. // // If the code does not care about error specifics, and only needs - // to return the error for example, this error should be used to make + // to return the error as an example, this error may be used to make // the test code more readable. ErrTest = assertions.ErrTest ) @@ -77,6 +77,9 @@ type ( // T is an interface wrapper around [testing.T]. T = assertions.T + // TestExampleError is a sentinel error type that may be used for testing. + TestExampleError = assertions.TestExampleError + // Text is any type of underlying type string or []byte. // // This is used by [RegexpT], [NotRegexpT], [JSONEqT], and [YAMLEqT]. diff --git a/assert/doc.go b/assert/doc.go index 63f3bcd6c..97862edbe 100644 --- a/assert/doc.go +++ b/assert/doc.go @@ -1 +1,58 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package assert provides a set of testing tools for use with the standard Go testing system. +// +// # Note +// +// All functions in this package return a bool value indicating whether the assertion has passed. +// +// # Example usage +// +// The following is a complete example using assert in a standard test function: +// +// import ( +// "testing" +// "github.com/go-openapi/testify/v2/assert" +// ) +// +// func TestSomething(t *testing.T) { +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(t, a, b, "The two words should be the same.") +// +// } +// +// if you assert many times, use the format below: +// +// import ( +// "testing" +// "github.com/go-openapi/testify/v2/assert" +// ) +// +// func TestSomething(t *testing.T) { +// assert := assert.New(t) +// +// var a string = "Hello" +// var b string = "Hello" +// +// assert.Equal(a, b, "The two words should be the same.") +// } +// +// # Assertions +// +// Assertions allow you to easily write test code. +// +// All assertion functions take as the first argument, the [*testing.T] object provided by the +// standard testing framework. +// +// This allows the assertion functions to write the failings and other details to the correct place. +// +// Every assertion function also takes an optional string message as the final argument, +// allowing custom error messages to be appended to the message the assertion method outputs. +// +// See [our doc site](https://go-openapi.github.io/testify/) for usage and examples and +// [go docs](https://pkg.go/dev/go-openapi/testify) for complete reference. package assert diff --git a/codegen/internal/generator/render.go b/codegen/internal/generator/render.go index 9c2411e90..99970551e 100644 --- a/codegen/internal/generator/render.go +++ b/codegen/internal/generator/render.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package generator import ( diff --git a/codegen/internal/model/documentation.go b/codegen/internal/model/documentation.go index 5e5ed8c42..518f3bf9c 100644 --- a/codegen/internal/model/documentation.go +++ b/codegen/internal/model/documentation.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package model import ( diff --git a/codegen/internal/scanner/module_test.go b/codegen/internal/scanner/module_test.go index 9c66bca11..4c54500ee 100644 --- a/codegen/internal/scanner/module_test.go +++ b/codegen/internal/scanner/module_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package scanner import "testing" diff --git a/docs/doc-site/api/_index.md b/docs/doc-site/api/_index.md index f4ceaa226..f6eac4150 100644 --- a/docs/doc-site/api/_index.md +++ b/docs/doc-site/api/_index.md @@ -6,7 +6,7 @@ description: | Find the assertion function you need for your data. weight: 1 -modified: 2026-01-25 +modified: 2026-01-26 --- **The v2 our tests wanted** @@ -49,7 +49,7 @@ Each domain contains assertions regrouped by their use case (e.g. http, json, er - [Time](./time.md) - Asserting Times And Durations (2) - [Type](./type.md) - Asserting Types Rather Than Values (10) - [Yaml](./yaml.md) - Asserting Yaml Documents (3) -- [Common](./common.md) - Other Uncategorized Helpers (4) +- [Common](./common.md) - Other Uncategorized Helpers (3) --- @@ -67,5 +67,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/boolean.md b/docs/doc-site/api/boolean.md index 01e4f8767..a0869116a 100644 --- a/docs/doc-site/api/boolean.md +++ b/docs/doc-site/api/boolean.md @@ -1,7 +1,7 @@ --- title: "Boolean" description: "Asserting Boolean Values" -modified: 2026-01-25 +modified: 2026-01-26 weight: 1 domains: - "boolean" @@ -235,5 +235,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/collection.md b/docs/doc-site/api/collection.md index c1e70fb8a..cc7f06c09 100644 --- a/docs/doc-site/api/collection.md +++ b/docs/doc-site/api/collection.md @@ -1,7 +1,7 @@ --- title: "Collection" description: "Asserting Slices And Maps" -modified: 2026-01-25 +modified: 2026-01-26 weight: 2 domains: - "collection" @@ -991,5 +991,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/common.md b/docs/doc-site/api/common.md index 5482b774a..3755724c7 100644 --- a/docs/doc-site/api/common.md +++ b/docs/doc-site/api/common.md @@ -1,15 +1,13 @@ --- title: "Common" description: "Other Uncategorized Helpers" -modified: 2026-01-25 +modified: 2026-01-26 weight: 18 domains: - "common" keywords: - "CallerInfo" - "CallerInfof" - - "New" - - "Newf" - "ObjectsAreEqual" - "ObjectsAreEqualf" - "ObjectsAreEqualValues" @@ -25,7 +23,7 @@ Other Uncategorized Helpers _All links point to _ -This domain exposes 4 functionalities. +This domain exposes 3 functionalities. ```tree ``` @@ -65,32 +63,6 @@ failed. {{% /tab %}} {{< /tabs >}} -### New - -New makes a new [Assertions] object for the specified [T]. - - -{{< tabs >}} -{{% tab title="assert" style="secondary" %}} -| Signature | Usage | -|--|--| -| [`assert.New(t T) *Assertions`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#New) | package-level function | -{{% /tab %}} -{{% tab title="require" style="secondary" %}} -| Signature | Usage | -|--|--| -| [`require.New(t T) *Assertions`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#New) | package-level function | -{{% /tab %}} - -{{% tab title="internal" style="accent" icon="wrench" %}} -| Signature | Usage | -|--|--| -| [`assertions.New(t T) *Assertions`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#New) | internal implementation | - -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#New](https://github.com/go-openapi/testify/blob/master/internal/assertions/assertion.go#L12) -{{% /tab %}} -{{< /tabs >}} - ### ObjectsAreEqual ObjectsAreEqual determines if two objects are considered equal. @@ -160,5 +132,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/comparison.md b/docs/doc-site/api/comparison.md index eaf3f6b9f..a32080125 100644 --- a/docs/doc-site/api/comparison.md +++ b/docs/doc-site/api/comparison.md @@ -1,7 +1,7 @@ --- title: "Comparison" description: "Comparing Ordered Values" -modified: 2026-01-25 +modified: 2026-01-26 weight: 3 domains: - "comparison" @@ -689,5 +689,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/condition.md b/docs/doc-site/api/condition.md index c446a851b..257d7927d 100644 --- a/docs/doc-site/api/condition.md +++ b/docs/doc-site/api/condition.md @@ -1,7 +1,7 @@ --- title: "Condition" description: "Expressing Assertions Using Conditions" -modified: 2026-01-25 +modified: 2026-01-26 weight: 4 domains: - "condition" @@ -294,5 +294,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/equality.md b/docs/doc-site/api/equality.md index f17f17b40..6cc7f64de 100644 --- a/docs/doc-site/api/equality.md +++ b/docs/doc-site/api/equality.md @@ -1,7 +1,7 @@ --- title: "Equality" description: "Asserting Two Things Are Equal" -modified: 2026-01-25 +modified: 2026-01-26 weight: 5 domains: - "equality" @@ -124,7 +124,7 @@ Pointer values are "empty" if the pointer is nil or if the pointed value is "emp |--|--| | [`assertions.Empty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Empty) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Empty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L70) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Empty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L73) {{% /tab %}} {{< /tabs >}} @@ -427,7 +427,7 @@ Nil asserts that the specified object is nil. |--|--| | [`assertions.Nil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Nil) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Nil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L18) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Nil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L21) {{% /tab %}} {{< /tabs >}} @@ -476,7 +476,7 @@ NotEmpty asserts that the specified object is NOT [Empty]. |--|--| | [`assertions.NotEmpty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEmpty) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L95) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L98) {{% /tab %}} {{< /tabs >}} @@ -667,7 +667,7 @@ assertions.NotNil(t, err) |--|--| | [`assertions.NotNil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotNil) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotNil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L39) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotNil](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_unary.go#L42) {{% /tab %}} {{< /tabs >}} @@ -716,7 +716,7 @@ See [Same]. |--|--| | [`assertions.NotSame(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSame) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSame](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L81) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSame](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L84) {{% /tab %}} {{< /tabs >}} @@ -761,7 +761,7 @@ See [SameT]. |--|--| | [`assertions.NotSameT(t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSameT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L113) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L116) {{% /tab %}} {{< /tabs >}} @@ -814,7 +814,7 @@ Unlike [Equal] pointers, [Same] pointers point to the same memory address. |--|--| | [`assertions.Same(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Same) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Same](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L21) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Same](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L24) {{% /tab %}} {{< /tabs >}} @@ -859,7 +859,7 @@ See [Same]. |--|--| | [`assertions.SameT(t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SameT) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L54) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#SameT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal_pointer.go#L57) {{% /tab %}} {{< /tabs >}} @@ -879,5 +879,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/error.md b/docs/doc-site/api/error.md index e98df1d5d..8c35822bf 100644 --- a/docs/doc-site/api/error.md +++ b/docs/doc-site/api/error.md @@ -1,7 +1,7 @@ --- title: "Error" description: "Asserting Errors" -modified: 2026-01-25 +modified: 2026-01-26 weight: 6 domains: - "error" @@ -91,7 +91,7 @@ and that it is equal to the provided error. |--|--| | [`assertions.EqualError(t T, err error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualError) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L80) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L87) {{% /tab %}} {{< /tabs >}} @@ -139,7 +139,7 @@ Error asserts that a function returned a non-nil error (ie. an error). |--|--| | [`assertions.Error(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Error) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Error](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L56) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#Error](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L63) {{% /tab %}} {{< /tabs >}} @@ -188,7 +188,7 @@ This is a wrapper for [errors.As](https://pkg.go.dev/errors#As). |--|--| | [`assertions.ErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorAs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L211) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L218) {{% /tab %}} {{< /tabs >}} @@ -237,7 +237,7 @@ error) and that the error contains the specified substring. |--|--| | [`assertions.ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorContains) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L111) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L118) {{% /tab %}} {{< /tabs >}} @@ -286,7 +286,7 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). |--|--| | [`assertions.ErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ErrorIs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L140) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#ErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L147) {{% /tab %}} {{< /tabs >}} @@ -336,7 +336,7 @@ NoError asserts that a function returned a nil error (ie. no error). |--|--| | [`assertions.NoError(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoError) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L33) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoError](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L40) {{% /tab %}} {{< /tabs >}} @@ -384,7 +384,7 @@ but if so, sets target to that error value. |--|--| | [`assertions.NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotErrorAs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L245) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorAs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L252) {{% /tab %}} {{< /tabs >}} @@ -433,7 +433,7 @@ This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). |--|--| | [`assertions.NotErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotErrorIs) | internal implementation | -**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L177) +**Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotErrorIs](https://github.com/go-openapi/testify/blob/master/internal/assertions/error.go#L184) {{% /tab %}} {{< /tabs >}} @@ -453,5 +453,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/file.md b/docs/doc-site/api/file.md index 7e9f1314c..930f0ec8b 100644 --- a/docs/doc-site/api/file.md +++ b/docs/doc-site/api/file.md @@ -1,7 +1,7 @@ --- title: "File" description: "Asserting OS Files" -modified: 2026-01-25 +modified: 2026-01-26 weight: 7 domains: - "file" @@ -344,5 +344,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/http.md b/docs/doc-site/api/http.md index 27691e14b..5c1dee8c6 100644 --- a/docs/doc-site/api/http.md +++ b/docs/doc-site/api/http.md @@ -1,7 +1,7 @@ --- title: "Http" description: "Asserting HTTP Response And Body" -modified: 2026-01-25 +modified: 2026-01-26 weight: 8 domains: - "http" @@ -382,5 +382,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/json.md b/docs/doc-site/api/json.md index 9613ba92e..408073ddf 100644 --- a/docs/doc-site/api/json.md +++ b/docs/doc-site/api/json.md @@ -1,7 +1,7 @@ --- title: "Json" description: "Asserting JSON Documents" -modified: 2026-01-25 +modified: 2026-01-26 weight: 9 domains: - "json" @@ -197,5 +197,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/number.md b/docs/doc-site/api/number.md index 7d79bb7a9..66a009073 100644 --- a/docs/doc-site/api/number.md +++ b/docs/doc-site/api/number.md @@ -1,7 +1,7 @@ --- title: "Number" description: "Asserting Numbers" -modified: 2026-01-25 +modified: 2026-01-26 weight: 10 domains: - "number" @@ -443,5 +443,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/ordering.md b/docs/doc-site/api/ordering.md index 8bce7f859..95452c9b3 100644 --- a/docs/doc-site/api/ordering.md +++ b/docs/doc-site/api/ordering.md @@ -1,7 +1,7 @@ --- title: "Ordering" description: "Asserting How Collections Are Ordered" -modified: 2026-01-25 +modified: 2026-01-26 weight: 11 domains: - "ordering" @@ -539,5 +539,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/panic.md b/docs/doc-site/api/panic.md index ad33664db..11a3f0355 100644 --- a/docs/doc-site/api/panic.md +++ b/docs/doc-site/api/panic.md @@ -1,7 +1,7 @@ --- title: "Panic" description: "Asserting A Panic Behavior" -modified: 2026-01-25 +modified: 2026-01-26 weight: 12 domains: - "panic" @@ -240,5 +240,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/string.md b/docs/doc-site/api/string.md index 07d08a2f1..4d42897ef 100644 --- a/docs/doc-site/api/string.md +++ b/docs/doc-site/api/string.md @@ -1,7 +1,7 @@ --- title: "String" description: "Asserting Strings" -modified: 2026-01-25 +modified: 2026-01-26 weight: 13 domains: - "string" @@ -241,5 +241,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/testing.md b/docs/doc-site/api/testing.md index 0320a3888..d62ec1c78 100644 --- a/docs/doc-site/api/testing.md +++ b/docs/doc-site/api/testing.md @@ -1,7 +1,7 @@ --- title: "Testing" description: "Mimicks Methods From The Testing Standard Library" -modified: 2026-01-25 +modified: 2026-01-26 weight: 14 domains: - "testing" @@ -136,5 +136,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/time.md b/docs/doc-site/api/time.md index 0303eb4b5..7e389f129 100644 --- a/docs/doc-site/api/time.md +++ b/docs/doc-site/api/time.md @@ -1,7 +1,7 @@ --- title: "Time" description: "Asserting Times And Durations" -modified: 2026-01-25 +modified: 2026-01-26 weight: 15 domains: - "time" @@ -138,5 +138,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/type.md b/docs/doc-site/api/type.md index 2ed6e540a..e328fdce7 100644 --- a/docs/doc-site/api/type.md +++ b/docs/doc-site/api/type.md @@ -1,7 +1,7 @@ --- title: "Type" description: "Asserting Types Rather Than Values" -modified: 2026-01-25 +modified: 2026-01-26 weight: 16 domains: - "type" @@ -537,5 +537,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/api/yaml.md b/docs/doc-site/api/yaml.md index 47aa0f22e..f81146287 100644 --- a/docs/doc-site/api/yaml.md +++ b/docs/doc-site/api/yaml.md @@ -1,7 +1,7 @@ --- title: "Yaml" description: "Asserting Yaml Documents" -modified: 2026-01-25 +modified: 2026-01-26 weight: 17 domains: - "yaml" @@ -202,5 +202,5 @@ SPDX-License-Identifier: Apache-2.0 Document generated by github.com/go-openapi/testify/codegen/v2 DO NOT EDIT. -Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] --> diff --git a/docs/doc-site/project/maintainers/ARCHITECTURE.md b/docs/doc-site/project/maintainers/ARCHITECTURE.md index b3f5cfbd8..904818ae3 100644 --- a/docs/doc-site/project/maintainers/ARCHITECTURE.md +++ b/docs/doc-site/project/maintainers/ARCHITECTURE.md @@ -48,4 +48,12 @@ Everything in these packages is generated. Never edit generated files directly. Exceptions: * `doc.go` is not generated * ad'hoc testable examples are not generated -* the `assert` package contains an `enable` package to enable features. This is not generated. + +**Optional Feature Packages: `enable/`** + +The `enable/` package provides optional features that users can activate via blank imports: +- `enable/stubs/` - Public stub APIs for enabling features (yaml, colors) +- `enable/yaml/` - Activates YAML support via `import _ "github.com/go-openapi/testify/v2/enable/yaml"` +- `enable/colors/` - Activates colorized output via `import _ "github.com/go-openapi/testify/v2/enable/colors"` + +These packages are not generated and allow optional dependencies to be isolated from the core library. diff --git a/docs/doc-site/project/maintainers/CODEGEN.md b/docs/doc-site/project/maintainers/CODEGEN.md index 70212c725..e7fecf11b 100644 --- a/docs/doc-site/project/maintainers/CODEGEN.md +++ b/docs/doc-site/project/maintainers/CODEGEN.md @@ -56,7 +56,6 @@ This repository uses code generation extensively to maintain consistency across direction LR docgo_assert@{ shape: document, label: "doc.go" } adhoc_assert@{ shape: document, label: "*_adhoc*_test.go" } - enable_assert@{ shape: lin-doc, label: "enable" } end end diff --git a/enable/colors/assertions_test.go b/enable/colors/assertions_test.go index a6393a6ec..21d2b3a53 100644 --- a/enable/colors/assertions_test.go +++ b/enable/colors/assertions_test.go @@ -10,7 +10,7 @@ import ( "testing" target "github.com/go-openapi/testify/v2/assert" - colorstub "github.com/go-openapi/testify/v2/assert/enable/colors" + colorstub "github.com/go-openapi/testify/v2/enable/stubs/colors" ) func TestMain(m *testing.M) { diff --git a/enable/colors/enable.go b/enable/colors/enable.go index 6fe82a3eb..c034b7a6b 100644 --- a/enable/colors/enable.go +++ b/enable/colors/enable.go @@ -11,7 +11,7 @@ import ( "golang.org/x/term" - colorstub "github.com/go-openapi/testify/v2/assert/enable/colors" + colorstub "github.com/go-openapi/testify/v2/enable/stubs/colors" ) const ( diff --git a/assert/enable/colors/enable_colors.go b/enable/stubs/colors/enable_colors.go similarity index 100% rename from assert/enable/colors/enable_colors.go rename to enable/stubs/colors/enable_colors.go diff --git a/enable/stubs/colors/enable_colors_test.go b/enable/stubs/colors/enable_colors_test.go new file mode 100644 index 000000000..9ab6391e4 --- /dev/null +++ b/enable/stubs/colors/enable_colors_test.go @@ -0,0 +1,28 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package colors + +import ( + "testing" + + target "github.com/go-openapi/testify/v2/assert" + colorstub "github.com/go-openapi/testify/v2/internal/assertions/enable/colors" +) + +func TestEnableColors(t *testing.T) { + t.Parallel() + + colorstub.Enable( + func() []colorstub.Option { + return []colorstub.Option{ + colorstub.WithEnable(true), + colorstub.WithSanitizedTheme("light"), + } + }) + + mock := new(testing.T) + target.NotPanics(mock, func() { + _ = target.JSONEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "worldwide", "foo": "bar"}`) + }) +} diff --git a/enable/stubs/doc.go b/enable/stubs/doc.go new file mode 100644 index 000000000..8325acae1 --- /dev/null +++ b/enable/stubs/doc.go @@ -0,0 +1,14 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package stubs provides public APIs for enabling optional features in testify. +// +// This package exports stub implementations that delegate to internal packages, +// maintaining a clean separation between internal implementation and public API. +// +// Subpackages: +// - yaml: API for enabling YAML assertions +// - colors: API for enabling colorized output +// +// These stubs are used by the enable/{yaml,colors} modules to activate optional features. +package stubs diff --git a/assert/enable/yaml/enable_yaml.go b/enable/stubs/yaml/enable_yaml.go similarity index 100% rename from assert/enable/yaml/enable_yaml.go rename to enable/stubs/yaml/enable_yaml.go diff --git a/enable/stubs/yaml/enable_yaml_test.go b/enable/stubs/yaml/enable_yaml_test.go new file mode 100644 index 000000000..73d5af724 --- /dev/null +++ b/enable/stubs/yaml/enable_yaml_test.go @@ -0,0 +1,22 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "fmt" + "testing" + + target "github.com/go-openapi/testify/v2/assert" +) + +func TestEnableYAML(t *testing.T) { + t.Parallel() + + EnableYAMLWithUnmarshal(func(_ []byte, _ any) error { + return fmt.Errorf("called: %w", target.ErrTest) + }) + + mock := new(testing.T) + target.False(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) +} diff --git a/enable/yaml/enable_yaml.go b/enable/yaml/enable_yaml.go index 77e790dc3..ec5a6a0bc 100644 --- a/enable/yaml/enable_yaml.go +++ b/enable/yaml/enable_yaml.go @@ -5,7 +5,7 @@ package yaml import ( - yamlstub "github.com/go-openapi/testify/v2/assert/enable/yaml" + yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" yaml "go.yaml.in/yaml/v3" ) diff --git a/enable/yaml/requirements_test.go b/enable/yaml/requirements_test.go index dc7144928..c195cb698 100644 --- a/enable/yaml/requirements_test.go +++ b/enable/yaml/requirements_test.go @@ -40,9 +40,9 @@ array: func TestRequireYAMLEq_EqualYAMLString(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) - if mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) + if mock.Failed { t.Error("Check should pass") } } @@ -50,9 +50,9 @@ func TestRequireYAMLEq_EqualYAMLString(t *testing.T) { func TestRequireYAMLEq_EquivalentButNotEqual(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) - if mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if mock.Failed { t.Error("Check should pass") } } @@ -60,9 +60,9 @@ func TestRequireYAMLEq_EquivalentButNotEqual(t *testing.T) { func TestRequireYAMLEq_HashOfArraysAndHashes(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, expectedYAML, actualYAML) - if mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, expectedYAML, actualYAML) + if mock.Failed { t.Error("Check should pass") } } @@ -70,9 +70,9 @@ func TestRequireYAMLEq_HashOfArraysAndHashes(t *testing.T) { func TestRequireYAMLEq_Array(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) - if mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) + if mock.Failed { t.Error("Check should pass") } } @@ -80,9 +80,9 @@ func TestRequireYAMLEq_Array(t *testing.T) { func TestRequireYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) - if !mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) + if !mock.Failed { t.Error("Check should fail") } } @@ -90,9 +90,9 @@ func TestRequireYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { func TestRequireYAMLEq_HashesNotEquivalent(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) - if !mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) + if !mock.Failed { t.Error("Check should fail") } } @@ -100,9 +100,9 @@ func TestRequireYAMLEq_HashesNotEquivalent(t *testing.T) { func TestRequireYAMLEq_ActualIsSimpleString(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `{"foo": "bar"}`, "Simple String") - if !mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `{"foo": "bar"}`, "Simple String") + if !mock.Failed { t.Error("Check should fail") } } @@ -110,9 +110,9 @@ func TestRequireYAMLEq_ActualIsSimpleString(t *testing.T) { func TestRequireYAMLEq_ExpectedIsSimpleString(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`) - if !mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, "Simple String", `{"foo": "bar", "hello": "world"}`) + if !mock.Failed { t.Error("Check should fail") } } @@ -120,9 +120,9 @@ func TestRequireYAMLEq_ExpectedIsSimpleString(t *testing.T) { func TestRequireYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, "Simple String", "Simple String") - if mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, "Simple String", "Simple String") + if mock.Failed { t.Error("Check should pass") } } @@ -130,9 +130,9 @@ func TestRequireYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { func TestRequireYAMLEq_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() - mockT := new(MockT) - target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) - if !mockT.Failed { + mock := new(MockT) + target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) + if !mock.Failed { t.Error("Check should fail") } } diff --git a/internal/assertions/assertion.go b/internal/assertions/assertion.go index a3d965013..a2dbd34ac 100644 --- a/internal/assertions/assertion.go +++ b/internal/assertions/assertion.go @@ -5,12 +5,5 @@ package assertions // Assertions provides assertion methods around the [T] interface. type Assertions struct { - t T -} - -// New makes a new [Assertions] object for the specified [T]. -func New(t T) *Assertions { - return &Assertions{ - t: t, - } + t T //nolint:unused // the internal version of this type doesn't use this field, but generated copies do. } diff --git a/internal/assertions/assertion_test.go b/internal/assertions/assertion_test.go deleted file mode 100644 index 67ca0e782..000000000 --- a/internal/assertions/assertion_test.go +++ /dev/null @@ -1,21 +0,0 @@ -// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers -// SPDX-License-Identifier: Apache-2.0 - -package assertions - -import "testing" - -func TestAssertionNew(t *testing.T) { - t.Parallel() - mock := new(mockT) - - a := New(mock) - if a == nil { - FailNow(t, "New should never return nil") - - return - } - if a.t == nil { - FailNow(t, "assertion should contain a T") - } -} diff --git a/internal/assertions/collection.go b/internal/assertions/collection.go index 74dabd8a5..6d5354f36 100644 --- a/internal/assertions/collection.go +++ b/internal/assertions/collection.go @@ -839,16 +839,16 @@ func formatListDiff(listA, listB any, extraA, extraB []any) string { msg.WriteString("elements differ") if len(extraA) > 0 { msg.WriteString("\n\nextra elements in list A:\n") - msg.WriteString(spewConfig.Sdump(extraA)) + msg.WriteString(dumper(extraA)) } if len(extraB) > 0 { msg.WriteString("\n\nextra elements in list B:\n") - msg.WriteString(spewConfig.Sdump(extraB)) + msg.WriteString(dumper(extraB)) } msg.WriteString("\n\nlistA:\n") - msg.WriteString(spewConfig.Sdump(listA)) + msg.WriteString(dumper(listA)) msg.WriteString("\n\nlistB:\n") - msg.WriteString(spewConfig.Sdump(listB)) + msg.WriteString(dumper(listB)) return msg.String() } diff --git a/internal/assertions/condition.go b/internal/assertions/condition.go index b6d040254..f27e3a9a2 100644 --- a/internal/assertions/condition.go +++ b/internal/assertions/condition.go @@ -155,14 +155,12 @@ func eventually(t T, condition func() bool, waitFor time.Duration, tick time.Dur h.Helper() } - return pollCondition(t, - condition, waitFor, tick, - pollOptions{ - mode: pollUntilTrue, - failMessage: "condition never satisfied", - }, - msgAndArgs..., - ) + p := newConditionPoller(pollOptions{ + mode: pollUntilTrue, + failMessage: "condition never satisfied", + }) + + return p.pollCondition(t, condition, waitFor, tick, msgAndArgs...) } func never(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { @@ -170,14 +168,12 @@ func never(t T, condition func() bool, waitFor time.Duration, tick time.Duration h.Helper() } - return pollCondition(t, - condition, waitFor, tick, - pollOptions{ - mode: pollUntilTimeout, - failMessage: "condition satisfied", - }, - msgAndArgs..., - ) + p := newConditionPoller(pollOptions{ + mode: pollUntilTimeout, + failMessage: "condition satisfied", + }) + + return p.pollCondition(t, condition, waitFor, tick, msgAndArgs...) } func eventuallyWithT(t T, collectCondition func(collector *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { @@ -205,16 +201,23 @@ func eventuallyWithT(t T, collectCondition func(collector *CollectT), waitFor ti } } - return pollCondition(t, - condition, waitFor, tick, - pollOptions{ - mode: pollUntilTrue, - failMessage: "condition never satisfied", - onFailure: copyCollected, - onSetup: func(cancel func()) { cancelFunc = cancel }, - }, - msgAndArgs..., - ) + p := newConditionPoller(pollOptions{ + mode: pollUntilTrue, + failMessage: "condition never satisfied", + onFailure: copyCollected, + onSetup: func(cancel func()) { cancelFunc = cancel }, + }) + + return p.pollCondition(t, condition, waitFor, tick, msgAndArgs...) +} + +type conditionPoller struct { + pollOptions + + ticker *time.Ticker + reported atomic.Bool + conditionChan chan func() bool + doneChan chan struct{} } // pollMode determines how the condition polling should behave. @@ -235,184 +238,240 @@ type pollOptions struct { onSetup func(cancel func()) // called after context setup to expose cancel function } +func newConditionPoller(o pollOptions) *conditionPoller { + return &conditionPoller{ + pollOptions: o, + conditionChan: make(chan func() bool, 1), + doneChan: make(chan struct{}), + } +} + // pollCondition is the common implementation for eventually, never, and eventuallyWithT. -// It polls a condition function at regular intervals until success or timeout. // -//nolint:gocognit,gocyclo,cyclop // A refactoring is planned for this complex function. -func pollCondition(t T, condition func() bool, waitFor, tick time.Duration, opts pollOptions, msgAndArgs ...any) bool { +// It polls a condition function at regular intervals until success or timeout. +func (p *conditionPoller) pollCondition(t T, condition func() bool, waitFor, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } - var parentCtx context.Context - if withContext, ok := t.(contextualizer); ok { - parentCtx = withContext.Context() - } - if parentCtx == nil { - parentCtx = context.Background() - } - - // For pollUntilTimeout (Never), we detach from parent cancellation - // so that timeout reaching is a success, not a failure. - var ctx context.Context - var cancel context.CancelFunc - if opts.mode == pollUntilTimeout { - ctx, cancel = context.WithTimeout(context.WithoutCancel(parentCtx), waitFor) - } else { - ctx, cancel = context.WithTimeout(parentCtx, waitFor) - } + parentCtx := p.parentContextFromT(t) + ctx, cancel := p.cancellableContext(parentCtx, waitFor) defer cancel() - // Allow caller to capture the cancel function (for eventuallyWithT's CollectT) - if opts.onSetup != nil { - opts.onSetup(cancel) - } + failFunc := p.failFunc(t, msgAndArgs...) - var reported atomic.Bool - failFunc := func(reason string) { - if reported.CompareAndSwap(false, true) { - if reason != "" { - t.Errorf("%s", reason) - } - Fail(t, opts.failMessage, msgAndArgs...) - } + // Allow caller to capture the cancel function (for eventuallyWithT's CollectT) + if p.onSetup != nil { + p.onSetup(cancel) } - conditionChan := make(chan func() bool, 1) - doneChan := make(chan struct{}) - - ticker := time.NewTicker(tick) - defer ticker.Stop() + p.ticker = time.NewTicker(tick) + defer p.ticker.Stop() // Check the condition once first on the initial call. - conditionChan <- condition + p.conditionChan <- condition var wg sync.WaitGroup // Goroutine 1: Poll for the condition at every tick wg.Add(1) - go func() { - defer wg.Done() + go p.pollAtTickFunc(parentCtx, ctx, condition, failFunc, &wg)() - for { - if opts.mode == pollUntilTimeout { - // For Never: check parent context separately + // Goroutine 2: Execute the condition and check results + wg.Add(1) + go p.executeCondition(parentCtx, ctx, failFunc, &wg)() + + wg.Wait() + + // Determine success based on mode + return p.determineOutcome(parentCtx, ctx, failFunc, t)() +} + +func (p *conditionPoller) failFunc(t T, msgAndArgs ...any) func(string) { + return func(reason string) { + if p.reported.CompareAndSwap(false, true) { + if reason != "" { + t.Errorf("%s", reason) + } + Fail(t, p.failMessage, msgAndArgs...) + } + } +} + +func (p *conditionPoller) pollAtTickFunc(parentCtx, ctx context.Context, condition func() bool, failFunc func(string), wg *sync.WaitGroup) func() { + if p.mode == pollUntilTimeout { + // For Never: check parent context separately + return func() { + defer wg.Done() + + for { select { case <-parentCtx.Done(): failFunc(parentCtx.Err().Error()) return case <-ctx.Done(): return // timeout reached = success for Never - case <-doneChan: + case <-p.doneChan: return - case <-ticker.C: + case <-p.ticker.C: + // Nested select prevents blocking on channel send if context was cancelled + // between receiving the tick and attempting to send the condition. select { case <-parentCtx.Done(): failFunc(parentCtx.Err().Error()) return case <-ctx.Done(): return - case <-doneChan: + case <-p.doneChan: return - case conditionChan <- condition: + case p.conditionChan <- condition: } } - } else { - // For Eventually: parent cancellation flows through ctx + } + } + } + + // For Eventually: parent cancellation flows through ctx + return func() { + defer wg.Done() + + for { + select { + case <-ctx.Done(): + failFunc(ctx.Err().Error()) + return + case <-p.doneChan: + return + case <-p.ticker.C: + // Nested select prevents blocking on channel send if context was cancelled + // between receiving the tick and attempting to send the condition. select { case <-ctx.Done(): failFunc(ctx.Err().Error()) return - case <-doneChan: + case <-p.doneChan: return - case <-ticker.C: - select { - case <-ctx.Done(): - failFunc(ctx.Err().Error()) - return - case <-doneChan: - return - case conditionChan <- condition: - } + case p.conditionChan <- condition: } } } - }() + } +} - // Goroutine 2: Execute the condition and check results - wg.Add(1) - go func() { - defer wg.Done() +func (p *conditionPoller) executeCondition(parentCtx, ctx context.Context, failFunc func(string), wg *sync.WaitGroup) func() { + if p.mode == pollUntilTimeout { + // For Never + return func() { + defer wg.Done() - for { - if opts.mode == pollUntilTimeout { + for { select { case <-parentCtx.Done(): failFunc(parentCtx.Err().Error()) return case <-ctx.Done(): return // timeout = success - case fn := <-conditionChan: + case fn := <-p.conditionChan: if fn() { - close(doneChan) // condition true = failure for Never + close(p.doneChan) // condition true = failure for Never return } } - } else { - select { - case <-ctx.Done(): - failFunc(ctx.Err().Error()) + } + } + } + + // For Eventually + return func() { + defer wg.Done() + + for { + select { + case <-ctx.Done(): + failFunc(ctx.Err().Error()) + return + case fn := <-p.conditionChan: + if fn() { + close(p.doneChan) // condition true = success return - case fn := <-conditionChan: - if fn() { - close(doneChan) // condition true = success - return - } } } } - }() + } +} - wg.Wait() +func (p *conditionPoller) determineOutcome(parentCtx, ctx context.Context, failFunc func(string), t T) func() bool { + if p.mode == pollUntilTimeout { + return func() bool { + select { + case <-p.doneChan: + // For Never: doneChan closed means condition became true + // But if timeout was reached first (ctx.Err != nil), it's still a success. + // This handles the race between timeout and condition becoming true. + if ctx.Err() != nil { + return true + } + // Condition became true before timeout = failure + failFunc("") + return false + default: + // doneChan not closed + // For Never: timeout reached without condition being true = success + // We should return a success, unless the parent context has failed. + return parentCtx.Err() == nil + } + } + } - // Determine success based on mode - select { - case <-doneChan: - if opts.mode == pollUntilTimeout { - // For Never: doneChan closed means condition became true - // But if timeout was reached first (ctx.Err != nil), it's still a success + return func() bool { + select { + case <-p.doneChan: + // For Eventually: doneChan closed means condition became true if ctx.Err() != nil { - return true + // Timeout occurred before or during success + if p.onFailure != nil { + p.onFailure(t) + } + return false } - // Condition became true before timeout = failure - failFunc("") - return false - } - // For Eventually: doneChan closed means condition became true - if ctx.Err() != nil { - // Timeout occurred before or during success - if opts.onFailure != nil { - opts.onFailure(t) + return true + default: + // doneChan not closed + // opts.mode = pollUntilTrue + // For Eventually: should not reach here (failFunc already called) + if p.onFailure != nil { + p.onFailure(t) } + return false } - return true - default: - // doneChan not closed - if opts.mode == pollUntilTimeout { - // For Never: timeout reached without condition being true = success - // We should return a success, unless the parent context has failed. - return parentCtx.Err() == nil - } + } +} - // opts.mode = pollUntilTrue - // For Eventually: should not reach here (failFunc already called) - if opts.onFailure != nil { - opts.onFailure(t) - } +func (p *conditionPoller) parentContextFromT(t T) context.Context { + var parentCtx context.Context + if withContext, ok := t.(contextualizer); ok { + parentCtx = withContext.Context() + } + if parentCtx == nil { + parentCtx = context.Background() + } - return false + return parentCtx +} + +func (p *conditionPoller) cancellableContext(parentCtx context.Context, waitFor time.Duration) (context.Context, func()) { + // For pollUntilTimeout (Never), we detach from parent cancellation + // so that timeout reaching is a success, not a failure. + var ctx context.Context + var cancel context.CancelFunc + if p.mode == pollUntilTimeout { + ctx, cancel = context.WithTimeout(context.WithoutCancel(parentCtx), waitFor) + } else { + ctx, cancel = context.WithTimeout(parentCtx, waitFor) } + + return ctx, cancel } // CollectT implements the [T] interface and collects all errors. @@ -428,7 +487,8 @@ type CollectT struct { // 2. Deprecated methods have been removed. // A slice of errors. Non-empty slice denotes a failure. - // A c.FailNow() will thee lose accumulated errors + // NOTE: When c.FailNow() is called, it cancels the context and exits the goroutine. + // The "failed now" error is appended but may be lost if the goroutine exits before collection. errors []error // cancelContext cancels the parent context on FailNow() diff --git a/internal/assertions/diff.go b/internal/assertions/diff.go index 26596ebf1..4c402cf16 100644 --- a/internal/assertions/diff.go +++ b/internal/assertions/diff.go @@ -1,8 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import ( "reflect" - "time" "github.com/go-openapi/testify/v2/internal/assertions/enable/colors" "github.com/go-openapi/testify/v2/internal/difflib" @@ -30,14 +32,12 @@ func diff(expected any, actual any) string { switch et { case reflect.TypeFor[string](): + // short-circuit for plain strings e = reflect.ValueOf(expected).String() a = reflect.ValueOf(actual).String() - case reflect.TypeFor[time.Time](): - e = spewConfigStringerEnabled.Sdump(expected) - a = spewConfigStringerEnabled.Sdump(actual) default: - e = spewConfig.Sdump(expected) - a = spewConfig.Sdump(actual) + e = dumper(expected) + a = dumper(actual) } unified := difflib.UnifiedDiff{ @@ -61,11 +61,16 @@ func diff(expected any, actual any) string { func typeAndKind(v any) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) - k := t.Kind() // Proposal for enhancement: check if t is not nil + if t == nil { + return nil, reflect.Invalid + } + + k := t.Kind() if k == reflect.Ptr { t = t.Elem() k = t.Kind() } + return t, k } diff --git a/internal/assertions/diff_test.go b/internal/assertions/diff_test.go new file mode 100644 index 000000000..f4779632d --- /dev/null +++ b/internal/assertions/diff_test.go @@ -0,0 +1,86 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package assertions + +import ( + "reflect" + "strings" + "testing" + "time" +) + +func TestDiff(t *testing.T) { + type myTime time.Time + t0 := time.Date(2026, 1, 1, 0, 0, 0, 0, time.UTC) + t1 := t0.Add(time.Second) + + t.Run("diff should render time with stringer", func(t *testing.T) { + diffResult := diff(t0, t1) + if strings.Contains(diffResult, "-(time.Time) 2026-01-01 00:00:00 +0000 UTC") && + strings.Contains(diffResult, "+(time.Time) 2026-01-01 00:00:01 +0000 UTC") { + return + } + + t.Errorf("unexpected diff time output, got: %q", diffResult) + }) + + t.Run("diff should render nested times with stringer", func(t *testing.T) { + type myStruct struct { + A time.Time + B myTime + C *time.Time + } + expected := myStruct{ + A: t0, + B: myTime(t0), + C: &t0, + } + actual := myStruct{ + A: t1, + B: myTime(t1), + C: &t1, + } + + diffResult := diff(expected, actual) + if strings.Contains(diffResult, "- A: (time.Time) 2026-01-01 00:00:00 +0000 UTC") && + strings.Contains(diffResult, "- B: (assertions.myTime) 2026-01-01 00:00:00 +0000 UTC") && + strings.Contains(diffResult, "- C: (*time.Time)(2026-01-01 00:00:00 +0000 UTC)") && + strings.Contains(diffResult, "+ A: (time.Time) 2026-01-01 00:00:01 +0000 UTC") && + strings.Contains(diffResult, "+ B: (assertions.myTime) 2026-01-01 00:00:01 +0000 UTC") && + strings.Contains(diffResult, "+ C: (*time.Time)(2026-01-01 00:00:01 +0000 UTC)") { + return + } + + t.Errorf("unexpected diff time output, got: %q", diffResult) + }) + + t.Run("diff on nil/nil interface types should render empty", func(t *testing.T) { + var a, b error + + diffResult := diff(a, &b) + if diffResult != "" { + t.Errorf("expected an empty string to render the diff") + } + + diffResult = diff((*error)(nil), (*error)(nil)) + if diffResult != "" { + t.Errorf("expected an empty string to render the diff") + } + }) +} + +func TestDiffTypeAndKind(t *testing.T) { + t.Run("should return nil and Invalid for nil interface", func(t *testing.T) { + var v any // nil interface value + + typ, kind := typeAndKind(v) + + if typ != nil { + t.Errorf("expected nil type, got %v", typ) + } + if kind != reflect.Invalid { + t.Errorf("expected Invalid kind, got %v", kind) + } + }) +} diff --git a/internal/assertions/enable/colors/options_test.go b/internal/assertions/enable/colors/options_test.go new file mode 100644 index 000000000..854e68661 --- /dev/null +++ b/internal/assertions/enable/colors/options_test.go @@ -0,0 +1,81 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package colors + +import "testing" + +func TestOptions(t *testing.T) { + t.Run("option should enable colors", func(t *testing.T) { + o := optionsWithDefaults([]Option{ + WithEnable(true), + }) + + if !o.enabled { + t.Errorf("expected option to enable colors") + } + }) + + t.Run("option should set Dark theme", func(t *testing.T) { + o := optionsWithDefaults([]Option{ + WithDark(), + }) + + if o.theme != ThemeDark { + t.Errorf("expected ThemeDark got: %v", o.theme) + } + }) + + t.Run("option should set Light theme", func(t *testing.T) { + o := optionsWithDefaults([]Option{ + WithLight(), + }) + + if o.theme != ThemeLight { + t.Errorf("expected ThemeLight got: %v", o.theme) + } + }) + + t.Run("option should sanitize string theme", func(t *testing.T) { + t.Run("with light", func(t *testing.T) { + o := optionsWithDefaults([]Option{ + WithSanitizedTheme("light"), + }) + + if o.theme != ThemeLight { + t.Errorf("expected ThemeLight got: %v", o.theme) + } + }) + + t.Run("with dark", func(t *testing.T) { + o := optionsWithDefaults([]Option{ + WithSanitizedTheme("dark"), + }) + + if o.theme != ThemeDark { + t.Errorf("expected ThemeDark got: %v", o.theme) + } + }) + + t.Run("with invalid value", func(t *testing.T) { + o := optionsWithDefaults([]Option{ + WithSanitizedTheme("invalid"), + }) + + defaultOptions := optionsWithDefaults(nil) + + if o.theme != defaultOptions.theme { + t.Errorf("expected %v (the default) got: %v", defaultOptions.theme, o.theme) + } + }) + }) +} + +func TestOptionsTheme(t *testing.T) { + t.Run("Theme should be a stringer", func(t *testing.T) { + th := ThemeDark + if str := th.String(); str != "dark" { + t.Errorf(`expected ThemeDark to stringify as "dark", but got: %q`, str) + } + }) +} diff --git a/internal/assertions/enable/colors/themes_test.go b/internal/assertions/enable/colors/themes_test.go new file mode 100644 index 000000000..8444ac56c --- /dev/null +++ b/internal/assertions/enable/colors/themes_test.go @@ -0,0 +1,262 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package colors + +import ( + "bufio" + "bytes" + "iter" + "slices" + "testing" + + "github.com/go-openapi/testify/v2/internal/difflib" +) + +func TestMakeDiffOptions(t *testing.T) { + t.Parallel() + + for c := range makeDiffOptionsCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + result := makeDiffOptions(c.opts) + + if c.expectNil { + if result != nil { + t.Errorf("expected nil, got %+v", result) + } + return + } + + if !c.expectPrinters { + t.Fatal("test case must specify expectPrinters or expectNil") + } + + if result == nil { + t.Fatal("expected non-nil result") + } + + if c.validatePrinters != nil { + c.validatePrinters(t, result) + } + }) + } +} + +func TestSetColorizers(t *testing.T) { + t.Parallel() + + for c := range setColorizerCases() { + t.Run(c.name, func(t *testing.T) { + t.Parallel() + + result := setColorizers(c.opts) + + if c.validateColorizers != nil { + c.validateColorizers(t, result) + } + }) + } +} + +// ============================== +// Test makeDiffOptions function +// ============================== + +type makeDiffOptionsCase struct { + name string + opts options + expectNil bool + expectPrinters bool + validatePrinters func(*testing.T, *difflib.Options) +} + +const testPrinter = "test" + +// printerSpec defines the expected ANSI marks for each printer type in a theme. +type printerSpec struct { + equal string + delete string + update string + insert string +} + +// validateAllPrinters validates all four printer types against the expected spec. +func validateAllPrinters(t *testing.T, o *difflib.Options, spec printerSpec) { + t.Helper() + + validatePrinter(t, o.EqualPrinter, spec.equal, "EqualPrinter") + validatePrinter(t, o.DeletePrinter, spec.delete, "DeletePrinter") + validatePrinter(t, o.UpdatePrinter, spec.update, "UpdatePrinter") + validatePrinter(t, o.InsertPrinter, spec.insert, "InsertPrinter") +} + +// validatePrinter tests that a printer builder produces the expected ANSI-wrapped output. +func validatePrinter(t *testing.T, builder difflib.PrinterBuilder, expectedMark, printerName string) { + t.Helper() + + if builder == nil { + t.Errorf("%s should not be nil", printerName) + return + } + + var buf bytes.Buffer + w := bufio.NewWriter(&buf) + + printer := builder(w) + if err := printer(testPrinter); err != nil { + t.Errorf("%s error: %v", printerName, err) + return + } + + if err := w.Flush(); err != nil { + t.Errorf("%s flush error: %v", printerName, err) + return + } + + expected := expectedMark + testPrinter + endMark + if buf.String() != expected { + t.Errorf("%s expected %q, got %q", printerName, expected, buf.String()) + } +} + +func makeDiffOptionsCases() iter.Seq[makeDiffOptionsCase] { + return slices.Values([]makeDiffOptionsCase{ + { + name: "disabled colors should return nil", + opts: options{ + enabled: false, + theme: ThemeDark, + }, + expectNil: true, + }, + { + name: "enabled with ThemeLight should return light color printers", + opts: options{ + enabled: true, + theme: ThemeLight, + }, + expectPrinters: true, + validatePrinters: func(t *testing.T, o *difflib.Options) { + t.Helper() + spec := printerSpec{ + equal: greenMark, + delete: redMark, + update: cyanMark, + insert: yellowMark, + } + validateAllPrinters(t, o, spec) + }, + }, + { + name: "enabled with ThemeDark should return bright color printers", + opts: options{ + enabled: true, + theme: ThemeDark, + }, + expectPrinters: true, + validatePrinters: func(t *testing.T, o *difflib.Options) { + t.Helper() + spec := printerSpec{ + equal: brightGreenMark, + delete: brightRedMark, + update: brightCyanMark, + insert: brightYellowMark, + } + validateAllPrinters(t, o, spec) + }, + }, + { + name: "enabled with unknown theme should return nil", + opts: options{ + enabled: true, + theme: Theme("unknown"), + }, + expectNil: true, + }, + }) +} + +// ============================== +// Test setColorizers function +// ============================== + +type setColorizerCase struct { + name string + opts options + validateColorizers func(*testing.T, colorizers) +} + +// validateColorizer tests that a colorizer produces the expected output. +func validateColorizer(t *testing.T, colorizer StringColorizer, expectedOutput, colorizerName string) { + t.Helper() + + got := colorizer(testPrinter) + if got != expectedOutput { + t.Errorf("%s expected %q, got %q", colorizerName, expectedOutput, got) + } +} + +// validateNoopColorizer tests that a colorizer returns input unchanged. +func validateNoopColorizer(t *testing.T, colorizer StringColorizer, colorizerName string) { + t.Helper() + + got := colorizer(testPrinter) + if got != testPrinter { + t.Errorf("%s should be noop, got %q for input %q", colorizerName, got, testPrinter) + } +} + +func setColorizerCases() iter.Seq[setColorizerCase] { + return slices.Values([]setColorizerCase{ + { + name: "disabled colors should return noop colorizers", + opts: options{ + enabled: false, + theme: ThemeDark, + }, + validateColorizers: func(t *testing.T, c colorizers) { + t.Helper() + validateNoopColorizer(t, c.expected, "expected") + validateNoopColorizer(t, c.actual, "actual") + }, + }, + { + name: "ThemeLight should return green/red colorizers", + opts: options{ + enabled: true, + theme: ThemeLight, + }, + validateColorizers: func(t *testing.T, c colorizers) { + t.Helper() + validateColorizer(t, c.expected, greenMark+testPrinter+endMark, "expected") + validateColorizer(t, c.actual, redMark+testPrinter+endMark, "actual") + }, + }, + { + name: "ThemeDark should return bright green/red colorizers", + opts: options{ + enabled: true, + theme: ThemeDark, + }, + validateColorizers: func(t *testing.T, c colorizers) { + t.Helper() + validateColorizer(t, c.expected, brightGreenMark+testPrinter+endMark, "expected") + validateColorizer(t, c.actual, brightRedMark+testPrinter+endMark, "actual") + }, + }, + { + name: "unknown theme should return noop colorizers", + opts: options{ + enabled: true, + theme: Theme("unknown"), + }, + validateColorizers: func(t *testing.T, c colorizers) { + t.Helper() + validateNoopColorizer(t, c.expected, "expected") + validateNoopColorizer(t, c.actual, "actual") + }, + }, + }) +} diff --git a/internal/assertions/equal_impl_test.go b/internal/assertions/equal_impl_test.go index 0a40b5c80..00953756e 100644 --- a/internal/assertions/equal_impl_test.go +++ b/internal/assertions/equal_impl_test.go @@ -4,12 +4,10 @@ package assertions import ( - "errors" "fmt" "iter" "slices" "testing" - "time" ) const shortpkg = "assertions" @@ -17,28 +15,10 @@ const shortpkg = "assertions" func TestEqualUnexportedImplementationDetails(t *testing.T) { t.Parallel() - t.Run("samePointers", testSamePointers()) t.Run("formatUnequalValue", testFormatUnequalValues()) - t.Run("isEmpty", testIsEmpty()) t.Run("validateEqualArgs", testValidateEqualArgs()) } -func testSamePointers() func(*testing.T) { - return func(t *testing.T) { - t.Parallel() - - for tt := range equalSamePointersCases() { - t.Run(tt.name, func(t *testing.T) { - t.Parallel() - - same, ok := samePointers(tt.args.first, tt.args.second) - tt.same(t, same) - tt.ok(t, ok) - }) - } - } -} - func testFormatUnequalValues() func(*testing.T) { return func(t *testing.T) { t.Parallel() @@ -55,131 +35,6 @@ func testFormatUnequalValues() func(*testing.T) { } } -func testIsEmpty() func(*testing.T) { - return func(t *testing.T) { - t.Parallel() - - chWithValue := make(chan struct{}, 1) - chWithValue <- struct{}{} - - True(t, isEmpty("")) - True(t, isEmpty(nil)) - True(t, isEmpty(error(nil))) - True(t, isEmpty((*int)(nil))) - True(t, isEmpty((*string)(nil))) - True(t, isEmpty(new(string))) - True(t, isEmpty([]string{})) - True(t, isEmpty([]string(nil))) - True(t, isEmpty([]byte(nil))) - True(t, isEmpty([]byte{})) - True(t, isEmpty([]byte(""))) - True(t, isEmpty([]bool(nil))) - True(t, isEmpty([]bool{})) - True(t, isEmpty([]any(nil))) - True(t, isEmpty([]any{})) - True(t, isEmpty(struct{}{})) - True(t, isEmpty(&struct{}{})) - True(t, isEmpty(struct{ A int }{A: 0})) - True(t, isEmpty(struct{ a int }{a: 0})) - True(t, isEmpty(struct { - a int - B int - }{a: 0, B: 0})) - True(t, isEmpty(0)) - True(t, isEmpty(int(0))) - True(t, isEmpty(int8(0))) - True(t, isEmpty(int16(0))) - True(t, isEmpty(uint16(0))) - True(t, isEmpty(int32(0))) - True(t, isEmpty(uint32(0))) - True(t, isEmpty(int64(0))) - True(t, isEmpty(uint64(0))) - True(t, isEmpty('\u0000')) // rune => int32 - True(t, isEmpty(float32(0))) - True(t, isEmpty(float64(0))) - True(t, isEmpty(0i)) // complex - True(t, isEmpty(0.0i)) // complex - True(t, isEmpty(false)) - True(t, isEmpty(new(bool))) - True(t, isEmpty(map[string]string{})) - True(t, isEmpty(map[string]string(nil))) - True(t, isEmpty(new(time.Time))) - True(t, isEmpty(time.Time{})) - True(t, isEmpty(make(chan struct{}))) - True(t, isEmpty(chan struct{}(nil))) - True(t, isEmpty(chan<- struct{}(nil))) - True(t, isEmpty(make(chan struct{}))) - True(t, isEmpty(make(chan<- struct{}))) - True(t, isEmpty(make(chan struct{}, 1))) - True(t, isEmpty(make(chan<- struct{}, 1))) - True(t, isEmpty([1]int{0})) - True(t, isEmpty([2]int{0, 0})) - True(t, isEmpty([8]int{})) - True(t, isEmpty([...]int{7: 0})) - True(t, isEmpty([...]bool{false, false})) - True(t, isEmpty(errors.New(""))) // BEWARE - True(t, isEmpty([]error{})) - True(t, isEmpty([]error(nil))) - True(t, isEmpty(&[1]int{0})) - True(t, isEmpty(&[2]int{0, 0})) - False(t, isEmpty("something")) - False(t, isEmpty(errors.New("something"))) - False(t, isEmpty([]string{"something"})) - False(t, isEmpty(1)) - False(t, isEmpty(int(1))) - False(t, isEmpty(uint(1))) - False(t, isEmpty(byte(1))) - False(t, isEmpty(int8(1))) - False(t, isEmpty(uint8(1))) - False(t, isEmpty(int16(1))) - False(t, isEmpty(uint16(1))) - False(t, isEmpty(int32(1))) - False(t, isEmpty(uint32(1))) - False(t, isEmpty(int64(1))) - False(t, isEmpty(uint64(1))) - False(t, isEmpty('A')) // rune => int32 - False(t, isEmpty(true)) - False(t, isEmpty(1.0)) - False(t, isEmpty(1i)) // complex - False(t, isEmpty([]byte{0})) // elements values are ignored for slices - False(t, isEmpty([]byte{0, 0})) // elements values are ignored for slices - False(t, isEmpty([]string{""})) // elements values are ignored for slices - False(t, isEmpty([]string{"a"})) // elements values are ignored for slices - False(t, isEmpty([]bool{false})) // elements values are ignored for slices - False(t, isEmpty([]bool{true})) // elements values are ignored for slices - False(t, isEmpty([]error{errors.New("xxx")})) - False(t, isEmpty([]error{nil})) // BEWARE - False(t, isEmpty([]error{errors.New("")})) // BEWARE - False(t, isEmpty(map[string]string{"Hello": "World"})) - False(t, isEmpty(map[string]string{"": ""})) - False(t, isEmpty(map[string]string{"foo": ""})) - False(t, isEmpty(map[string]string{"": "foo"})) - False(t, isEmpty(chWithValue)) - False(t, isEmpty([1]bool{true})) - False(t, isEmpty([2]bool{false, true})) - False(t, isEmpty([...]bool{10: true})) - False(t, isEmpty([]int{0})) - False(t, isEmpty([]int{42})) - False(t, isEmpty([1]int{42})) - False(t, isEmpty([2]int{0, 42})) - False(t, isEmpty(&[1]int{42})) - False(t, isEmpty(&[2]int{0, 42})) - False(t, isEmpty([1]*int{new(int)})) // array elements must be the zero value, not any Empty value - False(t, isEmpty(struct{ A int }{A: 42})) - False(t, isEmpty(struct{ a int }{a: 42})) - False(t, isEmpty(struct{ a *int }{a: new(int)})) // fields must be the zero value, not any Empty value - False(t, isEmpty(struct{ a []int }{a: []int{}})) // fields must be the zero value, not any Empty value - False(t, isEmpty(struct { - a int - B int - }{a: 0, B: 42})) - False(t, isEmpty(struct { - a int - B int - }{a: 42, B: 0})) - } -} - func testValidateEqualArgs() func(*testing.T) { return func(t *testing.T) { t.Parallel() @@ -230,63 +85,3 @@ func formatUnequalCases() iter.Seq[formatUnequalCase] { {uint64(123), uint64(124), `123`, `124`, "uint64 should print clean"}, }) } - -type samePointersCase struct { - name string - args args - same BoolAssertionFunc - ok BoolAssertionFunc -} - -type args struct { - first any - second any -} - -func equalSamePointersCases() iter.Seq[samePointersCase] { - p := ptr(2) - return slices.Values([]samePointersCase{ - { - name: "1 != 2", - args: args{first: 1, second: 2}, - same: False, - ok: False, - }, - { - name: "1 != 1 (not same ptr)", - args: args{first: 1, second: 1}, - same: False, - ok: False, - }, - { - name: "ptr(1) == ptr(1)", - args: args{first: p, second: p}, - same: True, - ok: True, - }, - { - name: "int(1) != float32(1)", - args: args{first: int(1), second: float32(1)}, - same: False, - ok: False, - }, - { - name: "array != slice", - args: args{first: [2]int{1, 2}, second: []int{1, 2}}, - same: False, - ok: False, - }, - { - name: "non-pointer vs pointer (1 != ptr(2))", - args: args{first: 1, second: p}, - same: False, - ok: False, - }, - { - name: "pointer vs non-pointer (ptr(2) != 1)", - args: args{first: p, second: 1}, - same: False, - ok: False, - }, - }) -} diff --git a/internal/assertions/equal_pointer.go b/internal/assertions/equal_pointer.go index c492c4200..08a953129 100644 --- a/internal/assertions/equal_pointer.go +++ b/internal/assertions/equal_pointer.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import "fmt" diff --git a/internal/assertions/equal_pointer_test.go b/internal/assertions/equal_pointer_test.go index 3d3cc6a82..b6e7ed539 100644 --- a/internal/assertions/equal_pointer_test.go +++ b/internal/assertions/equal_pointer_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import ( diff --git a/internal/assertions/equal_unary.go b/internal/assertions/equal_unary.go index 5d84122b3..0efc33c36 100644 --- a/internal/assertions/equal_unary.go +++ b/internal/assertions/equal_unary.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import ( diff --git a/internal/assertions/equal_unary_test.go b/internal/assertions/equal_unary_test.go index 7385dfc4b..15b29a74f 100644 --- a/internal/assertions/equal_unary_test.go +++ b/internal/assertions/equal_unary_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import ( @@ -51,11 +54,15 @@ func unifiedUnaryCases() iter.Seq[unaryTestCase] { xP := &x z := 0 zP := &z + var arr [1]int type TString string type TStruct struct { x int } + type FStruct struct { + x func() + } return slices.Values([]unaryTestCase{ // Nil category @@ -74,9 +81,16 @@ func unifiedUnaryCases() iter.Seq[unaryTestCase] { {"empty/aliased-string", TString(""), emptyNonNil}, {"empty/zero-array", [1]int{}, emptyNonNil}, {"empty/zero-ptr", zP, emptyNonNil}, + {"empty/zero-struct-ptr", &TStruct{}, emptyNonNil}, + {"empty/zero-array-ptr", &arr, emptyNonNil}, + {"empty/rune", '\u0000', emptyNonNil}, + {"empty/complex", 0i, emptyNonNil}, + {"empty/error", errors.New(""), emptyNonNil}, + {"empty/struct-with-func", FStruct{x: nil}, emptyNonNil}, // Non-empty comparable category {"non-empty/int", 42, nonEmptyComparable}, + {"non-empty/rune", 'A', nonEmptyComparable}, {"non-empty/string", "hello", nonEmptyComparable}, {"non-empty/bool", true, nonEmptyComparable}, {"non-empty/slice", []int{1}, nonEmptyComparable}, @@ -88,6 +102,11 @@ func unifiedUnaryCases() iter.Seq[unaryTestCase] { // Non-empty non-comparable category {"non-empty/error", errors.New("something"), nonEmptyNonComparable}, + {"non-empty/slice-error", []error{errors.New("")}, nonEmptyNonComparable}, + {"non-empty/slice-nil-error", []error{nil}, nonEmptyNonComparable}, + {"non-empty/slice-zero", []int{0}, nonEmptyNonComparable}, + {"non-empty/slice-nil", []*int{nil}, nonEmptyNonComparable}, + {"non-empty/struct-with-func", FStruct{x: func() {}}, nonEmptyNonComparable}, }) } @@ -144,6 +163,12 @@ type equalEmptyCase struct { expectedErrMsg string } +// Proposal for enhancement: should improve the overall readability and maintainability of the error message +// checks. Expectations are too specific to maintain, down to the tab/CR character. +// The line feeds and tab are not helping to spot what is expected. +// +// See [captureT] to figure +// a test refactoring plan. func equalEmptyCases() iter.Seq[equalEmptyCase] { chWithValue := make(chan struct{}, 1) chWithValue <- struct{}{} @@ -227,20 +252,18 @@ func equalEmptyCases() iter.Seq[equalEmptyCase] { name: "string with only spaces is not empty", value: " ", expectedResult: false, - expectedErrMsg: "Should be empty, but was \n", // Proposal for enhancement: FIX THIS strange error message + expectedErrMsg: "Should be empty, but was \n", }, { name: "string with a line feed is not empty", value: "\n", expectedResult: false, - // Proposal for enhancement: This is the exact same error message as for an empty string - expectedErrMsg: "Should be empty, but was \n", // Proposal for enhancement: FIX THIS strange error message + expectedErrMsg: "Should be empty, but was \n", }, { name: "string with only tabulation and lines feed is not empty", value: "\n\t\n", expectedResult: false, - // Proposal for enhancement: The line feeds and tab are not helping to spot what is expected expectedErrMsg: "" + // this syntax is used to show how errors are reported. "Should be empty, but was \n" + "\t\n", @@ -249,14 +272,12 @@ func equalEmptyCases() iter.Seq[equalEmptyCase] { name: "string with trailing lines feed is not empty", value: "foo\n\n", expectedResult: false, - // Proposal for enhancement: it's not clear if one or two lines feed are expected expectedErrMsg: "Should be empty, but was foo\n\n", }, { name: "string with leading and trailing tabulation and lines feed is not empty", value: "\n\nfoo\t\n\t\n", expectedResult: false, - // Proposal for enhancement: The line feeds and tab are not helping to figure what is expected expectedErrMsg: "" + "Should be empty, but was \n" + "\n" + @@ -267,11 +288,10 @@ func equalEmptyCases() iter.Seq[equalEmptyCase] { name: "non-printable character is not empty", value: "\u00a0", // NO-BREAK SPACE UNICODE CHARACTER expectedResult: false, - // Proposal for enhancement: here you cannot figure out what is expected expectedErrMsg: "Should be empty, but was \u00a0\n", }, - // Here we are testing there is no error message on success { + // check that there is no error message on success name: "Empty string is empty", value: "", expectedResult: true, diff --git a/internal/assertions/error.go b/internal/assertions/error.go index 0db9b4bc9..c2a5d0e13 100644 --- a/internal/assertions/error.go +++ b/internal/assertions/error.go @@ -10,12 +10,19 @@ import ( "strings" ) +// TestExampleError is a sentinel error type that may be used for testing. +type TestExampleError string + +func (e TestExampleError) Error() string { + return string(e) +} + // ErrTest is an error instance useful for testing. // // If the code does not care about error specifics, and only needs -// to return the error for example, this error should be used to make +// to return the error as an example, this error may be used to make // the test code more readable. -var ErrTest = errors.New("assert.ErrTest general error for testing") // Proposal for enhancement: make a type and a const. +const ErrTest TestExampleError = "assert.ErrTest general error for testing" // NoError asserts that a function returned a nil error (ie. no error). // diff --git a/internal/assertions/generics.go b/internal/assertions/generics.go index 659d8a966..e2e3a1168 100644 --- a/internal/assertions/generics.go +++ b/internal/assertions/generics.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import ( diff --git a/internal/assertions/mock_test.go b/internal/assertions/mock_test.go index bb35b3fe4..9d21fcf7e 100644 --- a/internal/assertions/mock_test.go +++ b/internal/assertions/mock_test.go @@ -133,6 +133,7 @@ func (ctt *captureT) Errorf(format string, args ...any) { func (ctt *captureT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, expectedErrMsg string) { t.Helper() + if res != expectedRes { t.Errorf("Should return %t", expectedRes) return @@ -152,6 +153,7 @@ func (ctt *captureT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, e t.Errorf("Should log an error. Log output: %q", ctt.msg) return } + for _, content := range contents { if content.label == "Error" { if expectedErrMsg == content.content { @@ -160,6 +162,7 @@ func (ctt *captureT) checkResultAndErrMsg(t *testing.T, expectedRes, res bool, e t.Errorf("Recorded Error: %q", content.content) } } + t.Errorf("Expected Error: %q", expectedErrMsg) } diff --git a/internal/assertions/order_impl_test.go b/internal/assertions/order_impl_test.go index a84222005..a4099b132 100644 --- a/internal/assertions/order_impl_test.go +++ b/internal/assertions/order_impl_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package assertions import "testing" diff --git a/internal/assertions/spew.go b/internal/assertions/spew.go index 9c06cc5ba..b9b2f6a20 100644 --- a/internal/assertions/spew.go +++ b/internal/assertions/spew.go @@ -5,26 +5,18 @@ package assertions import "github.com/go-openapi/testify/v2/internal/spew" -const spewMaxDepth = 10 - -//nolint:gochecknoglobals // spew is more easily configured using a global default config. This is okay in this context. -var ( - spewConfig = spew.ConfigState{ +func dumper(a ...any) string { + const spewMaxDepth = 10 + spewConfig := spew.ConfigState{ Indent: " ", DisablePointerAddresses: true, DisableCapacities: true, SortKeys: true, SpewKeys: true, DisableMethods: true, + EnableTimeStringer: true, MaxDepth: spewMaxDepth, } - spewConfigStringerEnabled = spew.ConfigState{ - Indent: " ", - DisablePointerAddresses: true, - DisableCapacities: true, - SortKeys: true, - SpewKeys: true, - MaxDepth: spewMaxDepth, - } -) + return spewConfig.Sdump(a...) +} diff --git a/internal/assertions/string.go b/internal/assertions/string.go index 45e57f080..8be312023 100644 --- a/internal/assertions/string.go +++ b/internal/assertions/string.go @@ -143,7 +143,7 @@ func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...a } func buildRegex(re any) (*regexp.Regexp, error) { - // Maintainer: proposal for enhancement(perf): cache regexp + // Maintainer: we decided that we won't cache regexp (too complex for very little value). switch v := re.(type) { case *regexp.Regexp: if v == nil { @@ -223,8 +223,14 @@ func asString(v any) (string, bool) { // weird reflection: numbers CanConvert but their string rep is wrong. Need to check further. typ := val.Type() - if kind != reflect.String && !(kind == reflect.Slice && typ.Elem().Kind() == reflect.Uint8) { //nolint:staticcheck // we want type.Elem() to be called only when kind is a slice - return "", false + if kind != reflect.String { + if kind != reflect.Slice { + return "", false + } + + if typ.Elem().Kind() != reflect.Uint8 && typ.Elem().Kind() != reflect.Uint32 { + return "", false + } } // handle ~string, ~[]byte diff --git a/internal/assertions/string_test.go b/internal/assertions/string_test.go index 183b4a881..eb1f1a90d 100644 --- a/internal/assertions/string_test.go +++ b/internal/assertions/string_test.go @@ -10,65 +10,6 @@ import ( "testing" ) -func TestStringRegexpEdgeCases(t *testing.T) { - // check edge cases for reflection-based Regexp, such as unsupported types or nil input or when the input - // is converted using fmt.Sprint. - - t.Run("with unsupported regexp type", func(t *testing.T) { - t.Parallel() - - const ( - str = "whatever" - msg = "expected this invalid call to fail (regexp=%v)" - ) - - mock := new(mockT) - - t.Run("should fail (invalid regexp type)", func(t *testing.T) { - invalidRex := struct{ a string }{a: "invalid"} - - if Regexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - if NotRegexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - }) - - t.Run("should fail (nil regexp)", func(t *testing.T) { - invalidRex := []byte(nil) - - if Regexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - if NotRegexp(mock, invalidRex, str) { - t.Errorf(msg, invalidRex) - } - }) - }) - - t.Run("with fmt.Sprint conversion (edge case)", func(t *testing.T) { - t.Parallel() - - const ( - numeric = 1234 - msg = "expected %q to match %q" - rex = "^[0-9]+$" - ) - - mock := new(mockT) - - t.Run("should match string representation of a number", func(t *testing.T) { - if !Regexp(mock, rex, numeric) { - t.Errorf(msg, numeric, rex) - } - if NotRegexp(mock, rex, numeric) { - t.Errorf(msg, numeric, rex) - } - }) - }) -} - func TestStringRegexp(t *testing.T) { t.Parallel() @@ -93,6 +34,13 @@ func TestStringRegexp(t *testing.T) { // - valid and invalid patterns // - matching and not matching expressions. func stringRegexpCases() iter.Seq[genericTestCase] { + const ( + numeric = 1234 + numRex = "^[0-9]+$" + ) + invalidRex := struct{ a string }{a: "invalid"} + nilRex := []byte(nil) + return slices.Values([]genericTestCase{ // successful matches {"^start (match)", testAllRegexpWithTypes( @@ -121,6 +69,26 @@ func stringRegexpCases() iter.Seq[genericTestCase] { {"invalid regexp", testAllRegexpWithTypes( "\\C", "whatever", false, false, )}, + // invalid type + {"invalid regexp type/struct", testRegexpWithAny( + invalidRex, "whatever", false, false, + )}, + {"invalid regexp type/nil", testRegexpWithAny( + nilRex, "whatever", false, false, + )}, + {"invalid regexp type/slice-int", testRegexpWithAny( + []int{1, 2}, "whatever", false, false, + )}, + // types that uses fmt.Print + {"use-fmt-print/slice-int", testRegexpWithAny( + "^\\[1", []int{1, 2}, true, true, + )}, + {"use-fmt-print/slice-rune", testRegexpWithAny( + "^\\[65", []rune{'A', 'B'}, true, true, + )}, + {"use-fmt-print/numeric", testRegexpWithAny( + numRex, numeric, true, true, + )}, }) } @@ -165,6 +133,42 @@ func testAllRegexpWithTypes(regString, str string, success, valid bool) func(*te } } +// testRegexpWithAny tests edge cases that are specific to the reflection-based variants. +// +//nolint:thelper // linter false positive: this is not a helper +func testRegexpWithAny(rx any, actual any, success, valid bool) func(*testing.T) { + return func(t *testing.T) { + t.Parallel() + + if !valid { + t.Run("should fail", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, false)) + t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) + }) + + return + } + + if success { + t.Run("should match", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, true)) + }) + t.Run("should fail", func(t *testing.T) { + t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) + }) + return + } + + t.Run("should NOT match", func(t *testing.T) { + t.Run("with NoRegexp", testNotRegexp(rx, actual, true)) + }) + + t.Run("should fail", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, false)) + }) + } +} + //nolint:thelper // linter false positive: this is not a helper func testAllRegexp[Rex RegExp, ADoc Text](rx Rex, actual ADoc, success, valid bool) func(*testing.T) { return func(t *testing.T) { @@ -174,8 +178,9 @@ func testAllRegexp[Rex RegExp, ADoc Text](rx Rex, actual ADoc, success, valid bo // all assertions fail on invalid regexp t.Run("should fail", func(t *testing.T) { t.Run("with Regexp", testRegexp(rx, actual, false)) - t.Run("with RegexpT", testRegexpT(rx, actual, false)) t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) + + t.Run("with RegexpT", testRegexpT(rx, actual, false)) t.Run("with NoRegexpT", testNotRegexpT(rx, actual, false)) }) @@ -192,21 +197,23 @@ func testAllRegexp[Rex RegExp, ADoc Text](rx Rex, actual ADoc, success, valid bo t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) t.Run("with NoRegexpT", testNotRegexpT(rx, actual, false)) }) - } else { - t.Run("should NOT match", func(t *testing.T) { - t.Run("with NoRegexp", testNotRegexp(rx, actual, true)) - t.Run("with NoRegexpT", testNotRegexpT(rx, actual, true)) - }) - t.Run("should fail", func(t *testing.T) { - t.Run("with Regexp", testRegexp(rx, actual, false)) - t.Run("with RegexpT", testRegexpT(rx, actual, false)) - }) + return } + + t.Run("should NOT match", func(t *testing.T) { + t.Run("with NoRegexp", testNotRegexp(rx, actual, true)) + t.Run("with NoRegexpT", testNotRegexpT(rx, actual, true)) + }) + + t.Run("should fail", func(t *testing.T) { + t.Run("with Regexp", testRegexp(rx, actual, false)) + t.Run("with RegexpT", testRegexpT(rx, actual, false)) + }) } } -func testRegexp[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { +func testRegexp(rx any, str any, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() @@ -222,7 +229,7 @@ func testRegexp[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*tes } } -func testNotRegexp[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { +func testNotRegexp(rx any, str any, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() diff --git a/internal/assertions/unsafetests/doc.go b/internal/assertions/unsafetests/doc.go index 3db0a4b7a..bc5c58c03 100644 --- a/internal/assertions/unsafetests/doc.go +++ b/internal/assertions/unsafetests/doc.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + // Package unsafetests exists only to isolate tests that reference the [unsafe] package. // // The tests in this package are totally safe. diff --git a/internal/assertions/unsafetests/unsafetests_test.go b/internal/assertions/unsafetests/unsafetests_test.go index bc4bd9b68..7aba95351 100644 --- a/internal/assertions/unsafetests/unsafetests_test.go +++ b/internal/assertions/unsafetests/unsafetests_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package unsafetests_test import ( diff --git a/internal/difflib/difflib_benchmarks_test.go b/internal/difflib/difflib_benchmarks_test.go index db914ead4..3e677d8d9 100644 --- a/internal/difflib/difflib_benchmarks_test.go +++ b/internal/difflib/difflib_benchmarks_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package difflib import ( diff --git a/internal/difflib/matcher.go b/internal/difflib/matcher.go index ec971face..dbc5054aa 100644 --- a/internal/difflib/matcher.go +++ b/internal/difflib/matcher.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package difflib type Match struct { diff --git a/internal/difflib/options_test.go b/internal/difflib/options_test.go index 62dbecbfb..ada5a5558 100644 --- a/internal/difflib/options_test.go +++ b/internal/difflib/options_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package difflib import ( diff --git a/internal/testintegration/doc.go b/internal/testintegration/doc.go index 9818291ec..aa65b33a4 100644 --- a/internal/testintegration/doc.go +++ b/internal/testintegration/doc.go @@ -1,2 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + // Package testintegration carries out cross-modules tests. package testintegration diff --git a/internal/testintegration/spew/edgecases_test.go b/internal/testintegration/spew/edgecases_test.go index c930261d6..287693022 100644 --- a/internal/testintegration/spew/edgecases_test.go +++ b/internal/testintegration/spew/edgecases_test.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spew import ( diff --git a/internal/testintegration/spew/options.go b/internal/testintegration/spew/options.go index a591213d5..cfab1c986 100644 --- a/internal/testintegration/spew/options.go +++ b/internal/testintegration/spew/options.go @@ -1,3 +1,6 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + package spew // Option to tune the [Generator]. diff --git a/internal/testintegration/yaml/assertions_test.go b/internal/testintegration/yaml/assertions_test.go new file mode 100644 index 000000000..c95876213 --- /dev/null +++ b/internal/testintegration/yaml/assertions_test.go @@ -0,0 +1,108 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + "testing" + + target "github.com/go-openapi/testify/v2/assert" +) + +func TestYAMLEq_EqualYAMLString(t *testing.T) { + t.Parallel() + + enableYAML() + mockT := new(testing.T) + target.True(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) +} + +/* +func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.True(t, target.YAMLEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.True(t, target.YAMLEq(mockT, expectedYAML, actualYAML)) +} + +func TestYAMLEq_Array(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.True(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) +} + +func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.False(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) +} + +func TestYAMLEq_HashesNotEquivalent(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.False(t, target.YAMLEq(mockT, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_ActualIsSimpleString(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.False(t, target.YAMLEq(mockT, `{"foo": "bar"}`, "Simple String")) +} + +func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.False(t, target.YAMLEq(mockT, "Simple String", `{"foo": "bar", "hello": "world"}`)) +} + +func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.True(t, target.YAMLEq(mockT, "Simple String", "Simple String")) +} + +func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.False(t, target.YAMLEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) +} + +func TestYAMLEq_OnlyFirstDocument(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.True(t, target.YAMLEq(mockT, + `--- +doc1: same +--- +doc2: different +`, + `--- +doc1: same +--- +doc2: notsame +`, + )) +} + +func TestYAMLEq_InvalidIdenticalYAML(t *testing.T) { + t.Parallel() + + mockT := new(testing.T) + target.False(t, target.YAMLEq(mockT, `}`, `}`)) +} +*/ diff --git a/internal/testintegration/yaml/doc.go b/internal/testintegration/yaml/doc.go new file mode 100644 index 000000000..c78a63987 --- /dev/null +++ b/internal/testintegration/yaml/doc.go @@ -0,0 +1,5 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +// Package yaml exercises the yaml feature. +package yaml diff --git a/internal/testintegration/yaml/enable.go b/internal/testintegration/yaml/enable.go new file mode 100644 index 000000000..c0a88bd4f --- /dev/null +++ b/internal/testintegration/yaml/enable.go @@ -0,0 +1,13 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + +package yaml + +import ( + yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" + yaml "go.yaml.in/yaml/v3" +) + +func enableYAML() { + yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) +} diff --git a/require/doc.go b/require/doc.go index 3be27e65f..41d812ad1 100644 --- a/require/doc.go +++ b/require/doc.go @@ -1,7 +1,10 @@ +// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers +// SPDX-License-Identifier: Apache-2.0 + // Package require implements the same assertions as the assert package but // stops test execution when a test fails. // -// # Example Usage +// # Example usage // // The following is a complete example using require in a standard test function: // @@ -19,11 +22,15 @@ // // # Assertions // -// The require package have same global functions as in the assert package, -// but instead of returning a boolean result they call [testing.T.FailNow]. +// The require package exposes the same functions as the assert package, +// but instead of returning a boolean result the functions call [testing.T.FailNow]. +// // A consequence of this is that it must be called from the goroutine running // the test function, not from other goroutines created during the test. // // Every assertion function also takes an optional string message as the final argument, // allowing custom error messages to be appended to the message the assertion method outputs. +// +// See [our doc site](https://go-openapi.github.io/testify/) for usage and examples and +// [go docs](https://pkg.go/dev/go-openapi/testify) for complete reference. package require diff --git a/require/require_assertions.go b/require/require_assertions.go index fc6c417ba..29b406903 100644 --- a/require/require_assertions.go +++ b/require/require_assertions.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_assertions_test.go b/require/require_assertions_test.go index be406b00c..e1131dfe1 100644 --- a/require/require_assertions_test.go +++ b/require/require_assertions_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_examples_test.go b/require/require_examples_test.go index 8c9b0522e..b7a05e951 100644 --- a/require/require_examples_test.go +++ b/require/require_examples_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require_test diff --git a/require/require_format.go b/require/require_format.go index 93e641036..599b841a4 100644 --- a/require/require_format.go +++ b/require/require_format.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_format_test.go b/require/require_format_test.go index 8aab484ef..1dec8d3ef 100644 --- a/require/require_format_test.go +++ b/require/require_format_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_forward.go b/require/require_forward.go index c7b46ba93..dcb82c5da 100644 --- a/require/require_forward.go +++ b/require/require_forward.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_forward_test.go b/require/require_forward_test.go index fe16c6974..ed916f820 100644 --- a/require/require_forward_test.go +++ b/require/require_forward_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_helpers.go b/require/require_helpers.go index d5e0332e1..69b484302 100644 --- a/require/require_helpers.go +++ b/require/require_helpers.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_helpers_test.go b/require/require_helpers_test.go index 56540d2be..0dbec8f93 100644 --- a/require/require_helpers_test.go +++ b/require/require_helpers_test.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require diff --git a/require/require_types.go b/require/require_types.go index d49942ac5..7b7862bbf 100644 --- a/require/require_types.go +++ b/require/require_types.go @@ -2,7 +2,7 @@ // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. -// Generated on 2026-01-25 (version f9aee45) using codegen version v2.1.9-0.20260125223317-f9aee45df796+dirty [sha: f9aee45df7969f1ad686953c5695ffe353eaa13a] +// Generated on 2026-01-26 (version cbd4c16) using codegen version v2.2.1-0.20260126160846-43574c83eea9+dirty [sha: 43574c83eea9c46dc5bb573128a4038e90e2f44b] package require @@ -10,11 +10,11 @@ import ( "github.com/go-openapi/testify/v2/internal/assertions" ) -var ( +const ( // ErrTest is an error instance useful for testing. // // If the code does not care about error specifics, and only needs - // to return the error for example, this error should be used to make + // to return the error as an example, this error may be used to make // the test code more readable. ErrTest = assertions.ErrTest ) @@ -80,6 +80,9 @@ type ( FailNow() } + // TestExampleError is a sentinel error type that may be used for testing. + TestExampleError = assertions.TestExampleError + // Text is any type of underlying type string or []byte. // // This is used by [RegexpT], [NotRegexpT], [JSONEqT], and [YAMLEqT].