pax_global_header00006660000000000000000000000064152030137750014515gustar00rootroot0000000000000052 comment=c83d81b641ee8d3545743ee7502f31b2a00dc6b6 go-openapi-testify-c10ca71/000077500000000000000000000000001520301377500156415ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/000077500000000000000000000000001520301377500171545ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/.gitignore000066400000000000000000000001231520301377500211400ustar00rootroot00000000000000plans/ !skills/ skills/.local-skills/ commands/ agents/ hooks/ settings.local.json go-openapi-testify-c10ca71/.claude/CLAUDE.md000066400000000000000000000073731520301377500204450ustar00rootroot00000000000000# CLAUDE.md This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository. ## Project Overview Zero-dependency fork of [stretchr/testify](https://github.com/stretchr/testify) used across all go-openapi repositories. The fork strips external dependencies (go-spew, difflib are internalized), removes mock/suite packages, and uses extensive code generation to maintain consistency across assertion variants. **Key difference:** `testifylint` does **not** work with this fork — it only recognizes `stretchr/testify` imports. ### Mono-repo structure This is a mono-repo with multiple Go modules tied together by `go.work`. | Module | Purpose | |--------|---------| | `.` (root) | Main module `github.com/go-openapi/testify/v2` — package-level doc, `go:generate` directive | | `codegen/` | Code generator: scans `internal/assertions/`, generates `assert/`, `require/`, and docs | | `enable/yaml` | Optional module — blank-import to activate YAML assertions | | `enable/colors` | Optional module — blank-import to activate colorized test output | | `hack/migrate-testify` | Migration tool for converting from `stretchr/testify` | | `internal/testintegration` | Integration tests | ### Key packages (within root module) | Package | Contents | |---------|----------| | `assert/` | Non-fatal test assertions (generated from `internal/assertions/`) | | `require/` | Fatal test assertions via `FailNow()` (generated from `internal/assertions/`) | | `internal/assertions/` | **Single source of truth** — all assertion implementations, organized by domain | | `internal/spew/` | Internalized go-spew for pretty-printing values | | `internal/difflib/` | Internalized go-difflib for generating diffs | | `internal/fdleak/` | File descriptor leak detection | | `internal/leak/` | Goroutine leak detection | | `internal/tools` | Internal tools exposed | | `enable/stubs/` | Public API for enabling optional features (yaml, colors) | ### Code generation architecture All assertion implementations live in `internal/assertions/`, organized by domain files (boolean.go, collection.go, equal.go, error.go, etc.). Each assertion function generates up to 8 variants: 1. `assert.Equal(t, ...)` — package-level function 2. `assert.Equalf(t, ..., "msg")` — format variant 3. `a.Equal(...)` — forward method (`a := assert.New(t)`) 4. `a.Equalf(..., "msg")` — forward format variant 5–8. Same four variants in `require/` (fatal on failure) Run `go generate ./...` to regenerate `assert/` and `require/` packages from source. ### Dependencies The root module has **zero external dependencies** by design. Do not add any. Optional functionality uses the "enable" pattern: users blank-import a sub-module (e.g., `_ "github.com/go-openapi/testify/v2/enable/yaml"`) to activate features that require external deps. ### Key API - `assert.Equal(t, expected, actual)` — non-fatal equality check - `require.Equal(t, expected, actual)` — fatal equality check - `assert.New(t)` / `require.New(t)` — create forwarded assertion objects - All assertions in `internal/assertions/` follow the pattern: `func Name(t T, args..., msgAndArgs ...any) bool` ### Notable design decisions - **Single source of truth** — write assertions once in `internal/assertions/`, generate everything else. - **Example-driven test generation** — `Examples:` sections in doc comments drive generated tests for all 8 variants. - **Domain tagging** — `// domain: equality` tags in doc comments organize documentation by concern. - **Enable pattern** — optional features (YAML, colors) activated via blank imports, keeping the core dependency-free. - **v2.0.0 is retracted** — see `go.mod`. ## Module Information - Module path: `github.com/go-openapi/testify/v2` - Go version: 1.25.0 - License: Apache-2.0 go-openapi-testify-c10ca71/.claude/rules/000077500000000000000000000000001520301377500203065ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/rules/contributions.md000066400000000000000000000026151520301377500235360ustar00rootroot00000000000000--- paths: - "**/*" --- # Contribution rules (go-openapi) Read `.github/CONTRIBUTING.md` before opening a pull request. ## Commit hygiene - Every commit **must** be DCO signed-off (`git commit -s`) with a real email address. PGP-signed commits are appreciated but not required. - Agents may be listed as co-authors (`Co-Authored-By:`) but the commit **author must be the human sponsor**. We do not accept commits solely authored by bots or agents. - Squash commits into logical units of work before requesting review (`git rebase -i`). ## Linting Before pushing, verify your changes pass linting against the base branch: ```sh golangci-lint run --new-from-rev master ``` Install the latest version if you don't have it: ```sh go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest ``` ## Problem statement - Clearly describe the problem the PR solves, or reference an existing issue. - PR descriptions must not be vague ("fix bug", "improve code") — explain *what* was wrong and *why* the change is correct. ## Tests are mandatory - Every bug fix or feature **must** include tests that demonstrate the problem and verify the fix. - The only exceptions are documentation changes and typo fixes. - Aim for at least 80% coverage of your patch. - Run the full test suite before submitting: For mono-repos: ```sh go test work ./... ``` For single module repos: ```sh go test ./... ``` go-openapi-testify-c10ca71/.claude/rules/github-workflows-conventions.md000066400000000000000000000206451520301377500265170ustar00rootroot00000000000000--- paths: - ".github/workflows/**.yml" - ".github/workflows/**.yaml" --- # GitHub Actions Workflows Formatting and Style Conventions This rule captures YAML and bash formatting rules to provide a consistent maintainer's experience across CI workflows. ## File Structure **REQUIRED**: All github action workflows are organized as a flat structure beneath `.github/workflows/`. > GitHub does not support a hierarchical organization for workflows yet. **REQUIRED**: YAML files are conventionally named `{workflow}.yml`, with the `.yml` extension. ## Code Style & Formatting ### Expression Spacing **REQUIRED**: All GitHub Actions expressions must have spaces inside the braces: ```yaml # ✅ CORRECT env: PR_URL: ${{ github.event.pull_request.html_url }} TOKEN: ${{ secrets.GITHUB_TOKEN }} # ❌ WRONG env: PR_URL: ${{github.event.pull_request.html_url}} TOKEN: ${{secrets.GITHUB_TOKEN}} ``` > Provides a consistent formatting rule. ### Conditional Syntax **REQUIRED**: Always use `${{ }}` in `if:` conditions: ```yaml # ✅ CORRECT if: ${{ inputs.enable-signing == 'true' }} if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} # ❌ WRONG (works but inconsistent) if: inputs.enable-signing == 'true' ``` > Provides a consistent formatting rule. ### GitHub Workflow Commands **REQUIRED**: Use workflow commands for status messages that should appear as annotations, with **double colon separator**: ```bash # ✅ CORRECT - Double colon (::) separator after title echo "::notice title=build::Build completed successfully" echo "::warning title=race-condition::Merge already in progress" echo "::error title=deployment::Failed to deploy" # ❌ WRONG - Single colon separator (won't render as annotation) echo "::notice title=build:Build completed" # Missing second ':' echo "::warning title=x:message" # Won't display correctly ``` **Syntax pattern:** `::LEVEL title=TITLE::MESSAGE` - `LEVEL`: notice, warning, or error - Double `::` separator is required between title and message > Wrong syntax may raise untidy warnings and produce botched output. ### YAML arrays formatting For steps, YAML arrays are formatted with the following indentation: ```yaml # ✅ CORRECT - Clear spacing between steps steps: - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # ❌ WRONG - Dense format, more difficult to read steps: - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 # ❌ WRONG - YAML comment or blank line could be avoided steps: # - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@21025c705c08248db411dc16f3619e6b5f9ea21a # v2.5.0 - name: Checkout repository uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 with: fetch-depth: 0 ``` ## Security Best Practices ### Version Pinning using SHAs **REQUIRED**: Always pin action versions to commit SHAs: > Runs must be repeatable with known pinned version. Automated updates are pushed frequently (e.g. daily or weekly) > to keep pinned versions up-to-date. ```yaml # ✅ CORRECT - Pinned to commit SHA with version comment uses: actions/checkout@8e8c483db84b4bee98b60c0593521ed34d9990e8 # v6.0.1 uses: crazy-max/ghaction-import-gpg@e89d40939c28e39f97cf32126055eeae86ba74ec # v6.3.0 # ❌ WRONG - Mutable tag reference uses: actions/checkout@v6 ``` ### Permission settings **REQUIRED**: Always set minimal permissions at the workflow level. ```yaml # ✅ CORRECT - Workflow level permissions set to minimum permissions: contents: read # ❌ WRONG - Workflow level permissions with undue privilege escalation permissions: contents: write pull-requests: write ``` **REQUIRED**: Whenever a job needs elevated privileges, always raise required permissions at the job level. ```yaml # ✅ CORRECT - Job level permissions set to the specific requirements for that job jobs: dependabot: permissions: contents: write pull-requests: write uses: ./.github/workflows/auto-merge.yml secrets: inherit # ❌ WRONG - Same permissions but set at workflow level instead of job level permissions: contents: write pull-requests: write ``` > (Security best practice detected by CodeQL analysis) ### Undue secret exposure **NEVER** use `secrets[inputs.name]` — always use explicit secret parameters. > Using keyed access to secrets forces the runner to expose ALL secrets to the job, which causes a security risk > (caught and reported by CodeQL security analysis). ```yaml # ❌ SECURITY VULNERABILITY # This exposes ALL organization and repository secrets to the runner on: workflow_call: inputs: secret-name: type: string jobs: my-job: steps: - uses: some-action@v1 with: token: ${{ secrets[inputs.secret-name] }} # ❌ DANGEROUS! ``` **SOLUTION**: Use explicit secret parameters with fallback for defaults: ```yaml # ✅ SECURE on: workflow_call: secrets: gpg-private-key: required: false jobs: my-job: steps: - uses: go-openapi/gh-actions/ci-jobs/bot-credentials@master with: # Falls back to go-openapi default if not explicitly passed gpg-private-key: ${{ secrets.gpg-private-key || secrets.CI_BOT_GPG_PRIVATE_KEY }} ``` ## Common Gotchas ### Description fields containing parsable expressions **REQUIRED**: **DO NOT** use `${{ }}` expressions in description fields: > They may be parsed by the runner, wrongly interpreted or causing failure (e.g. "not defined in this context"). ```yaml # ❌ WRONG - Can cause YAML parsing errors description: | Pass it as: gpg-private-key: ${{ secrets.MY_KEY }} # ✅ CORRECT description: | Pass it as: secrets.MY_KEY ``` ### Boolean inputs **Boolean inputs are forbidden**: NEVER use `type: boolean` for workflow inputs due to unpredictable type coercion > gh-action expressions using boolean job inputs are hard to predict and come with many quirks. ```yaml # ❌ FORBIDDEN - Boolean inputs have type coercion issues on: workflow_call: inputs: enable-feature: type: boolean # ❌ NEVER USE THIS default: true # The pattern `x == 'true' || x == true` seems safe but fails when: # - x is not a boolean: `x == true` evaluates to true if x != null # - Type coercion is unpredictable and error-prone # ✅ CORRECT - Always use string type for boolean-like inputs on: workflow_call: inputs: enable-feature: type: string # ✅ Use string instead default: 'true' # String value jobs: my-job: # Simple, reliable comparison if: ${{ inputs.enable-feature == 'true' }} # ✅ In bash, this works perfectly (inputs are always strings in bash): if [[ '${{ inputs.enable-feature }}' == 'true' ]]; then echo "Feature enabled" fi ``` **Rule**: Use `type: string` with values `'true'` or `'false'` for all boolean-like workflow inputs. **Note**: Step outputs and bash variables are always strings, so `x == 'true'` works fine for those. ### YAML fold scalars in action inputs **NEVER** use `>` or `>-` (fold scalars) for `with:` input values: > The YAML spec says fold scalars replace newlines with spaces, but the GitHub Actions runner > does not reliably honor this for action inputs. The action receives the literal multi-line string > instead of a single folded line, which breaks flag parsing. ```yaml # ❌ BROKEN - Fold scalar, args received with embedded newlines - uses: goreleaser/goreleaser-action@... with: args: >- release --clean --release-notes /tmp/notes.md # ✅ CORRECT - Single line - uses: goreleaser/goreleaser-action@... with: args: release --clean --release-notes /tmp/notes.md # ✅ CORRECT - Literal block scalar (|) is fine for run: scripts - run: | echo "line 1" echo "line 2" ``` **Rule**: Use single-line strings for `with:` inputs. Only use `|` (literal block scalar) for `run:` scripts where multi-line is intentional. go-openapi-testify-c10ca71/.claude/rules/go-conventions.md000066400000000000000000000004621520301377500236020ustar00rootroot00000000000000--- paths: - "**/*.go" --- # Code conventions (go-openapi) - All files must have SPDX license headers (Apache-2.0). - Go version policy: support the 2 latest stable Go minor versions. - Commits require DCO sign-off (`git commit -s`). - use `golangci-lint fmt` to format code (not `gofmt` or `gofumpt`) go-openapi-testify-c10ca71/.claude/rules/linting.md000066400000000000000000000006251520301377500222770ustar00rootroot00000000000000--- paths: - "**/*.go" --- # Linting conventions (go-openapi) ```sh golangci-lint run ``` Config: `.golangci.yml` — posture is `default: all` with explicit disables. See `docs/STYLE.md` for the rationale behind each disabled linter. Key rules: - Every `//nolint` directive **must** have an inline comment explaining why. - Prefer disabling a linter over scattering `//nolint` across the codebase. go-openapi-testify-c10ca71/.claude/rules/testing.md000066400000000000000000000017121520301377500223060ustar00rootroot00000000000000--- paths: - "**/*_test.go" --- # Testing conventions (go-openapi) ## Running tests **Single module repos:** ```sh go test ./... ``` **Mono-repos (with `go.work`):** ```sh # All modules go test work ./... # Single module go test ./conv/... ``` Note: in mono-repos, plain `go test ./...` only tests the root module. The `work` pattern expands to all modules listed in `go.work`. CI runs tests on `{ubuntu, macos, windows} x {stable, oldstable}` with `-race` via `gotestsum`. ## Fuzz tests ```sh # List all fuzz targets go test -list Fuzz ./... # Run a specific target (go test -fuzz cannot span multiple packages) go test -fuzz=Fuzz -run='FuzzTargetName$' -fuzztime=1m30s ./package ``` Fuzz corpus lives in `testdata/fuzz/` within each package. CI runs each fuzz target for 1m30s with a 5m minimize timeout. ## Test framework `github.com/go-openapi/testify/v2` — a zero-dep fork of `stretchr/testify`. Because it's a fork, `testifylint` does not work. go-openapi-testify-c10ca71/.claude/skills/000077500000000000000000000000001520301377500204555ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/skills/add-assertion/000077500000000000000000000000001520301377500232125ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/skills/add-assertion/SKILL.md000066400000000000000000000062711520301377500244200ustar00rootroot00000000000000# Adding a New Assertion Step-by-step workflow for adding a new assertion function to testify. ## Workflow 1. Add function to the appropriate domain file in `internal/assertions/` 2. Add `// Domain: ` as the first line inside the function body 3. Add `// Opposite: ` on the next line if a logical opposite exists 4. Add `Examples:` section to the doc comment 5. Add tests to the corresponding `*_test.go` file 6. Run `go generate ./...` to produce all 8 variants + docs 7. Run `go test work ./...` to verify everything ## Function template ```go // FuncName asserts that . // // # Usage // // assertions.FuncName(t, arg1, arg2) // // # Examples // // success: arg1Value, arg2Value // failure: arg1Value, arg2Value func FuncName(t T, arg1, arg2 any, msgAndArgs ...any) bool { // Domain: // Opposite: (omit if none) if h, ok := t.(H); ok { h.Helper() } // implementation if !condition { return Fail(t, "message", msgAndArgs...) } return true } ``` ## Doc comment annotations ### Domain tag (in doc comment header) ```go // domain: equality ``` Assigns the function to a documentation domain. Add domain descriptions in `internal/assertions/doc.go` if creating a new domain. ### Domain comment (inside function body) ```go // Domain: equality ``` First line inside the function body. Used by the codegen scanner. ### Opposite annotation ```go // Opposite: NotEqual ``` Second line inside the body (after Domain). Only on the affirmative form (e.g., on `Equal`, not on `NotEqual`). ### Examples section ```go // # Examples // // success: 123, 123 // failure: 123, 456 ``` Drives generated smoke tests for all 8 variants. Three case types: - `success: ` -- test should pass - `failure: ` -- test should fail - `panic: ` followed by `` on next line For complex values that can't be represented inline, use `// NOT IMPLEMENTED`: ```go // success: &customStruct{Field: "value"}, // NOT IMPLEMENTED ``` **Never use `// TODO`** -- it triggers false positives in code quality tools. ## What gets generated From a single function, `go generate` produces: | Variant | Package | Example | |---------|---------|---------| | Package-level | `assert` | `assert.FuncName(t, ...)` | | Formatted | `assert` | `assert.FuncNamef(t, ..., "msg")` | | Forward method | `assert` | `a.FuncName(...)` | | Forward formatted | `assert` | `a.FuncNamef(..., "msg")` | | Package-level | `require` | `require.FuncName(t, ...)` | | Formatted | `require` | `require.FuncNamef(t, ..., "msg")` | | Forward method | `require` | `r.FuncName(...)` | | Forward formatted | `require` | `r.FuncNamef(..., "msg")` | Plus: tests for all variants, documentation entry in `docs/doc-site/api/.md`. Generic assertions (with type params) produce 4 variants (no forward methods -- Go limitation). ## Checklist - [ ] Function in `internal/assertions/.go` - [ ] `// Domain:` and optionally `// Opposite:` inside function body - [ ] Doc comment with `# Usage`, `# Examples` sections - [ ] Tests in `internal/assertions/_test.go` - [ ] `go generate ./...` succeeds - [ ] `go test work ./...` passes - [ ] `golangci-lint run --new-from-rev master` clean go-openapi-testify-c10ca71/.claude/skills/codegen/000077500000000000000000000000001520301377500220615ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/skills/codegen/SKILL.md000066400000000000000000000062171520301377500232670ustar00rootroot00000000000000# Code Generation How the testify code and documentation generator works. ## Running ```bash # Generate everything (code + docs) from internal/assertions/ go generate ./... # Or run the generator directly cd codegen && go run . -output-packages assert,require -include-doc # Code only (skip docs) cd codegen && go run . -output-packages assert,require -include-doc=false ``` ## Generator pipeline 1. **Scanner** (`codegen/internal/scanner/`) parses `internal/assertions/` using `go/packages` and `go/types`. Extracts function signatures, doc comments, domain tags, examples, and metadata. - `comments/` -- doc comment extraction - `comments-parser/` -- domain tags, examples, metadata parsing - `signature/` -- function signature analysis 2. **Model** (`codegen/internal/model/`) holds the intermediate representation: functions, type params, tests, documentation, metrics. 3. **Generator** (`codegen/internal/generator/`) renders templates: - **Code templates** produce `assert/` and `require/` packages with all variants - **Doc templates** produce Hugo markdown in `docs/doc-site/api/` - **Metrics** produces `metrics.yaml` for Hugo site params ## Key templates Located in `codegen/internal/generator/templates/`: | Template | Produces | |----------|----------| | `assertion_assertions.gotmpl` | `assert` package-level functions | | `assertion_format.gotmpl` | `assert` formatted variants (`*f`) | | `assertion_forward.gotmpl` | `assert` forward methods | | `requirement_*.gotmpl` | `require` equivalents (calls `FailNow`) | | `doc_index.md.gotmpl` | API index page (`_index.md`) | | `doc_page.md.gotmpl` | Per-domain doc pages | | `doc_metrics.md.gotmpl` | Quick index & metrics page | ## Template functions Custom template functions in `codegen/internal/generator/funcmaps/`: - `Slugize(name)` -- converts function name to markdown anchor - `Titleize(s)` -- title-cases a string - `hopen` / `hclose` -- Hugo shortcode delimiters (`{{% ... %}}`) ## Domain organization Functions are grouped by `// Domain: ` tags inside the function body. Domain descriptions live in `internal/assertions/doc.go` as special comments. The generator reorganizes package-based docs into domain-based pages (19 domains currently). ## Generated output **Never edit generated files directly.** They carry a `DO NOT EDIT` header. | Output | Location | |--------|----------| | `assert/` package | Generated functions + tests | | `require/` package | Generated functions + tests | | `docs/doc-site/api/*.md` | Domain-organized Hugo pages | | `docs/doc-site/api/metrics.md` | Quick index + API metrics | | `hack/doc-site/hugo/metrics.yaml` | Hugo site params (counts) | Exceptions (not generated): `assert/doc.go`, `require/doc.go`, ad-hoc testable examples. ## Adding support for a new construct If the generator needs to support a new Go construct (e.g., new type param pattern), the work is in: 1. `codegen/internal/scanner/` -- teach the scanner to extract it 2. `codegen/internal/model/` -- add fields to the model 3. `codegen/internal/generator/templates/` -- update templates to render it The scanner and generator have comprehensive tests (~1,400+ lines across test files). go-openapi-testify-c10ca71/.claude/skills/doc-site/000077500000000000000000000000001520301377500221645ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/skills/doc-site/SKILL.md000066400000000000000000000055111520301377500233660ustar00rootroot00000000000000# Documentation Site Hugo-based documentation site for testify, auto-generated from source code. ## Running locally ```bash # 1. Generate docs from source go generate ./... # 2. Start Hugo dev server cd hack/doc-site/hugo ./gendoc.sh # Visit http://localhost:1313/testify/ # Auto-reloads on changes to docs/doc-site/ ``` ## Site structure ``` hack/doc-site/hugo/ hugo.yaml # Main Hugo config metrics.yaml # Generated metrics (codegen output, merged into site params) testify.yaml # Version info + build metadata gendoc.sh # Dev server launcher layouts/ # Custom layout overrides themes/hugo-relearn/ # Relearn documentation theme docs/doc-site/ # Content (mounted by Hugo) api/ # Generated: domain pages, index, metrics (DO NOT EDIT) usage/ # Hand-written: USAGE, GENERICS, CHANGES, MIGRATION, etc. project/ # Hand-written: APPROACH, maintainer docs ``` ## Generated vs hand-written content | Path | Generated? | Notes | |------|-----------|-------| | `docs/doc-site/api/*.md` | Yes | Domain pages, index, metrics. Regenerate with `go generate` | | `docs/doc-site/api/metrics.md` | Yes | Quick index table + API counts | | `docs/doc-site/usage/*.md` | No | Hand-written guides | | `docs/doc-site/project/*.md` | No | Hand-written project docs | ## Dynamic counts via Hugo params Function and assertion counts are generated into `metrics.yaml` and merged into Hugo's `site.Params.metrics`. Use the relearn `siteparam` shortcode to reference them in hand-written markdown: ```markdown We have {{% siteparam "metrics.assertions" %}} assertions across {{% siteparam "metrics.domains" %}} domains. ``` Available params: `metrics.domains`, `metrics.functions`, `metrics.assertions`, `metrics.generics`, `metrics.nongeneric_assertions`, `metrics.helpers`, `metrics.others`. Per-domain: `metrics.by_domain..count`, `metrics.by_domain..name`. Hugo math functions (`sub`, `mul`, `add`) are NOT available in markdown content. For computed values, add them to the codegen `buildMetrics()` in `codegen/internal/generator/doc_generator.go`. ## Adding a new hand-written page 1. Create `docs/doc-site/
/.md` with Hugo front matter 2. Set `weight:` to control ordering in the sidebar 3. Use relearn shortcodes: `{{% notice %}}`, `{{% expand %}}`, `{{< tabs >}}`, etc. 4. Reference API counts with `{{% siteparam "metrics." %}}` ## Relearn theme features used - `{{% notice style="info" %}}` -- callout boxes - `{{% expand title="..." %}}` -- collapsible sections - `{{< tabs >}}` / `{{% tab %}}` -- tabbed content - `{{< cards >}}` / `{{% card %}}` -- side-by-side cards - `{{% icon icon="star" color=orange %}}` -- inline icons - `{{% siteparam "key" %}}` -- site param substitution - `{{< mermaid >}}` -- diagrams go-openapi-testify-c10ca71/.claude/skills/testing-generic-functions/000077500000000000000000000000001520301377500255525ustar00rootroot00000000000000go-openapi-testify-c10ca71/.claude/skills/testing-generic-functions/SKILL.md000066400000000000000000000144551520301377500267630ustar00rootroot00000000000000# Testing Generic Functions with Table-Driven Tests ## The Challenge Go generics require type parameters to be resolved at compile time. This creates a problem for traditional table-driven tests where test cases are stored in a slice of `any`: ```go // This DOES NOT work - type parameter cannot be inferred from 'any' cases := []struct { name string value1 any value2 any }{ {"int", 1, 2}, {"string", "a", "b"}, } for _, c := range cases { GreaterT(mock, c.value1, c.value2) // Error: cannot infer type parameter } ``` ## The Solution: Test Function Closures Wrap each test case in a closure where the type parameter is resolved at slice construction time, not iteration time. ### Step 1: Define a Test Case Struct ```go import "testing" // genericTestCase wraps a test function with its name for table-driven tests. type genericTestCase struct { name string test func(*testing.T) } ``` ### Step 2: Create a Generic Test Helper The helper function is generic and returns a closure. The type parameter is resolved when the closure is created (at slice construction), not when it's executed. ```go import "testing" // testGreaterT creates a test function for GreaterT with specific type V. // Type parameter V is resolved when this function is called, not when the // returned closure executes. func testGreaterT[V Ordered](successE1, successE2, failE1, failE2 V) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) // Success case: e1 > e2 True(t, GreaterT(mock, successE1, successE2)) // Failure case: e1 <= e2 False(t, GreaterT(mock, failE1, failE2)) // Equal values should fail False(t, GreaterT(mock, successE1, successE1)) } } ``` ### Step 3: Create Iterator Function for Test Cases Use the iterator pattern (`iter.Seq`) to return test cases: ```go import ( "iter" "slices" ) func greaterTCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Numeric types - type inferred from arguments {"int", testGreaterT[int](2, 1, 1, 2)}, {"int8", testGreaterT[int8](2, 1, 1, 2)}, {"int16", testGreaterT[int16](2, 1, 1, 2)}, {"int32", testGreaterT[int32](2, 1, 1, 2)}, {"int64", testGreaterT[int64](2, 1, 1, 2)}, {"uint", testGreaterT[uint](2, 1, 1, 2)}, {"uint8", testGreaterT[uint8](2, 1, 1, 2)}, {"uint16", testGreaterT[uint16](2, 1, 1, 2)}, {"uint32", testGreaterT[uint32](2, 1, 1, 2)}, {"uint64", testGreaterT[uint64](2, 1, 1, 2)}, {"uintptr", testGreaterT[uintptr](2, 1, 1, 2)}, {"float32", testGreaterT[float32](2.0, 1.0, 1.0, 2.0)}, {"float64", testGreaterT[float64](2.0, 1.0, 1.0, 2.0)}, {"string", testGreaterT[string]("b", "a", "a", "b")}, // Special types requiring dedicated setup functions {"time.Time", testGreaterTTime()}, {"[]byte", testGreaterTBytes()}, // Custom types to verify constraint satisfaction {"custom int type", testGreaterTCustomInt()}, }) } ``` ### Step 4: Handle Special Types Some types need dedicated test functions because they require specific setup: ```go import ( "testing" "time" ) // testGreaterTTime tests GreaterT with time.Time values. func testGreaterTTime() func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) t0 := time.Now() t1 := t0.Add(-time.Second) // t1 is before t0 True(t, GreaterT(mock, t0, t1)) // t0 > t1 False(t, GreaterT(mock, t1, t0)) // t1 < t0 False(t, GreaterT(mock, t0, t0)) // equal } } // testGreaterTBytes tests GreaterT with []byte values. func testGreaterTBytes() func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) b1 := []byte("b") b2 := []byte("a") True(t, GreaterT(mock, b1, b2)) False(t, GreaterT(mock, b2, b1)) False(t, GreaterT(mock, b1, b1)) } } // testGreaterTCustomInt verifies custom types satisfying the constraint work. type myInt int func testGreaterTCustomInt() func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) True(t, GreaterT(mock, myInt(2), myInt(1))) False(t, GreaterT(mock, myInt(1), myInt(2))) } } ``` ### Step 5: Write the Test Function ```go import "testing" func TestCompareGreaterT(t *testing.T) { t.Parallel() for tc := range greaterTCases() { t.Run(tc.name, tc.test) } } ``` ## Complete Pattern Summary ```go import ( "iter" "slices" "testing" ) // 1. Test case wrapper struct type genericTestCase struct { name string test func(*testing.T) } // 2. Generic test helper returning closure func testFunctionUnderTest[V Constraint](args V) func(*testing.T) { return func(t *testing.T) { t.Parallel() // Test logic using args with type V } } // 3. Special type handlers func testFunctionUnderTestSpecialType() func(*testing.T) { return func(t *testing.T) { t.Parallel() // Test logic with special setup } } // 4. Iterator function func functionUnderTestCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ {"type1", testFunctionUnderTest[Type1](value1)}, {"type2", testFunctionUnderTest[Type2](value2)}, {"special", testFunctionUnderTestSpecialType()}, }) } // 5. Test function func TestFunctionUnderTest(t *testing.T) { t.Parallel() for tc := range functionUnderTestCases() { t.Run(tc.name, tc.test) } } ``` ## Key Insights 1. **Type resolution timing**: Generic type parameters are resolved at compile time. By creating closures at slice construction, each closure has its type parameter already resolved. 2. **Closure captures**: The generic test helper captures the typed arguments in its closure, preserving type information for when the test executes. 3. **Special type handling**: Types like `time.Time` and `[]byte` need dedicated functions because they require specific construction patterns that don't fit the simple value pattern. 4. **Custom type testing**: Always include at least one custom type (e.g., `type myInt int`) to verify the constraint works with user-defined types, not just built-in types. 5. **Parallel execution**: Each closure is independent, enabling parallel test execution with `t.Parallel()`. ## When to Use This Pattern - Testing generic functions with type constraints - When you need to verify behavior across multiple types satisfying a constraint - When traditional table-driven tests fail due to type inference limitations - Testing functions from `internal/assertions/` that have generic variants (e.g., `GreaterT`, `LessT`, `PositiveT`) go-openapi-testify-c10ca71/.github/000077500000000000000000000000001520301377500172015ustar00rootroot00000000000000go-openapi-testify-c10ca71/.github/CONTRIBUTING.md000066400000000000000000000001701520301377500214300ustar00rootroot00000000000000## Contribution Guidelines This document has moved there: go-openapi-testify-c10ca71/.github/DCO.md000066400000000000000000000026741520301377500201410ustar00rootroot00000000000000 # Developer's Certificate of Origin ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` go-openapi-testify-c10ca71/.github/copilot000077700000000000000000000000001520301377500233732../.claude/rulesustar00rootroot00000000000000go-openapi-testify-c10ca71/.github/copilot-instructions.md000066400000000000000000000032771520301377500237470ustar00rootroot00000000000000# Copilot Instructions This is the `go-openapi/testify` repository — a zero-dependency fork of `stretchr/testify` used across all go-openapi repositories. ## Architecture All assertion implementations live in `internal/assertions/`, organized by domain. The `assert/` and `require/` packages are **generated** from that source via `go generate ./...`. Do not edit generated files directly. Each assertion function generates up to 8 variants (package-level, format, forward, forward-format, in both assert and require). ## Adding assertions 1. Add function to appropriate domain file in `internal/assertions/` 2. Add `Examples:` and `domain:` tags in the doc comment 3. Add tests in the corresponding `*_test.go` file 4. Run `go generate ./...` ## Optional features YAML and color support require blank imports of enable packages: ```go import _ "github.com/go-openapi/testify/v2/enable/yaml" import _ "github.com/go-openapi/testify/v2/enable/colors" ``` ## Conventions Coding conventions are found beneath `.github/copilot` ### Summary - All `.go` files must have SPDX license headers (Apache-2.0). - Commits require DCO sign-off (`git commit -s`). - Linting: `golangci-lint run` — config in `.golangci.yml` (posture: `default: all` with explicit disables). - Every `//nolint` directive **must** have an inline comment explaining why. - Tests: `go test work ./...` (mono-repo). CI runs on `{ubuntu, macos, windows} x {stable, oldstable}` with `-race`. - Test framework: this IS the test framework (`github.com/go-openapi/testify/v2`); `testifylint` does not work with this fork. See `.github/copilot/` (symlinked to `.claude/rules/`) for detailed rules on Go conventions, linting, testing, and contributions. go-openapi-testify-c10ca71/.github/dependabot.yaml000066400000000000000000000034431520301377500221760ustar00rootroot00000000000000version: 2 updates: - package-ecosystem: "github-actions" directories: - "/" schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 2 # <- default is 5 allow: - dependency-type: all groups: # <- group all github actions updates in a single PR # 1. development-dependencies are auto-merged development-dependencies: patterns: - '*' assignees: - fredbi - package-ecosystem: "gomod" # We define 4 groups of dependencies to regroup update pull requests: # - development (e.g. test dependencies) # - go-openapi updates # - golang.org (e.g. golang.org/x/... packages) # - other dependencies (direct or indirect) # # * All groups are checked once a week and each produce at most 1 PR. # * All dependabot PRs are auto-approved # # Auto-merging policy, when requirements are met: # 1. development-dependencies are auto-merged # 2. golang.org-dependencies are auto-merged # 3. go-openapi patch updates are auto-merged. Minor/major version updates require a manual merge. # 4. other dependencies require a manual merge directories: - "**/*" schedule: interval: "weekly" day: "friday" open-pull-requests-limit: 4 allow: - dependency-type: all groups: development-dependencies: patterns: - "github.com/stretchr/testify" golang-org-dependencies: patterns: - "golang.org/*" go-openapi-dependencies: patterns: - "github.com/go-openapi/*" other-dependencies: exclude-patterns: - "github.com/go-openapi/*" - "github.com/stretchr/testify" - "golang.org/*" assignees: - fredbi go-openapi-testify-c10ca71/.github/wordlist.txt000066400000000000000000000047051520301377500216170ustar00rootroot00000000000000ADoc API APIs Acknowledgements Autocomplete BDD BSON CI CIDR CLI CLIs CSV CodeFactor CodeQL ComparisonOperators ContinueOnMethod DCO DSL DisableCapacities DisableMethods DisablePointerAddresses DisablePointerMethods DockerHub EDoc EType ElementsMatch's Enablement FAQ FDs GC GOPATH GOROOT HTTP HTTPS HUGO ID IDE's IDs IOW IP IPs ISBN ISC JSON JUnit Kubernetes LOC Markdown MaxDepth Mezard NUnit NaN OAI OAuth Obershelp OpenAPI Orderable OrderedSlice PHPUnit PR PR's PRs PkgGoDev Pre PyTest README RSpec Ratcliff ReadDir Readlink Reimplemented Relinted SCSS SSN SUnit SeqContains SetSeq SignedNumber SliceSubset SortKeys SpewKeys Subpackages Substitutability Subtests Superlinear TCP TLS TODO TTY Teardown Triaging UI ULID URI URL URLs USD UUID Unmarshalers XYZ YAML ad'hoc agentic allocs api apis arg args assignees async auth authenticated authenticator authenticators authorized authorizer authorizers autogenerate backquote backquoted bash benchmarked benchmarking bitmask bson bytesize cancelled cgo ci cidr cli clis cmp codebase codecov codegen colorizer colorizers config configs csv ctx customizable dependabot deps dereference dereferenced dereferencing deserialization deserialize deserialized deserializer dev developercertificate difflib disambiguates docker dumpcgo e.g. env err's de facto faq fd fka flattener fmt fromfile fromfiledate fuzzying gc github globals go-openapi godoc golang golangci golint goroutine goroutines grpc hexdump hostname hostnames html http httpOK https hugo i.e. id impactful implementor implementors initialism initialisms inodes io ipsum ipsums ipv4 ipv6 isbn iter json jsonschema k8s kubernetes lifecycle lineterm linter linter's linters listA listB logics loren lowercases maintainer's markdown marshaled marshaling matchers maths md metalinter middleware middlewares mixin mockFailNowT mockT monorepo multipart mutex ns oai oauth oauth2 openapi param params pmezard pollCondition pprof prepend prepended readlines rebase rebased redeclare relinting repo repos roadmap roundtrip roundtripper schema schemas semver serialize serialized serializer sexualized spdx ssn stdlib stretchr struct structs submodule subpackage substring subtests superlinearly swagger syncing testDataPath testcgo testify's testifylint tls tm tofile tofiledate toolchain ui ulid uncategorized unexported unidiff unmarshal unmarshaled unmarshaler unmarshaling untyped uri url urls utf-8 uuid v1 v2 v3 validator validators waitFor windiff workspace workspaces writelines xunit yaml ℹ go-openapi-testify-c10ca71/.github/workflows/000077500000000000000000000000001520301377500212365ustar00rootroot00000000000000go-openapi-testify-c10ca71/.github/workflows/auto-merge.yml000066400000000000000000000004621520301377500240300ustar00rootroot00000000000000name: Dependabot auto-merge permissions: contents: read on: pull_request: jobs: dependabot: permissions: contents: write pull-requests: write uses: go-openapi/ci-workflows/.github/workflows/auto-merge.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 secrets: inherit go-openapi-testify-c10ca71/.github/workflows/bump-release.yml000066400000000000000000000020121520301377500243350ustar00rootroot00000000000000name: Bump Release permissions: contents: read on: workflow_dispatch: inputs: bump-type: description: Type of bump (patch, minor, major) type: choice options: - patch - minor - major default: patch required: false tag-message-title: description: Tag message title to prepend to the release notes required: false type: string tag-message-body: description: | Tag message body to prepend to the release notes. (use "|" to replace end of line). required: false type: string jobs: bump-release: permissions: contents: write pull-requests: write uses: go-openapi/ci-workflows/.github/workflows/bump-release-monorepo.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 with: bump-type: ${{ inputs.bump-type }} tag-message-title: ${{ inputs.tag-message-title }} tag-message-body: ${{ inputs.tag-message-body }} secrets: inherit go-openapi-testify-c10ca71/.github/workflows/codeql.yml000066400000000000000000000007311520301377500232310ustar00rootroot00000000000000name: "CodeQL" on: push: branches: [ "master" ] pull_request: branches: [ "master" ] paths-ignore: # remove this clause if CodeQL is a required check - '**/*.md' schedule: - cron: '39 19 * * 5' permissions: contents: read jobs: codeql: permissions: contents: read security-events: write uses: go-openapi/ci-workflows/.github/workflows/codeql.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 secrets: inherit go-openapi-testify-c10ca71/.github/workflows/contributors.yml000066400000000000000000000005301520301377500245140ustar00rootroot00000000000000name: Contributors on: schedule: - cron: '18 4 * * 6' workflow_dispatch: permissions: contents: read jobs: contributors: permissions: pull-requests: write contents: write uses: go-openapi/ci-workflows/.github/workflows/contributors.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 secrets: inherit go-openapi-testify-c10ca71/.github/workflows/go-test.yml000066400000000000000000000007031520301377500233430ustar00rootroot00000000000000name: go test permissions: pull-requests: read contents: read on: push: branches: - master pull_request: jobs: test: uses: go-openapi/ci-workflows/.github/workflows/go-test-monorepo.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 with: extra-flags: '-tags testcgo,testcolorized' # (1) this is to trigger extra tests in spew, (2) this is to enable integration test for colorized output secrets: inherit go-openapi-testify-c10ca71/.github/workflows/scanner.yml000066400000000000000000000005741520301377500234200ustar00rootroot00000000000000name: Vulnerability scans on: branch_protection_rule: push: branches: ["master"] schedule: - cron: "18 4 * * 3" permissions: contents: read jobs: scanners: permissions: contents: read security-events: write uses: go-openapi/ci-workflows/.github/workflows/scanner.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 secrets: inherit go-openapi-testify-c10ca71/.github/workflows/tag-release.yml000066400000000000000000000005751520301377500241610ustar00rootroot00000000000000name: Release on tag permissions: contents: read on: push: tags: - v[0-9]+* jobs: gh-release: name: Create release permissions: contents: write uses: go-openapi/ci-workflows/.github/workflows/release.yml@e8e6599fe480362cb0d5cbdac5b245cc833742f5 # v0.2.15 with: tag: ${{ github.ref_name }} is-monorepo: true secrets: inherit go-openapi-testify-c10ca71/.github/workflows/update-doc.yml000066400000000000000000000105431520301377500240110ustar00rootroot00000000000000name: "Update documentation" permissions: contents: read on: push: tags: - v* branches: [ "master" ] paths: - docs/** - hack/doc-site/** - .github/workflows/update-doc.yml pull_request: paths: - docs/** - hack/doc-site/** - .github/workflows/update-doc.yml concurrency: group: "pages" cancel-in-progress: false defaults: run: shell: bash jobs: build-doc: runs-on: ubuntu-latest steps: - name: Checkout uses: actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8 # v5.0.0 with: fetch-depth: '1' submodules: recursive sparse-checkout: | hack/ docs/ - name: Get all tags [go-openapi repo] if: ${{ github.repository == 'go-openapi/testify' }} run: | git fetch origin --prune --update-shallow --tags 'refs/tags/*:refs/tags/*' - name: Get all tags [fork] if: ${{ github.repository != 'go-openapi/testify' }} run: | git remote add upstream "https://github.com/go-openapi/testify" git fetch upstream --prune --update-shallow --tags 'refs/tags/*:refs/tags/*' git fetch origin --prune --update-shallow --tags 'refs/tags/*:refs/tags/*' - name: Initialize theme env: RELEARN_VERSION: 9.0.3 run: | cd hack/doc-site/hugo # Clone theme curl -sL -o relearn.tgz https://github.com/McShelby/hugo-theme-relearn/archive/refs/tags/"${RELEARN_VERSION}".tar.gz tar xf relearn.tgz rm -rf themes/hugo-relearn mv "hugo-theme-relearn-${RELEARN_VERSION}" hugo-relearn mv hugo-relearn themes/ - name: Prepare config run: | # Builds a commit-dependant extra config to inject parameterization. # HUGO doesn't support config from the command line. # # Set specific parameters that are used in some parameterized document. # This is used to keep up-to-date installation instructions. cd hack/doc-site/hugo ROOT=$(git rev-parse --show-toplevel) VERSION_MESSAGE="Documentation set for latest master." REQUIRED_GO_VERSION=$(grep "^go\s" "${ROOT}"/go.mod|cut -d" " -f2) LATEST_RELEASE=$(git tag --list --sort -version:refname 'v*' 2>/dev/null | head -1 || echo "dev") BUILD_TIME=$(date -u +"%Y-%m-%dT%H:%M:%SZ") echo " Latest release: ${LATEST_RELEASE}" echo " Go version: ${REQUIRED_GO_VERSION}" echo " Build time: ${BUILD_TIME}" echo " Version message: ${VERSION_MESSAGE}" # Generate dynamic config cat testify.yaml.template \ | sed "s|{{ GO_VERSION }}|${REQUIRED_GO_VERSION}|g" \ | sed "s|{{ LATEST_RELEASE }}|${LATEST_RELEASE}|g" \ | sed "s|{{ VERSION_MESSAGE }}|${VERSION_MESSAGE}|g" \ | sed "s|{{ BUILD_TIME }}|${BUILD_TIME}|g" \ > testify.yaml - name: Build site with Hugo uses: crazy-max/ghaction-hugo@58bd8ea36dbac3f6155d275a04e0b55604a93c48 # v3.2.0 with: version: v0.153.3 # <- pin the HUGO version, as they often break things extended: true args: > --config hugo.yaml,testify.yaml --buildDrafts --cleanDestinationDir --minify --printPathWarnings --ignoreCache --noBuildLock --logLevel info --source ${{ github.workspace }}/hack/doc-site/hugo" - name: Upload artifact uses: actions/upload-pages-artifact@7b1f4a764d45c48632c6b24a0339c27f5614fb0b # v4.0.0 with: path: hack/doc-site/hugo/public deploy-doc: if: ${{ github.event_name != 'pull_request' }} needs: build-doc outputs: url: ${{ steps.deployment.outputs.page_url }} runs-on: ubuntu-latest permissions: pages: write id-token: write environment: name: github-pages url: ${{ steps.deployment.outputs.page_url }} steps: - name: Deploy to GitHub Pages id: deployment uses: actions/deploy-pages@d6db90164ac5ed86f2b6aed7e0febac5b3c0c03e # v4.0.5 - name: Report URL run: | echo "::notice::Deployed doc site to ${{ steps.deployment.outputs.page_url }}" go-openapi-testify-c10ca71/.gitignore000066400000000000000000000000741520301377500176320ustar00rootroot00000000000000secrets.yml vendor Godeps .idea *.out .mcp.json go.work.sum go-openapi-testify-c10ca71/.golangci.yml000066400000000000000000000027101520301377500202250ustar00rootroot00000000000000version: "2" linters: default: all disable: - depguard - err113 - funlen - godox - gomoddirectives - gomodguard - gomodguard_v2 - exhaustruct - nlreturn - nonamedreturns - noinlineerr - paralleltest - recvcheck - testpackage - thelper - tparallel - varnamelen - whitespace - wrapcheck - wsl - wsl_v5 settings: dupl: threshold: 200 goconst: min-len: 2 min-occurrences: 3 cyclop: max-complexity: 22 gocyclo: min-complexity: 22 gocognit: min-complexity: 35 exhaustive: default-signifies-exhaustive: true default-case-required: true lll: line-length: 180 exclusions: generated: lax presets: - comments - common-false-positives - legacy - std-error-handling paths: - .worktrees - third_party$ - builtin$ - examples$ formatters: enable: - gofumpt - goimports settings: # local prefixes regroup imports from these packages goimports: local-prefixes: - github.com/go-openapi exclusions: generated: lax paths: - .worktrees - third_party$ - builtin$ - examples$ issues: # Maximum issues count per one linter. # Set to 0 to disable. # Default: 50 max-issues-per-linter: 0 # Maximum count of issues with the same text. # Set to 0 to disable. # Default: 3 max-same-issues: 0 go-openapi-testify-c10ca71/AGENTS.md000077700000000000000000000000001520301377500252342.github/copilot-instructions.mdustar00rootroot00000000000000go-openapi-testify-c10ca71/CODE_OF_CONDUCT.md000066400000000000000000000062431520301377500204450ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [http://contributor-covenant.org/version/1/4][version] [homepage]: http://contributor-covenant.org [version]: http://contributor-covenant.org/version/1/4/ go-openapi-testify-c10ca71/CONTRIBUTORS.md000066400000000000000000000533671520301377500201360ustar00rootroot00000000000000# Contributors - Repository: ['go-openapi/testify'] | Total Contributors | Total Contributions | | --- | --- | | 257 | 1242 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @fredbi | 180 | | | @ernesto-jimenez | 129 | | | @brackendawson | 110 | | | @dolmen | 103 | | | @tylerb | 64 | | | @boyan-soubachov | 35 | | | @leighmcculloch | 26 | | | @pmezard | 19 | | | @alexandear | 18 | | | @nelsam | 17 | | | @alexpantyukhin | 16 | | | @DAddYE | 14 | | | @MovieStoreGuy | 11 | | | @mikeauclair | 11 | | | @architagr | 11 | | | @arjunmahishi | 10 | | | @arjun-1 | 9 | | | @esdrasbeleza | 9 | | | @matryer | 9 | | | @ccoVeille | 8 | | | @AdRiley | 8 | | | @mvdkleijn | 8 | | | @linusbarth | 8 | | | @craig65535 | 8 | | | @muesli | 7 | | | @emou | 7 | | | @HaraldNordgren | 7 | | | @cszczepaniak | 7 | | | @Ogoyukin | 6 | | | @Neokil | 6 | | | @torkelrogstad | 6 | | | @gohargasparyan | 6 | | | @mutaiib | 5 | | | @vkryukov | 5 | | | @posener | 5 | | | @kevinburkesegment | 5 | | | @Saluev | 5 | | | @hendrywiranto | 5 | | | @vyas-git | 4 | | | @tsioftas | 4 | | | @viblo | 4 | | | @paulbellamy | 4 | | | @palsivertsen | 4 | | | @uberbo | 4 | | | @bsoubachov | 4 | | | @anpez | 4 | | | @Copilot | 4 | | | @olivergondza | 4 | | | @mvrahden | 4 | | | @jveski | 4 | | | @npxcomplete | 4 | | | @taichi | 3 | | | @spirin | 3 | | | @zjx20 | 3 | | | @redachl | 3 | | | @senyasdt | 3 | | | @reynieroz | 3 | | | @obeattie | 3 | | | @nmiyake | 3 | | | @josephholsten | 3 | | | @jonhoo | 3 | | | @hanzei | 3 | | | @AngelKitty | 3 | | | @andreas | 3 | | | @yiminc-zz | 2 | | | @hawkingrei | 2 | | | @viswajithiii | 2 | | | @SuperQ | 2 | | | @xsleonard | 2 | | | @srenatus | 2 | | | @snirye | 2 | | | @ryu-ton10 | 2 | | | @izaurio | 2 | | | @gz-c | 2 | | | @techfg | 2 | | | @visualfc | 2 | | | @everdance | 2 | | | @greg0ire | 2 | | | @sikehish | 2 | | | @LandonTClipp | 2 | | | @anupcshan | 2 | | | @AchoArnold | 2 | | | @2pi | 2 | | | @bboreham | 2 | | | @bmatsuo | 2 | | | @dcormier | 2 | | | @dencold | 2 | | | @siliconbrain | 2 | | | @czeslavo | 2 | | | @ikravets | 2 | | | @jeffwidman | 2 | | | @joshrendek | 2 | | | @jcelliott | 2 | | | @mhamrle | 2 | | | @rinchsan | 2 | | | @mrbroll | 2 | | | @rubensayshi | 2 | | | @renatoprime | 2 | | | @oalders | 2 | | | @rdwilliamson | 1 | | | @RmbRT | 1 | | | @RichardKnop | 1 | | | @rgarcia | 1 | | | @pietern | 1 | | | @peterebden | 1 | | | @pdbrito | 1 | | | @pquerna | 1 | | | @pdufour | 1 | | | @moolmanruan | 1 | | | @rleungx | 1 | | | @sam-nelson-mcn | 1 | | | @g7r | 1 | | | @s-urbaniak | 1 | | | @shaneHowearth | 1 | | | @simonmulser | 1 | | | @brahmaroutu | 1 | | | @stdedos | 1 | | | @aldas | 1 | | | @comogo | 1 | | | @TechnotronicOz | 1 | | | @n9netales | 1 | | | @anacrolix | 1 | | | @mmorel-35 | 1 | | | @wallclockbuilder | 1 | | | @MAnyKey | 1 | | | @mchlp | 1 | | | @lesichkovm | 1 | | | @mlsteele | 1 | | | @neilconway | 1 | | | @nbaztec | 1 | | | @bobuhiro11 | 1 | | | @doloopwhile | 1 | | | @OladapoAjala | 1 | | | @omarkohl | 1 | | | @parkr | 1 | | | @pwfcurry | 1 | | | @phemmer | 1 | | | @stevenh | 1 | | | @cuishuang | 1 | | | @dave | 1 | | | @GlenDC | 1 | | | @egawata | 1 | | | @hectorj | 1 | | | @hidu | 1 | | | @lisitsky | 1 | | | @lummie | 1 | | | @myxo | 1 | | | @nicoche | 1 | | | @perrydunn | 1 | | | @philtay | 1 | | | @sarathsp06 | 1 | | | @sunpe | 1 | | | @timfeirg | 1 | | | @tscales | 1 | | | @api-card | 1 | | | @wwade | 1 | | | @agonzalezro | 1 | | | @hikyaru-suzuki | 1 | | | @groz | 1 | | | @tedeh | 1 | | | @terinjokes | 1 | | | @guettli | 1 | | | @tschaub | 1 | | | @tobikris | 1 | | | @TomWright | 1 | | | @prochac | 1 | | | @tabboud | 1 | | | @xtonyjiang | 1 | | | @tcsc | 1 | | | @qerdcv | 1 | | | @vincentcr | 1 | | | @vitalyisaev2 | 1 | | | @marshall-lee | 1 | | | @boekkooi-impossiblecloud | 1 | | | @willogden | 1 | | | @ybrustin | 1 | | | @yrk-lab | 1 | | | @ac7 | 1 | | | @bogdandrutu | 1 | | | @docbobo | 1 | | | @moorereason | 1 | | | @chakrit | 1 | | | @lazywei | 1 | | | @hugelgupf | 1 | | | @csmarchbanks | 1 | | | @cam72cam | 1 | | | @connor4312 | 1 | | | @shousper | 1 | | | @coryb | 1 | | | @SchumacherFM | 1 | | | @danielheller | 1 | | | @danielchatfield | 1 | | | @danielwhite | 1 | | | @hairyhenderson | 1 | | | @davidjb | 1 | | | @dpw | 1 | | | @dennwc | 1 | | | @lowjoel | 1 | | | @3scalation | 1 | | | @AutomateAaron | 1 | | | @adamluzsi | 1 | | | @adamwg | 1 | | | @packrat386 | 1 | | | @atombender | 1 | | | @AlekSi | 1 | | | @kejadlen | 1 | | | @alxn | 1 | | | @aud10slave | 1 | | | @ErebusBat | 1 | | | @shazow | 1 | | | @tamccall | 1 | | | @Ararsa-Derese | 1 | | | @bozaro | 1 | | | @bartventer | 1 | | | @bwplotka | 1 | | | @bayesianmind | 1 | | | @benbjohnson | 1 | | | @blakewilson-wf | 1 | | | @jonasfj | 1 | | | @jcamenisch | 1 | | | @JonCrowther | 1 | | | @ernsheong | 1 | | | @jinnovation | 1 | | | @xordspar0 | 1 | | | @jayd3e | 1 | | | @capoferro | 1 | | | @nyarly | 1 | | | @justbuchanan | 1 | | | @jedevc | 1 | | | @ingwarsw | 1 | | | @navytux | 1 | | | @itzamna314 | 1 | | | @leepa | 1 | | | @luan | 1 | | | @Lucaber | 1 | | | @miparnisari | 1 | | | @martin-sucha | 1 | | | @dominicbarnes | 1 | | | @dlclark | 1 | | | @bits01 | 1 | | | @dmacvicar | 1 | | | @echarrod | 1 | | | @evanphx | 1 | | | @fahimbagar | 1 | | | @gavincabbage | 1 | | | @glesica | 1 | | | @guncha | 1 | | | @cryptix | 1 | | | @henrahmagix | 1 | | | @hslatman | 1 | | | @chirino | 1 | | | @ianrose14 | 1 | | | @ifraixedes | 1 | | | @jszwec | 1 | | | @programmer04 | 1 | | | @jaguilar | 1 | | | @jbowes | 1 | | _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ go-openapi-testify-c10ca71/LICENSE000066400000000000000000000261351520301377500166550ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. go-openapi-testify-c10ca71/NOTICE000066400000000000000000000106001520301377500165420ustar00rootroot00000000000000Copyright 2025 go-swagger maintainers // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 This software library, github.com/go-openapi/testify, includes software developed by the go-swagger and go-openapi maintainers ("go-swagger maintainers"). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. You may obtain a copy of the License at This software is copied from, derived from, and inspired mainly by github.com/stretchr/testify. It ships with copies of other software which license terms are recalled below. github.com/stretchr/testify =========================== // SPDX-FileCopyrightText: Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. // SPDX-License-Identifier: MIT MIT License Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. github.com/davecgh/go-spew =========================== // SPDX-FileCopyrightText: Copyright (c) 2012-2016 Dave Collins // SPDX-License-Identifier: ISC ISC License Copyright (c) 2012-2016 Dave Collins Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. github.com/pmezard/go-difflib =========================== // SPDX-FileCopyrightText: Copyright (c) 2013, Patrick Mezard // SPDX-License-Identifier: MIT-like Copyright (c) 2013, Patrick Mezard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. go-openapi-testify-c10ca71/README.md000066400000000000000000000207061520301377500171250ustar00rootroot00000000000000# Testify/v2 [![Tests][test-badge]][test-url] [![Coverage][cov-badge]][cov-url] [![CI vulnerability scan][vuln-scan-badge]][vuln-scan-url] [![CodeQL][codeql-badge]][codeql-url] [![Release][release-badge]][release-url] [![Go Report Card][gocard-badge]][gocard-url] [![CodeFactor Grade][codefactor-badge]][codefactor-url] [![License][license-badge]][license-url] [![Doc][doc-badge]][doc-url] [![GoDoc][godoc-badge]][godoc-url] [![Discord Channel][discord-badge]][discord-url] [![go version][goversion-badge]][goversion-url] ![Top language][top-badge] ![Commits since latest release][commits-badge] --- **The v2 our tests wanted** A set of `go` packages that provide tools for testifying (verifying) that your code behaves as you intended. This is the go-openapi fork of the great [testify](https://github.com/stretchr/testify) package. > [!NOTE] > This is the home of `github.com/go-openapi/testify/v2`, an active, opinionated fork of `github.com/stretchr/testify`. ## Why choose `go-openapi/testify/v2` * 95% compatible with `stretchr/testify` — if you already use it, our migration tool automates the switch * Actively maintained: regular fixes and evolutions, many PRs proposed upstream are already in * Zero external dependencies — you import what you need, with opt-in modules for extras (e.g. YAML, colorized output) * Modernized codebase targeting go1.25+ * Go routine leak detection built in: zero-setup, no false positives, works with parallel tests (unlike `go.uber.org/goleak`) * File descriptor leak detection (linux-only) * Type-safe assertions with generics (see [a basic example][example-with-generics-url]) — migration to generics can be automated too. [Read the full story][doc-generics] * Safe async assertions, extended JSON & YAML assertions * Coming in `v2.5.0`: non-flaky async assertions using `synctest`, and internal tools exposed as standalone modules (spew, unified diff, goleak) * We take documentation seriously: [searchable doc site][doc-url] with testable examples and a complete tutorial, plus detailed [godoc][godoc-url] for every assertion ### This fork isn't for everyone * You need the `mock` package — we removed it and won't bring it back. For suites, we're [open to discussion][suite-discussion] about a redesigned approach * Your project must support Go versions older than 1.25 * You rely on `testifylint` or other tooling that expects the `stretchr/testify` import path * You need 100% API compatibility — we're at 95%, and the remaining 5% are intentional removals ## Announcements ### Status Design and exploration phase completed. The published API is now stable: moving forward, API changes will remain backward-compatible with v2.4.0. Feedback, contributions and proposals are welcome. > **Recent news** > > ✅ Preparing v2.5.0: new features: support for synctest, NoFileDescriptorLeak for macos, > plus a few fixes (`EventuallyWithT`, `Subset`). > > See also our [ROADMAP][doc-roadmap]. ## Getting started Import this library in your project like so. ```cmd go get github.com/go-openapi/testify/v2 ``` ... and start writing tests. Look at our [examples][doc-examples]. ## Basic usage `testify` simplifies your test assertions like so. ```go import ( "testing" ) ... const expected = "expected result" result := printImports(input) if result != expected { t.Errorf( "Expected: %s. Got: %s", expected, result, ) return } ``` Becomes: ```go import ( "testing" "github.com/go-openapi/testify/v2/require" ) ... const expected = "expected result" require.Equalf(t, expected, printImports(input), "Expected: %s. Got: %s", expected, result, ) ``` ## Usage at go-openapi and go-swagger This fork now fully replaces the original project for all go-openapi projects, thus reducing their dependencies footprint. Go-swagger has also adopted it. Now the work is to generalize the use of generics (leveraging our migration tool). Features might be added to support our main use cases there. ## Change log See ## Licensing `SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers` This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE). See the license [NOTICE](./NOTICE), which recalls the licensing terms of all the pieces of software distributed with this fork, including internalized libraries. ## Other documentation * [Getting started][doc-examples] * [Usage](https://go-openapi.github.io/testify/usage/) * [Motivations](https://go-openapi.github.io/testify/project/readme) * [Roadmap][doc-roadmap] * [Internal architecture](https://go-openapi.github.io/testify/project/maintainers/architecture) * [Benchmarks](https://go-openapi.github.io/testify/project/maintainers/benchmarks) * [All-time contributors](./CONTRIBUTORS.md) * [Contributing guidelines](https://go-openapi.github.io/testify/project/contributing/) * [Maintainers documentation](https://go-openapi.github.io/testify/project/maintainers/) * [Coding style](https://go-openapi.github.io/testify/project/contributing/style) * [Security policy](https://go-openapi.github.io/testify/project/security) ## Cutting a new release Maintainers can cut a new release by either: * running [this workflow][ci-release-workflow] (recommended) * or : 1. preparing go.mod files with the next tag, merge 2. pushing a semver tag * signed tags are preferred * The tag message is prepended to release notes [doc-roadmap]: https://go-openapi.github.io/testify/project/maintainers/roadmap [suite-discussion]: https://github.com/go-openapi/testify/discussions/75 [test-badge]: https://github.com/go-openapi/testify/actions/workflows/go-test.yml/badge.svg [test-url]: https://github.com/go-openapi/testify/actions/workflows/go-test.yml [cov-badge]: https://codecov.io/gh/go-openapi/testify/branch/master/graph/badge.svg [cov-url]: https://codecov.io/gh/go-openapi/testify [vuln-scan-badge]: https://github.com/go-openapi/testify/actions/workflows/scanner.yml/badge.svg [vuln-scan-url]: https://github.com/go-openapi/testify/actions/workflows/scanner.yml [codeql-badge]: https://github.com/go-openapi/testify/actions/workflows/codeql.yml/badge.svg [codeql-url]: https://github.com/go-openapi/testify/actions/workflows/codeql.yml [release-badge]: https://badge.fury.io/gh/go-openapi%2Ftestify.svg [release-url]: https://badge.fury.io/gh/go-openapi%2Ftestify [gomod-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Ftestify.svg [gomod-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Ftestify [gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/testify [gocard-url]: https://goreportcard.com/report/github.com/go-openapi/testify [codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/testify [codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/testify [doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgo-openapi.github.io%2Ftestify%2F [doc-url]: https://go-openapi.github.io/testify [doc-examples]: https://go-openapi.github.io/testify/usage/examples [doc-generics]: https://go-openapi.github.io/testify/usage/generics [example-with-generics-url]: https://go-openapi.github.io/testify#usage-with-generics [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue [discord-url]: https://discord.gg/FfnFYaC3k5 [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg [license-url]: https://github.com/go-openapi/testify/?tab=Apache-2.0-1-ov-file#readme [goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/testify [goversion-url]: https://github.com/go-openapi/testify/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/testify [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/testify/latest [ci-release-workflow]: https://github.com/go-openapi/testify/actions/workflows/bump-release.yml go-openapi-testify-c10ca71/SECURITY.md000066400000000000000000000026031520301377500174330ustar00rootroot00000000000000# Security Policy This policy outlines the commitment and practices of the go-openapi maintainers regarding security. ## Supported Versions | Version | Supported | | ------- | ------------------ | | 2.4.x | :white_check_mark: | ## Vulnerability checks in place This repository uses automated vulnerability scans, at every merged commit and at least once a week. We use: * [`GitHub CodeQL`][codeql-url] * [`trivy`][trivy-url] * [`govulncheck`][govulncheck-url] Reports are centralized in github security reports and visible only to the maintainers. ## Reporting a vulnerability If you become aware of a security vulnerability that affects the current repository, **please report it privately to the maintainers** rather than opening a publicly visible GitHub issue. Please follow the instructions provided by github to [Privately report a security vulnerability][github-guidance-url]. > [!NOTE] > On Github, navigate to the project's "Security" tab then click on "Report a vulnerability". [codeql-url]: https://github.com/github/codeql [trivy-url]: https://trivy.dev/docs/latest/getting-started [govulncheck-url]: https://go.dev/blog/govulncheck [github-guidance-url]: https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability go-openapi-testify-c10ca71/assert/000077500000000000000000000000001520301377500171425ustar00rootroot00000000000000go-openapi-testify-c10ca71/assert/assert_adhoc_example_1_test.go000066400000000000000000000016461520301377500251310ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "fmt" "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExampleComparisonAssertionFunc() { t := new(testing.T) // normally provided by test adder := func(x, y int) int { return x + y } for tt := range comparisonFuncCases() { tt.assertion(t, tt.expect, adder(tt.args.x, tt.args.y)) } fmt.Printf("passed: %t", !t.Failed()) // Output: passed: true } type args struct { x int y int } type comparisonFuncCase struct { name string args args expect int assertion assert.ComparisonAssertionFunc } func comparisonFuncCases() iter.Seq[comparisonFuncCase] { return slices.Values([]comparisonFuncCase{ {"2+2=4", args{2, 2}, 4, assert.Equal}, {"2+2!=5", args{2, 2}, 5, assert.NotEqual}, {"2+3==5", args{2, 3}, 5, assert.Exactly}, }) } go-openapi-testify-c10ca71/assert/assert_adhoc_example_2_test.go000066400000000000000000000017571520301377500251350ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "encoding/json" "fmt" "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExampleValueAssertionFunc() { t := new(testing.T) // normally provided by test dumbParse := func(input string) any { var x any _ = json.Unmarshal([]byte(input), &x) return x } for tt := range valueAssertionCases() { tt.assertion(t, dumbParse(tt.arg)) } fmt.Printf("passed: %t", !t.Failed()) // Output: passed: true } type valueAssertionCase struct { name string arg string assertion assert.ValueAssertionFunc } func valueAssertionCases() iter.Seq[valueAssertionCase] { return slices.Values([]valueAssertionCase{ {"true is not nil", "true", assert.NotNil}, {"empty string is nil", "", assert.Nil}, {"zero is not nil", "0", assert.NotNil}, {"zero is zero", "0", assert.Zero}, {"false is zero", "false", assert.Zero}, }) } go-openapi-testify-c10ca71/assert/assert_adhoc_example_3_test.go000066400000000000000000000015211520301377500251230ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "fmt" "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExampleBoolAssertionFunc() { t := new(testing.T) // normally provided by test isOkay := func(x int) bool { return x >= 42 } for tt := range boolAssertionCases() { tt.assertion(t, isOkay(tt.arg)) } fmt.Printf("passed: %t", !t.Failed()) // Output: passed: true } type boolAssertionCase struct { name string arg int assertion assert.BoolAssertionFunc } func boolAssertionCases() iter.Seq[boolAssertionCase] { return slices.Values([]boolAssertionCase{ {"-1 is bad", -1, assert.False}, {"42 is good", 42, assert.True}, {"41 is bad", 41, assert.False}, {"45 is cool", 45, assert.True}, }) } go-openapi-testify-c10ca71/assert/assert_adhoc_example_4_test.go000066400000000000000000000017331520301377500251310ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "encoding/json" "fmt" "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExampleErrorAssertionFunc() { t := new(testing.T) // normally provided by test dumbParseNum := func(input string, v any) error { return json.Unmarshal([]byte(input), v) } for tt := range errorAssertionCases() { var x float64 tt.assertion(t, dumbParseNum(tt.arg, &x)) } fmt.Printf("passed: %t", !t.Failed()) // Output: passed: true } type errorAssertionCase struct { name string arg string assertion assert.ErrorAssertionFunc } func errorAssertionCases() iter.Seq[errorAssertionCase] { return slices.Values([]errorAssertionCase{ {"1.2 is number", "1.2", assert.NoError}, {"1.2.3 not number", "1.2.3", assert.Error}, {"true is not number", "true", assert.Error}, {"3 is number", "3", assert.NoError}, }) } go-openapi-testify-c10ca71/assert/assert_adhoc_example_5_test.go000066400000000000000000000014021520301377500251230ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "fmt" "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExamplePanicAssertionFunc() { t := new(testing.T) // normally provided by test for tt := range panicAssertionCases() { tt.assertion(t, tt.panicFn) } fmt.Printf("passed: %t", !t.Failed()) // Output: passed: true } type panicAssertionCase struct { name string panicFn func() assertion assert.PanicAssertionFunc } func panicAssertionCases() iter.Seq[panicAssertionCase] { return slices.Values([]panicAssertionCase{ {"with panic", func() { panic(nil) }, assert.Panics}, {"without panic", func() {}, assert.NotPanics}, }) } go-openapi-testify-c10ca71/assert/assert_adhoc_example_6_test.go000066400000000000000000000014221520301377500251260ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExampleAssertions_with_generics() { t := new(testing.T) // normally provided by test a := assert.New(t) const expected = "hello" goodValue := "hello" wrongValue := "world" r0 := a.Equal(expected, goodValue) // classic reflect-based assertion fmt.Printf("good value is %t\n", r0) r1 := assert.EqualT(a.T, expected, goodValue) // usage with generic assertion fmt.Printf("good value is %t\n", r1) r2 := assert.EqualT(a.T, expected, wrongValue) fmt.Printf("wrong value is %t\n", r2) // Output: // good value is true // good value is true // wrong value is false } go-openapi-testify-c10ca71/assert/assert_adhoc_example_7_test.go000066400000000000000000000017141520301377500251330ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "fmt" "sync" "testing" "github.com/go-openapi/testify/v2/assert" ) func ExampleNoGoRoutineLeak_fail() { t := new(testing.T) // normally provided by test blocker := make(chan struct{}) var wg sync.WaitGroup defer func() { // clean resources _after_ the test close(blocker) wg.Wait() }() wg.Add(1) // This examplifies how a function that leaks a goroutine is detected. result := assert.NoGoRoutineLeak(t, func() { // true when there is no leak go func() { defer wg.Done() <-blocker // leaked: blocks until cleanup }() }) // Error message from test would typically return the leaked goroutine, e.g.: // # 0x69c8e8 github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48 .../assert_adhoc_example_7_test.go:30 fmt.Printf("has leak: %t", !result) // Output: // has leak: true } go-openapi-testify-c10ca71/assert/assert_adhoc_example_8_test.go000066400000000000000000000062641520301377500251410ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "context" "errors" "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) // ExampleEventually_asyncReady demonstrates polling a condition that becomes true // after a few attempts, simulating an asynchronous operation completing. func ExampleEventually_asyncReady() { t := new(testing.T) // normally provided by test // Simulate an async operation that completes after a short delay. var ready atomic.Bool go func() { time.Sleep(30 * time.Millisecond) ready.Store(true) }() result := assert.Eventually(t, ready.Load, 200*time.Millisecond, 10*time.Millisecond) fmt.Printf("eventually ready: %t", result) // Output: eventually ready: true } // ExampleEventually_healthCheck demonstrates [Eventually] with a // func(context.Context) error condition, polling until the operation // succeeds (returns nil). func ExampleEventually_healthCheck() { t := new(testing.T) // normally provided by test // Simulate a service that becomes healthy after a few attempts. var attempts atomic.Int32 healthCheck := func(_ context.Context) error { if attempts.Add(1) < 3 { return errors.New("service not ready") } return nil } result := assert.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond) fmt.Printf("eventually healthy: %t", result) // Output: eventually healthy: true } // ExampleNever_noSpuriousEvents demonstrates asserting that a condition never becomes true // during the observation period. func ExampleNever_noSpuriousEvents() { t := new(testing.T) // normally provided by test // A channel that should remain empty during the test. events := make(chan struct{}, 1) result := assert.Never(t, func() bool { select { case <-events: return true // event received = condition becomes true = Never fails default: return false } }, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("never received: %t", result) // Output: never received: true } // ExampleConsistently_invariant demonstrates asserting that a condition remains true // throughout the entire observation period. func ExampleConsistently_invariant() { t := new(testing.T) // normally provided by test // A counter that stays within bounds during the test. var counter atomic.Int32 counter.Store(5) result := assert.Consistently(t, func() bool { return counter.Load() < 10 }, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("consistently under limit: %t", result) // Output: consistently under limit: true } // ExampleConsistently_alwaysHealthy demonstrates [Consistently] with a // func(context.Context) error condition, asserting that the operation // always succeeds (returns nil) throughout the observation period. func ExampleConsistently_alwaysHealthy() { t := new(testing.T) // normally provided by test // Simulate a service that stays healthy. healthCheck := func(_ context.Context) error { return nil // always healthy } result := assert.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("consistently healthy: %t", result) // Output: consistently healthy: true } go-openapi-testify-c10ca71/assert/assert_adhoc_example_9_test.go000066400000000000000000000074771520301377500251510ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assert_test import ( "context" "errors" "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) // ExampleWithSynctest_asyncReady demonstrates opting into [testing/synctest] // bubble polling via [assert.WithSynctest]. Time operations inside the bubble // use a fake clock — a 1-hour timeout with a 1-minute tick completes in // microseconds of real wall-clock time while remaining deterministic. // // Prefer this wrapper when the condition is pure compute or uses [time.Sleep] // internally. See [assert.WithSynctest] for the constraints (no real I/O, no // external goroutines driving state change). func ExampleEventually_withSyncTest() { t := new(testing.T) // normally provided by test // A counter that converges on the 5th poll — no external time pressure. var attempts atomic.Int32 cond := func() bool { return attempts.Add(1) == 5 } // 1-hour/1-minute: under fake time this is instantaneous and // deterministic — exactly 5 calls to the condition. result := assert.Eventually(t, assert.WithSynctest(cond), 1*time.Hour, 1*time.Minute) fmt.Printf("ready: %t, attempts: %d", result, attempts.Load()) // Output: ready: true, attempts: 5 } // ExampleWithSynctestContext_healthCheck demonstrates the context/error // variant of the synctest opt-in. [assert.WithSynctestContext] wraps a // [func(context.Context) error] condition for fake-time polling. func ExampleEventually_withContext() { t := new(testing.T) // normally provided by test var attempts atomic.Int32 healthCheck := func(_ context.Context) error { if attempts.Add(1) < 3 { return errors.New("service not ready") } return nil } result := assert.Eventually(t, assert.WithSynctestContext(healthCheck), 1*time.Hour, 1*time.Minute) fmt.Printf("healthy: %t, attempts: %d", result, attempts.Load()) // Output: healthy: true, attempts: 3 } // ExampleWithSynctest_never demonstrates [assert.Never] with the synctest // opt-in. The condition is polled over the fake-time window without costing // real wall-clock time. func ExampleNever_withSyncTest() { t := new(testing.T) // normally provided by test // A flag that should remain false across the whole observation period. var flipped atomic.Bool result := assert.Never(t, assert.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute) fmt.Printf("never flipped: %t", result) // Output: never flipped: true } // ExampleWithSynctest_consistently demonstrates [assert.Consistently] with // the synctest opt-in — asserting an invariant holds across the entire // observation window under deterministic fake time. func ExampleConsistently_withSynctest() { t := new(testing.T) // normally provided by test // An invariant that must hold throughout the observation period. var counter atomic.Int32 counter.Store(5) invariant := func() bool { return counter.Load() < 10 } result := assert.Consistently(t, assert.WithSynctest(invariant), 1*time.Hour, 1*time.Minute) fmt.Printf("invariant held: %t", result) // Output: invariant held: true } // ExampleWithSynctestCollect_convergence demonstrates [assert.EventuallyWith] // with [assert.WithSynctestCollect] — a [CollectT]-based condition polled // inside a synctest bubble. Useful when the condition uses several require / // assert calls and you want deterministic retry behavior. func ExampleEventuallyWith_withSynctest() { t := new(testing.T) // normally provided by test var attempts atomic.Int32 cond := func(c *assert.CollectT) { n := attempts.Add(1) assert.Equal(c, int32(3), n, "not yet converged") } result := assert.EventuallyWith(t, assert.WithSynctestCollect(cond), 1*time.Hour, 1*time.Minute) fmt.Printf("converged: %t, attempts: %d", result, attempts.Load()) // Output: converged: true, attempts: 3 } go-openapi-testify-c10ca71/assert/assert_assertions.go000066400000000000000000003105751520301377500232570ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "iter" "net/http" "net/url" "reflect" "time" "github.com/go-openapi/testify/v2/internal/assertions" ) // Blocked asserts that a channel is blocked on receive. // // It always fails if the operand is not a channel, or if the channel is send-only. // // # Usage // // ch := make(chan struct{}) // assertions.Blocked(t, ch) // // # Examples // // success: make(chan struct{}) // failure: sendChanMessage() // // Upon failure, the test [T] is marked as failed and continues execution. func Blocked(t T, ch any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Blocked(t, ch, msgAndArgs...) } // BlockedT asserts that a channel is blocked on receive. // // # Usage // // ch := make(chan struct{}) // assertions.BlockedT(t, ch) // // # Examples // // success: make(chan struct{}) // failure: sendChanMessage() // // Upon failure, the test [T] is marked as failed and continues execution. func BlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.BlockedT[E, CHAN](t, ch, msgAndArgs...) } // Condition uses a comparison function to assert a complex condition. // // # Usage // // assertions.Condition(t, func() bool { return myCondition }) // // # Examples // // success: func() bool { return true } // failure: func() bool { return false } // // Upon failure, the test [T] is marked as failed and continues execution. func Condition(t T, comp func() bool, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Condition(t, comp, msgAndArgs...) } // Consistently asserts that the given condition is always satisfied until timeout, // periodically checking the target function at each tick. // // [Consistently] ("always") imposes a stronger constraint than [Eventually] ("at least once"): // it checks at every tick that every occurrence of the condition is satisfied, whereas // [Eventually] succeeds on the first occurrence of a successful condition. // // # Usage // // assertions.Consistently(t, func() bool { return true }, time.Second, 10*time.Millisecond) // // See also [Eventually] for details about using context, concurrency, and panic recovery. // // # Alternative condition signature // // The simplest form of condition is: // // func() bool // // The semantics of the assertion are "always returns true". // // To build more complex cases, a condition may also be defined as: // // func(context.Context) error // // It fails as soon as an error is returned before timeout expressing "always returns no error (nil)" // // This is consistent with [Eventually] expressing "eventually returns no error (nil)". // // It will be executed with the context of the assertion, which inherits the [testing.T.Context] and // is cancelled on timeout. // // # Panic recovery // // A panicking condition is treated as an error, causing [Consistently] to fail immediately. // See [Eventually] for details. // // # Concurrency // // See [Eventually]. // // # Attention point // // See [Eventually]. // // # Synctest (opt-in) // // Wrap the condition with [WithSynctest] (or [WithSynctestContext]) to run // the polling loop inside a [testing/synctest] bubble, which uses a fake // clock. This eliminates timing-induced flakiness and makes the tick count // deterministic. See [WithSynctest] for the constraints (no real I/O in // the condition, requires [*testing.T]). // // # Examples // // success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond // // Upon failure, the test [T] is marked as failed and continues execution. func Consistently[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Consistently[C](t, condition, timeout, tick, msgAndArgs...) } // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // # Usage // // assertions.Contains(t, "Hello World", "World") // assertions.Contains(t, []string{"Hello", "World"}, "World") // assertions.Contains(t, map[string]string{"Hello": "World"}, "Hello") // // # Examples // // success: []string{"A","B"}, "A" // failure: []string{"A","B"}, "C" // // Upon failure, the test [T] is marked as failed and continues execution. func Contains(t T, s any, contains any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Contains(t, s, contains, msgAndArgs...) } // DirExists checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. // // # Usage // // assertions.DirExists(t, "path/to/directory") // // # Examples // // success: filepath.Join(testDataPath(),"existing_dir") // failure: filepath.Join(testDataPath(),"non_existing_dir") // // Upon failure, the test [T] is marked as failed and continues execution. func DirExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.DirExists(t, path, msgAndArgs...) } // DirNotExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. // // # Usage // // assertions.DirNotExists(t, "path/to/directory") // // # Examples // // success: filepath.Join(testDataPath(),"non_existing_dir") // failure: filepath.Join(testDataPath(),"existing_dir") // // Upon failure, the test [T] is marked as failed and continues execution. func DirNotExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.DirNotExists(t, path, msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // # Usage // // assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // // success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} // failure: []int{1, 2, 3}, []int{1, 2, 4} // // Upon failure, the test [T] is marked as failed and continues execution. func ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.ElementsMatch(t, listA, listB, msgAndArgs...) } // ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // # Usage // // assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // // success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} // failure: []int{1, 2, 3}, []int{1, 2, 4} // // Upon failure, the test [T] is marked as failed and continues execution. func ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ElementsMatchT[E](t, listA, listB, msgAndArgs...) } // Empty asserts that the given value is "empty". // // Zero values are "empty". // // Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). // // Slices, maps and channels with zero length are "empty". // // Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // # Usage // // assertions.Empty(t, obj) // // # Examples // // success: "" // failure: "not empty" // // Upon failure, the test [T] is marked as failed and continues execution. // // [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t T, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Empty(t, object, msgAndArgs...) } // Equal asserts that two objects are equal. // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). // // Function equality cannot be determined and will always fail. // // # Usage // // assertions.Equal(t, 123, 123) // // # Examples // // success: 123, 123 // failure: 123, 456 // // Upon failure, the test [T] is marked as failed and continues execution. func Equal(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Equal(t, expected, actual, msgAndArgs...) } // EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // # Usage // // actualObj, err := SomeFunction() // assertions.EqualError(t, err, expectedErrorString) // // # Examples // // success: ErrTest, "assert.ErrTest general error for testing" // failure: ErrTest, "wrong error message" // // Upon failure, the test [T] is marked as failed and continues execution. func EqualError(t T, err error, errString string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualError(t, err, errString, msgAndArgs...) } // EqualExportedValues asserts that the types of two objects are equal and their public // fields are also equal. // // This is useful for comparing structs that have private fields that could potentially differ. // // Function equality cannot be determined and will always fail. // // # Usage // // type S struct { // Exported int // notExported int // } // assertions.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true // assertions.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false // // # Examples // // success: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2} // failure: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1} // // Upon failure, the test [T] is marked as failed and continues execution. func EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualExportedValues(t, expected, actual, msgAndArgs...) } // EqualT asserts that two objects of the same comparable type are equal. // // Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal], but like [Same]). // // Functions, slices and maps are not comparable. See also [ComparisonOperators]. // // If you need to compare values of non-comparable types, or compare pointers by the value they point to, // use [Equal] instead. // // # Usage // // assertions.EqualT(t, 123, 123) // // # Examples // // success: 123, 123 // failure: 123, 456 // // Upon failure, the test [T] is marked as failed and continues execution. // // [ComparisonOperators]: https://go.dev/ref/spec#Comparison_operators. func EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualT[V](t, expected, actual, msgAndArgs...) } // EqualValues asserts that two objects are equal or convertible to the larger // type and equal. // // Function equality cannot be determined and will always fail. // // # Usage // // assertions.EqualValues(t, uint32(123), int32(123)) // // # Examples // // success: uint32(123), int32(123) // failure: uint32(123), int32(456) // // Upon failure, the test [T] is marked as failed and continues execution. func EqualValues(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualValues(t, expected, actual, msgAndArgs...) } // Error asserts that a function returned a non-nil error (i.e. an error). // // # Usage // // actualObj, err := SomeFunction() // assertions.Error(t, err) // // # Examples // // success: ErrTest // failure: nil // // Upon failure, the test [T] is marked as failed and continues execution. func Error(t T, err error, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Error(t, err, msgAndArgs...) } // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // // This is a wrapper for [errors.As]. // // # Usage // // assertions.ErrorAs(t, err, &target) // // # Examples // // success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) // failure: ErrTest, new(*dummyError) // // Upon failure, the test [T] is marked as failed and continues execution. func ErrorAs(t T, err error, target any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ErrorAs(t, err, target, msgAndArgs...) } // ErrorContains asserts that a function returned a non-nil error (i.e. an // error) and that the error contains the specified substring. // // # Usage // // actualObj, err := SomeFunction() // assertions.ErrorContains(t, err, expectedErrorSubString) // // # Examples // // success: ErrTest, "general error" // failure: ErrTest, "not in message" // // Upon failure, the test [T] is marked as failed and continues execution. func ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ErrorContains(t, err, contains, msgAndArgs...) } // ErrorIs asserts that at least one of the errors in err's chain matches target. // // This is a wrapper for [errors.Is]. // // # Usage // // assertions.ErrorIs(t, err, io.EOF) // // # Examples // // success: fmt.Errorf("wrap: %w", io.EOF), io.EOF // failure: ErrTest, io.EOF // // Upon failure, the test [T] is marked as failed and continues execution. func ErrorIs(t T, err error, target error, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ErrorIs(t, err, target, msgAndArgs...) } // Eventually asserts that the given condition will be met before timeout, // periodically checking the target function on each tick. // // [Eventually] waits until the condition returns true, at most until timeout, // or until the parent context of the test is cancelled. // // If the condition takes longer than the timeout to complete, [Eventually] fails // but waits for the current condition execution to finish before returning. // // For long-running conditions to be interrupted early, check [testing.T.Context] // which is cancelled on test failure. // // # Usage // // assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) // // # Alternative condition signature // // The simplest form of condition is: // // func() bool // // To build more complex cases, a condition may also be defined as: // // func(context.Context) error // // It fails when an error has always been returned up to timeout (equivalent semantics to func() bool returns false), // expressing "eventually returns no error (nil)". // // It will be executed with the context of the assertion, which inherits the [testing.T.Context] and // is cancelled on timeout. // // The semantics of the three available async assertions read as follows. // // - [Eventually] (func() bool) : "eventually returns true" // // - [Never] (func() bool) : "never returns true" // // - [Consistently] (func() bool): "always returns true" // // - [Eventually] (func(ctx) error) : "eventually returns nil" // // - [Never] (func(ctx) error) : not supported, use [Consistently] instead (avoids confusion with double negation) // // - [Consistently] (func(ctx) error): "always returns nil" // // # Concurrency // // The condition function is always executed serially by a single goroutine. It is always executed at least once. // // It may thus write to variables outside its scope without triggering race conditions. // // A blocking condition will cause [Eventually] to hang until it returns. // // Notice that time ticks may be skipped if the condition takes longer than the tick interval. // // # Panic recovery // // If the condition panics, the panic is recovered and treated as a failed tick // (equivalent to returning false or a non-nil error). For [Eventually], this means // the poller retries on the next tick — if a later tick succeeds, the assertion // succeeds. For [Never] and [Consistently], a panic is treated as the condition // erroring, which causes immediate failure. // // The recovered panic is wrapped as an error with the sentinel [errConditionPanicked], // detectable with [errors.Is]. // // # Attention point // // Time-based tests may be flaky in a resource-constrained environment such as a CI runner and may produce // counter-intuitive results, such as ticks or timeouts not firing in time as expected. // // To avoid flaky tests, always make sure that ticks and timeouts differ by at least an order of magnitude (tick << // timeout). // // # Synctest (opt-in) // // Wrap the condition with [WithSynctest] (or [WithSynctestContext]) to run // the polling loop inside a [testing/synctest] bubble, which uses a fake // clock. This eliminates timing-induced flakiness and makes the tick count // deterministic. See [WithSynctest] for the constraints (no real I/O in // the condition, requires `*testing.T`). // // # Examples // // success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond // // Upon failure, the test [T] is marked as failed and continues execution. func Eventually[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Eventually[C](t, condition, timeout, tick, msgAndArgs...) } // EventuallyWith asserts that the given condition will be met before the timeout, // periodically checking the target function at each tick. // // In contrast to [Eventually], the condition function is supplied with a [CollectT] // to accumulate errors from calling other assertions. // // The condition is considered "met" if no errors are raised in a tick. // The supplied [CollectT] collects all errors from one tick. // // If the condition is not met before the timeout, the collected errors from the // last tick are copied to t. // // Calling [CollectT.FailNow] (directly, or transitively through [require] assertions) // fails the current tick only: the poller will retry on the next tick. This means // [require]-style assertions inside [EventuallyWith] behave naturally — they abort // the current evaluation and let the polling loop converge. // // To abort the whole assertion immediately (e.g. when the condition can no longer // be expected to succeed), call [CollectT.Cancel]. // // # Usage // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // // assertions.EventuallyWith(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, // 10*time.Second, // 1*time.Second, // "external state has not changed to 'true'; still false", // ) // // # Concurrency // // The condition function is never executed in parallel: only one goroutine executes it. // It may write to variables outside its scope without triggering race conditions. // // The condition is wrapped in its own goroutine, so a call to [runtime.Goexit] // (e.g. via [require] assertions or [CollectT.FailNow]) cleanly aborts only the // current tick. // // # Panic recovery // // If the condition panics, the panic is recovered and recorded as an error in the // [CollectT] for that tick. The poller treats it as a failed tick and retries on the // next one. If the assertion times out, the panic error is included in the collected // errors reported on the parent t. // // See [Eventually] for the general panic recovery semantics. // // # Synctest (opt-in) // // Wrap the condition with [WithSynctestCollect] (or [WithSynctestCollectContext]) // to run the polling loop inside a [testing/synctest] bubble, which uses // a fake clock. This eliminates timing-induced flakiness and makes the // tick count deterministic. See [WithSynctest] for the constraints (no // real I/O in the condition, requires [*testing.T]). // // # Examples // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond // failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond // failure: func(c *CollectT) { c.Cancel() }, 100*time.Millisecond, 20*time.Millisecond // // Upon failure, the test [T] is marked as failed and continues execution. func EventuallyWith[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EventuallyWith[C](t, condition, timeout, tick, msgAndArgs...) } // Exactly asserts that two objects are equal in value and type. // // # Usage // // assertions.Exactly(t, int32(123), int64(123)) // // # Examples // // success: int32(123), int32(123) // failure: int32(123), int64(123) // // Upon failure, the test [T] is marked as failed and continues execution. func Exactly(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Exactly(t, expected, actual, msgAndArgs...) } // Fail reports a failure through. // // # Usage // // assertions.Fail(t, "failed") // // # Examples // // failure: "failed" // // Upon failure, the test [T] is marked as failed and continues execution. func Fail(t T, failureMessage string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Fail(t, failureMessage, msgAndArgs...) } // FailNow fails test. // // # Usage // // assertions.FailNow(t, "failed") // // # Examples // // failure: "failed" // // Upon failure, the test [T] is marked as failed and continues execution. func FailNow(t T, failureMessage string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FailNow(t, failureMessage, msgAndArgs...) } // False asserts that the specified value is false. // // # Usage // // assertions.False(t, myBool) // // # Examples // // success: 1 == 0 // failure: 1 == 1 // // Upon failure, the test [T] is marked as failed and continues execution. func False(t T, value bool, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.False(t, value, msgAndArgs...) } // FalseT asserts that the specified value is false. // // The type constraint [Boolean] accepts any type which underlying type is bool. // // # Usage // // type B bool // var b B = true // // assertions.FalseT(t, b) // // # Examples // // success: 1 == 0 // failure: 1 == 1 // // Upon failure, the test [T] is marked as failed and continues execution. func FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FalseT[B](t, value, msgAndArgs...) } // FileEmpty checks whether a file exists in the given path and is empty. // It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file. // // # Usage // // assertions.FileEmpty(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"empty_file") // failure: filepath.Join(testDataPath(),"existing_file") // // Upon failure, the test [T] is marked as failed and continues execution. func FileEmpty(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileEmpty(t, path, msgAndArgs...) } // FileExists checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. // // # Usage // // assertions.FileExists(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"existing_file") // failure: filepath.Join(testDataPath(),"non_existing_file") // // Upon failure, the test [T] is marked as failed and continues execution. func FileExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileExists(t, path, msgAndArgs...) } // FileNotEmpty checks whether a file exists in the given path and is not empty. // It fails if the file is empty, if the path points to a directory or there is an error when trying to check the file. // // # Usage // // assertions.FileNotEmpty(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"existing_file") // failure: filepath.Join(testDataPath(),"empty_file") // // Upon failure, the test [T] is marked as failed and continues execution. func FileNotEmpty(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileNotEmpty(t, path, msgAndArgs...) } // FileNotExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. // // # Usage // // assertions.FileNotExists(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"non_existing_file") // failure: filepath.Join(testDataPath(),"existing_file") // // Upon failure, the test [T] is marked as failed and continues execution. func FileNotExists(t T, path string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileNotExists(t, path, msgAndArgs...) } // Greater asserts that the first element is strictly greater than the second. // // Both elements must be of the same type in the [reflect.Kind] sense. // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.Greater(t, 2, 1) // assertions.Greater(t, float64(2), float64(1)) // assertions.Greater(t, "b", "a") // // # Examples // // success: 2, 1 // failure: 1, 2 // // Upon failure, the test [T] is marked as failed and continues execution. func Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Greater(t, e1, e2, msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second. // // See also [Greater]. // // # Usage // // assertions.GreaterOrEqual(t, 2, 1) // assertions.GreaterOrEqual(t, 2, 2) // assertions.GreaterOrEqual(t, "b", "a") // assertions.GreaterOrEqual(t, "b", "b") // // # Examples // // success: 2, 1 // failure: 1, 2 // // Upon failure, the test [T] is marked as failed and continues execution. func GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.GreaterOrEqual(t, e1, e2, msgAndArgs...) } // GreaterOrEqualT asserts that for two elements of the same type, // the first element is greater than or equal to the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterOrEqualT] with [*time.Time]. // // [GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [GreaterOrEqual] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.GreaterOrEqualT(t, 2, 1) // assertions.GreaterOrEqualT(t, 2, 2) // assertions.GreaterOrEqualT(t, "b", "a") // assertions.GreaterOrEqualT(t, "b", "b") // // # Examples // // success: 2, 1 // failure: 1, 2 // // Upon failure, the test [T] is marked as failed and continues execution. func GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.GreaterOrEqualT[Orderable](t, e1, e2, msgAndArgs...) } // GreaterT asserts that for two elements of the same type, // the first element is strictly greater than the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterT] with [*time.Time]. // // [GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.GreaterT(t, 2, 1) // assertions.GreaterT(t, float64(2), float64(1)) // assertions.GreaterT(t, "b", "a") // assertions.GreaterT(t, time.Date(2026,1,1,0,0,0,0,nil), time.Now()) // // # Examples // // success: 2, 1 // failure: 1, 2 // // Upon failure, the test [T] is marked as failed and continues execution. func GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.GreaterT[Orderable](t, e1, e2, msgAndArgs...) } // HTTPBodyContains asserts that a specified handler returns a body that contains a string. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // # Examples // // success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!" // failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!" // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPBodyContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPBodyContains(t, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // # Examples // // success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!" // failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!" // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPBodyNotContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPBodyNotContains(t, handler, method, url, values, str, msgAndArgs...) } // HTTPError asserts that a specified handler returns an error status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // # Examples // // success: httpError, "GET", "/", nil // failure: httpOK, "GET", "/", nil // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPError(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPError(t, handler, method, url, values, msgAndArgs...) } // HTTPRedirect asserts that a specified handler returns a redirect status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // # Examples // // success: httpRedirect, "GET", "/", nil // failure: httpError, "GET", "/", nil // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPRedirect(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPRedirect(t, handler, method, url, values, msgAndArgs...) } // HTTPStatusCode asserts that a specified handler returns a specified status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // # Examples // // success: httpOK, "GET", "/", nil, http.StatusOK // failure: httpError, "GET", "/", nil, http.StatusOK // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPStatusCode(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPStatusCode(t, handler, method, url, values, statuscode, msgAndArgs...) } // HTTPSuccess asserts that a specified handler returns a success status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // # Examples // // success: httpOK, "GET", "/", nil // failure: httpError, "GET", "/", nil // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPSuccess(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPSuccess(t, handler, method, url, values, msgAndArgs...) } // Implements asserts that an object is implemented by the specified interface. // // # Usage // // assertions.Implements(t, (*MyInterface)(nil), new(MyObject)) // // # Examples // // success: ptr(dummyInterface), new(testing.T) // failure: (*error)(nil), new(testing.T) // // Upon failure, the test [T] is marked as failed and continues execution. func Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Implements(t, interfaceObject, object, msgAndArgs...) } // InDelta asserts that the two numerals are within delta of each other. // // Delta must be greater than or equal to zero. // // Expected and actual values should convert to float64. // To compare large integers that can't be represented accurately as float64 (e.g. uint64), // prefer [InDeltaT] to preserve the original type. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.Sqrt](-1), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // # Usage // // assertions.InDelta(t, math.Pi, 22/7.0, 0.01) // // # Examples // // success: 1.0, 1.01, 0.02 // failure: 1.0, 1.1, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDelta(t, expected, actual, delta, msgAndArgs...) } // InDeltaMapValues is the same as [InDelta], but it compares all values between two maps. Both maps must have exactly the same keys. // // See [InDelta]. // // # Usage // // assertions.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) // // # Examples // // success: map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02 // failure: map[string]float64{"a": 1.0}, map[string]float64{"a": 1.1}, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDeltaMapValues(t, expected, actual, delta, msgAndArgs...) } // InDeltaSlice is the same as [InDelta], except it compares two slices. // // See [InDelta]. // // # Usage // // assertions.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) // // # Examples // // success: []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02 // failure: []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDeltaSlice(t, expected, actual, delta, msgAndArgs...) } // InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. // // [InDeltaT] accepts any go numeric type, including integer types. // // The main difference with [InDelta] is that the delta is expressed with the same type as the values, not necessarily a float64. // // Delta must be greater than or equal to zero. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: InDeltaT([math.NaN](), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // # Usage // // assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) // // # Examples // // success: 1.0, 1.01, 0.02 // failure: 1.0, 1.1, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDeltaT[Number](t, expected, actual, delta, msgAndArgs...) } // InEpsilon asserts that expected and actual have a relative error less than epsilon. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.NaN](), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. // // Formula: // // - If expected == 0: fail if |actual - expected| > epsilon // - If expected != 0: fail if |actual - expected| > epsilon * |expected| // // This allows [InEpsilonT] to work naturally across the full numeric range including zero. // // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilon(t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlice is the same as [InEpsilon], except it compares each value from two slices. // // See [InEpsilon]. // // # Usage // // assertions.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) // // # Examples // // success: []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02 // failure: []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonSlice(t, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSymmetric asserts that 2 numbers are close, with a symmetric relative error. // // Unlike with [InEpsilon], both numbers play a symmetric role and the relative error is // computed relative to the number with greatest amplitude. This mirrors the behavior of // Python's [math.isclose] (with the relative-tolerance term only). // // See also [InEpsilon]. // // # Behavior with IEEE floating point arithmetic // // - NaN is matched only by a NaN, e.g. this works: [InEpsilonSymmetric]([math.NaN](), [math.Sqrt](-1), 0.0) // - +Inf is matched only by a +Inf // - -Inf is matched only by a -Inf // // Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDelta]. // // Formula: // // - If x == 0 and y == 0: success // - Otherwise fail if |x - y| > epsilon * max(|x|,|y|) // // # Usage // // assertions.InEpsilonSymmetric(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. // // [math.isclose]: https://docs.python.org/3/library/math.html#math.isclose func InEpsilonSymmetric(t T, x any, y any, epsilon float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonSymmetric(t, x, y, epsilon, msgAndArgs...) } // InEpsilonSymmetricT is the type-safe version of [InEpsilonSymmetric], comparing numbers of the same numerical type. // // See [InEpsilonSymmetric]. // // # Usage // // assertions.InEpsilonSymmetricT(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonSymmetricT[Number Measurable](t T, x Number, y Number, epsilon float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonSymmetricT[Number](t, x, y, epsilon, msgAndArgs...) } // InEpsilonT asserts that expected and actual have a relative error less than epsilon. // // When expected is zero, epsilon is interpreted as an absolute error threshold, // since relative error is mathematically undefined for zero values. // // Unlike [InDeltaT], which preserves the original type, [InEpsilonT] converts the expected and actual // numbers to float64, since the relative error doesn't make sense as an integer. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.NaN](), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. // // Formula: // // - If expected == 0: fail if |actual - expected| > epsilon // - If expected != 0: fail if |actual - expected| > epsilon * |expected| // // This allows [InEpsilonT] to work naturally across the full numeric range including zero. // // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonT[Number](t, expected, actual, epsilon, msgAndArgs...) } // IsDecreasing asserts that the collection is strictly decreasing. // // # Usage // // assertions.IsDecreasing(t, []int{2, 1, 0}) // assertions.IsDecreasing(t, []float{2, 1}) // assertions.IsDecreasing(t, []string{"b", "a"}) // // # Examples // // success: []int{3, 2, 1} // failure: []int{1, 2, 3} // // Upon failure, the test [T] is marked as failed and continues execution. func IsDecreasing(t T, collection any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsDecreasing(t, collection, msgAndArgs...) } // IsDecreasingT asserts that a slice of [Ordered] is strictly decreasing. // // # Usage // // assertions.IsDecreasingT(t, []int{2, 1, 0}) // assertions.IsDecreasingT(t, []float{2, 1}) // assertions.IsDecreasingT(t, []string{"b", "a"}) // // # Examples // // success: []int{3, 2, 1} // failure: []int{1, 2, 3} // // Upon failure, the test [T] is marked as failed and continues execution. func IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsDecreasingT[OrderedSlice, E](t, collection, msgAndArgs...) } // IsIncreasing asserts that the collection is strictly increasing. // // # Usage // // assertions.IsIncreasing(t, []int{1, 2, 3}) // assertions.IsIncreasing(t, []float{1, 2}) // assertions.IsIncreasing(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 2, 3} // failure: []int{1, 1, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func IsIncreasing(t T, collection any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsIncreasing(t, collection, msgAndArgs...) } // IsIncreasingT asserts that a slice of [Ordered] is strictly increasing. // // # Usage // // assertions.IsIncreasingT(t, []int{1, 2, 3}) // assertions.IsIncreasingT(t, []float{1, 2}) // assertions.IsIncreasingT(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 2, 3} // failure: []int{1, 1, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsIncreasingT[OrderedSlice, E](t, collection, msgAndArgs...) } // IsNonDecreasing asserts that the collection is not strictly decreasing. // // This is the logical negation of [IsDecreasing]: it succeeds whenever // [IsDecreasing] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [3, 1, 2]). // // It is NOT the same as "weakly increasing" (a[i] <= a[i+1] for all i). // // # Usage // // assertions.IsNonDecreasing(t, []int{1, 1, 2}) // assertions.IsNonDecreasing(t, []float{1, 2}) // assertions.IsNonDecreasing(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 1, 2} // failure: []int{2, 1, 0} // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonDecreasing(t T, collection any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonDecreasing(t, collection, msgAndArgs...) } // IsNonDecreasingT asserts that a slice of [Ordered] is NOT strictly decreasing. // // This is the logical negation of [IsDecreasingT]: it succeeds whenever // [IsDecreasingT] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [3, 1, 2]). // // It is NOT the same as "weakly increasing" (a[i] <= a[i+1] for all i). // // # Usage // // assertions.IsNonDecreasingT(t, []int{1, 1, 2}) // assertions.IsNonDecreasingT(t, []float{1, 2}) // assertions.IsNonDecreasingT(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 1, 2} // failure: []int{2, 1, 0} // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonDecreasingT[OrderedSlice, E](t, collection, msgAndArgs...) } // IsNonIncreasing asserts that the collection is not strictly increasing. // // This is the logical negation of [IsIncreasing]: it succeeds whenever // [IsIncreasing] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [1, 3, 2]). // // It is NOT the same as "weakly decreasing" (a[i] >= a[i+1] for all i). // // # Usage // // assertions.IsNonIncreasing(t, []int{2, 1, 1}) // assertions.IsNonIncreasing(t, []float{2, 1}) // assertions.IsNonIncreasing(t, []string{"b", "a"}) // // # Examples // // success: []int{2, 1, 1} // failure: []int{1, 2, 3} // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonIncreasing(t T, collection any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonIncreasing(t, collection, msgAndArgs...) } // IsNonIncreasingT asserts that a slice of [Ordered] is NOT strictly increasing. // // This is the logical negation of [IsIncreasingT]: it succeeds whenever // [IsIncreasingT] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [1, 3, 2]). // // It is NOT the same as "weakly decreasing" (a[i] >= a[i+1] for all i). // // # Usage // // assertions.IsNonIncreasingT(t, []int{2, 1, 1}) // assertions.IsNonIncreasingT(t, []float{2, 1}) // assertions.IsNonIncreasingT(t, []string{"b", "a"}) // // # Examples // // success: []int{2, 1, 1} // failure: []int{1, 2, 3} // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonIncreasingT[OrderedSlice, E](t, collection, msgAndArgs...) } // IsNotOfTypeT asserts that an object is not of a given type. // // # Usage // // assertions.IsOfType[MyType](t,myVar) // // # Examples // // success: 123.123 // failure: myType(123.123) // // Upon failure, the test [T] is marked as failed and continues execution. func IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNotOfTypeT[EType](t, object, msgAndArgs...) } // IsNotType asserts that the specified objects are not of the same type. // // # Usage // // assertions.IsNotType(t, &NotMyStruct{}, &MyStruct{}) // // # Examples // // success: int32(123), int64(456) // failure: 123, 456 // // Upon failure, the test [T] is marked as failed and continues execution. func IsNotType(t T, theType any, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNotType(t, theType, object, msgAndArgs...) } // IsOfTypeT asserts that an object is of a given type. // // # Usage // // assertions.IsOfTypeT[MyType](t,myVar) // // # Examples // // success: myType(123.123) // failure: 123.123 // // Upon failure, the test [T] is marked as failed and continues execution. func IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsOfTypeT[EType](t, object, msgAndArgs...) } // IsType asserts that the specified objects are of the same type. // // # Usage // // assertions.IsType(t, &MyStruct{}, &MyStruct{}) // // # Examples // // success: 123, 456 // failure: int32(123), int64(456) // // Upon failure, the test [T] is marked as failed and continues execution. func IsType(t T, expectedType any, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsType(t, expectedType, object, msgAndArgs...) } // JSONEq asserts that two JSON strings are semantically equivalent. // // Expected and actual must be valid JSON. // // For dynamic redaction of the input text via a callback, use [JSONEqT]. // // # Usage // // assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) // // # Examples // // success: `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}` // failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` // // Upon failure, the test [T] is marked as failed and continues execution. func JSONEq(t T, expected string, actual string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONEq(t, expected, actual, msgAndArgs...) } // JSONEqBytes asserts that two JSON slices of bytes are semantically equivalent. // // Expected and actual must be valid JSON. // // # Usage // // assertions.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) // // # Examples // // success: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`) // failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`) // // Upon failure, the test [T] is marked as failed and continues execution. func JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONEqBytes(t, expected, actual, msgAndArgs...) } // JSONEqT asserts that two JSON documents are semantically equivalent. // // The expected and actual arguments may be string or []byte. They do not need to be of the same type. // // Expected and actual must be valid JSON. // // NOTE: passed values (expected, actual) may be wrapped as functions to redact the input text dynamically. // // # Usage // // assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) // // # Examples // // success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) // failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` // // Upon failure, the test [T] is marked as failed and continues execution. func JSONEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) } // JSONMarshalAsT wraps [JSONEqT] after [json.Marshal]. // // The input JSON may be a string or []byte. // // It fails if the marshaling returns an error or if the expected JSON bytes differ semantically // from the expected ones. // // NOTE: passed expected value may be wrapped as a function to redact the input text dynamically. // // # Usage // // actual := struct { // A int `json:"a"` // }{ // A: 10, // } // // assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} // failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 // // Upon failure, the test [T] is marked as failed and continues execution. func JSONMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONMarshalAsT[EDoc](t, expected, object, msgAndArgs...) } // JSONUnmarshalAsT wraps [Equal] after [json.Unmarshal]. // // The input JSON may be a string or []byte. // // It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. // // Be careful not to wrap the expected object into an "any" interface if this is not what you expected: // the unmarshaling would take this type to unmarshal as a map[string]any. // // NOTE: passed jazon value may be wrapped as a function to redact the input JSON dynamically. // // # Usage // // expected := struct { // A int `json:"a"` // }{ // A: 10, // } // // assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) // failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` // // Upon failure, the test [T] is marked as failed and continues execution. func JSONUnmarshalAsT[Object any, ADoc RText](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONUnmarshalAsT[Object, ADoc](t, expected, jazon, msgAndArgs...) } // Kind asserts that the [reflect.Kind] of a given object matches the expected [reflect.Kind]. // // Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) // are comparable to [reflect.Invalid]. See also [reflect.Value.Kind]. // // # Usage // // assertions.Kind(t, reflect.String, "Hello World") // // # Examples // // success: reflect.String, "hello" // failure: reflect.String, 0 // // Upon failure, the test [T] is marked as failed and continues execution. func Kind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Kind(t, expectedKind, object, msgAndArgs...) } // Len asserts that the specified object has specific length. // // Len also fails if the object has a type that len() does not accept. // // The asserted object can be a string, a slice, a map, an array, pointer to array or a channel. // // # Usage // // assertions.Len(t, mySlice, 3) // assertions.Len(t, myString, 4) // assertions.Len(t, myMap, 5) // // # Examples // // success: []string{"A","B"}, 2 // failure: []string{"A","B"}, 1 // // Upon failure, the test [T] is marked as failed and continues execution. func Len(t T, object any, length int, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Len(t, object, length, msgAndArgs...) } // Less asserts that the first element is strictly less than the second. // // Both elements must be of the same type in the [reflect.Kind] sense. // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.Less(t, 1, 2) // assertions.Less(t, float64(1), float64(2)) // assertions.Less(t, "a", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 // // Upon failure, the test [T] is marked as failed and continues execution. func Less(t T, e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Less(t, e1, e2, msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second. // // # Usage // // assertions.LessOrEqual(t, 1, 2) // assertions.LessOrEqual(t, 2, 2) // assertions.LessOrEqual(t, "a", "b") // assertions.LessOrEqual(t, "b", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 // // Upon failure, the test [T] is marked as failed and continues execution. func LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.LessOrEqual(t, e1, e2, msgAndArgs...) } // LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessOrEqualT] with [*time.Time]. // // [LessOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [LessOrEqual] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual] instead. // // # Usage // // assertions.LessOrEqualT(t, 1, 2) // assertions.LessOrEqualT(t, 2, 2) // assertions.LessOrEqualT(t, "a", "b") // assertions.LessOrEqualT(t, "b", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 // // Upon failure, the test [T] is marked as failed and continues execution. func LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.LessOrEqualT[Orderable](t, e1, e2, msgAndArgs...) } // LessT asserts that for two elements of the same type, the first element is strictly less than the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessT] with [*time.Time]. // // [LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [Less] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.LessT(t, 1, 2) // assertions.LessT(t, float64(1), float64(2)) // assertions.LessT(t, "a", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 // // Upon failure, the test [T] is marked as failed and continues execution. func LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.LessT[Orderable](t, e1, e2, msgAndArgs...) } // MapContainsT asserts that the specified map contains a key. // // Go native comparable types are explained there: [comparable-types]. // // # Usage // // assertions.MapContainsT(t, map[string]string{"Hello": "x","World": "y"}, "World") // // # Examples // // success: map[string]string{"A": "B"}, "A" // failure: map[string]string{"A": "B"}, "C" // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.MapContainsT[Map, K, V](t, m, key, msgAndArgs...) } // MapEqualT asserts that 2 maps of comparable elements are equal, // that is have the same length and contain the keys with the same elements. // // See also [maps.Equal]. // // # Usage // // assertions.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) // // # Examples // // success: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} // failure: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.MapEqualT[K, V](t, listA, listB, msgAndArgs...) } // MapNotContainsT asserts that the specified map does not contain a key. // // # Usage // // assertions.MapNotContainsT(t, map[string]string{"Hello": "x","World": "y"}, "hi") // // # Examples // // success: map[string]string{"A": "B"}, "C" // failure: map[string]string{"A": "B"}, "A" // // Upon failure, the test [T] is marked as failed and continues execution. func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.MapNotContainsT[Map, K, V](t, m, key, msgAndArgs...) } // MapNotEqualT asserts that 2 maps of comparable elements are not equal. // // See also [MapEqualT]. // // # Usage // // assertions.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) // // # Examples // // success: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} // failure: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.MapNotEqualT[K, V](t, listA, listB, msgAndArgs...) } // Negative asserts that the specified element is strictly negative. // // # Usage // // assertions.Negative(t, -1) // assertions.Negative(t, -1.23) // // # Examples // // success: -1 // failure: 1 // // Upon failure, the test [T] is marked as failed and continues execution. func Negative(t T, e any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Negative(t, e, msgAndArgs...) } // NegativeT asserts that the specified element of a signed numeric type is strictly negative. // // # Usage // // assertions.NegativeT(t, -1) // assertions.NegativeT(t, -1.23) // // # Examples // // success: -1 // failure: 1 // // Upon failure, the test [T] is marked as failed and continues execution. func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NegativeT[SignedNumber](t, e, msgAndArgs...) } // Never asserts that the given condition is never satisfied until timeout, // periodically checking the target function at each tick. // // [Never] is the opposite of [Eventually] ("at least once"). // It succeeds if the timeout is reached without the condition ever returning true. // // If the parent context is cancelled before the timeout, [Never] fails. // // # Usage // // assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) // // See also [Eventually] for details about using context, concurrency, and panic recovery. // // # Alternative condition signature // // The simplest form of condition is: // // func() bool // // Use [Consistently] instead if you want to use a condition returning an error. // // # Panic recovery // // A panicking condition is treated as an error, causing [Never] to fail immediately. // See [Eventually] for details. // // # Concurrency // // See [Eventually]. // // # Attention point // // See [Eventually]. // // # Synctest (opt-in) // // Wrap the condition with [WithSynctest] to run the polling loop inside a // [testing/synctest] bubble, which uses a fake clock. This eliminates // timing-induced flakiness and makes the tick count deterministic. See // [WithSynctest] for the constraints (no real I/O in the condition, // requires [*testing.T]). Note: [Never] does not accept the context/error // form of condition, so [WithSynctestContext] does not apply here. // // # Examples // // success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond // // Upon failure, the test [T] is marked as failed and continues execution. func Never[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Never[C](t, condition, timeout, tick, msgAndArgs...) } // Nil asserts that the specified object is nil. // // # Usage // // assertions.Nil(t, err) // // # Examples // // success: nil // failure: "not nil" // // Upon failure, the test [T] is marked as failed and continues execution. func Nil(t T, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Nil(t, object, msgAndArgs...) } // NoError asserts that a function returned a nil error (i.e. no error). // // # Usage // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { // assertions.Equal(t, expectedObj, actualObj) // } // // # Examples // // success: nil // failure: ErrTest // // Upon failure, the test [T] is marked as failed and continues execution. func NoError(t T, err error, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NoError(t, err, msgAndArgs...) } // NoFileDescriptorLeak ensures that no file descriptor leaks from inside the tested function. // // This assertion works on Linux (via /proc/self/fd) and macOS (via fstat probing). // On other platforms, the test is skipped. // // NOTE: this assertion is not compatible with parallel tests. // File descriptors are a process-wide resource; concurrent tests // opening files would cause false positives. // // Sockets, pipes, and other kernel-internal descriptors (Linux anon_inode, // darwin kqueue) are filtered out by default, as these are typically // managed by the Go runtime. // // # Concurrency // // [NoFileDescriptorLeak] is not compatible with parallel tests. // File descriptors are a process-wide resource; any concurrent I/O // from other goroutines may cause false positives. // // Calls to [NoFileDescriptorLeak] are serialized with a mutex // to prevent multiple leak checks from interfering with each other. // // # Usage // // NoFileDescriptorLeak(t, func() { // // code that should not leak file descriptors // }) // // # Examples // // success: func() {} // // Upon failure, the test [T] is marked as failed and continues execution. func NoFileDescriptorLeak(t T, tested func(), msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NoFileDescriptorLeak(t, tested, msgAndArgs...) } // NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. // // NOTE: only the go routines spawned from inside the tested function are checked for leaks. // No filter or configuration is needed to exclude "known go routines". // // Resource cleanup should be done inside the tested function, and not using [testing.T.Cleanup], // as t.Cleanup is called after the leak check. // // # Edge cases // // - if the tested function panics leaving behind leaked goroutines, these are detected. // - if the tested function calls [runtime.Goexit] (e.g. from [testing.T.FailNow]) leaving behind leaked goroutines, // these are detected. // - if a panic occurs in one of the leaked go routines, it cannot be recovered with certainty and // the calling program will usually panic. // // # Concurrency // // [NoGoRoutineLeak] may be used safely in parallel tests. // // # Usage // // NoGoRoutineLeak(t, func() { // ... // }, // "should not leak any go routine", // ) // // # Examples // // success: func() {} // // Upon failure, the test [T] is marked as failed and continues execution. func NoGoRoutineLeak(t T, tested func(), msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NoGoRoutineLeak(t, tested, msgAndArgs...) } // NotBlocked asserts that a channel is not blocked on receive. // // It always fails if the operand is not a channel, or if the channel is send-only. // // A closed channel doesn't block and returns true. // Notice that this consumes any message available in the channel. // // # Usage // // ch := make(chan struct{}, 1) // ch <- struct{}{} // assertions.NotBlocked(t, ch) // // # Examples // // success: sendChanMessage() // failure: make(chan struct{}) // // Upon failure, the test [T] is marked as failed and continues execution. func NotBlocked(t T, ch any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotBlocked(t, ch, msgAndArgs...) } // NotBlockedT asserts that a channel is not blocked on receive. // // A closed channel doesn't block and returns true. // Notice that this consumes any message available in the channel. // // # Usage // // ch := make(chan struct{}, 1) // ch <- struct{}{} // assertions.NotBlockedT(t, ch) // // # Examples // // success: sendChanMessage() // failure: make(chan struct{}) // // Upon failure, the test [T] is marked as failed and continues execution. func NotBlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotBlockedT[E, CHAN](t, ch, msgAndArgs...) } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // # Usage // // assertions.NotContains(t, "Hello World", "Earth") // assertions.NotContains(t, ["Hello", "World"], "Earth") // assertions.NotContains(t, {"Hello": "World"}, "Earth") // // # Examples // // success: []string{"A","B"}, "C" // failure: []string{"A","B"}, "B" // // Upon failure, the test [T] is marked as failed and continues execution. func NotContains(t T, s any, contains any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotContains(t, s, contains, msgAndArgs...) } // NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should not match. // This is an inverse of [ElementsMatch]. // // # Usage // // assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false // assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true // assertions.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true // // # Examples // // success: []int{1, 2, 3}, []int{1, 2, 4} // failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.NotElementsMatch(t, listA, listB, msgAndArgs...) } // NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should not match. // This is an inverse of [ElementsMatch]. // // # Usage // // assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false // assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true // assertions.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true // // # Examples // // success: []int{1, 2, 3}, []int{1, 2, 4} // failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.NotElementsMatchT[E](t, listA, listB, msgAndArgs...) } // NotEmpty asserts that the specified object is NOT [Empty]. // // # Usage // // if assert.NotEmpty(t, obj) { // assertions.Equal(t, "two", obj[1]) // } // // # Examples // // success: "not empty" // failure: "" // // Upon failure, the test [T] is marked as failed and continues execution. func NotEmpty(t T, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEmpty(t, object, msgAndArgs...) } // NotEqual asserts that the specified values are NOT equal. // // # Usage // // assertions.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). // // Function equality cannot be determined and will always fail. // // # Examples // // success: 123, 456 // failure: 123, 123 // // Upon failure, the test [T] is marked as failed and continues execution. func NotEqual(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEqual(t, expected, actual, msgAndArgs...) } // NotEqualT asserts that the specified values of the same comparable type are NOT equal. // // See [EqualT]. // // # Usage // // assertions.NotEqualT(t, obj1, obj2) // // # Examples // // success: 123, 456 // failure: 123, 123 // // Upon failure, the test [T] is marked as failed and continues execution. func NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEqualT[V](t, expected, actual, msgAndArgs...) } // NotEqualValues asserts that two objects are not equal even when converted to the same type. // // Function equality cannot be determined and will always fail. // // # Usage // // assertions.NotEqualValues(t, obj1, obj2) // // # Examples // // success: uint32(123), int32(456) // failure: uint32(123), int32(123) // // Upon failure, the test [T] is marked as failed and continues execution. func NotEqualValues(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEqualValues(t, expected, actual, msgAndArgs...) } // NotErrorAs asserts that none of the errors in err's chain matches target, // but if so, sets target to that error value. // // # Usage // // assertions.NotErrorAs(t, err, &target) // // # Examples // // success: ErrTest, new(*dummyError) // failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) // // Upon failure, the test [T] is marked as failed and continues execution. func NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotErrorAs(t, err, target, msgAndArgs...) } // NotErrorIs asserts that none of the errors in err's chain matches target. // // This is a wrapper for [errors.Is]. // // # Usage // // assertions.NotErrorIs(t, err, io.EOF) // // # Examples // // success: ErrTest, io.EOF // failure: fmt.Errorf("wrap: %w", io.EOF), io.EOF // // Upon failure, the test [T] is marked as failed and continues execution. func NotErrorIs(t T, err error, target error, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotErrorIs(t, err, target, msgAndArgs...) } // NotImplements asserts that an object does not implement the specified interface. // // # Usage // // assertions.NotImplements(t, (*MyInterface)(nil), new(MyObject)) // // # Examples // // success: (*error)(nil), new(testing.T) // failure: ptr(dummyInterface), new(testing.T) // // Upon failure, the test [T] is marked as failed and continues execution. func NotImplements(t T, interfaceObject any, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotImplements(t, interfaceObject, object, msgAndArgs...) } // NotKind asserts that the [reflect.Kind] of a given object does not match the expected [reflect.Kind]. // // Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) // are comparable to [reflect.Invalid]. See also [reflect.Value.Kind]. // // # Usage // // assertions.NotKind(t, reflect.Int, "Hello World") // // # Examples // // success: reflect.String, 0 // failure: reflect.String, "hello" // // Upon failure, the test [T] is marked as failed and continues execution. func NotKind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotKind(t, expectedKind, object, msgAndArgs...) } // NotNil asserts that the specified object is not nil. // // # Usage // // assertions.NotNil(t, err) // // # Examples // // success: "not nil" // failure: nil // // Upon failure, the test [T] is marked as failed and continues execution. func NotNil(t T, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotNil(t, object, msgAndArgs...) } // NotPanics asserts that the code inside the specified function does NOT panic. // // # Usage // // assertions.NotPanics(t, func(){ RemainCalm() }) // // # Examples // // success: func() { } // failure: func() { panic("panicking") } // // Upon failure, the test [T] is marked as failed and continues execution. func NotPanics(t T, f func(), msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotPanics(t, f, msgAndArgs...) } // NotRegexp asserts that a specified regular expression does not match a string. // // See [Regexp]. // // # Usage // // assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assertions.NotRegexp(t, "^start", "it's not starting") // // # Examples // // success: "^start", "not starting" // failure: "^start", "starting" // // Upon failure, the test [T] is marked as failed and continues execution. func NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotRegexp(t, rx, actual, msgAndArgs...) } // NotRegexpT asserts that a specified regular expression does not match a string. // // See [RegexpT]. // // # Usage // // assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assertions.NotRegexp(t, "^start", "it's not starting") // // # Examples // // success: "^start", "not starting" // failure: "^start", "starting" // // Upon failure, the test [T] is marked as failed and continues execution. func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotRegexpT[Rex, ADoc](t, rx, actual, msgAndArgs...) } // NotSame asserts that two pointers do not reference the same object. // // See [Same]. // // # Usage // // assertions.NotSame(t, ptr1, ptr2) // // # Examples // // success: &staticVar, ptr("static string") // failure: &staticVar, staticVarPtr // // Upon failure, the test [T] is marked as failed and continues execution. func NotSame(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSame(t, expected, actual, msgAndArgs...) } // NotSameT asserts that two pointers do not reference the same object. // // See [SameT]. // // # Usage // // assertions.NotSameT(t, ptr1, ptr2) // // # Examples // // success: &staticVar, ptr("static string") // failure: &staticVar, staticVarPtr // // Upon failure, the test [T] is marked as failed and continues execution. func NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSameT[P](t, expected, actual, msgAndArgs...) } // NotSortedT asserts that the slice of [Ordered] is NOT sorted (i.e. non-strictly increasing). // // Unlike [IsDecreasingT], it accepts slices that are neither increasing nor decreasing. // // # Usage // // assertions.NotSortedT(t, []int{3, 2, 3}) // assertions.NotSortedT(t, []float{2, 1}) // assertions.NotSortedT(t, []string{"b", "a"}) // // # Examples // // success: []int{3, 1, 3} // failure: []int{1, 4, 8} // // Upon failure, the test [T] is marked as failed and continues execution. func NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSortedT[OrderedSlice, E](t, collection, msgAndArgs...) } // NotSubset asserts that the list (array, slice, or map) does NOT contain all // elements given in the subset (array, slice, or map). // Map elements are key-value pairs unless compared with an array or slice where // only the map key is evaluated. // // # Usage // // assertions.NotSubset(t, [1, 3, 4], [1, 2]) // assertions.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) // assertions.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) // assertions.NotSubset(t, {"x": 1, "y": 2}, ["z"]) // // # Examples // // success: []int{1, 2, 3}, []int{4, 5} // failure: []int{1, 2, 3}, []int{1, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func NotSubset(t T, list any, subset any, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSubset(t, list, subset, msgAndArgs...) } // NotZero asserts that i is not the zero value for its type. // // # Usage // // assertions.NotZero(t, obj) // // # Examples // // success: 1 // failure: 0 // // Upon failure, the test [T] is marked as failed and continues execution. func NotZero(t T, i any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotZero(t, i, msgAndArgs...) } // Panics asserts that the code inside the specified function panics. // // # Usage // // assertions.Panics(t, func(){ GoCrazy() }) // // # Examples // // success: func() { panic("panicking") } // failure: func() { } // // Upon failure, the test [T] is marked as failed and continues execution. func Panics(t T, f func(), msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Panics(t, f, msgAndArgs...) } // PanicsWithError asserts that the code inside the specified function panics, // and that the recovered panic value is an error that satisfies the [EqualError] comparison. // // # Usage // // assertions.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) // // # Examples // // success: ErrTest.Error(), func() { panic(ErrTest) } // failure: ErrTest.Error(), func() { } // // Upon failure, the test [T] is marked as failed and continues execution. func PanicsWithError(t T, errString string, f func(), msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.PanicsWithError(t, errString, f, msgAndArgs...) } // PanicsWithValue asserts that the code inside the specified function panics, // and that the recovered panic value equals the expected panic value. // // # Usage // // assertions.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) // // # Examples // // success: "panicking", func() { panic("panicking") } // failure: "panicking", func() { } // // Upon failure, the test [T] is marked as failed and continues execution. func PanicsWithValue(t T, expected any, f func(), msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.PanicsWithValue(t, expected, f, msgAndArgs...) } // Positive asserts that the specified element is strictly positive. // // # Usage // // assertions.Positive(t, 1) // assertions.Positive(t, 1.23) // // # Examples // // success: 1 // failure: -1 // // Upon failure, the test [T] is marked as failed and continues execution. func Positive(t T, e any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Positive(t, e, msgAndArgs...) } // PositiveT asserts that the specified element of a signed numeric type is strictly positive. // // # Usage // // assertions.PositiveT(t, 1) // assertions.PositiveT(t, 1.23) // // # Examples // // success: 1 // failure: -1 // // Upon failure, the test [T] is marked as failed and continues execution. func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.PositiveT[SignedNumber](t, e, msgAndArgs...) } // Regexp asserts that a specified regular expression matches a string. // // The regular expression may be passed as a [regexp.Regexp], a string or a []byte and will be compiled. // // The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint]. // // # Usage // // assertions.Regexp(t, regexp.MustCompile("start"), "it's starting") // assertions.Regexp(t, "start...$", "it's not starting") // // # Examples // // success: "^start", "starting" // failure: "^start", "not starting" // // Upon failure, the test [T] is marked as failed and continues execution. func Regexp(t T, rx any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Regexp(t, rx, actual, msgAndArgs...) } // RegexpT asserts that a specified regular expression matches a string. // // The actual argument to be matched may be a string or []byte. // // See [Regexp]. // // # Examples // // success: "^start", "starting" // failure: "^start", "not starting" // // Upon failure, the test [T] is marked as failed and continues execution. func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.RegexpT[Rex, ADoc](t, rx, actual, msgAndArgs...) } // Same asserts that two pointers reference the same object. // // Both arguments must be pointer variables. // // Pointer variable sameness is determined based on the equality of both type and value. // // Unlike [Equal] pointers, [Same] pointers point to the same memory address. // // # Usage // // assertions.Same(t, ptr1, ptr2) // // # Examples // // success: &staticVar, staticVarPtr // failure: &staticVar, ptr("static string") // // Upon failure, the test [T] is marked as failed and continues execution. func Same(t T, expected any, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Same(t, expected, actual, msgAndArgs...) } // SameT asserts that two pointers of the same type reference the same object. // // See [Same]. // // # Usage // // assertions.SameT(t, ptr1, ptr2) // // # Examples // // success: &staticVar, staticVarPtr // failure: &staticVar, ptr("static string") // // Upon failure, the test [T] is marked as failed and continues execution. func SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SameT[P](t, expected, actual, msgAndArgs...) } // SeqContainsT asserts that the specified iterator contains a comparable element. // // The sequence may not be consumed entirely: the iteration stops as soon as the specified element is found. // // Go native comparable types are explained there: [comparable-types]. // // # Usage // // assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") // // # Examples // // success: slices.Values([]string{"A","B"}), "A" // failure: slices.Values([]string{"A","B"}), "C" // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SeqContainsT[E](t, iter, element, msgAndArgs...) } // SeqNotContainsT asserts that the specified iterator does not contain a comparable element. // // See [SeqContainsT]. // // # Usage // // assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") // // # Examples // // success: slices.Values([]string{"A","B"}), "C" // failure: slices.Values([]string{"A","B"}), "A" // // Upon failure, the test [T] is marked as failed and continues execution. func SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SeqNotContainsT[E](t, iter, element, msgAndArgs...) } // SliceContainsT asserts that the specified slice contains a comparable element. // // Go native comparable types are explained there: [comparable-types]. // // # Usage // // assertions.SliceContainsT(t, []{"Hello","World"}, "World") // // # Examples // // success: []string{"A","B"}, "A" // failure: []string{"A","B"}, "C" // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceContainsT[Slice, E](t, s, element, msgAndArgs...) } // SliceEqualT asserts that 2 slices of comparable elements are equal, // that is have the same length and contain the same elements in the same order. // // See also [slices.Equal]. // // # Usage // // assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) // // # Examples // // success: []string{"Hello","World"}, []string{"Hello","World"} // failure: []string{"Hello","World"}, []string{"Hello"} // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceEqualT[E](t, listA, listB, msgAndArgs...) } // SliceNotContainsT asserts that the specified slice does not contain a comparable element. // // See [SliceContainsT]. // // # Usage // // assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") // // # Examples // // success: []string{"A","B"}, "C" // failure: []string{"A","B"}, "A" // // Upon failure, the test [T] is marked as failed and continues execution. func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceNotContainsT[Slice, E](t, s, element, msgAndArgs...) } // SliceNotEqualT asserts that 2 slices of comparable elements are not equal. // // See also [SliceEqualT]. // // # Usage // // assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) // // # Examples // // success: []string{"Hello","World"}, []string{"Hello"} // failure: []string{"Hello","World"}, []string{"Hello","World"} // // Upon failure, the test [T] is marked as failed and continues execution. // // [comparable-types]: https://go.dev/blog/comparable func SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceNotEqualT[E](t, listA, listB, msgAndArgs...) } // SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. // // # Usage // // assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) // // # Examples // // success: []int{1, 2, 3}, []int{4, 5} // failure: []int{1, 2, 3}, []int{1, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceNotSubsetT[Slice, E](t, list, subset, msgAndArgs...) } // SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. // // # Usage // // assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) // // # Examples // // success: []int{1, 2, 3}, []int{1, 2} // failure: []int{1, 2, 3}, []int{4, 5} // // Upon failure, the test [T] is marked as failed and continues execution. func SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceSubsetT[Slice, E](t, list, subset, msgAndArgs...) } // SortedT asserts that the slice of [Ordered] is sorted (i.e. non-strictly increasing). // // Unlike [IsIncreasingT], it accepts elements to be equal. // // # Usage // // assertions.SortedT(t, []int{1, 2, 3}) // assertions.SortedT(t, []float{1, 2}) // assertions.SortedT(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 1, 3} // failure: []int{1, 4, 2} // // Upon failure, the test [T] is marked as failed and continues execution. func SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SortedT[OrderedSlice, E](t, collection, msgAndArgs...) } // StringContainsT asserts that a string contains the specified substring. // // Strings may be go strings or []byte according to the type constraint [Text]. // // # Usage // // assertions.StringContainsT(t, "Hello World", "World") // // # Examples // // success: "AB", "A" // failure: "AB", "C" // // Upon failure, the test [T] is marked as failed and continues execution. func StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.StringContainsT[ADoc, EDoc](t, str, substring, msgAndArgs...) } // StringNotContainsT asserts that a string does not contain the specified substring. // // See [StringContainsT]. // // # Usage // // assertions.StringNotContainsT(t, "Hello World", "hi") // // # Examples // // success: "AB", "C" // failure: "AB", "A" // // Upon failure, the test [T] is marked as failed and continues execution. func StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.StringNotContainsT[ADoc, EDoc](t, str, substring, msgAndArgs...) } // Subset asserts that the list (array, slice, or map) contains all elements // given in the subset (array, slice, or map). // // Map elements are key-value pairs unless compared with an array or slice where // only the map key is evaluated. // // nil values are considered as empty sets. // // # Usage // // assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) // assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) // assertions.Subset(t, []int{1, 2, 3}, map[int]string{1: "one", 2: "two"}) // assertions.Subset(t, map[string]int{"x": 1, "y": 2}, []string{"x"}) // // # Examples // // success: []int{1, 2, 3}, []int{1, 2} // failure: []int{1, 2, 3}, []int{4, 5} // // Upon failure, the test [T] is marked as failed and continues execution. func Subset(t T, list any, subset any, msgAndArgs ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.Subset(t, list, subset, msgAndArgs...) } // True asserts that the specified value is true. // // # Usage // // assertions.True(t, myBool) // // # Examples // // success: 1 == 1 // failure: 1 == 0 // // Upon failure, the test [T] is marked as failed and continues execution. func True(t T, value bool, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.True(t, value, msgAndArgs...) } // TrueT asserts that the specified value is true. // // The type constraint [Boolean] accepts any type which underlying type is bool. // // # Usage // // type B bool // var b B = true // // assertions.True(t, b) // // # Examples // // success: 1 == 1 // failure: 1 == 0 // // Upon failure, the test [T] is marked as failed and continues execution. func TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.TrueT[B](t, value, msgAndArgs...) } // WithinDuration asserts that the two times are within duration delta of each other. // // # Usage // // assertions.WithinDuration(t, time.Now(), 10*time.Second) // // # Examples // // success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second // failure: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second // // Upon failure, the test [T] is marked as failed and continues execution. func WithinDuration(t T, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.WithinDuration(t, expected, actual, delta, msgAndArgs...) } // WithinRange asserts that a time is within a time range (inclusive). // // # Usage // // assertions.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) // // # Examples // // success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) // failure: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) // // Upon failure, the test [T] is marked as failed and continues execution. func WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.WithinRange(t, actual, start, end, msgAndArgs...) } // YAMLEq asserts that two YAML strings are equivalent. // // See [YAMLEqBytes]. // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLEq(t, expected, actual, msgAndArgs...) } // YAMLEqBytes asserts that two YAML slices of bytes are equivalent. // // Expected and actual must be valid YAML. // // # Important // // By default, this function is disabled and will panic. // // To enable it, you should add a blank import like so: // // import( // "github.com/go-openapi/testify/enable/yaml/v2" // ) // // For dynamic redaction of the input text via a callback, use [YAMLEqT]. // // # Usage // // expected := `--- // key: value // --- // key: this is a second document, it is not evaluated // ` // actual := `--- // key: value // --- // key: this is a subsequent document, it is not evaluated // ` // assertions.YAMLEq(t, expected, actual) // // # Examples // // panic: []byte("key: value"), []byte("key: value") // should panic without the yaml feature enabled. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLEqBytes(t, expected, actual, msgAndArgs...) } // YAMLEqT asserts that two YAML documents are equivalent. // // The expected and actual arguments may be string or []byte. They do not need to be of the same type. // // See [YAMLEqBytes]. // // NOTE: passed values (expected, actual) may be wrapped as functions to redact the input text dynamically. // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLEqT[EDoc, ADoc](t, expected, actual, msgAndArgs...) } // YAMLMarshalAsT wraps [YAMLEq] after [yaml.Marshal]. // // The input YAML may be a string or []byte. // // It fails if the marshaling returns an error or if the expected YAML bytes differ semantically // from the expected ones. // // NOTE: passed expected value may be wrapped as a function to redact the input text dynamically. // // # Usage // // actual := struct { // A int `yaml:"a"` // }{ // A: 10, // } // // assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLMarshalAsT[EDoc](t, expected, object, msgAndArgs...) } // YAMLUnmarshalAsT wraps [Equal] after [yaml.Unmarshal]. // // The input YAML may be a string or []byte. // // It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. // // Be careful not to wrap the expected object into an "any" interface if this is not what you expected: // the unmarshaling would take this type to unmarshal as a map[string]any. // // NOTE: passed yamlDoc value may be wrapped as a function to redact the input text dynamically. // // # Usage // // expected := struct { // A int `yaml:"a"` // }{ // A: 10, // } // // assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLUnmarshalAsT[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLUnmarshalAsT[Object, ADoc](t, expected, yamlDoc, msgAndArgs...) } // Zero asserts that i is the zero value for its type. // // # Usage // // assertions.Zero(t, obj) // // # Examples // // success: 0 // failure: 1 // // Upon failure, the test [T] is marked as failed and continues execution. func Zero(t T, i any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Zero(t, i, msgAndArgs...) } go-openapi-testify-c10ca71/assert/assert_assertions_test.go000066400000000000000000002263561520301377500243210ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "fmt" "io" "net/http" "net/url" "path/filepath" "reflect" "slices" "testing" "time" ) func TestBlocked(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Blocked(mock, make(chan struct{})) if !result { t.Error("Blocked should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Blocked(mock, sendChanMessage()) if result { t.Error("Blocked should return false on failure") } if !mock.failed { t.Error("Blocked should mark test as failed") } }) } func TestBlockedT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := BlockedT(mock, make(chan struct{})) if !result { t.Error("BlockedT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := BlockedT(mock, sendChanMessage()) if result { t.Error("BlockedT should return false on failure") } if !mock.failed { t.Error("BlockedT should mark test as failed") } }) } func TestCondition(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Condition(mock, func() bool { return true }) if !result { t.Error("Condition should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Condition(mock, func() bool { return false }) if result { t.Error("Condition should return false on failure") } if !mock.failed { t.Error("Condition should mark test as failed") } }) } func TestConsistently(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Consistently(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) if !result { t.Error("Consistently should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Consistently(mock, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) if result { t.Error("Consistently should return false on failure") } if !mock.failed { t.Error("Consistently should mark test as failed") } }) } func TestContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Contains(mock, []string{"A", "B"}, "A") if !result { t.Error("Contains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Contains(mock, []string{"A", "B"}, "C") if result { t.Error("Contains should return false on failure") } if !mock.failed { t.Error("Contains should mark test as failed") } }) } func TestDirExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirExists(mock, filepath.Join(testDataPath(), "existing_dir")) if !result { t.Error("DirExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirExists(mock, filepath.Join(testDataPath(), "non_existing_dir")) if result { t.Error("DirExists should return false on failure") } if !mock.failed { t.Error("DirExists should mark test as failed") } }) } func TestDirNotExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirNotExists(mock, filepath.Join(testDataPath(), "non_existing_dir")) if !result { t.Error("DirNotExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirNotExists(mock, filepath.Join(testDataPath(), "existing_dir")) if result { t.Error("DirNotExists should return false on failure") } if !mock.failed { t.Error("DirNotExists should mark test as failed") } }) } func TestElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatch(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) if !result { t.Error("ElementsMatch should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatch(mock, []int{1, 2, 3}, []int{1, 2, 4}) if result { t.Error("ElementsMatch should return false on failure") } if !mock.failed { t.Error("ElementsMatch should mark test as failed") } }) } func TestElementsMatchT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatchT(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) if !result { t.Error("ElementsMatchT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatchT(mock, []int{1, 2, 3}, []int{1, 2, 4}) if result { t.Error("ElementsMatchT should return false on failure") } if !mock.failed { t.Error("ElementsMatchT should mark test as failed") } }) } func TestEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Empty(mock, "") if !result { t.Error("Empty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Empty(mock, "not empty") if result { t.Error("Empty should return false on failure") } if !mock.failed { t.Error("Empty should mark test as failed") } }) } func TestEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Equal(mock, 123, 123) if !result { t.Error("Equal should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Equal(mock, 123, 456) if result { t.Error("Equal should return false on failure") } if !mock.failed { t.Error("Equal should mark test as failed") } }) } func TestEqualError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualError(mock, ErrTest, "assert.ErrTest general error for testing") if !result { t.Error("EqualError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualError(mock, ErrTest, "wrong error message") if result { t.Error("EqualError should return false on failure") } if !mock.failed { t.Error("EqualError should mark test as failed") } }) } func TestEqualExportedValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualExportedValues(mock, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) if !result { t.Error("EqualExportedValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualExportedValues(mock, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1}) if result { t.Error("EqualExportedValues should return false on failure") } if !mock.failed { t.Error("EqualExportedValues should mark test as failed") } }) } func TestEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualT(mock, 123, 123) if !result { t.Error("EqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualT(mock, 123, 456) if result { t.Error("EqualT should return false on failure") } if !mock.failed { t.Error("EqualT should mark test as failed") } }) } func TestEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualValues(mock, uint32(123), int32(123)) if !result { t.Error("EqualValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualValues(mock, uint32(123), int32(456)) if result { t.Error("EqualValues should return false on failure") } if !mock.failed { t.Error("EqualValues should mark test as failed") } }) } func TestError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Error(mock, ErrTest) if !result { t.Error("Error should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Error(mock, nil) if result { t.Error("Error should return false on failure") } if !mock.failed { t.Error("Error should mark test as failed") } }) } func TestErrorAs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorAs(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) if !result { t.Error("ErrorAs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorAs(mock, ErrTest, new(*dummyError)) if result { t.Error("ErrorAs should return false on failure") } if !mock.failed { t.Error("ErrorAs should mark test as failed") } }) } func TestErrorContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorContains(mock, ErrTest, "general error") if !result { t.Error("ErrorContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorContains(mock, ErrTest, "not in message") if result { t.Error("ErrorContains should return false on failure") } if !mock.failed { t.Error("ErrorContains should mark test as failed") } }) } func TestErrorIs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorIs(mock, fmt.Errorf("wrap: %w", io.EOF), io.EOF) if !result { t.Error("ErrorIs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorIs(mock, ErrTest, io.EOF) if result { t.Error("ErrorIs should return false on failure") } if !mock.failed { t.Error("ErrorIs should mark test as failed") } }) } func TestEventually(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Eventually(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) if !result { t.Error("Eventually should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Eventually(mock, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) if result { t.Error("Eventually should return false on failure") } if !mock.failed { t.Error("Eventually should mark test as failed") } }) } func TestEventuallyWith(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EventuallyWith(mock, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) if !result { t.Error("EventuallyWith should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EventuallyWith(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond) if result { t.Error("EventuallyWith should return false on failure") } if !mock.failed { t.Error("EventuallyWith should mark test as failed") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EventuallyWith(mock, func(c *CollectT) { c.Cancel() }, 100*time.Millisecond, 20*time.Millisecond) if result { t.Error("EventuallyWith should return false on failure") } if !mock.failed { t.Error("EventuallyWith should mark test as failed") } }) } func TestExactly(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Exactly(mock, int32(123), int32(123)) if !result { t.Error("Exactly should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Exactly(mock, int32(123), int64(123)) if result { t.Error("Exactly should return false on failure") } if !mock.failed { t.Error("Exactly should mark test as failed") } }) } func TestFail(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Fail(mock, "failed") if result { t.Error("Fail should return false on failure") } if !mock.failed { t.Error("Fail should mark test as failed") } }) } func TestFailNow(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockFailNowT) result := FailNow(mock, "failed") if result { t.Error("FailNow should return false on failure") } if !mock.failed { t.Error("FailNow should call FailNow()") } }) } func TestFalse(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := False(mock, 1 == 0) if !result { t.Error("False should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := False(mock, 1 == 1) if result { t.Error("False should return false on failure") } if !mock.failed { t.Error("False should mark test as failed") } }) } func TestFalseT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FalseT(mock, 1 == 0) if !result { t.Error("FalseT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FalseT(mock, 1 == 1) if result { t.Error("FalseT should return false on failure") } if !mock.failed { t.Error("FalseT should mark test as failed") } }) } func TestFileEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileEmpty(mock, filepath.Join(testDataPath(), "empty_file")) if !result { t.Error("FileEmpty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileEmpty(mock, filepath.Join(testDataPath(), "existing_file")) if result { t.Error("FileEmpty should return false on failure") } if !mock.failed { t.Error("FileEmpty should mark test as failed") } }) } func TestFileExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileExists(mock, filepath.Join(testDataPath(), "existing_file")) if !result { t.Error("FileExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileExists(mock, filepath.Join(testDataPath(), "non_existing_file")) if result { t.Error("FileExists should return false on failure") } if !mock.failed { t.Error("FileExists should mark test as failed") } }) } func TestFileNotEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotEmpty(mock, filepath.Join(testDataPath(), "existing_file")) if !result { t.Error("FileNotEmpty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotEmpty(mock, filepath.Join(testDataPath(), "empty_file")) if result { t.Error("FileNotEmpty should return false on failure") } if !mock.failed { t.Error("FileNotEmpty should mark test as failed") } }) } func TestFileNotExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotExists(mock, filepath.Join(testDataPath(), "non_existing_file")) if !result { t.Error("FileNotExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotExists(mock, filepath.Join(testDataPath(), "existing_file")) if result { t.Error("FileNotExists should return false on failure") } if !mock.failed { t.Error("FileNotExists should mark test as failed") } }) } func TestGreater(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Greater(mock, 2, 1) if !result { t.Error("Greater should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Greater(mock, 1, 2) if result { t.Error("Greater should return false on failure") } if !mock.failed { t.Error("Greater should mark test as failed") } }) } func TestGreaterOrEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqual(mock, 2, 1) if !result { t.Error("GreaterOrEqual should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqual(mock, 1, 2) if result { t.Error("GreaterOrEqual should return false on failure") } if !mock.failed { t.Error("GreaterOrEqual should mark test as failed") } }) } func TestGreaterOrEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqualT(mock, 2, 1) if !result { t.Error("GreaterOrEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqualT(mock, 1, 2) if result { t.Error("GreaterOrEqualT should return false on failure") } if !mock.failed { t.Error("GreaterOrEqualT should mark test as failed") } }) } func TestGreaterT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterT(mock, 2, 1) if !result { t.Error("GreaterT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterT(mock, 1, 2) if result { t.Error("GreaterT should return false on failure") } if !mock.failed { t.Error("GreaterT should mark test as failed") } }) } func TestHTTPBodyContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyContains(mock, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") if !result { t.Error("HTTPBodyContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyContains(mock, httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!") if result { t.Error("HTTPBodyContains should return false on failure") } if !mock.failed { t.Error("HTTPBodyContains should mark test as failed") } }) } func TestHTTPBodyNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyNotContains(mock, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") if !result { t.Error("HTTPBodyNotContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyNotContains(mock, httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!") if result { t.Error("HTTPBodyNotContains should return false on failure") } if !mock.failed { t.Error("HTTPBodyNotContains should mark test as failed") } }) } func TestHTTPError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPError(mock, httpError, "GET", "/", nil) if !result { t.Error("HTTPError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPError(mock, httpOK, "GET", "/", nil) if result { t.Error("HTTPError should return false on failure") } if !mock.failed { t.Error("HTTPError should mark test as failed") } }) } func TestHTTPRedirect(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPRedirect(mock, httpRedirect, "GET", "/", nil) if !result { t.Error("HTTPRedirect should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPRedirect(mock, httpError, "GET", "/", nil) if result { t.Error("HTTPRedirect should return false on failure") } if !mock.failed { t.Error("HTTPRedirect should mark test as failed") } }) } func TestHTTPStatusCode(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPStatusCode(mock, httpOK, "GET", "/", nil, http.StatusOK) if !result { t.Error("HTTPStatusCode should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPStatusCode(mock, httpError, "GET", "/", nil, http.StatusOK) if result { t.Error("HTTPStatusCode should return false on failure") } if !mock.failed { t.Error("HTTPStatusCode should mark test as failed") } }) } func TestHTTPSuccess(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPSuccess(mock, httpOK, "GET", "/", nil) if !result { t.Error("HTTPSuccess should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPSuccess(mock, httpError, "GET", "/", nil) if result { t.Error("HTTPSuccess should return false on failure") } if !mock.failed { t.Error("HTTPSuccess should mark test as failed") } }) } func TestImplements(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Implements(mock, ptr(dummyInterface), new(testing.T)) if !result { t.Error("Implements should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Implements(mock, (*error)(nil), new(testing.T)) if result { t.Error("Implements should return false on failure") } if !mock.failed { t.Error("Implements should mark test as failed") } }) } func TestInDelta(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDelta(mock, 1.0, 1.01, 0.02) if !result { t.Error("InDelta should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDelta(mock, 1.0, 1.1, 0.05) if result { t.Error("InDelta should return false on failure") } if !mock.failed { t.Error("InDelta should mark test as failed") } }) } func TestInDeltaMapValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaMapValues(mock, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) if !result { t.Error("InDeltaMapValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaMapValues(mock, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.1}, 0.05) if result { t.Error("InDeltaMapValues should return false on failure") } if !mock.failed { t.Error("InDeltaMapValues should mark test as failed") } }) } func TestInDeltaSlice(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaSlice(mock, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) if !result { t.Error("InDeltaSlice should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaSlice(mock, []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05) if result { t.Error("InDeltaSlice should return false on failure") } if !mock.failed { t.Error("InDeltaSlice should mark test as failed") } }) } func TestInDeltaT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaT(mock, 1.0, 1.01, 0.02) if !result { t.Error("InDeltaT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaT(mock, 1.0, 1.1, 0.05) if result { t.Error("InDeltaT should return false on failure") } if !mock.failed { t.Error("InDeltaT should mark test as failed") } }) } func TestInEpsilon(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilon(mock, 100.0, 101.0, 0.02) if !result { t.Error("InEpsilon should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilon(mock, 100.0, 110.0, 0.05) if result { t.Error("InEpsilon should return false on failure") } if !mock.failed { t.Error("InEpsilon should mark test as failed") } }) } func TestInEpsilonSlice(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSlice(mock, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) if !result { t.Error("InEpsilonSlice should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSlice(mock, []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05) if result { t.Error("InEpsilonSlice should return false on failure") } if !mock.failed { t.Error("InEpsilonSlice should mark test as failed") } }) } func TestInEpsilonSymmetric(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetric(mock, 100.0, 101.0, 0.02) if !result { t.Error("InEpsilonSymmetric should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetric(mock, 100.0, 110.0, 0.05) if result { t.Error("InEpsilonSymmetric should return false on failure") } if !mock.failed { t.Error("InEpsilonSymmetric should mark test as failed") } }) } func TestInEpsilonSymmetricT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricT(mock, 100.0, 101.0, 0.02) if !result { t.Error("InEpsilonSymmetricT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricT(mock, 100.0, 110.0, 0.05) if result { t.Error("InEpsilonSymmetricT should return false on failure") } if !mock.failed { t.Error("InEpsilonSymmetricT should mark test as failed") } }) } func TestInEpsilonT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonT(mock, 100.0, 101.0, 0.02) if !result { t.Error("InEpsilonT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonT(mock, 100.0, 110.0, 0.05) if result { t.Error("InEpsilonT should return false on failure") } if !mock.failed { t.Error("InEpsilonT should mark test as failed") } }) } func TestIsDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasing(mock, []int{3, 2, 1}) if !result { t.Error("IsDecreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasing(mock, []int{1, 2, 3}) if result { t.Error("IsDecreasing should return false on failure") } if !mock.failed { t.Error("IsDecreasing should mark test as failed") } }) } func TestIsDecreasingT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasingT(mock, []int{3, 2, 1}) if !result { t.Error("IsDecreasingT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasingT(mock, []int{1, 2, 3}) if result { t.Error("IsDecreasingT should return false on failure") } if !mock.failed { t.Error("IsDecreasingT should mark test as failed") } }) } func TestIsIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasing(mock, []int{1, 2, 3}) if !result { t.Error("IsIncreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasing(mock, []int{1, 1, 2}) if result { t.Error("IsIncreasing should return false on failure") } if !mock.failed { t.Error("IsIncreasing should mark test as failed") } }) } func TestIsIncreasingT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasingT(mock, []int{1, 2, 3}) if !result { t.Error("IsIncreasingT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasingT(mock, []int{1, 1, 2}) if result { t.Error("IsIncreasingT should return false on failure") } if !mock.failed { t.Error("IsIncreasingT should mark test as failed") } }) } func TestIsNonDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasing(mock, []int{1, 1, 2}) if !result { t.Error("IsNonDecreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasing(mock, []int{2, 1, 0}) if result { t.Error("IsNonDecreasing should return false on failure") } if !mock.failed { t.Error("IsNonDecreasing should mark test as failed") } }) } func TestIsNonDecreasingT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasingT(mock, []int{1, 1, 2}) if !result { t.Error("IsNonDecreasingT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasingT(mock, []int{2, 1, 0}) if result { t.Error("IsNonDecreasingT should return false on failure") } if !mock.failed { t.Error("IsNonDecreasingT should mark test as failed") } }) } func TestIsNonIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasing(mock, []int{2, 1, 1}) if !result { t.Error("IsNonIncreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasing(mock, []int{1, 2, 3}) if result { t.Error("IsNonIncreasing should return false on failure") } if !mock.failed { t.Error("IsNonIncreasing should mark test as failed") } }) } func TestIsNonIncreasingT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasingT(mock, []int{2, 1, 1}) if !result { t.Error("IsNonIncreasingT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasingT(mock, []int{1, 2, 3}) if result { t.Error("IsNonIncreasingT should return false on failure") } if !mock.failed { t.Error("IsNonIncreasingT should mark test as failed") } }) } func TestIsNotOfTypeT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotOfTypeT[myType](mock, 123.123) if !result { t.Error("IsNotOfTypeT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotOfTypeT[myType](mock, myType(123.123)) if result { t.Error("IsNotOfTypeT should return false on failure") } if !mock.failed { t.Error("IsNotOfTypeT should mark test as failed") } }) } func TestIsNotType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotType(mock, int32(123), int64(456)) if !result { t.Error("IsNotType should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotType(mock, 123, 456) if result { t.Error("IsNotType should return false on failure") } if !mock.failed { t.Error("IsNotType should mark test as failed") } }) } func TestIsOfTypeT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsOfTypeT[myType](mock, myType(123.123)) if !result { t.Error("IsOfTypeT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsOfTypeT[myType](mock, 123.123) if result { t.Error("IsOfTypeT should return false on failure") } if !mock.failed { t.Error("IsOfTypeT should mark test as failed") } }) } func TestIsType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsType(mock, 123, 456) if !result { t.Error("IsType should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsType(mock, int32(123), int64(456)) if result { t.Error("IsType should return false on failure") } if !mock.failed { t.Error("IsType should mark test as failed") } }) } func TestJSONEq(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEq(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if !result { t.Error("JSONEq should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEq(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`) if result { t.Error("JSONEq should return false on failure") } if !mock.failed { t.Error("JSONEq should mark test as failed") } }) } func TestJSONEqBytes(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqBytes(mock, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) if !result { t.Error("JSONEqBytes should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqBytes(mock, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`)) if result { t.Error("JSONEqBytes should return false on failure") } if !mock.failed { t.Error("JSONEqBytes should mark test as failed") } }) } func TestJSONEqT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqT(mock, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) if !result { t.Error("JSONEqT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqT(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`) if result { t.Error("JSONEqT should return false on failure") } if !mock.failed { t.Error("JSONEqT should mark test as failed") } }) } func TestJSONMarshalAsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONMarshalAsT(mock, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) if !result { t.Error("JSONMarshalAsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONMarshalAsT(mock, `[{"foo": "bar"}, {"hello": "world"}]`, 1) if result { t.Error("JSONMarshalAsT should return false on failure") } if !mock.failed { t.Error("JSONMarshalAsT should mark test as failed") } }) } func TestJSONUnmarshalAsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONUnmarshalAsT(mock, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) if !result { t.Error("JSONUnmarshalAsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONUnmarshalAsT(mock, 1, `[{"foo": "bar"}, {"hello": "world"}]`) if result { t.Error("JSONUnmarshalAsT should return false on failure") } if !mock.failed { t.Error("JSONUnmarshalAsT should mark test as failed") } }) } func TestKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Kind(mock, reflect.String, "hello") if !result { t.Error("Kind should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Kind(mock, reflect.String, 0) if result { t.Error("Kind should return false on failure") } if !mock.failed { t.Error("Kind should mark test as failed") } }) } func TestLen(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Len(mock, []string{"A", "B"}, 2) if !result { t.Error("Len should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Len(mock, []string{"A", "B"}, 1) if result { t.Error("Len should return false on failure") } if !mock.failed { t.Error("Len should mark test as failed") } }) } func TestLess(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Less(mock, 1, 2) if !result { t.Error("Less should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Less(mock, 2, 1) if result { t.Error("Less should return false on failure") } if !mock.failed { t.Error("Less should mark test as failed") } }) } func TestLessOrEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqual(mock, 1, 2) if !result { t.Error("LessOrEqual should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqual(mock, 2, 1) if result { t.Error("LessOrEqual should return false on failure") } if !mock.failed { t.Error("LessOrEqual should mark test as failed") } }) } func TestLessOrEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqualT(mock, 1, 2) if !result { t.Error("LessOrEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqualT(mock, 2, 1) if result { t.Error("LessOrEqualT should return false on failure") } if !mock.failed { t.Error("LessOrEqualT should mark test as failed") } }) } func TestLessT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessT(mock, 1, 2) if !result { t.Error("LessT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessT(mock, 2, 1) if result { t.Error("LessT should return false on failure") } if !mock.failed { t.Error("LessT should mark test as failed") } }) } func TestMapContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapContainsT(mock, map[string]string{"A": "B"}, "A") if !result { t.Error("MapContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapContainsT(mock, map[string]string{"A": "B"}, "C") if result { t.Error("MapContainsT should return false on failure") } if !mock.failed { t.Error("MapContainsT should mark test as failed") } }) } func TestMapEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapEqualT(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) if !result { t.Error("MapEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapEqualT(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) if result { t.Error("MapEqualT should return false on failure") } if !mock.failed { t.Error("MapEqualT should mark test as failed") } }) } func TestMapNotContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotContainsT(mock, map[string]string{"A": "B"}, "C") if !result { t.Error("MapNotContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotContainsT(mock, map[string]string{"A": "B"}, "A") if result { t.Error("MapNotContainsT should return false on failure") } if !mock.failed { t.Error("MapNotContainsT should mark test as failed") } }) } func TestMapNotEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotEqualT(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) if !result { t.Error("MapNotEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotEqualT(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) if result { t.Error("MapNotEqualT should return false on failure") } if !mock.failed { t.Error("MapNotEqualT should mark test as failed") } }) } func TestNegative(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Negative(mock, -1) if !result { t.Error("Negative should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Negative(mock, 1) if result { t.Error("Negative should return false on failure") } if !mock.failed { t.Error("Negative should mark test as failed") } }) } func TestNegativeT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NegativeT(mock, -1) if !result { t.Error("NegativeT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NegativeT(mock, 1) if result { t.Error("NegativeT should return false on failure") } if !mock.failed { t.Error("NegativeT should mark test as failed") } }) } func TestNever(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Never(mock, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) if !result { t.Error("Never should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Never(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) if result { t.Error("Never should return false on failure") } if !mock.failed { t.Error("Never should mark test as failed") } }) } func TestNil(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Nil(mock, nil) if !result { t.Error("Nil should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Nil(mock, "not nil") if result { t.Error("Nil should return false on failure") } if !mock.failed { t.Error("Nil should mark test as failed") } }) } func TestNoError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoError(mock, nil) if !result { t.Error("NoError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoError(mock, ErrTest) if result { t.Error("NoError should return false on failure") } if !mock.failed { t.Error("NoError should mark test as failed") } }) } func TestNoFileDescriptorLeak(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoFileDescriptorLeak(mock, func() {}) if !result { t.Error("NoFileDescriptorLeak should return true on success") } }) } func TestNoGoRoutineLeak(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoGoRoutineLeak(mock, func() {}) if !result { t.Error("NoGoRoutineLeak should return true on success") } }) } func TestNotBlocked(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlocked(mock, sendChanMessage()) if !result { t.Error("NotBlocked should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlocked(mock, make(chan struct{})) if result { t.Error("NotBlocked should return false on failure") } if !mock.failed { t.Error("NotBlocked should mark test as failed") } }) } func TestNotBlockedT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlockedT(mock, sendChanMessage()) if !result { t.Error("NotBlockedT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlockedT(mock, make(chan struct{})) if result { t.Error("NotBlockedT should return false on failure") } if !mock.failed { t.Error("NotBlockedT should mark test as failed") } }) } func TestNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotContains(mock, []string{"A", "B"}, "C") if !result { t.Error("NotContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotContains(mock, []string{"A", "B"}, "B") if result { t.Error("NotContains should return false on failure") } if !mock.failed { t.Error("NotContains should mark test as failed") } }) } func TestNotElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatch(mock, []int{1, 2, 3}, []int{1, 2, 4}) if !result { t.Error("NotElementsMatch should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatch(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) if result { t.Error("NotElementsMatch should return false on failure") } if !mock.failed { t.Error("NotElementsMatch should mark test as failed") } }) } func TestNotElementsMatchT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatchT(mock, []int{1, 2, 3}, []int{1, 2, 4}) if !result { t.Error("NotElementsMatchT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatchT(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) if result { t.Error("NotElementsMatchT should return false on failure") } if !mock.failed { t.Error("NotElementsMatchT should mark test as failed") } }) } func TestNotEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEmpty(mock, "not empty") if !result { t.Error("NotEmpty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEmpty(mock, "") if result { t.Error("NotEmpty should return false on failure") } if !mock.failed { t.Error("NotEmpty should mark test as failed") } }) } func TestNotEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqual(mock, 123, 456) if !result { t.Error("NotEqual should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqual(mock, 123, 123) if result { t.Error("NotEqual should return false on failure") } if !mock.failed { t.Error("NotEqual should mark test as failed") } }) } func TestNotEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualT(mock, 123, 456) if !result { t.Error("NotEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualT(mock, 123, 123) if result { t.Error("NotEqualT should return false on failure") } if !mock.failed { t.Error("NotEqualT should mark test as failed") } }) } func TestNotEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualValues(mock, uint32(123), int32(456)) if !result { t.Error("NotEqualValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualValues(mock, uint32(123), int32(123)) if result { t.Error("NotEqualValues should return false on failure") } if !mock.failed { t.Error("NotEqualValues should mark test as failed") } }) } func TestNotErrorAs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorAs(mock, ErrTest, new(*dummyError)) if !result { t.Error("NotErrorAs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorAs(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) if result { t.Error("NotErrorAs should return false on failure") } if !mock.failed { t.Error("NotErrorAs should mark test as failed") } }) } func TestNotErrorIs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorIs(mock, ErrTest, io.EOF) if !result { t.Error("NotErrorIs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorIs(mock, fmt.Errorf("wrap: %w", io.EOF), io.EOF) if result { t.Error("NotErrorIs should return false on failure") } if !mock.failed { t.Error("NotErrorIs should mark test as failed") } }) } func TestNotImplements(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotImplements(mock, (*error)(nil), new(testing.T)) if !result { t.Error("NotImplements should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotImplements(mock, ptr(dummyInterface), new(testing.T)) if result { t.Error("NotImplements should return false on failure") } if !mock.failed { t.Error("NotImplements should mark test as failed") } }) } func TestNotKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotKind(mock, reflect.String, 0) if !result { t.Error("NotKind should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotKind(mock, reflect.String, "hello") if result { t.Error("NotKind should return false on failure") } if !mock.failed { t.Error("NotKind should mark test as failed") } }) } func TestNotNil(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotNil(mock, "not nil") if !result { t.Error("NotNil should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotNil(mock, nil) if result { t.Error("NotNil should return false on failure") } if !mock.failed { t.Error("NotNil should mark test as failed") } }) } func TestNotPanics(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotPanics(mock, func() {}) if !result { t.Error("NotPanics should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotPanics(mock, func() { panic("panicking") }) if result { t.Error("NotPanics should return false on failure") } if !mock.failed { t.Error("NotPanics should mark test as failed") } }) } func TestNotRegexp(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexp(mock, "^start", "not starting") if !result { t.Error("NotRegexp should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexp(mock, "^start", "starting") if result { t.Error("NotRegexp should return false on failure") } if !mock.failed { t.Error("NotRegexp should mark test as failed") } }) } func TestNotRegexpT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexpT(mock, "^start", "not starting") if !result { t.Error("NotRegexpT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexpT(mock, "^start", "starting") if result { t.Error("NotRegexpT should return false on failure") } if !mock.failed { t.Error("NotRegexpT should mark test as failed") } }) } func TestNotSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSame(mock, &staticVar, ptr("static string")) if !result { t.Error("NotSame should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSame(mock, &staticVar, staticVarPtr) if result { t.Error("NotSame should return false on failure") } if !mock.failed { t.Error("NotSame should mark test as failed") } }) } func TestNotSameT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSameT(mock, &staticVar, ptr("static string")) if !result { t.Error("NotSameT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSameT(mock, &staticVar, staticVarPtr) if result { t.Error("NotSameT should return false on failure") } if !mock.failed { t.Error("NotSameT should mark test as failed") } }) } func TestNotSortedT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSortedT(mock, []int{3, 1, 3}) if !result { t.Error("NotSortedT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSortedT(mock, []int{1, 4, 8}) if result { t.Error("NotSortedT should return false on failure") } if !mock.failed { t.Error("NotSortedT should mark test as failed") } }) } func TestNotSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSubset(mock, []int{1, 2, 3}, []int{4, 5}) if !result { t.Error("NotSubset should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSubset(mock, []int{1, 2, 3}, []int{1, 2}) if result { t.Error("NotSubset should return false on failure") } if !mock.failed { t.Error("NotSubset should mark test as failed") } }) } func TestNotZero(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotZero(mock, 1) if !result { t.Error("NotZero should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotZero(mock, 0) if result { t.Error("NotZero should return false on failure") } if !mock.failed { t.Error("NotZero should mark test as failed") } }) } func TestPanics(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(mock, func() { panic("panicking") }) if !result { t.Error("Panics should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(mock, func() {}) if result { t.Error("Panics should return false on failure") } if !mock.failed { t.Error("Panics should mark test as failed") } }) } func TestPanicsWithError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithError(mock, ErrTest.Error(), func() { panic(ErrTest) }) if !result { t.Error("PanicsWithError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithError(mock, ErrTest.Error(), func() {}) if result { t.Error("PanicsWithError should return false on failure") } if !mock.failed { t.Error("PanicsWithError should mark test as failed") } }) } func TestPanicsWithValue(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithValue(mock, "panicking", func() { panic("panicking") }) if !result { t.Error("PanicsWithValue should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithValue(mock, "panicking", func() {}) if result { t.Error("PanicsWithValue should return false on failure") } if !mock.failed { t.Error("PanicsWithValue should mark test as failed") } }) } func TestPositive(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Positive(mock, 1) if !result { t.Error("Positive should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Positive(mock, -1) if result { t.Error("Positive should return false on failure") } if !mock.failed { t.Error("Positive should mark test as failed") } }) } func TestPositiveT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PositiveT(mock, 1) if !result { t.Error("PositiveT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PositiveT(mock, -1) if result { t.Error("PositiveT should return false on failure") } if !mock.failed { t.Error("PositiveT should mark test as failed") } }) } func TestRegexp(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Regexp(mock, "^start", "starting") if !result { t.Error("Regexp should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Regexp(mock, "^start", "not starting") if result { t.Error("Regexp should return false on failure") } if !mock.failed { t.Error("Regexp should mark test as failed") } }) } func TestRegexpT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := RegexpT(mock, "^start", "starting") if !result { t.Error("RegexpT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := RegexpT(mock, "^start", "not starting") if result { t.Error("RegexpT should return false on failure") } if !mock.failed { t.Error("RegexpT should mark test as failed") } }) } func TestSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Same(mock, &staticVar, staticVarPtr) if !result { t.Error("Same should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Same(mock, &staticVar, ptr("static string")) if result { t.Error("Same should return false on failure") } if !mock.failed { t.Error("Same should mark test as failed") } }) } func TestSameT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SameT(mock, &staticVar, staticVarPtr) if !result { t.Error("SameT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SameT(mock, &staticVar, ptr("static string")) if result { t.Error("SameT should return false on failure") } if !mock.failed { t.Error("SameT should mark test as failed") } }) } func TestSeqContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqContainsT(mock, slices.Values([]string{"A", "B"}), "A") if !result { t.Error("SeqContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqContainsT(mock, slices.Values([]string{"A", "B"}), "C") if result { t.Error("SeqContainsT should return false on failure") } if !mock.failed { t.Error("SeqContainsT should mark test as failed") } }) } func TestSeqNotContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqNotContainsT(mock, slices.Values([]string{"A", "B"}), "C") if !result { t.Error("SeqNotContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqNotContainsT(mock, slices.Values([]string{"A", "B"}), "A") if result { t.Error("SeqNotContainsT should return false on failure") } if !mock.failed { t.Error("SeqNotContainsT should mark test as failed") } }) } func TestSliceContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceContainsT(mock, []string{"A", "B"}, "A") if !result { t.Error("SliceContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceContainsT(mock, []string{"A", "B"}, "C") if result { t.Error("SliceContainsT should return false on failure") } if !mock.failed { t.Error("SliceContainsT should mark test as failed") } }) } func TestSliceEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceEqualT(mock, []string{"Hello", "World"}, []string{"Hello", "World"}) if !result { t.Error("SliceEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceEqualT(mock, []string{"Hello", "World"}, []string{"Hello"}) if result { t.Error("SliceEqualT should return false on failure") } if !mock.failed { t.Error("SliceEqualT should mark test as failed") } }) } func TestSliceNotContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotContainsT(mock, []string{"A", "B"}, "C") if !result { t.Error("SliceNotContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotContainsT(mock, []string{"A", "B"}, "A") if result { t.Error("SliceNotContainsT should return false on failure") } if !mock.failed { t.Error("SliceNotContainsT should mark test as failed") } }) } func TestSliceNotEqualT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotEqualT(mock, []string{"Hello", "World"}, []string{"Hello"}) if !result { t.Error("SliceNotEqualT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotEqualT(mock, []string{"Hello", "World"}, []string{"Hello", "World"}) if result { t.Error("SliceNotEqualT should return false on failure") } if !mock.failed { t.Error("SliceNotEqualT should mark test as failed") } }) } func TestSliceNotSubsetT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotSubsetT(mock, []int{1, 2, 3}, []int{4, 5}) if !result { t.Error("SliceNotSubsetT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotSubsetT(mock, []int{1, 2, 3}, []int{1, 2}) if result { t.Error("SliceNotSubsetT should return false on failure") } if !mock.failed { t.Error("SliceNotSubsetT should mark test as failed") } }) } func TestSliceSubsetT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceSubsetT(mock, []int{1, 2, 3}, []int{1, 2}) if !result { t.Error("SliceSubsetT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceSubsetT(mock, []int{1, 2, 3}, []int{4, 5}) if result { t.Error("SliceSubsetT should return false on failure") } if !mock.failed { t.Error("SliceSubsetT should mark test as failed") } }) } func TestSortedT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SortedT(mock, []int{1, 1, 3}) if !result { t.Error("SortedT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SortedT(mock, []int{1, 4, 2}) if result { t.Error("SortedT should return false on failure") } if !mock.failed { t.Error("SortedT should mark test as failed") } }) } func TestStringContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringContainsT(mock, "AB", "A") if !result { t.Error("StringContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringContainsT(mock, "AB", "C") if result { t.Error("StringContainsT should return false on failure") } if !mock.failed { t.Error("StringContainsT should mark test as failed") } }) } func TestStringNotContainsT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringNotContainsT(mock, "AB", "C") if !result { t.Error("StringNotContainsT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringNotContainsT(mock, "AB", "A") if result { t.Error("StringNotContainsT should return false on failure") } if !mock.failed { t.Error("StringNotContainsT should mark test as failed") } }) } func TestSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Subset(mock, []int{1, 2, 3}, []int{1, 2}) if !result { t.Error("Subset should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Subset(mock, []int{1, 2, 3}, []int{4, 5}) if result { t.Error("Subset should return false on failure") } if !mock.failed { t.Error("Subset should mark test as failed") } }) } func TestTrue(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := True(mock, 1 == 1) if !result { t.Error("True should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := True(mock, 1 == 0) if result { t.Error("True should return false on failure") } if !mock.failed { t.Error("True should mark test as failed") } }) } func TestTrueT(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := TrueT(mock, 1 == 1) if !result { t.Error("TrueT should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := TrueT(mock, 1 == 0) if result { t.Error("TrueT should return false on failure") } if !mock.failed { t.Error("TrueT should mark test as failed") } }) } func TestWithinDuration(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinDuration(mock, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) if !result { t.Error("WithinDuration should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinDuration(mock, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second) if result { t.Error("WithinDuration should return false on failure") } if !mock.failed { t.Error("WithinDuration should mark test as failed") } }) } func TestWithinRange(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinRange(mock, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) if !result { t.Error("WithinRange should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinRange(mock, time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) if result { t.Error("WithinRange should return false on failure") } if !mock.failed { t.Error("WithinRange should mark test as failed") } }) } func TestYAMLEq(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLEq(mock, "key: value", "key: value") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLEq should return true on panic") } if mock.failed { t.Error("YAMLEq should panic as expected") } }) } func TestYAMLEqBytes(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLEqBytes(mock, []byte("key: value"), []byte("key: value")) }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLEqBytes should return true on panic") } if mock.failed { t.Error("YAMLEqBytes should panic as expected") } }) } func TestYAMLEqT(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLEqT(mock, "key: value", "key: value") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLEqT should return true on panic") } if mock.failed { t.Error("YAMLEqT should panic as expected") } }) } func TestYAMLMarshalAsT(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLMarshalAsT(mock, "key: value", "key: value") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLMarshalAsT should return true on panic") } if mock.failed { t.Error("YAMLMarshalAsT should panic as expected") } }) } func TestYAMLUnmarshalAsT(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLUnmarshalAsT(mock, "key: value", "key: value") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLUnmarshalAsT should return true on panic") } if mock.failed { t.Error("YAMLUnmarshalAsT should panic as expected") } }) } func TestZero(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Zero(mock, 0) if !result { t.Error("Zero should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Zero(mock, 1) if result { t.Error("Zero should return false on failure") } if !mock.failed { t.Error("Zero should mark test as failed") } }) } // mockT is a mock testing.T for assertion tests type mockT struct { failed bool } func (m *mockT) Helper() {} func (m *mockT) Errorf(format string, args ...any) { m.failed = true } type mockFailNowT struct { failed bool } // Helper is like [testing.T.Helper] but does nothing. func (mockFailNowT) Helper() {} func (m *mockFailNowT) Errorf(format string, args ...any) { _ = format _ = args } func (m *mockFailNowT) FailNow() { m.failed = true } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } func httpError(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) } func httpRedirect(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusTemporaryRedirect) } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar dummyInterface T ) func ptr[T any](value T) *T { p := value return &p } type dummyStruct struct { A string b int } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } var dummyChan = make(chan struct{}) type myType float64 go-openapi-testify-c10ca71/assert/assert_examples_test.go000066400000000000000000001044611520301377500237350ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert_test import ( "fmt" "io" "net/http" "net/url" "path/filepath" "reflect" "runtime" "slices" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func ExampleBlocked() { t := new(testing.T) // should come from testing, e.g. func TestBlocked(t *testing.T) success := assert.Blocked(t, make(chan struct{})) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleBlockedT() { t := new(testing.T) // should come from testing, e.g. func TestBlockedT(t *testing.T) success := assert.BlockedT(t, make(chan struct{})) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleCondition() { t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T) success := assert.Condition(t, func() bool { return true }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleConsistently() { t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T) success := assert.Consistently(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleContains() { t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T) success := assert.Contains(t, []string{"A", "B"}, "A") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleDirExists() { t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T) success := assert.DirExists(t, filepath.Join(testDataPath(), "existing_dir")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleDirNotExists() { t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T) success := assert.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleElementsMatch() { t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T) success := assert.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleElementsMatchT() { t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T) success := assert.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEmpty() { t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T) success := assert.Empty(t, "") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEqual() { t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T) success := assert.Equal(t, 123, 123) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEqualError() { t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T) success := assert.EqualError(t, assert.ErrTest, "assert.ErrTest general error for testing") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEqualExportedValues() { t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T) success := assert.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEqualT() { t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T) success := assert.EqualT(t, 123, 123) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEqualValues() { t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T) success := assert.EqualValues(t, uint32(123), int32(123)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleError() { t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T) success := assert.Error(t, assert.ErrTest) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleErrorAs() { t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T) success := assert.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleErrorContains() { t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) success := assert.ErrorContains(t, assert.ErrTest, "general error") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleErrorIs() { t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T) success := assert.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEventually() { t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T) success := assert.Eventually(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleEventuallyWith() { t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T) success := assert.EventuallyWith(t, func(c *assert.CollectT) { assert.True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleExactly() { t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T) success := assert.Exactly(t, int32(123), int32(123)) fmt.Printf("success: %t\n", success) // Output: success: true } // func ExampleFail() { // no success example available. Please add some examples to produce a testable example. // } // func ExampleFailNow() { // no success example available. Please add some examples to produce a testable example. // } func ExampleFalse() { t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T) success := assert.False(t, 1 == 0) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleFalseT() { t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T) success := assert.FalseT(t, 1 == 0) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleFileEmpty() { t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T) success := assert.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleFileExists() { t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T) success := assert.FileExists(t, filepath.Join(testDataPath(), "existing_file")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleFileNotEmpty() { t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T) success := assert.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleFileNotExists() { t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T) success := assert.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleGreater() { t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T) success := assert.Greater(t, 2, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleGreaterOrEqual() { t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T) success := assert.GreaterOrEqual(t, 2, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleGreaterOrEqualT() { t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T) success := assert.GreaterOrEqualT(t, 2, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleGreaterT() { t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T) success := assert.GreaterT(t, 2, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleHTTPBodyContains() { t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T) success := assert.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleHTTPBodyNotContains() { t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T) success := assert.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleHTTPError() { t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T) success := assert.HTTPError(t, httpError, "GET", "/", nil) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleHTTPRedirect() { t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T) success := assert.HTTPRedirect(t, httpRedirect, "GET", "/", nil) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleHTTPStatusCode() { t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T) success := assert.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleHTTPSuccess() { t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T) success := assert.HTTPSuccess(t, httpOK, "GET", "/", nil) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleImplements() { t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T) success := assert.Implements(t, ptr(dummyInterface), new(testing.T)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInDelta() { t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T) success := assert.InDelta(t, 1.0, 1.01, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInDeltaMapValues() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T) success := assert.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInDeltaSlice() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T) success := assert.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInDeltaT() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T) success := assert.InDeltaT(t, 1.0, 1.01, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInEpsilon() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T) success := assert.InEpsilon(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInEpsilonSlice() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T) success := assert.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInEpsilonSymmetric() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSymmetric(t *testing.T) success := assert.InEpsilonSymmetric(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInEpsilonSymmetricT() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSymmetricT(t *testing.T) success := assert.InEpsilonSymmetricT(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleInEpsilonT() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T) success := assert.InEpsilonT(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsDecreasing() { t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T) success := assert.IsDecreasing(t, []int{3, 2, 1}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsDecreasingT() { t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T) success := assert.IsDecreasingT(t, []int{3, 2, 1}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsIncreasing() { t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T) success := assert.IsIncreasing(t, []int{1, 2, 3}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsIncreasingT() { t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T) success := assert.IsIncreasingT(t, []int{1, 2, 3}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsNonDecreasing() { t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T) success := assert.IsNonDecreasing(t, []int{1, 1, 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsNonDecreasingT() { t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T) success := assert.IsNonDecreasingT(t, []int{1, 1, 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsNonIncreasing() { t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T) success := assert.IsNonIncreasing(t, []int{2, 1, 1}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsNonIncreasingT() { t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T) success := assert.IsNonIncreasingT(t, []int{2, 1, 1}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsNotOfTypeT() { t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T) success := assert.IsNotOfTypeT[myType](t, 123.123) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsNotType() { t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T) success := assert.IsNotType(t, int32(123), int64(456)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsOfTypeT() { t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T) success := assert.IsOfTypeT[myType](t, myType(123.123)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleIsType() { t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T) success := assert.IsType(t, 123, 456) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleJSONEq() { t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T) success := assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleJSONEqBytes() { t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T) success := assert.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleJSONEqT() { t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T) success := assert.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleJSONMarshalAsT() { t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T) success := assert.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleJSONUnmarshalAsT() { t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T) success := assert.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleKind() { t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T) success := assert.Kind(t, reflect.String, "hello") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleLen() { t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T) success := assert.Len(t, []string{"A", "B"}, 2) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleLess() { t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T) success := assert.Less(t, 1, 2) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleLessOrEqual() { t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T) success := assert.LessOrEqual(t, 1, 2) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleLessOrEqualT() { t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T) success := assert.LessOrEqualT(t, 1, 2) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleLessT() { t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T) success := assert.LessT(t, 1, 2) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleMapContainsT() { t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T) success := assert.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleMapEqualT() { t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) success := assert.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleMapNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) success := assert.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleMapNotEqualT() { t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) success := assert.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNegative() { t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) success := assert.Negative(t, -1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNegativeT() { t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T) success := assert.NegativeT(t, -1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNever() { t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T) success := assert.Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNil() { t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T) success := assert.Nil(t, nil) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNoError() { t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T) success := assert.NoError(t, nil) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNoFileDescriptorLeak() { if runtime.GOOS != "linux" { // This example is only runnable on linux. On other platforms, the assertion skips the test. // We force the expected output below, so that tests don't fail on other platforms. fmt.Println("success: true") return } t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T) success := assert.NoFileDescriptorLeak(t, func() { }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNoGoRoutineLeak() { t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T) success := assert.NoGoRoutineLeak(t, func() { }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotBlocked() { t := new(testing.T) // should come from testing, e.g. func TestNotBlocked(t *testing.T) success := assert.NotBlocked(t, sendChanMessage()) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotBlockedT() { t := new(testing.T) // should come from testing, e.g. func TestNotBlockedT(t *testing.T) success := assert.NotBlockedT(t, sendChanMessage()) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotContains() { t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T) success := assert.NotContains(t, []string{"A", "B"}, "C") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotElementsMatch() { t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T) success := assert.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotElementsMatchT() { t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T) success := assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotEmpty() { t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T) success := assert.NotEmpty(t, "not empty") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotEqual() { t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T) success := assert.NotEqual(t, 123, 456) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotEqualT() { t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T) success := assert.NotEqualT(t, 123, 456) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotEqualValues() { t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T) success := assert.NotEqualValues(t, uint32(123), int32(456)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotErrorAs() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T) success := assert.NotErrorAs(t, assert.ErrTest, new(*dummyError)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotErrorIs() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) success := assert.NotErrorIs(t, assert.ErrTest, io.EOF) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotImplements() { t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T) success := assert.NotImplements(t, (*error)(nil), new(testing.T)) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotKind() { t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T) success := assert.NotKind(t, reflect.String, 0) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotNil() { t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T) success := assert.NotNil(t, "not nil") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotPanics() { t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T) success := assert.NotPanics(t, func() { }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotRegexp() { t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T) success := assert.NotRegexp(t, "^start", "not starting") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotRegexpT() { t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T) success := assert.NotRegexpT(t, "^start", "not starting") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotSame() { t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T) success := assert.NotSame(t, &staticVar, ptr("static string")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotSameT() { t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T) success := assert.NotSameT(t, &staticVar, ptr("static string")) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotSortedT() { t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T) success := assert.NotSortedT(t, []int{3, 1, 3}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotSubset() { t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T) success := assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleNotZero() { t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T) success := assert.NotZero(t, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExamplePanics() { t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T) success := assert.Panics(t, func() { panic("panicking") }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExamplePanicsWithError() { t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T) success := assert.PanicsWithError(t, assert.ErrTest.Error(), func() { panic(assert.ErrTest) }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExamplePanicsWithValue() { t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T) success := assert.PanicsWithValue(t, "panicking", func() { panic("panicking") }) fmt.Printf("success: %t\n", success) // Output: success: true } func ExamplePositive() { t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T) success := assert.Positive(t, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExamplePositiveT() { t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T) success := assert.PositiveT(t, 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleRegexp() { t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T) success := assert.Regexp(t, "^start", "starting") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleRegexpT() { t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T) success := assert.RegexpT(t, "^start", "starting") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSame() { t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T) success := assert.Same(t, &staticVar, staticVarPtr) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSameT() { t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T) success := assert.SameT(t, &staticVar, staticVarPtr) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSeqContainsT() { t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T) success := assert.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSeqNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T) success := assert.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSliceContainsT() { t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T) success := assert.SliceContainsT(t, []string{"A", "B"}, "A") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSliceEqualT() { t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) success := assert.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSliceNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) success := assert.SliceNotContainsT(t, []string{"A", "B"}, "C") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSliceNotEqualT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) success := assert.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSliceNotSubsetT() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) success := assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSliceSubsetT() { t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T) success := assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSortedT() { t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T) success := assert.SortedT(t, []int{1, 1, 3}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleStringContainsT() { t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T) success := assert.StringContainsT(t, "AB", "A") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleStringNotContainsT() { t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T) success := assert.StringNotContainsT(t, "AB", "C") fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleSubset() { t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T) success := assert.Subset(t, []int{1, 2, 3}, []int{1, 2}) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleTrue() { t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T) success := assert.True(t, 1 == 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleTrueT() { t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T) success := assert.TrueT(t, 1 == 1) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleWithinDuration() { t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T) success := assert.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) fmt.Printf("success: %t\n", success) // Output: success: true } func ExampleWithinRange() { t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T) success := assert.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) fmt.Printf("success: %t\n", success) // Output: success: true } // func ExampleYAMLEq() { // no success example available. Please add some examples to produce a testable example. // } // func ExampleYAMLEqBytes() { // no success example available. Please add some examples to produce a testable example. // } // func ExampleYAMLEqT() { // no success example available. Please add some examples to produce a testable example. // } // func ExampleYAMLMarshalAsT() { // no success example available. Please add some examples to produce a testable example. // } // func ExampleYAMLUnmarshalAsT() { // no success example available. Please add some examples to produce a testable example. // } func ExampleZero() { t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T) success := assert.Zero(t, 0) fmt.Printf("success: %t\n", success) // Output: success: true } // Test helpers (also in the tests for package assert. // // This code is duplicated because the current test is run as a separate test package: assert_test. func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } func httpError(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) } func httpRedirect(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusTemporaryRedirect) } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar dummyInterface assert.T ) func ptr[T any](value T) *T { p := value return &p } type dummyStruct struct { A string b int } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } var dummyChan = make(chan struct{}) type myType float64 go-openapi-testify-c10ca71/assert/assert_format.go000066400000000000000000001547131520301377500223550ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "iter" "net/http" "net/url" "reflect" "time" "github.com/go-openapi/testify/v2/internal/assertions" ) // Blockedf is the same as [Blocked], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Blockedf(t T, ch any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Blocked(t, ch, forwardArgs(msg, args)...) } // BlockedTf is the same as [BlockedT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func BlockedTf[E any, CHAN ~chan E](t T, ch CHAN, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.BlockedT[E, CHAN](t, ch, forwardArgs(msg, args)...) } // Conditionf is the same as [Condition], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Conditionf(t T, comp func() bool, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Condition(t, comp, forwardArgs(msg, args)...) } // Consistentlyf is the same as [Consistently], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Consistentlyf[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Consistently[C](t, condition, timeout, tick, forwardArgs(msg, args)...) } // Containsf is the same as [Contains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Containsf(t T, s any, contains any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Contains(t, s, contains, forwardArgs(msg, args)...) } // DirExistsf is the same as [DirExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func DirExistsf(t T, path string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.DirExists(t, path, forwardArgs(msg, args)...) } // DirNotExistsf is the same as [DirNotExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func DirNotExistsf(t T, path string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.DirNotExists(t, path, forwardArgs(msg, args)...) } // ElementsMatchf is the same as [ElementsMatch], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func ElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.ElementsMatch(t, listA, listB, forwardArgs(msg, args)...) } // ElementsMatchTf is the same as [ElementsMatchT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ElementsMatchT[E](t, listA, listB, forwardArgs(msg, args)...) } // Emptyf is the same as [Empty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Emptyf(t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Empty(t, object, forwardArgs(msg, args)...) } // Equalf is the same as [Equal], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Equalf(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Equal(t, expected, actual, forwardArgs(msg, args)...) } // EqualErrorf is the same as [EqualError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func EqualErrorf(t T, err error, errString string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualError(t, err, errString, forwardArgs(msg, args)...) } // EqualExportedValuesf is the same as [EqualExportedValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualExportedValues(t, expected, actual, forwardArgs(msg, args)...) } // EqualTf is the same as [EqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualT[V](t, expected, actual, forwardArgs(msg, args)...) } // EqualValuesf is the same as [EqualValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func EqualValuesf(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EqualValues(t, expected, actual, forwardArgs(msg, args)...) } // Errorf is the same as [Error], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Errorf(t T, err error, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Error(t, err, forwardArgs(msg, args)...) } // ErrorAsf is the same as [ErrorAs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func ErrorAsf(t T, err error, target any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ErrorAs(t, err, target, forwardArgs(msg, args)...) } // ErrorContainsf is the same as [ErrorContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func ErrorContainsf(t T, err error, contains string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ErrorContains(t, err, contains, forwardArgs(msg, args)...) } // ErrorIsf is the same as [ErrorIs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func ErrorIsf(t T, err error, target error, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.ErrorIs(t, err, target, forwardArgs(msg, args)...) } // Eventuallyf is the same as [Eventually], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Eventuallyf[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Eventually[C](t, condition, timeout, tick, forwardArgs(msg, args)...) } // EventuallyWithf is the same as [EventuallyWith], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func EventuallyWithf[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.EventuallyWith[C](t, condition, timeout, tick, forwardArgs(msg, args)...) } // Exactlyf is the same as [Exactly], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Exactlyf(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Exactly(t, expected, actual, forwardArgs(msg, args)...) } // Failf is the same as [Fail], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Failf(t T, failureMessage string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Fail(t, failureMessage, forwardArgs(msg, args)...) } // FailNowf is the same as [FailNow], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func FailNowf(t T, failureMessage string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FailNow(t, failureMessage, forwardArgs(msg, args)...) } // Falsef is the same as [False], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Falsef(t T, value bool, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.False(t, value, forwardArgs(msg, args)...) } // FalseTf is the same as [FalseT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func FalseTf[B Boolean](t T, value B, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FalseT[B](t, value, forwardArgs(msg, args)...) } // FileEmptyf is the same as [FileEmpty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func FileEmptyf(t T, path string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileEmpty(t, path, forwardArgs(msg, args)...) } // FileExistsf is the same as [FileExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func FileExistsf(t T, path string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileExists(t, path, forwardArgs(msg, args)...) } // FileNotEmptyf is the same as [FileNotEmpty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func FileNotEmptyf(t T, path string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileNotEmpty(t, path, forwardArgs(msg, args)...) } // FileNotExistsf is the same as [FileNotExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func FileNotExistsf(t T, path string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.FileNotExists(t, path, forwardArgs(msg, args)...) } // Greaterf is the same as [Greater], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Greaterf(t T, e1 any, e2 any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Greater(t, e1, e2, forwardArgs(msg, args)...) } // GreaterOrEqualf is the same as [GreaterOrEqual], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.GreaterOrEqual(t, e1, e2, forwardArgs(msg, args)...) } // GreaterOrEqualTf is the same as [GreaterOrEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.GreaterOrEqualT[Orderable](t, e1, e2, forwardArgs(msg, args)...) } // GreaterTf is the same as [GreaterT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.GreaterT[Orderable](t, e1, e2, forwardArgs(msg, args)...) } // HTTPBodyContainsf is the same as [HTTPBodyContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPBodyContains(t, handler, method, url, values, str, forwardArgs(msg, args)...) } // HTTPBodyNotContainsf is the same as [HTTPBodyNotContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPBodyNotContains(t, handler, method, url, values, str, forwardArgs(msg, args)...) } // HTTPErrorf is the same as [HTTPError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPError(t, handler, method, url, values, forwardArgs(msg, args)...) } // HTTPRedirectf is the same as [HTTPRedirect], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPRedirect(t, handler, method, url, values, forwardArgs(msg, args)...) } // HTTPStatusCodef is the same as [HTTPStatusCode], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPStatusCode(t, handler, method, url, values, statuscode, forwardArgs(msg, args)...) } // HTTPSuccessf is the same as [HTTPSuccess], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.HTTPSuccess(t, handler, method, url, values, forwardArgs(msg, args)...) } // Implementsf is the same as [Implements], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Implementsf(t T, interfaceObject any, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Implements(t, interfaceObject, object, forwardArgs(msg, args)...) } // InDeltaf is the same as [InDelta], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaf(t T, expected any, actual any, delta float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDelta(t, expected, actual, delta, forwardArgs(msg, args)...) } // InDeltaMapValuesf is the same as [InDeltaMapValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDeltaMapValues(t, expected, actual, delta, forwardArgs(msg, args)...) } // InDeltaSlicef is the same as [InDeltaSlice], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDeltaSlice(t, expected, actual, delta, forwardArgs(msg, args)...) } // InDeltaTf is the same as [InDeltaT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InDeltaT[Number](t, expected, actual, delta, forwardArgs(msg, args)...) } // InEpsilonf is the same as [InEpsilon], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilon(t, expected, actual, epsilon, forwardArgs(msg, args)...) } // InEpsilonSlicef is the same as [InEpsilonSlice], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonSlice(t, expected, actual, epsilon, forwardArgs(msg, args)...) } // InEpsilonSymmetricf is the same as [InEpsilonSymmetric], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonSymmetricf(t T, x any, y any, epsilon float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonSymmetric(t, x, y, epsilon, forwardArgs(msg, args)...) } // InEpsilonSymmetricTf is the same as [InEpsilonSymmetricT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonSymmetricTf[Number Measurable](t T, x Number, y Number, epsilon float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonSymmetricT[Number](t, x, y, epsilon, forwardArgs(msg, args)...) } // InEpsilonTf is the same as [InEpsilonT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.InEpsilonT[Number](t, expected, actual, epsilon, forwardArgs(msg, args)...) } // IsDecreasingf is the same as [IsDecreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsDecreasingf(t T, collection any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsDecreasing(t, collection, forwardArgs(msg, args)...) } // IsDecreasingTf is the same as [IsDecreasingT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsDecreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)...) } // IsIncreasingf is the same as [IsIncreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsIncreasingf(t T, collection any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsIncreasing(t, collection, forwardArgs(msg, args)...) } // IsIncreasingTf is the same as [IsIncreasingT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsIncreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)...) } // IsNonDecreasingf is the same as [IsNonDecreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonDecreasingf(t T, collection any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonDecreasing(t, collection, forwardArgs(msg, args)...) } // IsNonDecreasingTf is the same as [IsNonDecreasingT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonDecreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)...) } // IsNonIncreasingf is the same as [IsNonIncreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonIncreasingf(t T, collection any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonIncreasing(t, collection, forwardArgs(msg, args)...) } // IsNonIncreasingTf is the same as [IsNonIncreasingT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNonIncreasingT[OrderedSlice, E](t, collection, forwardArgs(msg, args)...) } // IsNotOfTypeTf is the same as [IsNotOfTypeT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNotOfTypeT[EType](t, object, forwardArgs(msg, args)...) } // IsNotTypef is the same as [IsNotType], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsNotTypef(t T, theType any, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsNotType(t, theType, object, forwardArgs(msg, args)...) } // IsOfTypeTf is the same as [IsOfTypeT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsOfTypeTf[EType any](t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsOfTypeT[EType](t, object, forwardArgs(msg, args)...) } // IsTypef is the same as [IsType], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func IsTypef(t T, expectedType any, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.IsType(t, expectedType, object, forwardArgs(msg, args)...) } // JSONEqf is the same as [JSONEq], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func JSONEqf(t T, expected string, actual string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONEq(t, expected, actual, forwardArgs(msg, args)...) } // JSONEqBytesf is the same as [JSONEqBytes], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONEqBytes(t, expected, actual, forwardArgs(msg, args)...) } // JSONEqTf is the same as [JSONEqT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func JSONEqTf[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONEqT[EDoc, ADoc](t, expected, actual, forwardArgs(msg, args)...) } // JSONMarshalAsTf is the same as [JSONMarshalAsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func JSONMarshalAsTf[EDoc RText](t T, expected EDoc, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONMarshalAsT[EDoc](t, expected, object, forwardArgs(msg, args)...) } // JSONUnmarshalAsTf is the same as [JSONUnmarshalAsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func JSONUnmarshalAsTf[Object any, ADoc RText](t T, expected Object, jazon ADoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.JSONUnmarshalAsT[Object, ADoc](t, expected, jazon, forwardArgs(msg, args)...) } // Kindf is the same as [Kind], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Kind(t, expectedKind, object, forwardArgs(msg, args)...) } // Lenf is the same as [Len], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Lenf(t T, object any, length int, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Len(t, object, length, forwardArgs(msg, args)...) } // Lessf is the same as [Less], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Lessf(t T, e1 any, e2 any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Less(t, e1, e2, forwardArgs(msg, args)...) } // LessOrEqualf is the same as [LessOrEqual], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.LessOrEqual(t, e1, e2, forwardArgs(msg, args)...) } // LessOrEqualTf is the same as [LessOrEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.LessOrEqualT[Orderable](t, e1, e2, forwardArgs(msg, args)...) } // LessTf is the same as [LessT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.LessT[Orderable](t, e1, e2, forwardArgs(msg, args)...) } // MapContainsTf is the same as [MapContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.MapContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)...) } // MapEqualTf is the same as [MapEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.MapEqualT[K, V](t, listA, listB, forwardArgs(msg, args)...) } // MapNotContainsTf is the same as [MapNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.MapNotContainsT[Map, K, V](t, m, key, forwardArgs(msg, args)...) } // MapNotEqualTf is the same as [MapNotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.MapNotEqualT[K, V](t, listA, listB, forwardArgs(msg, args)...) } // Negativef is the same as [Negative], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Negativef(t T, e any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Negative(t, e, forwardArgs(msg, args)...) } // NegativeTf is the same as [NegativeT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NegativeT[SignedNumber](t, e, forwardArgs(msg, args)...) } // Neverf is the same as [Never], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Neverf[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Never[C](t, condition, timeout, tick, forwardArgs(msg, args)...) } // Nilf is the same as [Nil], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Nilf(t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Nil(t, object, forwardArgs(msg, args)...) } // NoErrorf is the same as [NoError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NoErrorf(t T, err error, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NoError(t, err, forwardArgs(msg, args)...) } // NoFileDescriptorLeakf is the same as [NoFileDescriptorLeak], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NoFileDescriptorLeakf(t T, tested func(), msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NoFileDescriptorLeak(t, tested, forwardArgs(msg, args)...) } // NoGoRoutineLeakf is the same as [NoGoRoutineLeak], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NoGoRoutineLeakf(t T, tested func(), msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NoGoRoutineLeak(t, tested, forwardArgs(msg, args)...) } // NotBlockedf is the same as [NotBlocked], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotBlockedf(t T, ch any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotBlocked(t, ch, forwardArgs(msg, args)...) } // NotBlockedTf is the same as [NotBlockedT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotBlockedTf[E any, CHAN ~chan E](t T, ch CHAN, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotBlockedT[E, CHAN](t, ch, forwardArgs(msg, args)...) } // NotContainsf is the same as [NotContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotContainsf(t T, s any, contains any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotContains(t, s, contains, forwardArgs(msg, args)...) } // NotElementsMatchf is the same as [NotElementsMatch], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.NotElementsMatch(t, listA, listB, forwardArgs(msg, args)...) } // NotElementsMatchTf is the same as [NotElementsMatchT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.NotElementsMatchT[E](t, listA, listB, forwardArgs(msg, args)...) } // NotEmptyf is the same as [NotEmpty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotEmptyf(t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEmpty(t, object, forwardArgs(msg, args)...) } // NotEqualf is the same as [NotEqual], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotEqualf(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEqual(t, expected, actual, forwardArgs(msg, args)...) } // NotEqualTf is the same as [NotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEqualT[V](t, expected, actual, forwardArgs(msg, args)...) } // NotEqualValuesf is the same as [NotEqualValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotEqualValues(t, expected, actual, forwardArgs(msg, args)...) } // NotErrorAsf is the same as [NotErrorAs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotErrorAsf(t T, err error, target any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotErrorAs(t, err, target, forwardArgs(msg, args)...) } // NotErrorIsf is the same as [NotErrorIs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotErrorIsf(t T, err error, target error, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotErrorIs(t, err, target, forwardArgs(msg, args)...) } // NotImplementsf is the same as [NotImplements], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotImplementsf(t T, interfaceObject any, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotImplements(t, interfaceObject, object, forwardArgs(msg, args)...) } // NotKindf is the same as [NotKind], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotKind(t, expectedKind, object, forwardArgs(msg, args)...) } // NotNilf is the same as [NotNil], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotNilf(t T, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotNil(t, object, forwardArgs(msg, args)...) } // NotPanicsf is the same as [NotPanics], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotPanicsf(t T, f func(), msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotPanics(t, f, forwardArgs(msg, args)...) } // NotRegexpf is the same as [NotRegexp], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotRegexpf(t T, rx any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotRegexp(t, rx, actual, forwardArgs(msg, args)...) } // NotRegexpTf is the same as [NotRegexpT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotRegexpT[Rex, ADoc](t, rx, actual, forwardArgs(msg, args)...) } // NotSamef is the same as [NotSame], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotSamef(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSame(t, expected, actual, forwardArgs(msg, args)...) } // NotSameTf is the same as [NotSameT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSameT[P](t, expected, actual, forwardArgs(msg, args)...) } // NotSortedTf is the same as [NotSortedT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSortedT[OrderedSlice, E](t, collection, forwardArgs(msg, args)...) } // NotSubsetf is the same as [NotSubset], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotSubsetf(t T, list any, subset any, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.NotSubset(t, list, subset, forwardArgs(msg, args)...) } // NotZerof is the same as [NotZero], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func NotZerof(t T, i any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.NotZero(t, i, forwardArgs(msg, args)...) } // Panicsf is the same as [Panics], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Panicsf(t T, f func(), msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Panics(t, f, forwardArgs(msg, args)...) } // PanicsWithErrorf is the same as [PanicsWithError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func PanicsWithErrorf(t T, errString string, f func(), msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.PanicsWithError(t, errString, f, forwardArgs(msg, args)...) } // PanicsWithValuef is the same as [PanicsWithValue], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func PanicsWithValuef(t T, expected any, f func(), msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.PanicsWithValue(t, expected, f, forwardArgs(msg, args)...) } // Positivef is the same as [Positive], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Positivef(t T, e any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Positive(t, e, forwardArgs(msg, args)...) } // PositiveTf is the same as [PositiveT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.PositiveT[SignedNumber](t, e, forwardArgs(msg, args)...) } // Regexpf is the same as [Regexp], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Regexpf(t T, rx any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Regexp(t, rx, actual, forwardArgs(msg, args)...) } // RegexpTf is the same as [RegexpT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.RegexpT[Rex, ADoc](t, rx, actual, forwardArgs(msg, args)...) } // Samef is the same as [Same], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Samef(t T, expected any, actual any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Same(t, expected, actual, forwardArgs(msg, args)...) } // SameTf is the same as [SameT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SameT[P](t, expected, actual, forwardArgs(msg, args)...) } // SeqContainsTf is the same as [SeqContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SeqContainsT[E](t, iter, element, forwardArgs(msg, args)...) } // SeqNotContainsTf is the same as [SeqNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SeqNotContainsT[E](t, iter, element, forwardArgs(msg, args)...) } // SliceContainsTf is the same as [SliceContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceContainsT[Slice, E](t, s, element, forwardArgs(msg, args)...) } // SliceEqualTf is the same as [SliceEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceEqualT[E](t, listA, listB, forwardArgs(msg, args)...) } // SliceNotContainsTf is the same as [SliceNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceNotContainsT[Slice, E](t, s, element, forwardArgs(msg, args)...) } // SliceNotEqualTf is the same as [SliceNotEqualT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceNotEqualT[E](t, listA, listB, forwardArgs(msg, args)...) } // SliceNotSubsetTf is the same as [SliceNotSubsetT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceNotSubsetT[Slice, E](t, list, subset, forwardArgs(msg, args)...) } // SliceSubsetTf is the same as [SliceSubsetT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.SliceSubsetT[Slice, E](t, list, subset, forwardArgs(msg, args)...) } // SortedTf is the same as [SortedT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.SortedT[OrderedSlice, E](t, collection, forwardArgs(msg, args)...) } // StringContainsTf is the same as [StringContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.StringContainsT[ADoc, EDoc](t, str, substring, forwardArgs(msg, args)...) } // StringNotContainsTf is the same as [StringNotContainsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.StringNotContainsT[ADoc, EDoc](t, str, substring, forwardArgs(msg, args)...) } // Subsetf is the same as [Subset], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Subsetf(t T, list any, subset any, msg string, args ...any) (ok bool) { if h, ok := t.(H); ok { h.Helper() } return assertions.Subset(t, list, subset, forwardArgs(msg, args)...) } // Truef is the same as [True], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Truef(t T, value bool, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.True(t, value, forwardArgs(msg, args)...) } // TrueTf is the same as [TrueT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func TrueTf[B Boolean](t T, value B, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.TrueT[B](t, value, forwardArgs(msg, args)...) } // WithinDurationf is the same as [WithinDuration], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.WithinDuration(t, expected, actual, delta, forwardArgs(msg, args)...) } // WithinRangef is the same as [WithinRange], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.WithinRange(t, actual, start, end, forwardArgs(msg, args)...) } // YAMLEqf is the same as [YAMLEq], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLEq(t, expected, actual, forwardArgs(msg, args)...) } // YAMLEqBytesf is the same as [YAMLEqBytes], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLEqBytes(t, expected, actual, forwardArgs(msg, args)...) } // YAMLEqTf is the same as [YAMLEqT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLEqTf[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLEqT[EDoc, ADoc](t, expected, actual, forwardArgs(msg, args)...) } // YAMLMarshalAsTf is the same as [YAMLMarshalAsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLMarshalAsTf[EDoc RText](t T, expected EDoc, object any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLMarshalAsT[EDoc](t, expected, object, forwardArgs(msg, args)...) } // YAMLUnmarshalAsTf is the same as [YAMLUnmarshalAsT], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func YAMLUnmarshalAsTf[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.YAMLUnmarshalAsT[Object, ADoc](t, expected, yamlDoc, forwardArgs(msg, args)...) } // Zerof is the same as [Zero], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func Zerof(t T, i any, msg string, args ...any) bool { if h, ok := t.(H); ok { h.Helper() } return assertions.Zero(t, i, forwardArgs(msg, args)...) } func forwardArgs(msg string, args []any) []any { result := make([]any, len(args)+1) result[0] = msg copy(result[1:], args) return result } go-openapi-testify-c10ca71/assert/assert_format_test.go000066400000000000000000002353031520301377500234070ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "fmt" "io" "net/http" "net/url" "path/filepath" "reflect" "slices" "testing" "time" ) func TestBlockedf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Blockedf(mock, make(chan struct{}), "test message") if !result { t.Error("Blockedf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Blockedf(mock, sendChanMessage(), "test message") if result { t.Error("Blockedf should return false on failure") } if !mock.failed { t.Error("Blockedf should mark test as failed") } }) } func TestBlockedTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := BlockedTf(mock, make(chan struct{}), "test message") if !result { t.Error("BlockedTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := BlockedTf(mock, sendChanMessage(), "test message") if result { t.Error("BlockedTf should return false on failure") } if !mock.failed { t.Error("BlockedTf should mark test as failed") } }) } func TestConditionf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Conditionf(mock, func() bool { return true }, "test message") if !result { t.Error("Conditionf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Conditionf(mock, func() bool { return false }, "test message") if result { t.Error("Conditionf should return false on failure") } if !mock.failed { t.Error("Conditionf should mark test as failed") } }) } func TestConsistentlyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Consistentlyf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { t.Error("Consistentlyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Consistentlyf(mock, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { t.Error("Consistentlyf should return false on failure") } if !mock.failed { t.Error("Consistentlyf should mark test as failed") } }) } func TestContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Containsf(mock, []string{"A", "B"}, "A", "test message") if !result { t.Error("Containsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Containsf(mock, []string{"A", "B"}, "C", "test message") if result { t.Error("Containsf should return false on failure") } if !mock.failed { t.Error("Containsf should mark test as failed") } }) } func TestDirExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirExistsf(mock, filepath.Join(testDataPath(), "existing_dir"), "test message") if !result { t.Error("DirExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirExistsf(mock, filepath.Join(testDataPath(), "non_existing_dir"), "test message") if result { t.Error("DirExistsf should return false on failure") } if !mock.failed { t.Error("DirExistsf should mark test as failed") } }) } func TestDirNotExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirNotExistsf(mock, filepath.Join(testDataPath(), "non_existing_dir"), "test message") if !result { t.Error("DirNotExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := DirNotExistsf(mock, filepath.Join(testDataPath(), "existing_dir"), "test message") if result { t.Error("DirNotExistsf should return false on failure") } if !mock.failed { t.Error("DirNotExistsf should mark test as failed") } }) } func TestElementsMatchf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatchf(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") if !result { t.Error("ElementsMatchf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatchf(mock, []int{1, 2, 3}, []int{1, 2, 4}, "test message") if result { t.Error("ElementsMatchf should return false on failure") } if !mock.failed { t.Error("ElementsMatchf should mark test as failed") } }) } func TestElementsMatchTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatchTf(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") if !result { t.Error("ElementsMatchTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ElementsMatchTf(mock, []int{1, 2, 3}, []int{1, 2, 4}, "test message") if result { t.Error("ElementsMatchTf should return false on failure") } if !mock.failed { t.Error("ElementsMatchTf should mark test as failed") } }) } func TestEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Emptyf(mock, "", "test message") if !result { t.Error("Emptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Emptyf(mock, "not empty", "test message") if result { t.Error("Emptyf should return false on failure") } if !mock.failed { t.Error("Emptyf should mark test as failed") } }) } func TestEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Equalf(mock, 123, 123, "test message") if !result { t.Error("Equalf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Equalf(mock, 123, 456, "test message") if result { t.Error("Equalf should return false on failure") } if !mock.failed { t.Error("Equalf should mark test as failed") } }) } func TestEqualErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualErrorf(mock, ErrTest, "assert.ErrTest general error for testing", "test message") if !result { t.Error("EqualErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualErrorf(mock, ErrTest, "wrong error message", "test message") if result { t.Error("EqualErrorf should return false on failure") } if !mock.failed { t.Error("EqualErrorf should mark test as failed") } }) } func TestEqualExportedValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualExportedValuesf(mock, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}, "test message") if !result { t.Error("EqualExportedValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualExportedValuesf(mock, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1}, "test message") if result { t.Error("EqualExportedValuesf should return false on failure") } if !mock.failed { t.Error("EqualExportedValuesf should mark test as failed") } }) } func TestEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualTf(mock, 123, 123, "test message") if !result { t.Error("EqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualTf(mock, 123, 456, "test message") if result { t.Error("EqualTf should return false on failure") } if !mock.failed { t.Error("EqualTf should mark test as failed") } }) } func TestEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualValuesf(mock, uint32(123), int32(123), "test message") if !result { t.Error("EqualValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EqualValuesf(mock, uint32(123), int32(456), "test message") if result { t.Error("EqualValuesf should return false on failure") } if !mock.failed { t.Error("EqualValuesf should mark test as failed") } }) } func TestErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Errorf(mock, ErrTest, "test message") if !result { t.Error("Errorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Errorf(mock, nil, "test message") if result { t.Error("Errorf should return false on failure") } if !mock.failed { t.Error("Errorf should mark test as failed") } }) } func TestErrorAsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorAsf(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") if !result { t.Error("ErrorAsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorAsf(mock, ErrTest, new(*dummyError), "test message") if result { t.Error("ErrorAsf should return false on failure") } if !mock.failed { t.Error("ErrorAsf should mark test as failed") } }) } func TestErrorContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorContainsf(mock, ErrTest, "general error", "test message") if !result { t.Error("ErrorContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorContainsf(mock, ErrTest, "not in message", "test message") if result { t.Error("ErrorContainsf should return false on failure") } if !mock.failed { t.Error("ErrorContainsf should mark test as failed") } }) } func TestErrorIsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorIsf(mock, fmt.Errorf("wrap: %w", io.EOF), io.EOF, "test message") if !result { t.Error("ErrorIsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := ErrorIsf(mock, ErrTest, io.EOF, "test message") if result { t.Error("ErrorIsf should return false on failure") } if !mock.failed { t.Error("ErrorIsf should mark test as failed") } }) } func TestEventuallyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Eventuallyf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { t.Error("Eventuallyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Eventuallyf(mock, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { t.Error("Eventuallyf should return false on failure") } if !mock.failed { t.Error("Eventuallyf should mark test as failed") } }) } func TestEventuallyWithf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EventuallyWithf(mock, func(c *CollectT) { True(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { t.Error("EventuallyWithf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EventuallyWithf(mock, func(c *CollectT) { False(c, true) }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { t.Error("EventuallyWithf should return false on failure") } if !mock.failed { t.Error("EventuallyWithf should mark test as failed") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := EventuallyWithf(mock, func(c *CollectT) { c.Cancel() }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { t.Error("EventuallyWithf should return false on failure") } if !mock.failed { t.Error("EventuallyWithf should mark test as failed") } }) } func TestExactlyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Exactlyf(mock, int32(123), int32(123), "test message") if !result { t.Error("Exactlyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Exactlyf(mock, int32(123), int64(123), "test message") if result { t.Error("Exactlyf should return false on failure") } if !mock.failed { t.Error("Exactlyf should mark test as failed") } }) } func TestFailf(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Failf(mock, "failed", "test message") if result { t.Error("Failf should return false on failure") } if !mock.failed { t.Error("Failf should mark test as failed") } }) } func TestFailNowf(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockFailNowT) result := FailNowf(mock, "failed", "test message") if result { t.Error("FailNowf should return false on failure") } if !mock.failed { t.Error("FailNowf should call FailNow()") } }) } func TestFalsef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Falsef(mock, 1 == 0, "test message") if !result { t.Error("Falsef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Falsef(mock, 1 == 1, "test message") if result { t.Error("Falsef should return false on failure") } if !mock.failed { t.Error("Falsef should mark test as failed") } }) } func TestFalseTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FalseTf(mock, 1 == 0, "test message") if !result { t.Error("FalseTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FalseTf(mock, 1 == 1, "test message") if result { t.Error("FalseTf should return false on failure") } if !mock.failed { t.Error("FalseTf should mark test as failed") } }) } func TestFileEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileEmptyf(mock, filepath.Join(testDataPath(), "empty_file"), "test message") if !result { t.Error("FileEmptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileEmptyf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") if result { t.Error("FileEmptyf should return false on failure") } if !mock.failed { t.Error("FileEmptyf should mark test as failed") } }) } func TestFileExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileExistsf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") if !result { t.Error("FileExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileExistsf(mock, filepath.Join(testDataPath(), "non_existing_file"), "test message") if result { t.Error("FileExistsf should return false on failure") } if !mock.failed { t.Error("FileExistsf should mark test as failed") } }) } func TestFileNotEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotEmptyf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") if !result { t.Error("FileNotEmptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotEmptyf(mock, filepath.Join(testDataPath(), "empty_file"), "test message") if result { t.Error("FileNotEmptyf should return false on failure") } if !mock.failed { t.Error("FileNotEmptyf should mark test as failed") } }) } func TestFileNotExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotExistsf(mock, filepath.Join(testDataPath(), "non_existing_file"), "test message") if !result { t.Error("FileNotExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := FileNotExistsf(mock, filepath.Join(testDataPath(), "existing_file"), "test message") if result { t.Error("FileNotExistsf should return false on failure") } if !mock.failed { t.Error("FileNotExistsf should mark test as failed") } }) } func TestGreaterf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Greaterf(mock, 2, 1, "test message") if !result { t.Error("Greaterf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Greaterf(mock, 1, 2, "test message") if result { t.Error("Greaterf should return false on failure") } if !mock.failed { t.Error("Greaterf should mark test as failed") } }) } func TestGreaterOrEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqualf(mock, 2, 1, "test message") if !result { t.Error("GreaterOrEqualf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqualf(mock, 1, 2, "test message") if result { t.Error("GreaterOrEqualf should return false on failure") } if !mock.failed { t.Error("GreaterOrEqualf should mark test as failed") } }) } func TestGreaterOrEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqualTf(mock, 2, 1, "test message") if !result { t.Error("GreaterOrEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterOrEqualTf(mock, 1, 2, "test message") if result { t.Error("GreaterOrEqualTf should return false on failure") } if !mock.failed { t.Error("GreaterOrEqualTf should mark test as failed") } }) } func TestGreaterTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterTf(mock, 2, 1, "test message") if !result { t.Error("GreaterTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := GreaterTf(mock, 1, 2, "test message") if result { t.Error("GreaterTf should return false on failure") } if !mock.failed { t.Error("GreaterTf should mark test as failed") } }) } func TestHTTPBodyContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyContainsf(mock, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!", "test message") if !result { t.Error("HTTPBodyContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyContainsf(mock, httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!", "test message") if result { t.Error("HTTPBodyContainsf should return false on failure") } if !mock.failed { t.Error("HTTPBodyContainsf should mark test as failed") } }) } func TestHTTPBodyNotContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyNotContainsf(mock, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!", "test message") if !result { t.Error("HTTPBodyNotContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPBodyNotContainsf(mock, httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!", "test message") if result { t.Error("HTTPBodyNotContainsf should return false on failure") } if !mock.failed { t.Error("HTTPBodyNotContainsf should mark test as failed") } }) } func TestHTTPErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPErrorf(mock, httpError, "GET", "/", nil, "test message") if !result { t.Error("HTTPErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPErrorf(mock, httpOK, "GET", "/", nil, "test message") if result { t.Error("HTTPErrorf should return false on failure") } if !mock.failed { t.Error("HTTPErrorf should mark test as failed") } }) } func TestHTTPRedirectf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPRedirectf(mock, httpRedirect, "GET", "/", nil, "test message") if !result { t.Error("HTTPRedirectf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPRedirectf(mock, httpError, "GET", "/", nil, "test message") if result { t.Error("HTTPRedirectf should return false on failure") } if !mock.failed { t.Error("HTTPRedirectf should mark test as failed") } }) } func TestHTTPStatusCodef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPStatusCodef(mock, httpOK, "GET", "/", nil, http.StatusOK, "test message") if !result { t.Error("HTTPStatusCodef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPStatusCodef(mock, httpError, "GET", "/", nil, http.StatusOK, "test message") if result { t.Error("HTTPStatusCodef should return false on failure") } if !mock.failed { t.Error("HTTPStatusCodef should mark test as failed") } }) } func TestHTTPSuccessf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPSuccessf(mock, httpOK, "GET", "/", nil, "test message") if !result { t.Error("HTTPSuccessf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := HTTPSuccessf(mock, httpError, "GET", "/", nil, "test message") if result { t.Error("HTTPSuccessf should return false on failure") } if !mock.failed { t.Error("HTTPSuccessf should mark test as failed") } }) } func TestImplementsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Implementsf(mock, ptr(dummyInterface), new(testing.T), "test message") if !result { t.Error("Implementsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Implementsf(mock, (*error)(nil), new(testing.T), "test message") if result { t.Error("Implementsf should return false on failure") } if !mock.failed { t.Error("Implementsf should mark test as failed") } }) } func TestInDeltaf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaf(mock, 1.0, 1.01, 0.02, "test message") if !result { t.Error("InDeltaf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaf(mock, 1.0, 1.1, 0.05, "test message") if result { t.Error("InDeltaf should return false on failure") } if !mock.failed { t.Error("InDeltaf should mark test as failed") } }) } func TestInDeltaMapValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaMapValuesf(mock, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02, "test message") if !result { t.Error("InDeltaMapValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaMapValuesf(mock, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.1}, 0.05, "test message") if result { t.Error("InDeltaMapValuesf should return false on failure") } if !mock.failed { t.Error("InDeltaMapValuesf should mark test as failed") } }) } func TestInDeltaSlicef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaSlicef(mock, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02, "test message") if !result { t.Error("InDeltaSlicef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaSlicef(mock, []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05, "test message") if result { t.Error("InDeltaSlicef should return false on failure") } if !mock.failed { t.Error("InDeltaSlicef should mark test as failed") } }) } func TestInDeltaTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaTf(mock, 1.0, 1.01, 0.02, "test message") if !result { t.Error("InDeltaTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaTf(mock, 1.0, 1.1, 0.05, "test message") if result { t.Error("InDeltaTf should return false on failure") } if !mock.failed { t.Error("InDeltaTf should mark test as failed") } }) } func TestInEpsilonf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonf(mock, 100.0, 101.0, 0.02, "test message") if !result { t.Error("InEpsilonf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonf(mock, 100.0, 110.0, 0.05, "test message") if result { t.Error("InEpsilonf should return false on failure") } if !mock.failed { t.Error("InEpsilonf should mark test as failed") } }) } func TestInEpsilonSlicef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSlicef(mock, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02, "test message") if !result { t.Error("InEpsilonSlicef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSlicef(mock, []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05, "test message") if result { t.Error("InEpsilonSlicef should return false on failure") } if !mock.failed { t.Error("InEpsilonSlicef should mark test as failed") } }) } func TestInEpsilonSymmetricf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricf(mock, 100.0, 101.0, 0.02, "test message") if !result { t.Error("InEpsilonSymmetricf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricf(mock, 100.0, 110.0, 0.05, "test message") if result { t.Error("InEpsilonSymmetricf should return false on failure") } if !mock.failed { t.Error("InEpsilonSymmetricf should mark test as failed") } }) } func TestInEpsilonSymmetricTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricTf(mock, 100.0, 101.0, 0.02, "test message") if !result { t.Error("InEpsilonSymmetricTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricTf(mock, 100.0, 110.0, 0.05, "test message") if result { t.Error("InEpsilonSymmetricTf should return false on failure") } if !mock.failed { t.Error("InEpsilonSymmetricTf should mark test as failed") } }) } func TestInEpsilonTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonTf(mock, 100.0, 101.0, 0.02, "test message") if !result { t.Error("InEpsilonTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonTf(mock, 100.0, 110.0, 0.05, "test message") if result { t.Error("InEpsilonTf should return false on failure") } if !mock.failed { t.Error("InEpsilonTf should mark test as failed") } }) } func TestIsDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasingf(mock, []int{3, 2, 1}, "test message") if !result { t.Error("IsDecreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasingf(mock, []int{1, 2, 3}, "test message") if result { t.Error("IsDecreasingf should return false on failure") } if !mock.failed { t.Error("IsDecreasingf should mark test as failed") } }) } func TestIsDecreasingTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasingTf(mock, []int{3, 2, 1}, "test message") if !result { t.Error("IsDecreasingTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsDecreasingTf(mock, []int{1, 2, 3}, "test message") if result { t.Error("IsDecreasingTf should return false on failure") } if !mock.failed { t.Error("IsDecreasingTf should mark test as failed") } }) } func TestIsIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasingf(mock, []int{1, 2, 3}, "test message") if !result { t.Error("IsIncreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasingf(mock, []int{1, 1, 2}, "test message") if result { t.Error("IsIncreasingf should return false on failure") } if !mock.failed { t.Error("IsIncreasingf should mark test as failed") } }) } func TestIsIncreasingTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasingTf(mock, []int{1, 2, 3}, "test message") if !result { t.Error("IsIncreasingTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsIncreasingTf(mock, []int{1, 1, 2}, "test message") if result { t.Error("IsIncreasingTf should return false on failure") } if !mock.failed { t.Error("IsIncreasingTf should mark test as failed") } }) } func TestIsNonDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasingf(mock, []int{1, 1, 2}, "test message") if !result { t.Error("IsNonDecreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasingf(mock, []int{2, 1, 0}, "test message") if result { t.Error("IsNonDecreasingf should return false on failure") } if !mock.failed { t.Error("IsNonDecreasingf should mark test as failed") } }) } func TestIsNonDecreasingTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasingTf(mock, []int{1, 1, 2}, "test message") if !result { t.Error("IsNonDecreasingTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonDecreasingTf(mock, []int{2, 1, 0}, "test message") if result { t.Error("IsNonDecreasingTf should return false on failure") } if !mock.failed { t.Error("IsNonDecreasingTf should mark test as failed") } }) } func TestIsNonIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasingf(mock, []int{2, 1, 1}, "test message") if !result { t.Error("IsNonIncreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasingf(mock, []int{1, 2, 3}, "test message") if result { t.Error("IsNonIncreasingf should return false on failure") } if !mock.failed { t.Error("IsNonIncreasingf should mark test as failed") } }) } func TestIsNonIncreasingTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasingTf(mock, []int{2, 1, 1}, "test message") if !result { t.Error("IsNonIncreasingTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNonIncreasingTf(mock, []int{1, 2, 3}, "test message") if result { t.Error("IsNonIncreasingTf should return false on failure") } if !mock.failed { t.Error("IsNonIncreasingTf should mark test as failed") } }) } func TestIsNotOfTypeTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotOfTypeTf[myType](mock, 123.123, "test message") if !result { t.Error("IsNotOfTypeTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotOfTypeTf[myType](mock, myType(123.123), "test message") if result { t.Error("IsNotOfTypeTf should return false on failure") } if !mock.failed { t.Error("IsNotOfTypeTf should mark test as failed") } }) } func TestIsNotTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotTypef(mock, int32(123), int64(456), "test message") if !result { t.Error("IsNotTypef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsNotTypef(mock, 123, 456, "test message") if result { t.Error("IsNotTypef should return false on failure") } if !mock.failed { t.Error("IsNotTypef should mark test as failed") } }) } func TestIsOfTypeTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsOfTypeTf[myType](mock, myType(123.123), "test message") if !result { t.Error("IsOfTypeTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsOfTypeTf[myType](mock, 123.123, "test message") if result { t.Error("IsOfTypeTf should return false on failure") } if !mock.failed { t.Error("IsOfTypeTf should mark test as failed") } }) } func TestIsTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsTypef(mock, 123, 456, "test message") if !result { t.Error("IsTypef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := IsTypef(mock, int32(123), int64(456), "test message") if result { t.Error("IsTypef should return false on failure") } if !mock.failed { t.Error("IsTypef should mark test as failed") } }) } func TestJSONEqf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqf(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "test message") if !result { t.Error("JSONEqf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqf(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") if result { t.Error("JSONEqf should return false on failure") } if !mock.failed { t.Error("JSONEqf should mark test as failed") } }) } func TestJSONEqBytesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqBytesf(mock, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`), "test message") if !result { t.Error("JSONEqBytesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqBytesf(mock, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`), "test message") if result { t.Error("JSONEqBytesf should return false on failure") } if !mock.failed { t.Error("JSONEqBytesf should mark test as failed") } }) } func TestJSONEqTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqTf(mock, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`), "test message") if !result { t.Error("JSONEqTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONEqTf(mock, `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") if result { t.Error("JSONEqTf should return false on failure") } if !mock.failed { t.Error("JSONEqTf should mark test as failed") } }) } func TestJSONMarshalAsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONMarshalAsTf(mock, []byte(`{"A": "a"}`), dummyStruct{A: "a"}, "test message") if !result { t.Error("JSONMarshalAsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONMarshalAsTf(mock, `[{"foo": "bar"}, {"hello": "world"}]`, 1, "test message") if result { t.Error("JSONMarshalAsTf should return false on failure") } if !mock.failed { t.Error("JSONMarshalAsTf should mark test as failed") } }) } func TestJSONUnmarshalAsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONUnmarshalAsTf(mock, dummyStruct{A: "a"}, []byte(`{"A": "a"}`), "test message") if !result { t.Error("JSONUnmarshalAsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := JSONUnmarshalAsTf(mock, 1, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") if result { t.Error("JSONUnmarshalAsTf should return false on failure") } if !mock.failed { t.Error("JSONUnmarshalAsTf should mark test as failed") } }) } func TestKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Kindf(mock, reflect.String, "hello", "test message") if !result { t.Error("Kindf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Kindf(mock, reflect.String, 0, "test message") if result { t.Error("Kindf should return false on failure") } if !mock.failed { t.Error("Kindf should mark test as failed") } }) } func TestLenf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Lenf(mock, []string{"A", "B"}, 2, "test message") if !result { t.Error("Lenf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Lenf(mock, []string{"A", "B"}, 1, "test message") if result { t.Error("Lenf should return false on failure") } if !mock.failed { t.Error("Lenf should mark test as failed") } }) } func TestLessf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Lessf(mock, 1, 2, "test message") if !result { t.Error("Lessf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Lessf(mock, 2, 1, "test message") if result { t.Error("Lessf should return false on failure") } if !mock.failed { t.Error("Lessf should mark test as failed") } }) } func TestLessOrEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqualf(mock, 1, 2, "test message") if !result { t.Error("LessOrEqualf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqualf(mock, 2, 1, "test message") if result { t.Error("LessOrEqualf should return false on failure") } if !mock.failed { t.Error("LessOrEqualf should mark test as failed") } }) } func TestLessOrEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqualTf(mock, 1, 2, "test message") if !result { t.Error("LessOrEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessOrEqualTf(mock, 2, 1, "test message") if result { t.Error("LessOrEqualTf should return false on failure") } if !mock.failed { t.Error("LessOrEqualTf should mark test as failed") } }) } func TestLessTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessTf(mock, 1, 2, "test message") if !result { t.Error("LessTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := LessTf(mock, 2, 1, "test message") if result { t.Error("LessTf should return false on failure") } if !mock.failed { t.Error("LessTf should mark test as failed") } }) } func TestMapContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapContainsTf(mock, map[string]string{"A": "B"}, "A", "test message") if !result { t.Error("MapContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapContainsTf(mock, map[string]string{"A": "B"}, "C", "test message") if result { t.Error("MapContainsTf should return false on failure") } if !mock.failed { t.Error("MapContainsTf should mark test as failed") } }) } func TestMapEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapEqualTf(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") if !result { t.Error("MapEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapEqualTf(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") if result { t.Error("MapEqualTf should return false on failure") } if !mock.failed { t.Error("MapEqualTf should mark test as failed") } }) } func TestMapNotContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotContainsTf(mock, map[string]string{"A": "B"}, "C", "test message") if !result { t.Error("MapNotContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotContainsTf(mock, map[string]string{"A": "B"}, "A", "test message") if result { t.Error("MapNotContainsTf should return false on failure") } if !mock.failed { t.Error("MapNotContainsTf should mark test as failed") } }) } func TestMapNotEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotEqualTf(mock, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") if !result { t.Error("MapNotEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := MapNotEqualTf(mock, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}, "test message") if result { t.Error("MapNotEqualTf should return false on failure") } if !mock.failed { t.Error("MapNotEqualTf should mark test as failed") } }) } func TestNegativef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Negativef(mock, -1, "test message") if !result { t.Error("Negativef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Negativef(mock, 1, "test message") if result { t.Error("Negativef should return false on failure") } if !mock.failed { t.Error("Negativef should mark test as failed") } }) } func TestNegativeTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NegativeTf(mock, -1, "test message") if !result { t.Error("NegativeTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NegativeTf(mock, 1, "test message") if result { t.Error("NegativeTf should return false on failure") } if !mock.failed { t.Error("NegativeTf should mark test as failed") } }) } func TestNeverf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Neverf(mock, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond, "test message") if !result { t.Error("Neverf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Neverf(mock, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond, "test message") if result { t.Error("Neverf should return false on failure") } if !mock.failed { t.Error("Neverf should mark test as failed") } }) } func TestNilf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Nilf(mock, nil, "test message") if !result { t.Error("Nilf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Nilf(mock, "not nil", "test message") if result { t.Error("Nilf should return false on failure") } if !mock.failed { t.Error("Nilf should mark test as failed") } }) } func TestNoErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoErrorf(mock, nil, "test message") if !result { t.Error("NoErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoErrorf(mock, ErrTest, "test message") if result { t.Error("NoErrorf should return false on failure") } if !mock.failed { t.Error("NoErrorf should mark test as failed") } }) } func TestNoFileDescriptorLeakf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoFileDescriptorLeakf(mock, func() {}, "test message") if !result { t.Error("NoFileDescriptorLeakf should return true on success") } }) } func TestNoGoRoutineLeakf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NoGoRoutineLeakf(mock, func() {}, "test message") if !result { t.Error("NoGoRoutineLeakf should return true on success") } }) } func TestNotBlockedf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlockedf(mock, sendChanMessage(), "test message") if !result { t.Error("NotBlockedf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlockedf(mock, make(chan struct{}), "test message") if result { t.Error("NotBlockedf should return false on failure") } if !mock.failed { t.Error("NotBlockedf should mark test as failed") } }) } func TestNotBlockedTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlockedTf(mock, sendChanMessage(), "test message") if !result { t.Error("NotBlockedTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotBlockedTf(mock, make(chan struct{}), "test message") if result { t.Error("NotBlockedTf should return false on failure") } if !mock.failed { t.Error("NotBlockedTf should mark test as failed") } }) } func TestNotContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotContainsf(mock, []string{"A", "B"}, "C", "test message") if !result { t.Error("NotContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotContainsf(mock, []string{"A", "B"}, "B", "test message") if result { t.Error("NotContainsf should return false on failure") } if !mock.failed { t.Error("NotContainsf should mark test as failed") } }) } func TestNotElementsMatchf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatchf(mock, []int{1, 2, 3}, []int{1, 2, 4}, "test message") if !result { t.Error("NotElementsMatchf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatchf(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") if result { t.Error("NotElementsMatchf should return false on failure") } if !mock.failed { t.Error("NotElementsMatchf should mark test as failed") } }) } func TestNotElementsMatchTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatchTf(mock, []int{1, 2, 3}, []int{1, 2, 4}, "test message") if !result { t.Error("NotElementsMatchTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotElementsMatchTf(mock, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") if result { t.Error("NotElementsMatchTf should return false on failure") } if !mock.failed { t.Error("NotElementsMatchTf should mark test as failed") } }) } func TestNotEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEmptyf(mock, "not empty", "test message") if !result { t.Error("NotEmptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEmptyf(mock, "", "test message") if result { t.Error("NotEmptyf should return false on failure") } if !mock.failed { t.Error("NotEmptyf should mark test as failed") } }) } func TestNotEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualf(mock, 123, 456, "test message") if !result { t.Error("NotEqualf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualf(mock, 123, 123, "test message") if result { t.Error("NotEqualf should return false on failure") } if !mock.failed { t.Error("NotEqualf should mark test as failed") } }) } func TestNotEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualTf(mock, 123, 456, "test message") if !result { t.Error("NotEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualTf(mock, 123, 123, "test message") if result { t.Error("NotEqualTf should return false on failure") } if !mock.failed { t.Error("NotEqualTf should mark test as failed") } }) } func TestNotEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualValuesf(mock, uint32(123), int32(456), "test message") if !result { t.Error("NotEqualValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotEqualValuesf(mock, uint32(123), int32(123), "test message") if result { t.Error("NotEqualValuesf should return false on failure") } if !mock.failed { t.Error("NotEqualValuesf should mark test as failed") } }) } func TestNotErrorAsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorAsf(mock, ErrTest, new(*dummyError), "test message") if !result { t.Error("NotErrorAsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorAsf(mock, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") if result { t.Error("NotErrorAsf should return false on failure") } if !mock.failed { t.Error("NotErrorAsf should mark test as failed") } }) } func TestNotErrorIsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorIsf(mock, ErrTest, io.EOF, "test message") if !result { t.Error("NotErrorIsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotErrorIsf(mock, fmt.Errorf("wrap: %w", io.EOF), io.EOF, "test message") if result { t.Error("NotErrorIsf should return false on failure") } if !mock.failed { t.Error("NotErrorIsf should mark test as failed") } }) } func TestNotImplementsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotImplementsf(mock, (*error)(nil), new(testing.T), "test message") if !result { t.Error("NotImplementsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotImplementsf(mock, ptr(dummyInterface), new(testing.T), "test message") if result { t.Error("NotImplementsf should return false on failure") } if !mock.failed { t.Error("NotImplementsf should mark test as failed") } }) } func TestNotKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotKindf(mock, reflect.String, 0, "test message") if !result { t.Error("NotKindf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotKindf(mock, reflect.String, "hello", "test message") if result { t.Error("NotKindf should return false on failure") } if !mock.failed { t.Error("NotKindf should mark test as failed") } }) } func TestNotNilf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotNilf(mock, "not nil", "test message") if !result { t.Error("NotNilf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotNilf(mock, nil, "test message") if result { t.Error("NotNilf should return false on failure") } if !mock.failed { t.Error("NotNilf should mark test as failed") } }) } func TestNotPanicsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotPanicsf(mock, func() {}, "test message") if !result { t.Error("NotPanicsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotPanicsf(mock, func() { panic("panicking") }, "test message") if result { t.Error("NotPanicsf should return false on failure") } if !mock.failed { t.Error("NotPanicsf should mark test as failed") } }) } func TestNotRegexpf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexpf(mock, "^start", "not starting", "test message") if !result { t.Error("NotRegexpf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexpf(mock, "^start", "starting", "test message") if result { t.Error("NotRegexpf should return false on failure") } if !mock.failed { t.Error("NotRegexpf should mark test as failed") } }) } func TestNotRegexpTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexpTf(mock, "^start", "not starting", "test message") if !result { t.Error("NotRegexpTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotRegexpTf(mock, "^start", "starting", "test message") if result { t.Error("NotRegexpTf should return false on failure") } if !mock.failed { t.Error("NotRegexpTf should mark test as failed") } }) } func TestNotSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSamef(mock, &staticVar, ptr("static string"), "test message") if !result { t.Error("NotSamef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSamef(mock, &staticVar, staticVarPtr, "test message") if result { t.Error("NotSamef should return false on failure") } if !mock.failed { t.Error("NotSamef should mark test as failed") } }) } func TestNotSameTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSameTf(mock, &staticVar, ptr("static string"), "test message") if !result { t.Error("NotSameTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSameTf(mock, &staticVar, staticVarPtr, "test message") if result { t.Error("NotSameTf should return false on failure") } if !mock.failed { t.Error("NotSameTf should mark test as failed") } }) } func TestNotSortedTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSortedTf(mock, []int{3, 1, 3}, "test message") if !result { t.Error("NotSortedTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSortedTf(mock, []int{1, 4, 8}, "test message") if result { t.Error("NotSortedTf should return false on failure") } if !mock.failed { t.Error("NotSortedTf should mark test as failed") } }) } func TestNotSubsetf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSubsetf(mock, []int{1, 2, 3}, []int{4, 5}, "test message") if !result { t.Error("NotSubsetf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotSubsetf(mock, []int{1, 2, 3}, []int{1, 2}, "test message") if result { t.Error("NotSubsetf should return false on failure") } if !mock.failed { t.Error("NotSubsetf should mark test as failed") } }) } func TestNotZerof(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotZerof(mock, 1, "test message") if !result { t.Error("NotZerof should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := NotZerof(mock, 0, "test message") if result { t.Error("NotZerof should return false on failure") } if !mock.failed { t.Error("NotZerof should mark test as failed") } }) } func TestPanicsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panicsf(mock, func() { panic("panicking") }, "test message") if !result { t.Error("Panicsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panicsf(mock, func() {}, "test message") if result { t.Error("Panicsf should return false on failure") } if !mock.failed { t.Error("Panicsf should mark test as failed") } }) } func TestPanicsWithErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithErrorf(mock, ErrTest.Error(), func() { panic(ErrTest) }, "test message") if !result { t.Error("PanicsWithErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithErrorf(mock, ErrTest.Error(), func() {}, "test message") if result { t.Error("PanicsWithErrorf should return false on failure") } if !mock.failed { t.Error("PanicsWithErrorf should mark test as failed") } }) } func TestPanicsWithValuef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithValuef(mock, "panicking", func() { panic("panicking") }, "test message") if !result { t.Error("PanicsWithValuef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PanicsWithValuef(mock, "panicking", func() {}, "test message") if result { t.Error("PanicsWithValuef should return false on failure") } if !mock.failed { t.Error("PanicsWithValuef should mark test as failed") } }) } func TestPositivef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Positivef(mock, 1, "test message") if !result { t.Error("Positivef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Positivef(mock, -1, "test message") if result { t.Error("Positivef should return false on failure") } if !mock.failed { t.Error("Positivef should mark test as failed") } }) } func TestPositiveTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PositiveTf(mock, 1, "test message") if !result { t.Error("PositiveTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := PositiveTf(mock, -1, "test message") if result { t.Error("PositiveTf should return false on failure") } if !mock.failed { t.Error("PositiveTf should mark test as failed") } }) } func TestRegexpf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Regexpf(mock, "^start", "starting", "test message") if !result { t.Error("Regexpf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Regexpf(mock, "^start", "not starting", "test message") if result { t.Error("Regexpf should return false on failure") } if !mock.failed { t.Error("Regexpf should mark test as failed") } }) } func TestRegexpTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := RegexpTf(mock, "^start", "starting", "test message") if !result { t.Error("RegexpTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := RegexpTf(mock, "^start", "not starting", "test message") if result { t.Error("RegexpTf should return false on failure") } if !mock.failed { t.Error("RegexpTf should mark test as failed") } }) } func TestSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Samef(mock, &staticVar, staticVarPtr, "test message") if !result { t.Error("Samef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Samef(mock, &staticVar, ptr("static string"), "test message") if result { t.Error("Samef should return false on failure") } if !mock.failed { t.Error("Samef should mark test as failed") } }) } func TestSameTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SameTf(mock, &staticVar, staticVarPtr, "test message") if !result { t.Error("SameTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SameTf(mock, &staticVar, ptr("static string"), "test message") if result { t.Error("SameTf should return false on failure") } if !mock.failed { t.Error("SameTf should mark test as failed") } }) } func TestSeqContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqContainsTf(mock, slices.Values([]string{"A", "B"}), "A", "test message") if !result { t.Error("SeqContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqContainsTf(mock, slices.Values([]string{"A", "B"}), "C", "test message") if result { t.Error("SeqContainsTf should return false on failure") } if !mock.failed { t.Error("SeqContainsTf should mark test as failed") } }) } func TestSeqNotContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqNotContainsTf(mock, slices.Values([]string{"A", "B"}), "C", "test message") if !result { t.Error("SeqNotContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SeqNotContainsTf(mock, slices.Values([]string{"A", "B"}), "A", "test message") if result { t.Error("SeqNotContainsTf should return false on failure") } if !mock.failed { t.Error("SeqNotContainsTf should mark test as failed") } }) } func TestSliceContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceContainsTf(mock, []string{"A", "B"}, "A", "test message") if !result { t.Error("SliceContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceContainsTf(mock, []string{"A", "B"}, "C", "test message") if result { t.Error("SliceContainsTf should return false on failure") } if !mock.failed { t.Error("SliceContainsTf should mark test as failed") } }) } func TestSliceEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceEqualTf(mock, []string{"Hello", "World"}, []string{"Hello", "World"}, "test message") if !result { t.Error("SliceEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceEqualTf(mock, []string{"Hello", "World"}, []string{"Hello"}, "test message") if result { t.Error("SliceEqualTf should return false on failure") } if !mock.failed { t.Error("SliceEqualTf should mark test as failed") } }) } func TestSliceNotContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotContainsTf(mock, []string{"A", "B"}, "C", "test message") if !result { t.Error("SliceNotContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotContainsTf(mock, []string{"A", "B"}, "A", "test message") if result { t.Error("SliceNotContainsTf should return false on failure") } if !mock.failed { t.Error("SliceNotContainsTf should mark test as failed") } }) } func TestSliceNotEqualTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotEqualTf(mock, []string{"Hello", "World"}, []string{"Hello"}, "test message") if !result { t.Error("SliceNotEqualTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotEqualTf(mock, []string{"Hello", "World"}, []string{"Hello", "World"}, "test message") if result { t.Error("SliceNotEqualTf should return false on failure") } if !mock.failed { t.Error("SliceNotEqualTf should mark test as failed") } }) } func TestSliceNotSubsetTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotSubsetTf(mock, []int{1, 2, 3}, []int{4, 5}, "test message") if !result { t.Error("SliceNotSubsetTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceNotSubsetTf(mock, []int{1, 2, 3}, []int{1, 2}, "test message") if result { t.Error("SliceNotSubsetTf should return false on failure") } if !mock.failed { t.Error("SliceNotSubsetTf should mark test as failed") } }) } func TestSliceSubsetTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceSubsetTf(mock, []int{1, 2, 3}, []int{1, 2}, "test message") if !result { t.Error("SliceSubsetTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SliceSubsetTf(mock, []int{1, 2, 3}, []int{4, 5}, "test message") if result { t.Error("SliceSubsetTf should return false on failure") } if !mock.failed { t.Error("SliceSubsetTf should mark test as failed") } }) } func TestSortedTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SortedTf(mock, []int{1, 1, 3}, "test message") if !result { t.Error("SortedTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := SortedTf(mock, []int{1, 4, 2}, "test message") if result { t.Error("SortedTf should return false on failure") } if !mock.failed { t.Error("SortedTf should mark test as failed") } }) } func TestStringContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringContainsTf(mock, "AB", "A", "test message") if !result { t.Error("StringContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringContainsTf(mock, "AB", "C", "test message") if result { t.Error("StringContainsTf should return false on failure") } if !mock.failed { t.Error("StringContainsTf should mark test as failed") } }) } func TestStringNotContainsTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringNotContainsTf(mock, "AB", "C", "test message") if !result { t.Error("StringNotContainsTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := StringNotContainsTf(mock, "AB", "A", "test message") if result { t.Error("StringNotContainsTf should return false on failure") } if !mock.failed { t.Error("StringNotContainsTf should mark test as failed") } }) } func TestSubsetf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Subsetf(mock, []int{1, 2, 3}, []int{1, 2}, "test message") if !result { t.Error("Subsetf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Subsetf(mock, []int{1, 2, 3}, []int{4, 5}, "test message") if result { t.Error("Subsetf should return false on failure") } if !mock.failed { t.Error("Subsetf should mark test as failed") } }) } func TestTruef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Truef(mock, 1 == 1, "test message") if !result { t.Error("Truef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Truef(mock, 1 == 0, "test message") if result { t.Error("Truef should return false on failure") } if !mock.failed { t.Error("Truef should mark test as failed") } }) } func TestTrueTf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := TrueTf(mock, 1 == 1, "test message") if !result { t.Error("TrueTf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := TrueTf(mock, 1 == 0, "test message") if result { t.Error("TrueTf should return false on failure") } if !mock.failed { t.Error("TrueTf should mark test as failed") } }) } func TestWithinDurationf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinDurationf(mock, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second, "test message") if !result { t.Error("WithinDurationf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinDurationf(mock, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second, "test message") if result { t.Error("WithinDurationf should return false on failure") } if !mock.failed { t.Error("WithinDurationf should mark test as failed") } }) } func TestWithinRangef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinRangef(mock, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC), "test message") if !result { t.Error("WithinRangef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := WithinRangef(mock, time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC), "test message") if result { t.Error("WithinRangef should return false on failure") } if !mock.failed { t.Error("WithinRangef should mark test as failed") } }) } func TestYAMLEqf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLEqf(mock, "key: value", "key: value", "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLEqf should return true on panic") } if mock.failed { t.Error("YAMLEqf should panic as expected") } }) } func TestYAMLEqBytesf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLEqBytesf(mock, []byte("key: value"), []byte("key: value"), "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLEqBytesf should return true on panic") } if mock.failed { t.Error("YAMLEqBytesf should panic as expected") } }) } func TestYAMLEqTf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLEqTf(mock, "key: value", "key: value", "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLEqTf should return true on panic") } if mock.failed { t.Error("YAMLEqTf should panic as expected") } }) } func TestYAMLMarshalAsTf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLMarshalAsTf(mock, "key: value", "key: value", "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLMarshalAsTf should return true on panic") } if mock.failed { t.Error("YAMLMarshalAsTf should panic as expected") } }) } func TestYAMLUnmarshalAsTf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Panics(t, func() { YAMLUnmarshalAsTf(mock, "key: value", "key: value", "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("YAMLUnmarshalAsTf should return true on panic") } if mock.failed { t.Error("YAMLUnmarshalAsTf should panic as expected") } }) } func TestZerof(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Zerof(mock, 0, "test message") if !result { t.Error("Zerof should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) result := Zerof(mock, 1, "test message") if result { t.Error("Zerof should return false on failure") } if !mock.failed { t.Error("Zerof should mark test as failed") } }) } go-openapi-testify-c10ca71/assert/assert_forward.go000066400000000000000000002014471520301377500225260ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "net/http" "net/url" "reflect" "time" "github.com/go-openapi/testify/v2/internal/assertions" ) // Assertions exposes all assertion functions as methods. // // NOTE: assertion methods with parameterized types (generics) are not supported as methods. // // Upon failure, the test [T] is marked as failed and continues execution. type Assertions struct { T } // New makes a new [Assertions] object for the specified [T] (e.g. [testing.T]). func New(t T) *Assertions { return &Assertions{ T: t, } } // Blocked is the same as [Blocked], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Blocked(ch any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Blocked(a.T, ch, msgAndArgs...) } // Blockedf is the same as [Assertions.Blocked], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Blockedf(ch any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Blocked(a.T, ch, forwardArgs(msg, args)...) } // Condition is the same as [Condition], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Condition(comp func() bool, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Condition(a.T, comp, msgAndArgs...) } // Conditionf is the same as [Assertions.Condition], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Conditionf(comp func() bool, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Condition(a.T, comp, forwardArgs(msg, args)...) } // Contains is the same as [Contains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Contains(s any, contains any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Contains(a.T, s, contains, msgAndArgs...) } // Containsf is the same as [Assertions.Contains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Containsf(s any, contains any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Contains(a.T, s, contains, forwardArgs(msg, args)...) } // DirExists is the same as [DirExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirExists(path string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.DirExists(a.T, path, msgAndArgs...) } // DirExistsf is the same as [Assertions.DirExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirExistsf(path string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.DirExists(a.T, path, forwardArgs(msg, args)...) } // DirNotExists is the same as [DirNotExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirNotExists(path string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.DirNotExists(a.T, path, msgAndArgs...) } // DirNotExistsf is the same as [Assertions.DirNotExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) DirNotExistsf(path string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.DirNotExists(a.T, path, forwardArgs(msg, args)...) } // ElementsMatch is the same as [ElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ElementsMatch(listA any, listB any, msgAndArgs ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ElementsMatch(a.T, listA, listB, msgAndArgs...) } // ElementsMatchf is the same as [Assertions.ElementsMatch], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ElementsMatchf(listA any, listB any, msg string, args ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ElementsMatch(a.T, listA, listB, forwardArgs(msg, args)...) } // Empty is the same as [Empty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Empty(object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Empty(a.T, object, msgAndArgs...) } // Emptyf is the same as [Assertions.Empty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Emptyf(object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Empty(a.T, object, forwardArgs(msg, args)...) } // Equal is the same as [Equal], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Equal(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Equal(a.T, expected, actual, msgAndArgs...) } // Equalf is the same as [Assertions.Equal], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Equalf(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Equal(a.T, expected, actual, forwardArgs(msg, args)...) } // EqualError is the same as [EqualError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualError(err error, errString string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.EqualError(a.T, err, errString, msgAndArgs...) } // EqualErrorf is the same as [Assertions.EqualError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualErrorf(err error, errString string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.EqualError(a.T, err, errString, forwardArgs(msg, args)...) } // EqualExportedValues is the same as [EqualExportedValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualExportedValues(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.EqualExportedValues(a.T, expected, actual, msgAndArgs...) } // EqualExportedValuesf is the same as [Assertions.EqualExportedValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualExportedValuesf(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.EqualExportedValues(a.T, expected, actual, forwardArgs(msg, args)...) } // EqualValues is the same as [EqualValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualValues(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.EqualValues(a.T, expected, actual, msgAndArgs...) } // EqualValuesf is the same as [Assertions.EqualValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) EqualValuesf(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.EqualValues(a.T, expected, actual, forwardArgs(msg, args)...) } // Error is the same as [Error], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Error(err error, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Error(a.T, err, msgAndArgs...) } // Errorf is the same as [Assertions.Error], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Errorf(err error, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Error(a.T, err, forwardArgs(msg, args)...) } // ErrorAs is the same as [ErrorAs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorAs(err error, target any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ErrorAs(a.T, err, target, msgAndArgs...) } // ErrorAsf is the same as [Assertions.ErrorAs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorAsf(err error, target any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ErrorAs(a.T, err, target, forwardArgs(msg, args)...) } // ErrorContains is the same as [ErrorContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorContains(err error, contains string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ErrorContains(a.T, err, contains, msgAndArgs...) } // ErrorContainsf is the same as [Assertions.ErrorContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorContainsf(err error, contains string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ErrorContains(a.T, err, contains, forwardArgs(msg, args)...) } // ErrorIs is the same as [ErrorIs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorIs(err error, target error, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ErrorIs(a.T, err, target, msgAndArgs...) } // ErrorIsf is the same as [Assertions.ErrorIs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) ErrorIsf(err error, target error, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.ErrorIs(a.T, err, target, forwardArgs(msg, args)...) } // Exactly is the same as [Exactly], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Exactly(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Exactly(a.T, expected, actual, msgAndArgs...) } // Exactlyf is the same as [Assertions.Exactly], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Exactlyf(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Exactly(a.T, expected, actual, forwardArgs(msg, args)...) } // Fail is the same as [Fail], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Fail(failureMessage string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Fail(a.T, failureMessage, msgAndArgs...) } // Failf is the same as [Assertions.Fail], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Failf(failureMessage string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Fail(a.T, failureMessage, forwardArgs(msg, args)...) } // FailNow is the same as [FailNow], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FailNow(failureMessage string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FailNow(a.T, failureMessage, msgAndArgs...) } // FailNowf is the same as [Assertions.FailNow], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FailNowf(failureMessage string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FailNow(a.T, failureMessage, forwardArgs(msg, args)...) } // False is the same as [False], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) False(value bool, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.False(a.T, value, msgAndArgs...) } // Falsef is the same as [Assertions.False], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Falsef(value bool, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.False(a.T, value, forwardArgs(msg, args)...) } // FileEmpty is the same as [FileEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileEmpty(path string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileEmpty(a.T, path, msgAndArgs...) } // FileEmptyf is the same as [Assertions.FileEmpty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileEmptyf(path string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileEmpty(a.T, path, forwardArgs(msg, args)...) } // FileExists is the same as [FileExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileExists(path string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileExists(a.T, path, msgAndArgs...) } // FileExistsf is the same as [Assertions.FileExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileExistsf(path string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileExists(a.T, path, forwardArgs(msg, args)...) } // FileNotEmpty is the same as [FileNotEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotEmpty(path string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileNotEmpty(a.T, path, msgAndArgs...) } // FileNotEmptyf is the same as [Assertions.FileNotEmpty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotEmptyf(path string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileNotEmpty(a.T, path, forwardArgs(msg, args)...) } // FileNotExists is the same as [FileNotExists], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotExists(path string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileNotExists(a.T, path, msgAndArgs...) } // FileNotExistsf is the same as [Assertions.FileNotExists], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) FileNotExistsf(path string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.FileNotExists(a.T, path, forwardArgs(msg, args)...) } // Greater is the same as [Greater], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Greater(e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Greater(a.T, e1, e2, msgAndArgs...) } // Greaterf is the same as [Assertions.Greater], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Greaterf(e1 any, e2 any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Greater(a.T, e1, e2, forwardArgs(msg, args)...) } // GreaterOrEqual is the same as [GreaterOrEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) GreaterOrEqual(e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.GreaterOrEqual(a.T, e1, e2, msgAndArgs...) } // GreaterOrEqualf is the same as [Assertions.GreaterOrEqual], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) GreaterOrEqualf(e1 any, e2 any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.GreaterOrEqual(a.T, e1, e2, forwardArgs(msg, args)...) } // HTTPBodyContains is the same as [HTTPBodyContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPBodyContains(a.T, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyContainsf is the same as [Assertions.HTTPBodyContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPBodyContains(a.T, handler, method, url, values, str, forwardArgs(msg, args)...) } // HTTPBodyNotContains is the same as [HTTPBodyNotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPBodyNotContains(a.T, handler, method, url, values, str, msgAndArgs...) } // HTTPBodyNotContainsf is the same as [Assertions.HTTPBodyNotContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPBodyNotContains(a.T, handler, method, url, values, str, forwardArgs(msg, args)...) } // HTTPError is the same as [HTTPError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPError(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPError(a.T, handler, method, url, values, msgAndArgs...) } // HTTPErrorf is the same as [Assertions.HTTPError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPError(a.T, handler, method, url, values, forwardArgs(msg, args)...) } // HTTPRedirect is the same as [HTTPRedirect], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPRedirect(a.T, handler, method, url, values, msgAndArgs...) } // HTTPRedirectf is the same as [Assertions.HTTPRedirect], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPRedirect(a.T, handler, method, url, values, forwardArgs(msg, args)...) } // HTTPStatusCode is the same as [HTTPStatusCode], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPStatusCode(a.T, handler, method, url, values, statuscode, msgAndArgs...) } // HTTPStatusCodef is the same as [Assertions.HTTPStatusCode], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPStatusCode(a.T, handler, method, url, values, statuscode, forwardArgs(msg, args)...) } // HTTPSuccess is the same as [HTTPSuccess], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPSuccess(a.T, handler, method, url, values, msgAndArgs...) } // HTTPSuccessf is the same as [Assertions.HTTPSuccess], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.HTTPSuccess(a.T, handler, method, url, values, forwardArgs(msg, args)...) } // Implements is the same as [Implements], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Implements(interfaceObject any, object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Implements(a.T, interfaceObject, object, msgAndArgs...) } // Implementsf is the same as [Assertions.Implements], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Implementsf(interfaceObject any, object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Implements(a.T, interfaceObject, object, forwardArgs(msg, args)...) } // InDelta is the same as [InDelta], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDelta(expected any, actual any, delta float64, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InDelta(a.T, expected, actual, delta, msgAndArgs...) } // InDeltaf is the same as [Assertions.InDelta], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaf(expected any, actual any, delta float64, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InDelta(a.T, expected, actual, delta, forwardArgs(msg, args)...) } // InDeltaMapValues is the same as [InDeltaMapValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaMapValues(expected any, actual any, delta float64, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InDeltaMapValues(a.T, expected, actual, delta, msgAndArgs...) } // InDeltaMapValuesf is the same as [Assertions.InDeltaMapValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaMapValuesf(expected any, actual any, delta float64, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InDeltaMapValues(a.T, expected, actual, delta, forwardArgs(msg, args)...) } // InDeltaSlice is the same as [InDeltaSlice], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaSlice(expected any, actual any, delta float64, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InDeltaSlice(a.T, expected, actual, delta, msgAndArgs...) } // InDeltaSlicef is the same as [Assertions.InDeltaSlice], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InDeltaSlicef(expected any, actual any, delta float64, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InDeltaSlice(a.T, expected, actual, delta, forwardArgs(msg, args)...) } // InEpsilon is the same as [InEpsilon], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilon(expected any, actual any, epsilon float64, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InEpsilon(a.T, expected, actual, epsilon, msgAndArgs...) } // InEpsilonf is the same as [Assertions.InEpsilon], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonf(expected any, actual any, epsilon float64, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InEpsilon(a.T, expected, actual, epsilon, forwardArgs(msg, args)...) } // InEpsilonSlice is the same as [InEpsilonSlice], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonSlice(expected any, actual any, epsilon float64, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InEpsilonSlice(a.T, expected, actual, epsilon, msgAndArgs...) } // InEpsilonSlicef is the same as [Assertions.InEpsilonSlice], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonSlicef(expected any, actual any, epsilon float64, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InEpsilonSlice(a.T, expected, actual, epsilon, forwardArgs(msg, args)...) } // InEpsilonSymmetric is the same as [InEpsilonSymmetric], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonSymmetric(x any, y any, epsilon float64, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InEpsilonSymmetric(a.T, x, y, epsilon, msgAndArgs...) } // InEpsilonSymmetricf is the same as [Assertions.InEpsilonSymmetric], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) InEpsilonSymmetricf(x any, y any, epsilon float64, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.InEpsilonSymmetric(a.T, x, y, epsilon, forwardArgs(msg, args)...) } // IsDecreasing is the same as [IsDecreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsDecreasing(collection any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsDecreasing(a.T, collection, msgAndArgs...) } // IsDecreasingf is the same as [Assertions.IsDecreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsDecreasingf(collection any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsDecreasing(a.T, collection, forwardArgs(msg, args)...) } // IsIncreasing is the same as [IsIncreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsIncreasing(collection any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsIncreasing(a.T, collection, msgAndArgs...) } // IsIncreasingf is the same as [Assertions.IsIncreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsIncreasingf(collection any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsIncreasing(a.T, collection, forwardArgs(msg, args)...) } // IsNonDecreasing is the same as [IsNonDecreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonDecreasing(collection any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsNonDecreasing(a.T, collection, msgAndArgs...) } // IsNonDecreasingf is the same as [Assertions.IsNonDecreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonDecreasingf(collection any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsNonDecreasing(a.T, collection, forwardArgs(msg, args)...) } // IsNonIncreasing is the same as [IsNonIncreasing], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonIncreasing(collection any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsNonIncreasing(a.T, collection, msgAndArgs...) } // IsNonIncreasingf is the same as [Assertions.IsNonIncreasing], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNonIncreasingf(collection any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsNonIncreasing(a.T, collection, forwardArgs(msg, args)...) } // IsNotType is the same as [IsNotType], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNotType(theType any, object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsNotType(a.T, theType, object, msgAndArgs...) } // IsNotTypef is the same as [Assertions.IsNotType], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsNotTypef(theType any, object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsNotType(a.T, theType, object, forwardArgs(msg, args)...) } // IsType is the same as [IsType], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsType(expectedType any, object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsType(a.T, expectedType, object, msgAndArgs...) } // IsTypef is the same as [Assertions.IsType], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) IsTypef(expectedType any, object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.IsType(a.T, expectedType, object, forwardArgs(msg, args)...) } // JSONEq is the same as [JSONEq], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEq(expected string, actual string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.JSONEq(a.T, expected, actual, msgAndArgs...) } // JSONEqf is the same as [Assertions.JSONEq], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEqf(expected string, actual string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.JSONEq(a.T, expected, actual, forwardArgs(msg, args)...) } // JSONEqBytes is the same as [JSONEqBytes], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEqBytes(expected []byte, actual []byte, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.JSONEqBytes(a.T, expected, actual, msgAndArgs...) } // JSONEqBytesf is the same as [Assertions.JSONEqBytes], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) JSONEqBytesf(expected []byte, actual []byte, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.JSONEqBytes(a.T, expected, actual, forwardArgs(msg, args)...) } // Kind is the same as [Kind], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Kind(expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Kind(a.T, expectedKind, object, msgAndArgs...) } // Kindf is the same as [Assertions.Kind], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Kindf(expectedKind reflect.Kind, object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Kind(a.T, expectedKind, object, forwardArgs(msg, args)...) } // Len is the same as [Len], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Len(object any, length int, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Len(a.T, object, length, msgAndArgs...) } // Lenf is the same as [Assertions.Len], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Lenf(object any, length int, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Len(a.T, object, length, forwardArgs(msg, args)...) } // Less is the same as [Less], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Less(e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Less(a.T, e1, e2, msgAndArgs...) } // Lessf is the same as [Assertions.Less], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Lessf(e1 any, e2 any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Less(a.T, e1, e2, forwardArgs(msg, args)...) } // LessOrEqual is the same as [LessOrEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) LessOrEqual(e1 any, e2 any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.LessOrEqual(a.T, e1, e2, msgAndArgs...) } // LessOrEqualf is the same as [Assertions.LessOrEqual], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) LessOrEqualf(e1 any, e2 any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.LessOrEqual(a.T, e1, e2, forwardArgs(msg, args)...) } // Negative is the same as [Negative], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Negative(e any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Negative(a.T, e, msgAndArgs...) } // Negativef is the same as [Assertions.Negative], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Negativef(e any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Negative(a.T, e, forwardArgs(msg, args)...) } // Nil is the same as [Nil], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Nil(object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Nil(a.T, object, msgAndArgs...) } // Nilf is the same as [Assertions.Nil], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Nilf(object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Nil(a.T, object, forwardArgs(msg, args)...) } // NoError is the same as [NoError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoError(err error, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NoError(a.T, err, msgAndArgs...) } // NoErrorf is the same as [Assertions.NoError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoErrorf(err error, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NoError(a.T, err, forwardArgs(msg, args)...) } // NoFileDescriptorLeak is the same as [NoFileDescriptorLeak], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoFileDescriptorLeak(tested func(), msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NoFileDescriptorLeak(a.T, tested, msgAndArgs...) } // NoFileDescriptorLeakf is the same as [Assertions.NoFileDescriptorLeak], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoFileDescriptorLeakf(tested func(), msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NoFileDescriptorLeak(a.T, tested, forwardArgs(msg, args)...) } // NoGoRoutineLeak is the same as [NoGoRoutineLeak], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoGoRoutineLeak(tested func(), msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NoGoRoutineLeak(a.T, tested, msgAndArgs...) } // NoGoRoutineLeakf is the same as [Assertions.NoGoRoutineLeak], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NoGoRoutineLeakf(tested func(), msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NoGoRoutineLeak(a.T, tested, forwardArgs(msg, args)...) } // NotBlocked is the same as [NotBlocked], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotBlocked(ch any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotBlocked(a.T, ch, msgAndArgs...) } // NotBlockedf is the same as [Assertions.NotBlocked], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotBlockedf(ch any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotBlocked(a.T, ch, forwardArgs(msg, args)...) } // NotContains is the same as [NotContains], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotContains(s any, contains any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotContains(a.T, s, contains, msgAndArgs...) } // NotContainsf is the same as [Assertions.NotContains], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotContainsf(s any, contains any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotContains(a.T, s, contains, forwardArgs(msg, args)...) } // NotElementsMatch is the same as [NotElementsMatch], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotElementsMatch(listA any, listB any, msgAndArgs ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotElementsMatch(a.T, listA, listB, msgAndArgs...) } // NotElementsMatchf is the same as [Assertions.NotElementsMatch], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotElementsMatchf(listA any, listB any, msg string, args ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotElementsMatch(a.T, listA, listB, forwardArgs(msg, args)...) } // NotEmpty is the same as [NotEmpty], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEmpty(object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotEmpty(a.T, object, msgAndArgs...) } // NotEmptyf is the same as [Assertions.NotEmpty], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEmptyf(object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotEmpty(a.T, object, forwardArgs(msg, args)...) } // NotEqual is the same as [NotEqual], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqual(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotEqual(a.T, expected, actual, msgAndArgs...) } // NotEqualf is the same as [Assertions.NotEqual], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqualf(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotEqual(a.T, expected, actual, forwardArgs(msg, args)...) } // NotEqualValues is the same as [NotEqualValues], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqualValues(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotEqualValues(a.T, expected, actual, msgAndArgs...) } // NotEqualValuesf is the same as [Assertions.NotEqualValues], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotEqualValuesf(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotEqualValues(a.T, expected, actual, forwardArgs(msg, args)...) } // NotErrorAs is the same as [NotErrorAs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorAs(err error, target any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotErrorAs(a.T, err, target, msgAndArgs...) } // NotErrorAsf is the same as [Assertions.NotErrorAs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorAsf(err error, target any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotErrorAs(a.T, err, target, forwardArgs(msg, args)...) } // NotErrorIs is the same as [NotErrorIs], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorIs(err error, target error, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotErrorIs(a.T, err, target, msgAndArgs...) } // NotErrorIsf is the same as [Assertions.NotErrorIs], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotErrorIsf(err error, target error, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotErrorIs(a.T, err, target, forwardArgs(msg, args)...) } // NotImplements is the same as [NotImplements], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotImplements(interfaceObject any, object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotImplements(a.T, interfaceObject, object, msgAndArgs...) } // NotImplementsf is the same as [Assertions.NotImplements], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotImplementsf(interfaceObject any, object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotImplements(a.T, interfaceObject, object, forwardArgs(msg, args)...) } // NotKind is the same as [NotKind], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotKind(expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotKind(a.T, expectedKind, object, msgAndArgs...) } // NotKindf is the same as [Assertions.NotKind], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotKindf(expectedKind reflect.Kind, object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotKind(a.T, expectedKind, object, forwardArgs(msg, args)...) } // NotNil is the same as [NotNil], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotNil(object any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotNil(a.T, object, msgAndArgs...) } // NotNilf is the same as [Assertions.NotNil], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotNilf(object any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotNil(a.T, object, forwardArgs(msg, args)...) } // NotPanics is the same as [NotPanics], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotPanics(f func(), msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotPanics(a.T, f, msgAndArgs...) } // NotPanicsf is the same as [Assertions.NotPanics], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotPanicsf(f func(), msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotPanics(a.T, f, forwardArgs(msg, args)...) } // NotRegexp is the same as [NotRegexp], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotRegexp(rx any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotRegexp(a.T, rx, actual, msgAndArgs...) } // NotRegexpf is the same as [Assertions.NotRegexp], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotRegexpf(rx any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotRegexp(a.T, rx, actual, forwardArgs(msg, args)...) } // NotSame is the same as [NotSame], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSame(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotSame(a.T, expected, actual, msgAndArgs...) } // NotSamef is the same as [Assertions.NotSame], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSamef(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotSame(a.T, expected, actual, forwardArgs(msg, args)...) } // NotSubset is the same as [NotSubset], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSubset(list any, subset any, msgAndArgs ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotSubset(a.T, list, subset, msgAndArgs...) } // NotSubsetf is the same as [Assertions.NotSubset], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotSubsetf(list any, subset any, msg string, args ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotSubset(a.T, list, subset, forwardArgs(msg, args)...) } // NotZero is the same as [NotZero], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotZero(i any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotZero(a.T, i, msgAndArgs...) } // NotZerof is the same as [Assertions.NotZero], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) NotZerof(i any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.NotZero(a.T, i, forwardArgs(msg, args)...) } // Panics is the same as [Panics], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Panics(f func(), msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Panics(a.T, f, msgAndArgs...) } // Panicsf is the same as [Assertions.Panics], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Panicsf(f func(), msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Panics(a.T, f, forwardArgs(msg, args)...) } // PanicsWithError is the same as [PanicsWithError], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithError(errString string, f func(), msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.PanicsWithError(a.T, errString, f, msgAndArgs...) } // PanicsWithErrorf is the same as [Assertions.PanicsWithError], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithErrorf(errString string, f func(), msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.PanicsWithError(a.T, errString, f, forwardArgs(msg, args)...) } // PanicsWithValue is the same as [PanicsWithValue], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithValue(expected any, f func(), msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.PanicsWithValue(a.T, expected, f, msgAndArgs...) } // PanicsWithValuef is the same as [Assertions.PanicsWithValue], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) PanicsWithValuef(expected any, f func(), msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.PanicsWithValue(a.T, expected, f, forwardArgs(msg, args)...) } // Positive is the same as [Positive], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Positive(e any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Positive(a.T, e, msgAndArgs...) } // Positivef is the same as [Assertions.Positive], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Positivef(e any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Positive(a.T, e, forwardArgs(msg, args)...) } // Regexp is the same as [Regexp], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Regexp(rx any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Regexp(a.T, rx, actual, msgAndArgs...) } // Regexpf is the same as [Assertions.Regexp], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Regexpf(rx any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Regexp(a.T, rx, actual, forwardArgs(msg, args)...) } // Same is the same as [Same], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Same(expected any, actual any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Same(a.T, expected, actual, msgAndArgs...) } // Samef is the same as [Assertions.Same], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Samef(expected any, actual any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Same(a.T, expected, actual, forwardArgs(msg, args)...) } // Subset is the same as [Subset], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Subset(list any, subset any, msgAndArgs ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Subset(a.T, list, subset, msgAndArgs...) } // Subsetf is the same as [Assertions.Subset], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Subsetf(list any, subset any, msg string, args ...any) (ok bool) { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Subset(a.T, list, subset, forwardArgs(msg, args)...) } // True is the same as [True], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) True(value bool, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.True(a.T, value, msgAndArgs...) } // Truef is the same as [Assertions.True], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Truef(value bool, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.True(a.T, value, forwardArgs(msg, args)...) } // WithinDuration is the same as [WithinDuration], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinDuration(expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.WithinDuration(a.T, expected, actual, delta, msgAndArgs...) } // WithinDurationf is the same as [Assertions.WithinDuration], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.WithinDuration(a.T, expected, actual, delta, forwardArgs(msg, args)...) } // WithinRange is the same as [WithinRange], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinRange(actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.WithinRange(a.T, actual, start, end, msgAndArgs...) } // WithinRangef is the same as [Assertions.WithinRange], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.WithinRange(a.T, actual, start, end, forwardArgs(msg, args)...) } // YAMLEq is the same as [YAMLEq], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEq(expected string, actual string, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.YAMLEq(a.T, expected, actual, msgAndArgs...) } // YAMLEqf is the same as [Assertions.YAMLEq], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEqf(expected string, actual string, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.YAMLEq(a.T, expected, actual, forwardArgs(msg, args)...) } // YAMLEqBytes is the same as [YAMLEqBytes], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEqBytes(expected []byte, actual []byte, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.YAMLEqBytes(a.T, expected, actual, msgAndArgs...) } // YAMLEqBytesf is the same as [Assertions.YAMLEqBytes], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) YAMLEqBytesf(expected []byte, actual []byte, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.YAMLEqBytes(a.T, expected, actual, forwardArgs(msg, args)...) } // Zero is the same as [Zero], as a method rather than a package-level function. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Zero(i any, msgAndArgs ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Zero(a.T, i, msgAndArgs...) } // Zerof is the same as [Assertions.Zero], but it accepts a format string to format arguments like [fmt.Printf]. // // Upon failure, the test [T] is marked as failed and continues execution. func (a *Assertions) Zerof(i any, msg string, args ...any) bool { if h, ok := a.T.(H); ok { h.Helper() } return assertions.Zero(a.T, i, forwardArgs(msg, args)...) } go-openapi-testify-c10ca71/assert/assert_forward_test.go000066400000000000000000003155321520301377500235660ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "fmt" "io" "net/http" "net/url" "path/filepath" "reflect" "testing" "time" ) func TestAssertionsBlocked(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Blocked(make(chan struct{})) if !result { t.Error("Assertions.Blocked should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Blocked(sendChanMessage()) if result { t.Error("Assertions.Blocked should return false on failure") } if !mock.failed { t.Error("Assertions.Blocked should mark test as failed") } }) } func TestAssertionsCondition(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Condition(func() bool { return true }) if !result { t.Error("Assertions.Condition should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Condition(func() bool { return false }) if result { t.Error("Assertions.Condition should return false on failure") } if !mock.failed { t.Error("Assertions.Condition should mark test as failed") } }) } func TestAssertionsContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Contains([]string{"A", "B"}, "A") if !result { t.Error("Assertions.Contains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Contains([]string{"A", "B"}, "C") if result { t.Error("Assertions.Contains should return false on failure") } if !mock.failed { t.Error("Assertions.Contains should mark test as failed") } }) } func TestAssertionsDirExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirExists(filepath.Join(testDataPath(), "existing_dir")) if !result { t.Error("Assertions.DirExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirExists(filepath.Join(testDataPath(), "non_existing_dir")) if result { t.Error("Assertions.DirExists should return false on failure") } if !mock.failed { t.Error("Assertions.DirExists should mark test as failed") } }) } func TestAssertionsDirNotExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirNotExists(filepath.Join(testDataPath(), "non_existing_dir")) if !result { t.Error("Assertions.DirNotExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirNotExists(filepath.Join(testDataPath(), "existing_dir")) if result { t.Error("Assertions.DirNotExists should return false on failure") } if !mock.failed { t.Error("Assertions.DirNotExists should mark test as failed") } }) } func TestAssertionsElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ElementsMatch([]int{1, 3, 2, 3}, []int{1, 3, 3, 2}) if !result { t.Error("Assertions.ElementsMatch should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ElementsMatch([]int{1, 2, 3}, []int{1, 2, 4}) if result { t.Error("Assertions.ElementsMatch should return false on failure") } if !mock.failed { t.Error("Assertions.ElementsMatch should mark test as failed") } }) } func TestAssertionsEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Empty("") if !result { t.Error("Assertions.Empty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Empty("not empty") if result { t.Error("Assertions.Empty should return false on failure") } if !mock.failed { t.Error("Assertions.Empty should mark test as failed") } }) } func TestAssertionsEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Equal(123, 123) if !result { t.Error("Assertions.Equal should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Equal(123, 456) if result { t.Error("Assertions.Equal should return false on failure") } if !mock.failed { t.Error("Assertions.Equal should mark test as failed") } }) } func TestAssertionsEqualError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualError(ErrTest, "assert.ErrTest general error for testing") if !result { t.Error("Assertions.EqualError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualError(ErrTest, "wrong error message") if result { t.Error("Assertions.EqualError should return false on failure") } if !mock.failed { t.Error("Assertions.EqualError should mark test as failed") } }) } func TestAssertionsEqualExportedValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualExportedValues(&dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) if !result { t.Error("Assertions.EqualExportedValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualExportedValues(&dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1}) if result { t.Error("Assertions.EqualExportedValues should return false on failure") } if !mock.failed { t.Error("Assertions.EqualExportedValues should mark test as failed") } }) } func TestAssertionsEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualValues(uint32(123), int32(123)) if !result { t.Error("Assertions.EqualValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualValues(uint32(123), int32(456)) if result { t.Error("Assertions.EqualValues should return false on failure") } if !mock.failed { t.Error("Assertions.EqualValues should mark test as failed") } }) } func TestAssertionsError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Error(ErrTest) if !result { t.Error("Assertions.Error should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Error(nil) if result { t.Error("Assertions.Error should return false on failure") } if !mock.failed { t.Error("Assertions.Error should mark test as failed") } }) } func TestAssertionsErrorAs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorAs(fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) if !result { t.Error("Assertions.ErrorAs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorAs(ErrTest, new(*dummyError)) if result { t.Error("Assertions.ErrorAs should return false on failure") } if !mock.failed { t.Error("Assertions.ErrorAs should mark test as failed") } }) } func TestAssertionsErrorContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorContains(ErrTest, "general error") if !result { t.Error("Assertions.ErrorContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorContains(ErrTest, "not in message") if result { t.Error("Assertions.ErrorContains should return false on failure") } if !mock.failed { t.Error("Assertions.ErrorContains should mark test as failed") } }) } func TestAssertionsErrorIs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorIs(fmt.Errorf("wrap: %w", io.EOF), io.EOF) if !result { t.Error("Assertions.ErrorIs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorIs(ErrTest, io.EOF) if result { t.Error("Assertions.ErrorIs should return false on failure") } if !mock.failed { t.Error("Assertions.ErrorIs should mark test as failed") } }) } func TestAssertionsExactly(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Exactly(int32(123), int32(123)) if !result { t.Error("Assertions.Exactly should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Exactly(int32(123), int64(123)) if result { t.Error("Assertions.Exactly should return false on failure") } if !mock.failed { t.Error("Assertions.Exactly should mark test as failed") } }) } func TestAssertionsFail(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Fail("failed") if result { t.Error("Assertions.Fail should return false on failure") } if !mock.failed { t.Error("Assertions.Fail should mark test as failed") } }) } func TestAssertionsFailNow(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockFailNowT) a := New(mock) result := a.FailNow("failed") if result { t.Error("Assertions.FailNow should return false on failure") } if !mock.failed { t.Error("Assertions.FailNow should call FailNow()") } }) } func TestAssertionsFalse(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.False(1 == 0) if !result { t.Error("Assertions.False should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.False(1 == 1) if result { t.Error("Assertions.False should return false on failure") } if !mock.failed { t.Error("Assertions.False should mark test as failed") } }) } func TestAssertionsFileEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileEmpty(filepath.Join(testDataPath(), "empty_file")) if !result { t.Error("Assertions.FileEmpty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileEmpty(filepath.Join(testDataPath(), "existing_file")) if result { t.Error("Assertions.FileEmpty should return false on failure") } if !mock.failed { t.Error("Assertions.FileEmpty should mark test as failed") } }) } func TestAssertionsFileExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileExists(filepath.Join(testDataPath(), "existing_file")) if !result { t.Error("Assertions.FileExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileExists(filepath.Join(testDataPath(), "non_existing_file")) if result { t.Error("Assertions.FileExists should return false on failure") } if !mock.failed { t.Error("Assertions.FileExists should mark test as failed") } }) } func TestAssertionsFileNotEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotEmpty(filepath.Join(testDataPath(), "existing_file")) if !result { t.Error("Assertions.FileNotEmpty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotEmpty(filepath.Join(testDataPath(), "empty_file")) if result { t.Error("Assertions.FileNotEmpty should return false on failure") } if !mock.failed { t.Error("Assertions.FileNotEmpty should mark test as failed") } }) } func TestAssertionsFileNotExists(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotExists(filepath.Join(testDataPath(), "non_existing_file")) if !result { t.Error("Assertions.FileNotExists should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotExists(filepath.Join(testDataPath(), "existing_file")) if result { t.Error("Assertions.FileNotExists should return false on failure") } if !mock.failed { t.Error("Assertions.FileNotExists should mark test as failed") } }) } func TestAssertionsGreater(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Greater(2, 1) if !result { t.Error("Assertions.Greater should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Greater(1, 2) if result { t.Error("Assertions.Greater should return false on failure") } if !mock.failed { t.Error("Assertions.Greater should mark test as failed") } }) } func TestAssertionsGreaterOrEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.GreaterOrEqual(2, 1) if !result { t.Error("Assertions.GreaterOrEqual should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.GreaterOrEqual(1, 2) if result { t.Error("Assertions.GreaterOrEqual should return false on failure") } if !mock.failed { t.Error("Assertions.GreaterOrEqual should mark test as failed") } }) } func TestAssertionsHTTPBodyContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyContains(httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") if !result { t.Error("Assertions.HTTPBodyContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyContains(httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!") if result { t.Error("Assertions.HTTPBodyContains should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPBodyContains should mark test as failed") } }) } func TestAssertionsHTTPBodyNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyNotContains(httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") if !result { t.Error("Assertions.HTTPBodyNotContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyNotContains(httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!") if result { t.Error("Assertions.HTTPBodyNotContains should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPBodyNotContains should mark test as failed") } }) } func TestAssertionsHTTPError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPError(httpError, "GET", "/", nil) if !result { t.Error("Assertions.HTTPError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPError(httpOK, "GET", "/", nil) if result { t.Error("Assertions.HTTPError should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPError should mark test as failed") } }) } func TestAssertionsHTTPRedirect(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPRedirect(httpRedirect, "GET", "/", nil) if !result { t.Error("Assertions.HTTPRedirect should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPRedirect(httpError, "GET", "/", nil) if result { t.Error("Assertions.HTTPRedirect should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPRedirect should mark test as failed") } }) } func TestAssertionsHTTPStatusCode(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPStatusCode(httpOK, "GET", "/", nil, http.StatusOK) if !result { t.Error("Assertions.HTTPStatusCode should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPStatusCode(httpError, "GET", "/", nil, http.StatusOK) if result { t.Error("Assertions.HTTPStatusCode should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPStatusCode should mark test as failed") } }) } func TestAssertionsHTTPSuccess(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPSuccess(httpOK, "GET", "/", nil) if !result { t.Error("Assertions.HTTPSuccess should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPSuccess(httpError, "GET", "/", nil) if result { t.Error("Assertions.HTTPSuccess should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPSuccess should mark test as failed") } }) } func TestAssertionsImplements(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Implements(ptr(dummyInterface), new(testing.T)) if !result { t.Error("Assertions.Implements should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Implements((*error)(nil), new(testing.T)) if result { t.Error("Assertions.Implements should return false on failure") } if !mock.failed { t.Error("Assertions.Implements should mark test as failed") } }) } func TestAssertionsInDelta(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDelta(1.0, 1.01, 0.02) if !result { t.Error("Assertions.InDelta should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDelta(1.0, 1.1, 0.05) if result { t.Error("Assertions.InDelta should return false on failure") } if !mock.failed { t.Error("Assertions.InDelta should mark test as failed") } }) } func TestAssertionsInDeltaMapValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaMapValues(map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) if !result { t.Error("Assertions.InDeltaMapValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaMapValues(map[string]float64{"a": 1.0}, map[string]float64{"a": 1.1}, 0.05) if result { t.Error("Assertions.InDeltaMapValues should return false on failure") } if !mock.failed { t.Error("Assertions.InDeltaMapValues should mark test as failed") } }) } func TestAssertionsInDeltaSlice(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaSlice([]float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) if !result { t.Error("Assertions.InDeltaSlice should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaSlice([]float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05) if result { t.Error("Assertions.InDeltaSlice should return false on failure") } if !mock.failed { t.Error("Assertions.InDeltaSlice should mark test as failed") } }) } func TestAssertionsInEpsilon(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilon(100.0, 101.0, 0.02) if !result { t.Error("Assertions.InEpsilon should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilon(100.0, 110.0, 0.05) if result { t.Error("Assertions.InEpsilon should return false on failure") } if !mock.failed { t.Error("Assertions.InEpsilon should mark test as failed") } }) } func TestAssertionsInEpsilonSlice(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSlice([]float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) if !result { t.Error("Assertions.InEpsilonSlice should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSlice([]float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05) if result { t.Error("Assertions.InEpsilonSlice should return false on failure") } if !mock.failed { t.Error("Assertions.InEpsilonSlice should mark test as failed") } }) } func TestAssertionsInEpsilonSymmetric(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSymmetric(100.0, 101.0, 0.02) if !result { t.Error("Assertions.InEpsilonSymmetric should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSymmetric(100.0, 110.0, 0.05) if result { t.Error("Assertions.InEpsilonSymmetric should return false on failure") } if !mock.failed { t.Error("Assertions.InEpsilonSymmetric should mark test as failed") } }) } func TestAssertionsIsDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsDecreasing([]int{3, 2, 1}) if !result { t.Error("Assertions.IsDecreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsDecreasing([]int{1, 2, 3}) if result { t.Error("Assertions.IsDecreasing should return false on failure") } if !mock.failed { t.Error("Assertions.IsDecreasing should mark test as failed") } }) } func TestAssertionsIsIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsIncreasing([]int{1, 2, 3}) if !result { t.Error("Assertions.IsIncreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsIncreasing([]int{1, 1, 2}) if result { t.Error("Assertions.IsIncreasing should return false on failure") } if !mock.failed { t.Error("Assertions.IsIncreasing should mark test as failed") } }) } func TestAssertionsIsNonDecreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonDecreasing([]int{1, 1, 2}) if !result { t.Error("Assertions.IsNonDecreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonDecreasing([]int{2, 1, 0}) if result { t.Error("Assertions.IsNonDecreasing should return false on failure") } if !mock.failed { t.Error("Assertions.IsNonDecreasing should mark test as failed") } }) } func TestAssertionsIsNonIncreasing(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonIncreasing([]int{2, 1, 1}) if !result { t.Error("Assertions.IsNonIncreasing should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonIncreasing([]int{1, 2, 3}) if result { t.Error("Assertions.IsNonIncreasing should return false on failure") } if !mock.failed { t.Error("Assertions.IsNonIncreasing should mark test as failed") } }) } func TestAssertionsIsNotType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNotType(int32(123), int64(456)) if !result { t.Error("Assertions.IsNotType should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNotType(123, 456) if result { t.Error("Assertions.IsNotType should return false on failure") } if !mock.failed { t.Error("Assertions.IsNotType should mark test as failed") } }) } func TestAssertionsIsType(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsType(123, 456) if !result { t.Error("Assertions.IsType should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsType(int32(123), int64(456)) if result { t.Error("Assertions.IsType should return false on failure") } if !mock.failed { t.Error("Assertions.IsType should mark test as failed") } }) } func TestAssertionsJSONEq(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if !result { t.Error("Assertions.JSONEq should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEq(`{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`) if result { t.Error("Assertions.JSONEq should return false on failure") } if !mock.failed { t.Error("Assertions.JSONEq should mark test as failed") } }) } func TestAssertionsJSONEqBytes(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEqBytes([]byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) if !result { t.Error("Assertions.JSONEqBytes should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEqBytes([]byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`)) if result { t.Error("Assertions.JSONEqBytes should return false on failure") } if !mock.failed { t.Error("Assertions.JSONEqBytes should mark test as failed") } }) } func TestAssertionsKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Kind(reflect.String, "hello") if !result { t.Error("Assertions.Kind should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Kind(reflect.String, 0) if result { t.Error("Assertions.Kind should return false on failure") } if !mock.failed { t.Error("Assertions.Kind should mark test as failed") } }) } func TestAssertionsLen(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Len([]string{"A", "B"}, 2) if !result { t.Error("Assertions.Len should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Len([]string{"A", "B"}, 1) if result { t.Error("Assertions.Len should return false on failure") } if !mock.failed { t.Error("Assertions.Len should mark test as failed") } }) } func TestAssertionsLess(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Less(1, 2) if !result { t.Error("Assertions.Less should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Less(2, 1) if result { t.Error("Assertions.Less should return false on failure") } if !mock.failed { t.Error("Assertions.Less should mark test as failed") } }) } func TestAssertionsLessOrEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.LessOrEqual(1, 2) if !result { t.Error("Assertions.LessOrEqual should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.LessOrEqual(2, 1) if result { t.Error("Assertions.LessOrEqual should return false on failure") } if !mock.failed { t.Error("Assertions.LessOrEqual should mark test as failed") } }) } func TestAssertionsNegative(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Negative(-1) if !result { t.Error("Assertions.Negative should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Negative(1) if result { t.Error("Assertions.Negative should return false on failure") } if !mock.failed { t.Error("Assertions.Negative should mark test as failed") } }) } func TestAssertionsNil(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Nil(nil) if !result { t.Error("Assertions.Nil should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Nil("not nil") if result { t.Error("Assertions.Nil should return false on failure") } if !mock.failed { t.Error("Assertions.Nil should mark test as failed") } }) } func TestAssertionsNoError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoError(nil) if !result { t.Error("Assertions.NoError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoError(ErrTest) if result { t.Error("Assertions.NoError should return false on failure") } if !mock.failed { t.Error("Assertions.NoError should mark test as failed") } }) } func TestAssertionsNoFileDescriptorLeak(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoFileDescriptorLeak(func() {}) if !result { t.Error("Assertions.NoFileDescriptorLeak should return true on success") } }) } func TestAssertionsNoGoRoutineLeak(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoGoRoutineLeak(func() {}) if !result { t.Error("Assertions.NoGoRoutineLeak should return true on success") } }) } func TestAssertionsNotBlocked(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotBlocked(sendChanMessage()) if !result { t.Error("Assertions.NotBlocked should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotBlocked(make(chan struct{})) if result { t.Error("Assertions.NotBlocked should return false on failure") } if !mock.failed { t.Error("Assertions.NotBlocked should mark test as failed") } }) } func TestAssertionsNotContains(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotContains([]string{"A", "B"}, "C") if !result { t.Error("Assertions.NotContains should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotContains([]string{"A", "B"}, "B") if result { t.Error("Assertions.NotContains should return false on failure") } if !mock.failed { t.Error("Assertions.NotContains should mark test as failed") } }) } func TestAssertionsNotElementsMatch(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotElementsMatch([]int{1, 2, 3}, []int{1, 2, 4}) if !result { t.Error("Assertions.NotElementsMatch should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotElementsMatch([]int{1, 3, 2, 3}, []int{1, 3, 3, 2}) if result { t.Error("Assertions.NotElementsMatch should return false on failure") } if !mock.failed { t.Error("Assertions.NotElementsMatch should mark test as failed") } }) } func TestAssertionsNotEmpty(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEmpty("not empty") if !result { t.Error("Assertions.NotEmpty should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEmpty("") if result { t.Error("Assertions.NotEmpty should return false on failure") } if !mock.failed { t.Error("Assertions.NotEmpty should mark test as failed") } }) } func TestAssertionsNotEqual(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqual(123, 456) if !result { t.Error("Assertions.NotEqual should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqual(123, 123) if result { t.Error("Assertions.NotEqual should return false on failure") } if !mock.failed { t.Error("Assertions.NotEqual should mark test as failed") } }) } func TestAssertionsNotEqualValues(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqualValues(uint32(123), int32(456)) if !result { t.Error("Assertions.NotEqualValues should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqualValues(uint32(123), int32(123)) if result { t.Error("Assertions.NotEqualValues should return false on failure") } if !mock.failed { t.Error("Assertions.NotEqualValues should mark test as failed") } }) } func TestAssertionsNotErrorAs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorAs(ErrTest, new(*dummyError)) if !result { t.Error("Assertions.NotErrorAs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorAs(fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) if result { t.Error("Assertions.NotErrorAs should return false on failure") } if !mock.failed { t.Error("Assertions.NotErrorAs should mark test as failed") } }) } func TestAssertionsNotErrorIs(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorIs(ErrTest, io.EOF) if !result { t.Error("Assertions.NotErrorIs should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorIs(fmt.Errorf("wrap: %w", io.EOF), io.EOF) if result { t.Error("Assertions.NotErrorIs should return false on failure") } if !mock.failed { t.Error("Assertions.NotErrorIs should mark test as failed") } }) } func TestAssertionsNotImplements(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotImplements((*error)(nil), new(testing.T)) if !result { t.Error("Assertions.NotImplements should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotImplements(ptr(dummyInterface), new(testing.T)) if result { t.Error("Assertions.NotImplements should return false on failure") } if !mock.failed { t.Error("Assertions.NotImplements should mark test as failed") } }) } func TestAssertionsNotKind(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotKind(reflect.String, 0) if !result { t.Error("Assertions.NotKind should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotKind(reflect.String, "hello") if result { t.Error("Assertions.NotKind should return false on failure") } if !mock.failed { t.Error("Assertions.NotKind should mark test as failed") } }) } func TestAssertionsNotNil(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotNil("not nil") if !result { t.Error("Assertions.NotNil should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotNil(nil) if result { t.Error("Assertions.NotNil should return false on failure") } if !mock.failed { t.Error("Assertions.NotNil should mark test as failed") } }) } func TestAssertionsNotPanics(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotPanics(func() {}) if !result { t.Error("Assertions.NotPanics should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotPanics(func() { panic("panicking") }) if result { t.Error("Assertions.NotPanics should return false on failure") } if !mock.failed { t.Error("Assertions.NotPanics should mark test as failed") } }) } func TestAssertionsNotRegexp(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotRegexp("^start", "not starting") if !result { t.Error("Assertions.NotRegexp should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotRegexp("^start", "starting") if result { t.Error("Assertions.NotRegexp should return false on failure") } if !mock.failed { t.Error("Assertions.NotRegexp should mark test as failed") } }) } func TestAssertionsNotSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSame(&staticVar, ptr("static string")) if !result { t.Error("Assertions.NotSame should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSame(&staticVar, staticVarPtr) if result { t.Error("Assertions.NotSame should return false on failure") } if !mock.failed { t.Error("Assertions.NotSame should mark test as failed") } }) } func TestAssertionsNotSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSubset([]int{1, 2, 3}, []int{4, 5}) if !result { t.Error("Assertions.NotSubset should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSubset([]int{1, 2, 3}, []int{1, 2}) if result { t.Error("Assertions.NotSubset should return false on failure") } if !mock.failed { t.Error("Assertions.NotSubset should mark test as failed") } }) } func TestAssertionsNotZero(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotZero(1) if !result { t.Error("Assertions.NotZero should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotZero(0) if result { t.Error("Assertions.NotZero should return false on failure") } if !mock.failed { t.Error("Assertions.NotZero should mark test as failed") } }) } func TestAssertionsPanics(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panics(func() { panic("panicking") }) if !result { t.Error("Assertions.Panics should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panics(func() {}) if result { t.Error("Assertions.Panics should return false on failure") } if !mock.failed { t.Error("Assertions.Panics should mark test as failed") } }) } func TestAssertionsPanicsWithError(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithError(ErrTest.Error(), func() { panic(ErrTest) }) if !result { t.Error("Assertions.PanicsWithError should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithError(ErrTest.Error(), func() {}) if result { t.Error("Assertions.PanicsWithError should return false on failure") } if !mock.failed { t.Error("Assertions.PanicsWithError should mark test as failed") } }) } func TestAssertionsPanicsWithValue(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithValue("panicking", func() { panic("panicking") }) if !result { t.Error("Assertions.PanicsWithValue should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithValue("panicking", func() {}) if result { t.Error("Assertions.PanicsWithValue should return false on failure") } if !mock.failed { t.Error("Assertions.PanicsWithValue should mark test as failed") } }) } func TestAssertionsPositive(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Positive(1) if !result { t.Error("Assertions.Positive should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Positive(-1) if result { t.Error("Assertions.Positive should return false on failure") } if !mock.failed { t.Error("Assertions.Positive should mark test as failed") } }) } func TestAssertionsRegexp(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Regexp("^start", "starting") if !result { t.Error("Assertions.Regexp should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Regexp("^start", "not starting") if result { t.Error("Assertions.Regexp should return false on failure") } if !mock.failed { t.Error("Assertions.Regexp should mark test as failed") } }) } func TestAssertionsSame(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Same(&staticVar, staticVarPtr) if !result { t.Error("Assertions.Same should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Same(&staticVar, ptr("static string")) if result { t.Error("Assertions.Same should return false on failure") } if !mock.failed { t.Error("Assertions.Same should mark test as failed") } }) } func TestAssertionsSubset(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Subset([]int{1, 2, 3}, []int{1, 2}) if !result { t.Error("Assertions.Subset should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Subset([]int{1, 2, 3}, []int{4, 5}) if result { t.Error("Assertions.Subset should return false on failure") } if !mock.failed { t.Error("Assertions.Subset should mark test as failed") } }) } func TestAssertionsTrue(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.True(1 == 1) if !result { t.Error("Assertions.True should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.True(1 == 0) if result { t.Error("Assertions.True should return false on failure") } if !mock.failed { t.Error("Assertions.True should mark test as failed") } }) } func TestAssertionsWithinDuration(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinDuration(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) if !result { t.Error("Assertions.WithinDuration should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinDuration(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second) if result { t.Error("Assertions.WithinDuration should return false on failure") } if !mock.failed { t.Error("Assertions.WithinDuration should mark test as failed") } }) } func TestAssertionsWithinRange(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinRange(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) if !result { t.Error("Assertions.WithinRange should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinRange(time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) if result { t.Error("Assertions.WithinRange should return false on failure") } if !mock.failed { t.Error("Assertions.WithinRange should mark test as failed") } }) } func TestAssertionsYAMLEq(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panics(func() { a.YAMLEq("key: value", "key: value") }, "should panic without the yaml feature enabled.") if !result { t.Error("Assertions.YAMLEq should return true on panic") } if mock.failed { t.Error("Assertions.YAMLEq should panic as expected") } }) } func TestAssertionsYAMLEqBytes(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panics(func() { a.YAMLEqBytes([]byte("key: value"), []byte("key: value")) }, "should panic without the yaml feature enabled.") if !result { t.Error("Assertions.YAMLEqBytes should return true on panic") } if mock.failed { t.Error("Assertions.YAMLEqBytes should panic as expected") } }) } func TestAssertionsZero(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Zero(0) if !result { t.Error("Assertions.Zero should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Zero(1) if result { t.Error("Assertions.Zero should return false on failure") } if !mock.failed { t.Error("Assertions.Zero should mark test as failed") } }) } func TestAssertionsBlockedf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Blockedf(make(chan struct{}), "test message") if !result { t.Error("Assertions.Blockedf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Blockedf(sendChanMessage(), "test message") if result { t.Error("Assertions.Blockedf should return false on failure") } if !mock.failed { t.Error("Assertions.Blockedf should mark test as failed") } }) } func TestAssertionsConditionf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Conditionf(func() bool { return true }, "test message") if !result { t.Error("Assertions.Conditionf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Conditionf(func() bool { return false }, "test message") if result { t.Error("Assertions.Conditionf should return false on failure") } if !mock.failed { t.Error("Assertions.Conditionf should mark test as failed") } }) } func TestAssertionsContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Containsf([]string{"A", "B"}, "A", "test message") if !result { t.Error("Assertions.Containsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Containsf([]string{"A", "B"}, "C", "test message") if result { t.Error("Assertions.Containsf should return false on failure") } if !mock.failed { t.Error("Assertions.Containsf should mark test as failed") } }) } func TestAssertionsDirExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirExistsf(filepath.Join(testDataPath(), "existing_dir"), "test message") if !result { t.Error("Assertions.DirExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirExistsf(filepath.Join(testDataPath(), "non_existing_dir"), "test message") if result { t.Error("Assertions.DirExistsf should return false on failure") } if !mock.failed { t.Error("Assertions.DirExistsf should mark test as failed") } }) } func TestAssertionsDirNotExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirNotExistsf(filepath.Join(testDataPath(), "non_existing_dir"), "test message") if !result { t.Error("Assertions.DirNotExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.DirNotExistsf(filepath.Join(testDataPath(), "existing_dir"), "test message") if result { t.Error("Assertions.DirNotExistsf should return false on failure") } if !mock.failed { t.Error("Assertions.DirNotExistsf should mark test as failed") } }) } func TestAssertionsElementsMatchf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ElementsMatchf([]int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") if !result { t.Error("Assertions.ElementsMatchf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ElementsMatchf([]int{1, 2, 3}, []int{1, 2, 4}, "test message") if result { t.Error("Assertions.ElementsMatchf should return false on failure") } if !mock.failed { t.Error("Assertions.ElementsMatchf should mark test as failed") } }) } func TestAssertionsEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Emptyf("", "test message") if !result { t.Error("Assertions.Emptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Emptyf("not empty", "test message") if result { t.Error("Assertions.Emptyf should return false on failure") } if !mock.failed { t.Error("Assertions.Emptyf should mark test as failed") } }) } func TestAssertionsEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Equalf(123, 123, "test message") if !result { t.Error("Assertions.Equalf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Equalf(123, 456, "test message") if result { t.Error("Assertions.Equalf should return false on failure") } if !mock.failed { t.Error("Assertions.Equalf should mark test as failed") } }) } func TestAssertionsEqualErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualErrorf(ErrTest, "assert.ErrTest general error for testing", "test message") if !result { t.Error("Assertions.EqualErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualErrorf(ErrTest, "wrong error message", "test message") if result { t.Error("Assertions.EqualErrorf should return false on failure") } if !mock.failed { t.Error("Assertions.EqualErrorf should mark test as failed") } }) } func TestAssertionsEqualExportedValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualExportedValuesf(&dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}, "test message") if !result { t.Error("Assertions.EqualExportedValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualExportedValuesf(&dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1}, "test message") if result { t.Error("Assertions.EqualExportedValuesf should return false on failure") } if !mock.failed { t.Error("Assertions.EqualExportedValuesf should mark test as failed") } }) } func TestAssertionsEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualValuesf(uint32(123), int32(123), "test message") if !result { t.Error("Assertions.EqualValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.EqualValuesf(uint32(123), int32(456), "test message") if result { t.Error("Assertions.EqualValuesf should return false on failure") } if !mock.failed { t.Error("Assertions.EqualValuesf should mark test as failed") } }) } func TestAssertionsErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Errorf(ErrTest, "test message") if !result { t.Error("Assertions.Errorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Errorf(nil, "test message") if result { t.Error("Assertions.Errorf should return false on failure") } if !mock.failed { t.Error("Assertions.Errorf should mark test as failed") } }) } func TestAssertionsErrorAsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorAsf(fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") if !result { t.Error("Assertions.ErrorAsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorAsf(ErrTest, new(*dummyError), "test message") if result { t.Error("Assertions.ErrorAsf should return false on failure") } if !mock.failed { t.Error("Assertions.ErrorAsf should mark test as failed") } }) } func TestAssertionsErrorContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorContainsf(ErrTest, "general error", "test message") if !result { t.Error("Assertions.ErrorContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorContainsf(ErrTest, "not in message", "test message") if result { t.Error("Assertions.ErrorContainsf should return false on failure") } if !mock.failed { t.Error("Assertions.ErrorContainsf should mark test as failed") } }) } func TestAssertionsErrorIsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorIsf(fmt.Errorf("wrap: %w", io.EOF), io.EOF, "test message") if !result { t.Error("Assertions.ErrorIsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.ErrorIsf(ErrTest, io.EOF, "test message") if result { t.Error("Assertions.ErrorIsf should return false on failure") } if !mock.failed { t.Error("Assertions.ErrorIsf should mark test as failed") } }) } func TestAssertionsExactlyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Exactlyf(int32(123), int32(123), "test message") if !result { t.Error("Assertions.Exactlyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Exactlyf(int32(123), int64(123), "test message") if result { t.Error("Assertions.Exactlyf should return false on failure") } if !mock.failed { t.Error("Assertions.Exactlyf should mark test as failed") } }) } func TestAssertionsFailf(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Failf("failed", "test message") if result { t.Error("Assertions.Failf should return false on failure") } if !mock.failed { t.Error("Assertions.Failf should mark test as failed") } }) } func TestAssertionsFailNowf(t *testing.T) { t.Parallel() t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockFailNowT) a := New(mock) result := a.FailNowf("failed", "test message") if result { t.Error("Assertions.FailNowf should return false on failure") } if !mock.failed { t.Error("Assertions.FailNowf should call FailNow()") } }) } func TestAssertionsFalsef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Falsef(1 == 0, "test message") if !result { t.Error("Assertions.Falsef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Falsef(1 == 1, "test message") if result { t.Error("Assertions.Falsef should return false on failure") } if !mock.failed { t.Error("Assertions.Falsef should mark test as failed") } }) } func TestAssertionsFileEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileEmptyf(filepath.Join(testDataPath(), "empty_file"), "test message") if !result { t.Error("Assertions.FileEmptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileEmptyf(filepath.Join(testDataPath(), "existing_file"), "test message") if result { t.Error("Assertions.FileEmptyf should return false on failure") } if !mock.failed { t.Error("Assertions.FileEmptyf should mark test as failed") } }) } func TestAssertionsFileExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileExistsf(filepath.Join(testDataPath(), "existing_file"), "test message") if !result { t.Error("Assertions.FileExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileExistsf(filepath.Join(testDataPath(), "non_existing_file"), "test message") if result { t.Error("Assertions.FileExistsf should return false on failure") } if !mock.failed { t.Error("Assertions.FileExistsf should mark test as failed") } }) } func TestAssertionsFileNotEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotEmptyf(filepath.Join(testDataPath(), "existing_file"), "test message") if !result { t.Error("Assertions.FileNotEmptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotEmptyf(filepath.Join(testDataPath(), "empty_file"), "test message") if result { t.Error("Assertions.FileNotEmptyf should return false on failure") } if !mock.failed { t.Error("Assertions.FileNotEmptyf should mark test as failed") } }) } func TestAssertionsFileNotExistsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotExistsf(filepath.Join(testDataPath(), "non_existing_file"), "test message") if !result { t.Error("Assertions.FileNotExistsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.FileNotExistsf(filepath.Join(testDataPath(), "existing_file"), "test message") if result { t.Error("Assertions.FileNotExistsf should return false on failure") } if !mock.failed { t.Error("Assertions.FileNotExistsf should mark test as failed") } }) } func TestAssertionsGreaterf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Greaterf(2, 1, "test message") if !result { t.Error("Assertions.Greaterf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Greaterf(1, 2, "test message") if result { t.Error("Assertions.Greaterf should return false on failure") } if !mock.failed { t.Error("Assertions.Greaterf should mark test as failed") } }) } func TestAssertionsGreaterOrEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.GreaterOrEqualf(2, 1, "test message") if !result { t.Error("Assertions.GreaterOrEqualf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.GreaterOrEqualf(1, 2, "test message") if result { t.Error("Assertions.GreaterOrEqualf should return false on failure") } if !mock.failed { t.Error("Assertions.GreaterOrEqualf should mark test as failed") } }) } func TestAssertionsHTTPBodyContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyContainsf(httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!", "test message") if !result { t.Error("Assertions.HTTPBodyContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyContainsf(httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!", "test message") if result { t.Error("Assertions.HTTPBodyContainsf should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPBodyContainsf should mark test as failed") } }) } func TestAssertionsHTTPBodyNotContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyNotContainsf(httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!", "test message") if !result { t.Error("Assertions.HTTPBodyNotContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPBodyNotContainsf(httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!", "test message") if result { t.Error("Assertions.HTTPBodyNotContainsf should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPBodyNotContainsf should mark test as failed") } }) } func TestAssertionsHTTPErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPErrorf(httpError, "GET", "/", nil, "test message") if !result { t.Error("Assertions.HTTPErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPErrorf(httpOK, "GET", "/", nil, "test message") if result { t.Error("Assertions.HTTPErrorf should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPErrorf should mark test as failed") } }) } func TestAssertionsHTTPRedirectf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPRedirectf(httpRedirect, "GET", "/", nil, "test message") if !result { t.Error("Assertions.HTTPRedirectf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPRedirectf(httpError, "GET", "/", nil, "test message") if result { t.Error("Assertions.HTTPRedirectf should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPRedirectf should mark test as failed") } }) } func TestAssertionsHTTPStatusCodef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPStatusCodef(httpOK, "GET", "/", nil, http.StatusOK, "test message") if !result { t.Error("Assertions.HTTPStatusCodef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPStatusCodef(httpError, "GET", "/", nil, http.StatusOK, "test message") if result { t.Error("Assertions.HTTPStatusCodef should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPStatusCodef should mark test as failed") } }) } func TestAssertionsHTTPSuccessf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPSuccessf(httpOK, "GET", "/", nil, "test message") if !result { t.Error("Assertions.HTTPSuccessf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.HTTPSuccessf(httpError, "GET", "/", nil, "test message") if result { t.Error("Assertions.HTTPSuccessf should return false on failure") } if !mock.failed { t.Error("Assertions.HTTPSuccessf should mark test as failed") } }) } func TestAssertionsImplementsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Implementsf(ptr(dummyInterface), new(testing.T), "test message") if !result { t.Error("Assertions.Implementsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Implementsf((*error)(nil), new(testing.T), "test message") if result { t.Error("Assertions.Implementsf should return false on failure") } if !mock.failed { t.Error("Assertions.Implementsf should mark test as failed") } }) } func TestAssertionsInDeltaf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaf(1.0, 1.01, 0.02, "test message") if !result { t.Error("Assertions.InDeltaf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaf(1.0, 1.1, 0.05, "test message") if result { t.Error("Assertions.InDeltaf should return false on failure") } if !mock.failed { t.Error("Assertions.InDeltaf should mark test as failed") } }) } func TestAssertionsInDeltaMapValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaMapValuesf(map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02, "test message") if !result { t.Error("Assertions.InDeltaMapValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaMapValuesf(map[string]float64{"a": 1.0}, map[string]float64{"a": 1.1}, 0.05, "test message") if result { t.Error("Assertions.InDeltaMapValuesf should return false on failure") } if !mock.failed { t.Error("Assertions.InDeltaMapValuesf should mark test as failed") } }) } func TestAssertionsInDeltaSlicef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaSlicef([]float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02, "test message") if !result { t.Error("Assertions.InDeltaSlicef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InDeltaSlicef([]float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05, "test message") if result { t.Error("Assertions.InDeltaSlicef should return false on failure") } if !mock.failed { t.Error("Assertions.InDeltaSlicef should mark test as failed") } }) } func TestAssertionsInEpsilonf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonf(100.0, 101.0, 0.02, "test message") if !result { t.Error("Assertions.InEpsilonf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonf(100.0, 110.0, 0.05, "test message") if result { t.Error("Assertions.InEpsilonf should return false on failure") } if !mock.failed { t.Error("Assertions.InEpsilonf should mark test as failed") } }) } func TestAssertionsInEpsilonSlicef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSlicef([]float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02, "test message") if !result { t.Error("Assertions.InEpsilonSlicef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSlicef([]float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05, "test message") if result { t.Error("Assertions.InEpsilonSlicef should return false on failure") } if !mock.failed { t.Error("Assertions.InEpsilonSlicef should mark test as failed") } }) } func TestAssertionsInEpsilonSymmetricf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSymmetricf(100.0, 101.0, 0.02, "test message") if !result { t.Error("Assertions.InEpsilonSymmetricf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.InEpsilonSymmetricf(100.0, 110.0, 0.05, "test message") if result { t.Error("Assertions.InEpsilonSymmetricf should return false on failure") } if !mock.failed { t.Error("Assertions.InEpsilonSymmetricf should mark test as failed") } }) } func TestAssertionsIsDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsDecreasingf([]int{3, 2, 1}, "test message") if !result { t.Error("Assertions.IsDecreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsDecreasingf([]int{1, 2, 3}, "test message") if result { t.Error("Assertions.IsDecreasingf should return false on failure") } if !mock.failed { t.Error("Assertions.IsDecreasingf should mark test as failed") } }) } func TestAssertionsIsIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsIncreasingf([]int{1, 2, 3}, "test message") if !result { t.Error("Assertions.IsIncreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsIncreasingf([]int{1, 1, 2}, "test message") if result { t.Error("Assertions.IsIncreasingf should return false on failure") } if !mock.failed { t.Error("Assertions.IsIncreasingf should mark test as failed") } }) } func TestAssertionsIsNonDecreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonDecreasingf([]int{1, 1, 2}, "test message") if !result { t.Error("Assertions.IsNonDecreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonDecreasingf([]int{2, 1, 0}, "test message") if result { t.Error("Assertions.IsNonDecreasingf should return false on failure") } if !mock.failed { t.Error("Assertions.IsNonDecreasingf should mark test as failed") } }) } func TestAssertionsIsNonIncreasingf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonIncreasingf([]int{2, 1, 1}, "test message") if !result { t.Error("Assertions.IsNonIncreasingf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNonIncreasingf([]int{1, 2, 3}, "test message") if result { t.Error("Assertions.IsNonIncreasingf should return false on failure") } if !mock.failed { t.Error("Assertions.IsNonIncreasingf should mark test as failed") } }) } func TestAssertionsIsNotTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNotTypef(int32(123), int64(456), "test message") if !result { t.Error("Assertions.IsNotTypef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsNotTypef(123, 456, "test message") if result { t.Error("Assertions.IsNotTypef should return false on failure") } if !mock.failed { t.Error("Assertions.IsNotTypef should mark test as failed") } }) } func TestAssertionsIsTypef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsTypef(123, 456, "test message") if !result { t.Error("Assertions.IsTypef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.IsTypef(int32(123), int64(456), "test message") if result { t.Error("Assertions.IsTypef should return false on failure") } if !mock.failed { t.Error("Assertions.IsTypef should mark test as failed") } }) } func TestAssertionsJSONEqf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, "test message") if !result { t.Error("Assertions.JSONEqf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEqf(`{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]`, "test message") if result { t.Error("Assertions.JSONEqf should return false on failure") } if !mock.failed { t.Error("Assertions.JSONEqf should mark test as failed") } }) } func TestAssertionsJSONEqBytesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEqBytesf([]byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`), "test message") if !result { t.Error("Assertions.JSONEqBytesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.JSONEqBytesf([]byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`), "test message") if result { t.Error("Assertions.JSONEqBytesf should return false on failure") } if !mock.failed { t.Error("Assertions.JSONEqBytesf should mark test as failed") } }) } func TestAssertionsKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Kindf(reflect.String, "hello", "test message") if !result { t.Error("Assertions.Kindf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Kindf(reflect.String, 0, "test message") if result { t.Error("Assertions.Kindf should return false on failure") } if !mock.failed { t.Error("Assertions.Kindf should mark test as failed") } }) } func TestAssertionsLenf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Lenf([]string{"A", "B"}, 2, "test message") if !result { t.Error("Assertions.Lenf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Lenf([]string{"A", "B"}, 1, "test message") if result { t.Error("Assertions.Lenf should return false on failure") } if !mock.failed { t.Error("Assertions.Lenf should mark test as failed") } }) } func TestAssertionsLessf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Lessf(1, 2, "test message") if !result { t.Error("Assertions.Lessf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Lessf(2, 1, "test message") if result { t.Error("Assertions.Lessf should return false on failure") } if !mock.failed { t.Error("Assertions.Lessf should mark test as failed") } }) } func TestAssertionsLessOrEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.LessOrEqualf(1, 2, "test message") if !result { t.Error("Assertions.LessOrEqualf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.LessOrEqualf(2, 1, "test message") if result { t.Error("Assertions.LessOrEqualf should return false on failure") } if !mock.failed { t.Error("Assertions.LessOrEqualf should mark test as failed") } }) } func TestAssertionsNegativef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Negativef(-1, "test message") if !result { t.Error("Assertions.Negativef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Negativef(1, "test message") if result { t.Error("Assertions.Negativef should return false on failure") } if !mock.failed { t.Error("Assertions.Negativef should mark test as failed") } }) } func TestAssertionsNilf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Nilf(nil, "test message") if !result { t.Error("Assertions.Nilf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Nilf("not nil", "test message") if result { t.Error("Assertions.Nilf should return false on failure") } if !mock.failed { t.Error("Assertions.Nilf should mark test as failed") } }) } func TestAssertionsNoErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoErrorf(nil, "test message") if !result { t.Error("Assertions.NoErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoErrorf(ErrTest, "test message") if result { t.Error("Assertions.NoErrorf should return false on failure") } if !mock.failed { t.Error("Assertions.NoErrorf should mark test as failed") } }) } func TestAssertionsNoFileDescriptorLeakf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoFileDescriptorLeakf(func() {}, "test message") if !result { t.Error("Assertions.NoFileDescriptorLeakf should return true on success") } }) } func TestAssertionsNoGoRoutineLeakf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NoGoRoutineLeakf(func() {}, "test message") if !result { t.Error("Assertions.NoGoRoutineLeakf should return true on success") } }) } func TestAssertionsNotBlockedf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotBlockedf(sendChanMessage(), "test message") if !result { t.Error("Assertions.NotBlockedf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotBlockedf(make(chan struct{}), "test message") if result { t.Error("Assertions.NotBlockedf should return false on failure") } if !mock.failed { t.Error("Assertions.NotBlockedf should mark test as failed") } }) } func TestAssertionsNotContainsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotContainsf([]string{"A", "B"}, "C", "test message") if !result { t.Error("Assertions.NotContainsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotContainsf([]string{"A", "B"}, "B", "test message") if result { t.Error("Assertions.NotContainsf should return false on failure") } if !mock.failed { t.Error("Assertions.NotContainsf should mark test as failed") } }) } func TestAssertionsNotElementsMatchf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotElementsMatchf([]int{1, 2, 3}, []int{1, 2, 4}, "test message") if !result { t.Error("Assertions.NotElementsMatchf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotElementsMatchf([]int{1, 3, 2, 3}, []int{1, 3, 3, 2}, "test message") if result { t.Error("Assertions.NotElementsMatchf should return false on failure") } if !mock.failed { t.Error("Assertions.NotElementsMatchf should mark test as failed") } }) } func TestAssertionsNotEmptyf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEmptyf("not empty", "test message") if !result { t.Error("Assertions.NotEmptyf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEmptyf("", "test message") if result { t.Error("Assertions.NotEmptyf should return false on failure") } if !mock.failed { t.Error("Assertions.NotEmptyf should mark test as failed") } }) } func TestAssertionsNotEqualf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqualf(123, 456, "test message") if !result { t.Error("Assertions.NotEqualf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqualf(123, 123, "test message") if result { t.Error("Assertions.NotEqualf should return false on failure") } if !mock.failed { t.Error("Assertions.NotEqualf should mark test as failed") } }) } func TestAssertionsNotEqualValuesf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqualValuesf(uint32(123), int32(456), "test message") if !result { t.Error("Assertions.NotEqualValuesf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotEqualValuesf(uint32(123), int32(123), "test message") if result { t.Error("Assertions.NotEqualValuesf should return false on failure") } if !mock.failed { t.Error("Assertions.NotEqualValuesf should mark test as failed") } }) } func TestAssertionsNotErrorAsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorAsf(ErrTest, new(*dummyError), "test message") if !result { t.Error("Assertions.NotErrorAsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorAsf(fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError), "test message") if result { t.Error("Assertions.NotErrorAsf should return false on failure") } if !mock.failed { t.Error("Assertions.NotErrorAsf should mark test as failed") } }) } func TestAssertionsNotErrorIsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorIsf(ErrTest, io.EOF, "test message") if !result { t.Error("Assertions.NotErrorIsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotErrorIsf(fmt.Errorf("wrap: %w", io.EOF), io.EOF, "test message") if result { t.Error("Assertions.NotErrorIsf should return false on failure") } if !mock.failed { t.Error("Assertions.NotErrorIsf should mark test as failed") } }) } func TestAssertionsNotImplementsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotImplementsf((*error)(nil), new(testing.T), "test message") if !result { t.Error("Assertions.NotImplementsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotImplementsf(ptr(dummyInterface), new(testing.T), "test message") if result { t.Error("Assertions.NotImplementsf should return false on failure") } if !mock.failed { t.Error("Assertions.NotImplementsf should mark test as failed") } }) } func TestAssertionsNotKindf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotKindf(reflect.String, 0, "test message") if !result { t.Error("Assertions.NotKindf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotKindf(reflect.String, "hello", "test message") if result { t.Error("Assertions.NotKindf should return false on failure") } if !mock.failed { t.Error("Assertions.NotKindf should mark test as failed") } }) } func TestAssertionsNotNilf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotNilf("not nil", "test message") if !result { t.Error("Assertions.NotNilf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotNilf(nil, "test message") if result { t.Error("Assertions.NotNilf should return false on failure") } if !mock.failed { t.Error("Assertions.NotNilf should mark test as failed") } }) } func TestAssertionsNotPanicsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotPanicsf(func() {}, "test message") if !result { t.Error("Assertions.NotPanicsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotPanicsf(func() { panic("panicking") }, "test message") if result { t.Error("Assertions.NotPanicsf should return false on failure") } if !mock.failed { t.Error("Assertions.NotPanicsf should mark test as failed") } }) } func TestAssertionsNotRegexpf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotRegexpf("^start", "not starting", "test message") if !result { t.Error("Assertions.NotRegexpf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotRegexpf("^start", "starting", "test message") if result { t.Error("Assertions.NotRegexpf should return false on failure") } if !mock.failed { t.Error("Assertions.NotRegexpf should mark test as failed") } }) } func TestAssertionsNotSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSamef(&staticVar, ptr("static string"), "test message") if !result { t.Error("Assertions.NotSamef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSamef(&staticVar, staticVarPtr, "test message") if result { t.Error("Assertions.NotSamef should return false on failure") } if !mock.failed { t.Error("Assertions.NotSamef should mark test as failed") } }) } func TestAssertionsNotSubsetf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSubsetf([]int{1, 2, 3}, []int{4, 5}, "test message") if !result { t.Error("Assertions.NotSubsetf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotSubsetf([]int{1, 2, 3}, []int{1, 2}, "test message") if result { t.Error("Assertions.NotSubsetf should return false on failure") } if !mock.failed { t.Error("Assertions.NotSubsetf should mark test as failed") } }) } func TestAssertionsNotZerof(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotZerof(1, "test message") if !result { t.Error("Assertions.NotZerof should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.NotZerof(0, "test message") if result { t.Error("Assertions.NotZerof should return false on failure") } if !mock.failed { t.Error("Assertions.NotZerof should mark test as failed") } }) } func TestAssertionsPanicsf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panicsf(func() { panic("panicking") }, "test message") if !result { t.Error("Assertions.Panicsf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panicsf(func() {}, "test message") if result { t.Error("Assertions.Panicsf should return false on failure") } if !mock.failed { t.Error("Assertions.Panicsf should mark test as failed") } }) } func TestAssertionsPanicsWithErrorf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithErrorf(ErrTest.Error(), func() { panic(ErrTest) }, "test message") if !result { t.Error("Assertions.PanicsWithErrorf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithErrorf(ErrTest.Error(), func() {}, "test message") if result { t.Error("Assertions.PanicsWithErrorf should return false on failure") } if !mock.failed { t.Error("Assertions.PanicsWithErrorf should mark test as failed") } }) } func TestAssertionsPanicsWithValuef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithValuef("panicking", func() { panic("panicking") }, "test message") if !result { t.Error("Assertions.PanicsWithValuef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.PanicsWithValuef("panicking", func() {}, "test message") if result { t.Error("Assertions.PanicsWithValuef should return false on failure") } if !mock.failed { t.Error("Assertions.PanicsWithValuef should mark test as failed") } }) } func TestAssertionsPositivef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Positivef(1, "test message") if !result { t.Error("Assertions.Positivef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Positivef(-1, "test message") if result { t.Error("Assertions.Positivef should return false on failure") } if !mock.failed { t.Error("Assertions.Positivef should mark test as failed") } }) } func TestAssertionsRegexpf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Regexpf("^start", "starting", "test message") if !result { t.Error("Assertions.Regexpf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Regexpf("^start", "not starting", "test message") if result { t.Error("Assertions.Regexpf should return false on failure") } if !mock.failed { t.Error("Assertions.Regexpf should mark test as failed") } }) } func TestAssertionsSamef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Samef(&staticVar, staticVarPtr, "test message") if !result { t.Error("Assertions.Samef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Samef(&staticVar, ptr("static string"), "test message") if result { t.Error("Assertions.Samef should return false on failure") } if !mock.failed { t.Error("Assertions.Samef should mark test as failed") } }) } func TestAssertionsSubsetf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Subsetf([]int{1, 2, 3}, []int{1, 2}, "test message") if !result { t.Error("Assertions.Subsetf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Subsetf([]int{1, 2, 3}, []int{4, 5}, "test message") if result { t.Error("Assertions.Subsetf should return false on failure") } if !mock.failed { t.Error("Assertions.Subsetf should mark test as failed") } }) } func TestAssertionsTruef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Truef(1 == 1, "test message") if !result { t.Error("Assertions.Truef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Truef(1 == 0, "test message") if result { t.Error("Assertions.Truef should return false on failure") } if !mock.failed { t.Error("Assertions.Truef should mark test as failed") } }) } func TestAssertionsWithinDurationf(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinDurationf(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second, "test message") if !result { t.Error("Assertions.WithinDurationf should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinDurationf(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second, "test message") if result { t.Error("Assertions.WithinDurationf should return false on failure") } if !mock.failed { t.Error("Assertions.WithinDurationf should mark test as failed") } }) } func TestAssertionsWithinRangef(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinRangef(time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC), "test message") if !result { t.Error("Assertions.WithinRangef should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.WithinRangef(time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC), "test message") if result { t.Error("Assertions.WithinRangef should return false on failure") } if !mock.failed { t.Error("Assertions.WithinRangef should mark test as failed") } }) } func TestAssertionsYAMLEqf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panics(func() { a.YAMLEqf("key: value", "key: value", "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("Assertions.YAMLEqf should return true on panic") } if mock.failed { t.Error("Assertions.YAMLEqf should panic as expected") } }) } func TestAssertionsYAMLEqBytesf(t *testing.T) { t.Parallel() t.Run("panic", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Panics(func() { a.YAMLEqBytesf([]byte("key: value"), []byte("key: value"), "test message") }, "should panic without the yaml feature enabled.") if !result { t.Error("Assertions.YAMLEqBytesf should return true on panic") } if mock.failed { t.Error("Assertions.YAMLEqBytesf should panic as expected") } }) } func TestAssertionsZerof(t *testing.T) { t.Parallel() t.Run("success", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Zerof(0, "test message") if !result { t.Error("Assertions.Zerof should return true on success") } }) t.Run("failure", func(t *testing.T) { t.Parallel() mock := new(mockT) a := New(mock) result := a.Zerof(1, "test message") if result { t.Error("Assertions.Zerof should return false on failure") } if !mock.failed { t.Error("Assertions.Zerof should mark test as failed") } }) } go-openapi-testify-c10ca71/assert/assert_helpers.go000066400000000000000000000023451520301377500225200ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "net/http" "net/url" "github.com/go-openapi/testify/v2/internal/assertions" ) // CallerInfo returns an array of strings containing the file and line number // of each stack frame leading from the current test to the assert call that // failed. func CallerInfo() []string { return assertions.CallerInfo() } // HTTPBody is a helper that returns the HTTP body of the response. // It returns the empty string if building a new request fails. func HTTPBody(handler http.HandlerFunc, method string, url string, values url.Values) string { return assertions.HTTPBody(handler, method, url, values) } // ObjectsAreEqual determines if two objects are considered equal. // // This function does no assertion of any kind. func ObjectsAreEqual(expected any, actual any) bool { return assertions.ObjectsAreEqual(expected, actual) } // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected any, actual any) bool { return assertions.ObjectsAreEqualValues(expected, actual) } go-openapi-testify-c10ca71/assert/assert_helpers_test.go000066400000000000000000000011241520301377500235510ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "testing" ) func TestCallerInfof(t *testing.T) { t.Skip() // this function doesn't have tests yet } func TestHTTPBodyf(t *testing.T) { t.Skip() // this function doesn't have tests yet } func TestObjectsAreEqualf(t *testing.T) { t.Skip() // this function doesn't have tests yet } func TestObjectsAreEqualValuesf(t *testing.T) { t.Skip() // this function doesn't have tests yet } go-openapi-testify-c10ca71/assert/assert_types.go000066400000000000000000000161461520301377500222260ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Code generated with github.com/go-openapi/testify/codegen/v2; DO NOT EDIT. package assert import ( "github.com/go-openapi/testify/v2/internal/assertions" ) 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 as an example, this error may be used to make // the test code more readable. ErrTest = assertions.ErrTest ) type ( // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. BoolAssertionFunc = assertions.BoolAssertionFunc // Boolean is a bool or any type that can be converted to a bool. Boolean = assertions.Boolean // CollectT implements the [T] interface and collects all errors. // // [CollectT] is specifically intended to be used with [EventuallyWith] and // should not be used outside of that context. CollectT = assertions.CollectT // CollectibleConditioner is a function used in asynchronous condition assertions that use [CollectT]. // // This type constraint allows for "overloaded" versions of the condition assertions ([EventuallyWith]). // // The [WithSynctestCollect] and [WithSynctestCollectContext] wrappers opt a // call into fake-time polling. See [WithSynctest] for details. CollectibleConditioner = assertions.CollectibleConditioner // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. ComparisonAssertionFunc = assertions.ComparisonAssertionFunc // Conditioner is a function used in asynchronous condition assertions. // // This type constraint allows for "overloaded" versions of the condition assertions ([Eventually], [Consistently]). // // The [WithSynctest] and [WithSynctestContext] wrappers opt a call into // fake-time polling via [testing/synctest]. See [WithSynctest] for details. Conditioner = assertions.Conditioner // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. ErrorAssertionFunc = assertions.ErrorAssertionFunc // H is an interface for types that implement the Helper method. // This allows marking functions as test helpers, e.g. [testing.T.Helper]. H = assertions.H // Measurable is any number for which we can compute a delta (floats or integers). // // This is used by [InDeltaT] and [InEpsilonT]. // // NOTE: unfortunately complex64 and complex128 are not supported. Measurable = assertions.Measurable // NeverConditioner is a function used by [Never]. // // Unlike [Conditioner], [Never] does not accept the context-returning-error // form to avoid the double-negation confusion ("never returns no error"). // // The [WithSynctest] wrapper opts a call into fake-time polling. NeverConditioner = assertions.NeverConditioner // Ordered is a standard ordered type (i.e. types that support "<": [cmp.Ordered]) plus []byte and [time.Time]. // // This is used by [GreaterT], [GreaterOrEqualT], [LessT], [LessOrEqualT], [IsIncreasingT], [IsDecreasingT]. // // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. Ordered = assertions.Ordered // PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful // for table driven tests. PanicAssertionFunc = assertions.PanicAssertionFunc // RText extends [Text] by supporting dynamic construction of the // expected or actual value, e.g. "redact" functions. RText = assertions.RText // Redactor allows dynamic construction of expected or actual values, e.g. "redacting" values dynamically. // // This is used by json and yaml assertions. Redactor = assertions.Redactor // RegExp is either a text containing a regular expression to compile (string or []byte), or directly the compiled regexp. // // This is used by [RegexpT] and [NotRegexpT]. RegExp = assertions.RegExp // SignedNumeric is a signed integer or a floating point number or any type that can be converted to one of these. SignedNumeric = assertions.SignedNumeric // 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]. // // NOTE: unfortunately, []rune is not supported. Text = assertions.Text // UnsignedNumeric is an unsigned integer. // // NOTE: there are no unsigned floating point numbers. UnsignedNumeric = assertions.UnsignedNumeric // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. ValueAssertionFunc = assertions.ValueAssertionFunc // WithSynctest wraps a [func() bool] condition to run [Eventually] / // [Never] / [Consistently] polling inside a [testing/synctest] bubble, // so `time.Ticker`, `time.After`, and `context.WithTimeout` use a fake // clock. Activation requires the caller to pass a real `*testing.T`; // with mocks or other [T] implementations, the wrapper falls back to // real-time polling. // // # When to use // // Use when the condition is pure compute, relies on `time.Sleep`, or // coordinates via channels created inside the condition. Fake time // eliminates timing-induced flakiness and enables deterministic tick // counts. // // # When not to use // // Do NOT use when the condition performs real I/O (network, filesystem, // syscalls): those block goroutines non-durably, so the fake clock // stalls and the timeout may not fire. Also do NOT use inside a test // that is already running in a [synctest.Test] bubble — nested bubbles // are forbidden and will panic. // // # Shared state // // The condition may read and write variables captured from the enclosing // scope; condition execution is serialized by design (see [Eventually]'s // Concurrency section). Avoid sharing channels or mutexes with goroutines // outside the bubble, as this will stall the fake clock. WithSynctest = assertions.WithSynctest // WithSynctestCollect is the [func(*CollectT)] counterpart of // [WithSynctest] for use with [EventuallyWith]. See [WithSynctest] for details. WithSynctestCollect = assertions.WithSynctestCollect // WithSynctestCollectContext is the [func(context.Context, *CollectT)] // counterpart of [WithSynctest] for use with [EventuallyWith]. See // [WithSynctest] for details. WithSynctestCollectContext = assertions.WithSynctestCollectContext // WithSynctestContext is the [func(context.Context) error] counterpart // of [WithSynctest]. See [WithSynctest] for details. WithSynctestContext = assertions.WithSynctestContext ) // Type declarations for backward compatibility. type ( // TestingT is like [T] and is declared here to remain compatible with previous versions of this package. // // Most users should not be affected, as the implementation of [T] that is widely used is [testing.T]. // // Deprecated: use [T] as a more concise alternative. TestingT = T ) go-openapi-testify-c10ca71/assert/doc.go000066400000000000000000000033301520301377500202350ustar00rootroot00000000000000// 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, you might find the format below more concise: // // 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 using go's standard testing library. // // 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 their failings and other details to the correct place. // // Every assertion function 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 at https://go-openapi.github.io/testify/ for usage, examples and searchable reference. // // See https://pkg.go/dev/go-openapi/testify/ for complete reference. package assert go-openapi-testify-c10ca71/codegen/000077500000000000000000000000001520301377500172455ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/.gitignore000066400000000000000000000000101520301377500212240ustar00rootroot00000000000000codegen go-openapi-testify-c10ca71/codegen/go.mod000066400000000000000000000003641520301377500203560ustar00rootroot00000000000000module github.com/go-openapi/testify/codegen/v2 go 1.25.0 require ( go.yaml.in/yaml/v3 v3.0.4 golang.org/x/text v0.34.0 golang.org/x/tools v0.42.0 ) require ( golang.org/x/mod v0.33.0 // indirect golang.org/x/sync v0.19.0 // indirect ) go-openapi-testify-c10ca71/codegen/go.sum000066400000000000000000000021771520301377500204070ustar00rootroot00000000000000github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/text v0.34.0 h1:oL/Qq0Kdaqxa1KbNeMKwQq0reLCCaFtqu2eNuSeNHbk= golang.org/x/text v0.34.0/go.mod h1:homfLqTYRFyVYemLBFl5GgL/DWEiH5wcsQ5gSh1yziA= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= go-openapi-testify-c10ca71/codegen/internal/000077500000000000000000000000001520301377500210615ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/generator/000077500000000000000000000000001520301377500230475ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/generator/doc.go000066400000000000000000000005261520301377500241460ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package generator generates the testify public API. // // It adapts the internal testify API from github.com/go-openapi/testify/v2/internal/assertions // and generates the content in packages "assert" and "require". package generator go-openapi-testify-c10ca71/codegen/internal/generator/doc_generator.go000066400000000000000000000323121520301377500262120ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import ( "cmp" "errors" "fmt" "iter" "os" "path" "path/filepath" "slices" yaml "go.yaml.in/yaml/v3" "github.com/go-openapi/testify/codegen/v2/internal/generator/domains" "github.com/go-openapi/testify/codegen/v2/internal/generator/funcmaps" "github.com/go-openapi/testify/codegen/v2/internal/model" exparser "github.com/go-openapi/testify/codegen/v2/internal/scanner/examples-parser" ) const ( // index page metadata. indexTitle = "Assertions index" indexDescription = "Index of assertion domains" indexFile = "_index.md" metricsTitle = "Quick API index" metricsDescription = "API quick index & metrics" metricsFile = "metrics.md" // sensible default preallocated slots. allocatedEntries = 15 ) type DocGenerator struct { options ctx *genCtx doc model.Documentation } func NewDocGenerator(doc model.Documentation, opts ...Option) *DocGenerator { return &DocGenerator{ options: optionsWithDefaults(opts), doc: doc, } } func (d *DocGenerator) Generate(opts ...GenerateOption) error { // prepare options d.ctx = &genCtx{ generateOptions: generateOptionsWithDefaults(opts), } if d.ctx.targetDoc == "" { return errors.New("a target directory is required for docs") } if err := d.loadTemplates(); err != nil { return err } // capture testable examples from generated packages and attach them to // the model so templates may render their source code. if err := d.populateExamples(); err != nil { return err } // reorganize accumulated package-based docs into domain-based docs // // This iterator renders all domains in the desired order. domainDocs, extraUniqueValues := d.reorganizeByDomain() // generate an index page with all domains indexDoc := d.buildIndexDocument(domainDocs, extraUniqueValues) if err := d.generateDomainIndex(indexDoc); err != nil { return err } // generate one page per domain. Each document knows about the domain. for _, document := range domainDocs { if err := d.generateDomainPage(document); err != nil { return err } } if err := d.generateMetricsPage(indexDoc); err != nil { return err } return d.writeYAMLMetrics(indexDoc.Metrics) } type uniqueValues struct { tool string receiver string copyright string header string githubURL string pkggodevURL string } func (d *DocGenerator) reorganizeByDomain() (iter.Seq2[string, model.Document], uniqueValues) { docs := domains.FlattenDocumentation(d.doc) discoveredDomains := domains.MakeDomainIndex(docs) githubURL := "https://" + path.Dir(discoveredDomains.RootPackage()) pkggodevURL := "https://pkg.go.dev/" + discoveredDomains.RootPackage() return func(yield func(string, model.Document) bool) { weight := 1 for domain, entry := range discoveredDomains.Entries() { doc := model.Document{ Title: funcmaps.Titleize(domain), Domain: domain, Description: entry.Description(), Kind: model.KindPage, File: domain + ".md", Package: &model.AssertionPackage{ Package: assertions, // package that is the single source of truth Tool: discoveredDomains.Tool(), Copyright: discoveredDomains.Copyright(), Receiver: discoveredDomains.Receiver(), Header: discoveredDomains.Header(), EnableFormat: d.ctx.enableFormat, EnableForward: d.ctx.enableForward, EnableGenerics: d.ctx.enableGenerics, EnableExamples: d.ctx.generateExamples, RunnableExamples: d.ctx.runnableExamples, // skip package-level docstring // skip other package-level extra comments // filtered functions and types for this domain across all packages Functions: entry.Functions(), Types: entry.Types(), Vars: entry.Vars(), Consts: entry.Consts(), }, ExtraPackages: entry.ExtraPackages(), GitHubURL: githubURL, PkgGoDevURL: pkggodevURL, RefCount: entry.Len(), Weight: weight, } weight++ // populate document context in all children doc.Package.Context = &doc for i, fn := range doc.Package.Functions { fn.Context = &doc doc.Package.Functions[i] = fn } if !yield(doc.Domain, doc) { return } } }, uniqueValues{ // metadata that are unique tool: discoveredDomains.Tool(), receiver: discoveredDomains.Receiver(), copyright: discoveredDomains.Copyright(), header: discoveredDomains.Header(), githubURL: githubURL, pkggodevURL: pkggodevURL, } } func (d *DocGenerator) buildIndexDocument(docsByDomain iter.Seq2[string, model.Document], extras uniqueValues) model.Document { doc := model.Document{ Title: indexTitle, Description: indexDescription, Kind: model.KindIndex, File: indexFile, Index: buildIndexEntries(docsByDomain), Package: &model.AssertionPackage{ Tool: extras.tool, Copyright: extras.copyright, Receiver: extras.receiver, Header: extras.header, // skip everything else }, GitHubURL: extras.githubURL, PkgGoDevURL: extras.pkggodevURL, } doc.RefCount = len(doc.Index) doc.Metrics = d.buildMetrics(docsByDomain) doc.QuickIndex = buildQuickIndex(docsByDomain) return doc } func buildIndexEntries(docsByDomain iter.Seq2[string, model.Document]) []model.IndexEntry { entries := make([]model.IndexEntry, 0, allocatedEntries) weight := 1 for domain, doc := range docsByDomain { entries = append(entries, model.IndexEntry{ Name: domain, Title: doc.Title, Description: doc.Description, Link: "./" + doc.File, RefCount: len(doc.Package.Functions), Weight: weight, }) weight++ } return entries } func (d *DocGenerator) buildMetrics(docsByDomain iter.Seq2[string, model.Document]) (metrics model.Metrics) { metrics.ByDomain = make(map[string]model.DomainMetrics) for domain, doc := range docsByDomain { metrics.Domains++ var domainMetrics model.DomainMetrics domainMetrics.Name = doc.Title for _, fn := range doc.Package.Functions { metrics.Functions++ if fn.IsHelper { metrics.Helpers++ continue } if fn.IsConstructor { metrics.Others++ continue } metrics.Assertions++ domainMetrics.Count++ if fn.IsGeneric { metrics.Generics++ } } metrics.ByDomain[domain] = domainMetrics } metrics.NonGenerics = metrics.Assertions - metrics.Generics variantsMultiplier := 1 genericsVariantsMultiplier := 1 if d.ctx.enableForward { variantsMultiplier++ if d.ctx.enableFormat { variantsMultiplier++ } } if d.ctx.enableFormat { variantsMultiplier++ genericsVariantsMultiplier++ } metrics.PackageVariants = metrics.NonGenerics*variantsMultiplier + metrics.Generics*genericsVariantsMultiplier // caveat: assume 2 target packages (not really available from options atm). const generatedPackages = 2 metrics.TotalVariants = generatedPackages * metrics.PackageVariants metrics.TotalFunctions = generatedPackages * (metrics.PackageVariants + metrics.Helpers + 1) // add the Assertion constructor. return metrics } func buildQuickIndex(docsByDomain iter.Seq2[string, model.Document]) model.QuickIndex { const sensibleAlloc = 150 index := make(model.QuickIndex, 0, sensibleAlloc) seen := make(map[string]struct{}, sensibleAlloc) opposites := make(map[string]string, sensibleAlloc) genericNames := make(map[string]string, sensibleAlloc) for _, doc := range docsByDomain { for _, fn := range doc.Package.Functions { genericNames[fn.Name] = fn.GenericName() for _, tag := range fn.ExtraComments { opposite := tag.Opposite() if opposite != "" { seen[opposite] = struct{}{} opposites[fn.Name] = opposite break } } } } for domain, doc := range docsByDomain { for _, fn := range doc.Package.Functions { _, isOpposite := seen[fn.Name] if isOpposite { continue } opposite := opposites[fn.Name] entry := model.QuickIndexEntry{ Name: fn.GenericName(), Anchor: funcmaps.Slugize(fn.GenericName()), Opposite: opposite, Domain: domain, IsGeneric: fn.IsGeneric, IsHelper: fn.IsHelper, } if opposite != "" { if gn, ok := genericNames[opposite]; ok { entry.OppositeAnchor = funcmaps.Slugize(gn) } } index = append(index, entry) } } slices.SortFunc(index, func(a, b model.QuickIndexEntry) int { return cmp.Compare(a.Name, b.Name) }) return index } func (d *DocGenerator) generateDomainIndex(document model.Document) error { base := filepath.Join(d.ctx.targetRoot, d.ctx.targetDoc, document.Path) if err := os.MkdirAll(base, dirPermissions); err != nil { return fmt.Errorf("can't make target folder: %w", err) } return d.render("doc_index", filepath.Join(base, document.File), document) } func (d *DocGenerator) generateDomainPage(document model.Document) error { base := filepath.Join(d.ctx.targetRoot, d.ctx.targetDoc, document.Path) if err := os.MkdirAll(base, dirPermissions); err != nil { return fmt.Errorf("can't make target folder: %w", err) } return d.render("doc_page", filepath.Join(base, document.File), document) } func (d *DocGenerator) generateMetricsPage(document model.Document) error { document.File = metricsFile document.Title = metricsTitle document.Description = metricsDescription base := filepath.Join(d.ctx.targetRoot, d.ctx.targetDoc, document.Path) if err := os.MkdirAll(base, dirPermissions); err != nil { return fmt.Errorf("can't make target folder: %w", err) } return d.render("doc_metrics", filepath.Join(base, document.File), document) } func (d *DocGenerator) loadTemplates() error { const ( tplExt = ".md.gotmpl" expectedTemplates = 10 ) index := make(map[string]string, expectedTemplates) index["doc_index"] = "doc_index" index["doc_page"] = "doc_page" index["doc_metrics"] = "doc_metrics" templates, err := loadTemplatesFromIndex(index, tplExt, templatesFS) if err != nil { return err } d.ctx.index = index d.ctx.templates = templates return nil } // populateExamples runs the examples-parser against all generated packages in the // merged Documentation and attaches the discovered testable examples to the // corresponding Function and Ident objects. // // This must run before [reorganizeByDomain] because domain discovery copies // functions and types into domain entries. func (d *DocGenerator) populateExamples() error { if !d.ctx.runnableExamples { return nil } docs := domains.FlattenDocumentation(d.doc) // derive the module root from the assertions import that every generated // package carries (e.g. "github.com/go-openapi/testify/v2"). var rootPkg string for _, doc := range docs { if doc.Package != nil && doc.Package.Imports != nil { if assertionsPath, ok := doc.Package.Imports[assertions]; ok { rootPkg = path.Dir(path.Dir(assertionsPath)) break } } } if rootPkg == "" { return nil // nothing to do } workDir, err := filepath.Abs(d.ctx.targetRoot) if err != nil { return fmt.Errorf("resolving target root: %w", err) } for _, doc := range docs { pkg := doc.Package if pkg == nil { continue } // Skip the internal assertions package: testable examples live in the // generated packages (assert, require), not in the source package. if path.Base(pkg.Package) == assertions { continue } importPath := rootPkg + "/" + pkg.Package examples, parseErr := exparser.New(importPath, exparser.WithWorkDir(workDir)).Parse() if parseErr != nil { return fmt.Errorf("parsing examples for %s: %w", pkg.Package, parseErr) } populateFunctionExamples(pkg, examples) populateIdentExamples(pkg.Types, examples) } return nil } func populateFunctionExamples(pkg *model.AssertionPackage, examples exparser.Examples) { for i, fn := range pkg.Functions { exs, ok := examples[fn.Name] if !ok { continue } renderables := make([]model.Renderable, len(exs)) for j := range exs { renderables[j] = exs[j] } pkg.Functions[i].Examples = renderables } } func populateIdentExamples(idents []model.Ident, examples exparser.Examples) { for i, id := range idents { exs, ok := examples[id.Name] if !ok { continue } renderables := make([]model.Renderable, len(exs)) for j := range exs { renderables[j] = exs[j] } idents[i].Examples = renderables } } func (d *DocGenerator) render(name string, target string, data any) error { return renderTemplate( d.ctx.index, d.ctx.templates, name, target, data, renderMD, ) } // writeYAMLMetrics writes the Metrics structure as a YAML file in the hugo generation folder. // // This allows doc pages to use metrics directly with shortcodes. func (d *DocGenerator) writeYAMLMetrics(metrics model.Metrics) error { base := filepath.Join(d.ctx.targetRoot, "hack", "doc-site", "hugo") if err := os.MkdirAll(base, dirPermissions); err != nil { return fmt.Errorf("can't make target folder: %w", err) } target := filepath.Join(base, "metrics.yaml") type containerT struct { Params struct { Metrics model.Metrics `yaml:"metrics"` } `yaml:"params"` } var container containerT container.Params.Metrics = metrics buf, err := yaml.Marshal(container) if err != nil { return err } return os.WriteFile(target, buf, filePermissions) } go-openapi-testify-c10ca71/codegen/internal/generator/domains/000077500000000000000000000000001520301377500245015ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/generator/domains/doc.go000066400000000000000000000007521520301377500256010ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package domains provides domain-based organization of assertions for documentation generation. // // This package handles the reorganization of assertion functions, types, variables, and constants // from package-based organization (assert, require, internal/assertions) to domain-based organization // (boolean, collection, error, etc.) for documentation purposes. package domains go-openapi-testify-c10ca71/codegen/internal/generator/domains/domains.go000066400000000000000000000256021520301377500264670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package domains import ( "iter" "path" "slices" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" ) const ( nodomain = "common" assertions = "assertions" ) // FlattenDocumentation flattens a nested documentation structure into a map of packages. func FlattenDocumentation(documentation model.Documentation) map[string]model.Document { index := make(map[string]model.Document, len(documentation.Documents)) flattenDocuments(index, documentation.Documents) return index } func flattenDocuments(index map[string]model.Document, docs []model.Document) { for _, doc := range docs { key := doc.Package.Package if _, ok := index[key]; !ok { index[key] = doc } flattenDocuments(index, doc.Documents) } } // Index represents a domain-based index of assertions organized by domain. type Index struct { index map[string]Entry tool string copyright string receiver string rootPackage string header string } // mapEntry is used for sorting domain entries. type mapEntry struct { key string value Entry } // Entries returns an iterator over domain entries, sorted alphabetically with "common" domain last. func (idx Index) Entries() iter.Seq2[string, Entry] { list := make([]mapEntry, 0, len(idx.index)) for k, v := range idx.index { list = append(list, mapEntry{key: k, value: v}) } slices.SortFunc(list, compareMapEntries) return func(yield func(string, Entry) bool) { for _, entry := range list { if !yield(entry.key, entry.value) { return } } } } // compareMapEntries compares two mapEntry items by their key field. // // The "common" domain (nodomain) always sorts last, all others sort alphabetically. func compareMapEntries(a, b mapEntry) int { if a.key == nodomain { if b.key == nodomain { return 0 } return 1 } if b.key == nodomain { return -1 } return strings.Compare(a.key, b.key) } // Tool returns the tool name. func (idx Index) Tool() string { return idx.tool } // Copyright returns the copyright string. func (idx Index) Copyright() string { return idx.copyright } // Receiver returns the receiver name. func (idx Index) Receiver() string { return idx.receiver } // RootPackage returns the root package path. func (idx Index) RootPackage() string { return idx.rootPackage } // Header returns the header prepared by the Scanner. func (idx Index) Header() string { return idx.header } // MakeDomainIndex creates a domain-based index from flattened documentation. func MakeDomainIndex(docs map[string]model.Document) Index { // collect all functions from all packages (assert, require) describedDomains := make(map[string]string) discoveredDomains := make(map[string]Entry) var tool, receiver, copyright, rootPackage, header string for _, doc := range docs { data := doc.Package findDescribedDomains(data, describedDomains) pkg := data.Package // the generated package // unique values resolved once if tool == "" { tool = data.Tool } if receiver == "" { receiver = data.Receiver } if copyright == "" { copyright = data.Copyright } if rootPackage == "" { rootPackage = path.Dir(path.Dir(data.Imports[assertions])) } if header == "" { header = data.Header } discoverDomainsInFunctions(pkg, data, discoveredDomains) discoverDomainsInTypes(pkg, data, discoveredDomains) discoverDomainsInVariables(pkg, data, discoveredDomains) discoverDomainsInConstants(pkg, data, discoveredDomains) } // now add descriptions to domains for domain, description := range describedDomains { entry, ok := discoveredDomains[domain] if !ok { continue } entry.description = description discoveredDomains[domain] = entry } entry, ok := discoveredDomains[nodomain] if ok { entry.description = "Other uncategorized helpers" discoveredDomains[nodomain] = entry } return Index{ index: discoveredDomains, tool: tool, receiver: receiver, copyright: copyright, header: header, rootPackage: rootPackage, } } // findDescribedDomains look for annotations in the package comments // that describe assertion domains. // // Example: github.com/go-openapi/testify/v2/internal/assertions/doc.go // // Comment format: // // domain: description func findDescribedDomains(data *model.AssertionPackage, describedDomains map[string]string) { for _, tagged := range data.ExtraComments { if tagged.Tag != model.CommentTagDomainDescription { continue } description := describedDomains[tagged.Key] if description == "" { describedDomains[tagged.Key] = tagged.Text } } } func discoverDomainsInFunctions(pkg string, data *model.AssertionPackage, discoveredDomains map[string]Entry) { for _, fn := range data.Functions { domain := fn.Domain if domain == "" { entry := discoveredDomains[nodomain] entry.AddPackage(pkg, data) fn.Domain = nodomain entry.AddFunction(pkg, fn) discoveredDomains[nodomain] = entry continue } entry := discoveredDomains[domain] entry.AddPackage(pkg, data) entry.AddFunction(pkg, fn) discoveredDomains[domain] = entry } } func discoverDomainsInTypes(pkg string, data *model.AssertionPackage, discoveredDomains map[string]Entry) { for _, ty := range data.Types { domain := ty.Domain if domain == "" { entry := discoveredDomains[nodomain] entry.AddPackage(pkg, data) ty.Domain = nodomain entry.AddType(pkg, ty) discoveredDomains[nodomain] = entry continue } entry := discoveredDomains[domain] entry.AddPackage(pkg, data) entry.AddType(pkg, ty) discoveredDomains[domain] = entry } } func discoverDomainsInVariables(pkg string, data *model.AssertionPackage, discoveredDomains map[string]Entry) { for _, va := range data.Vars { domain := va.Domain if domain == "" { entry := discoveredDomains[nodomain] entry.AddPackage(pkg, data) va.Domain = nodomain entry.AddVariable(pkg, va) discoveredDomains[nodomain] = entry continue } entry := discoveredDomains[domain] entry.AddPackage(pkg, data) entry.AddVariable(pkg, va) discoveredDomains[domain] = entry } } func discoverDomainsInConstants(pkg string, data *model.AssertionPackage, discoveredDomains map[string]Entry) { for _, co := range data.Consts { domain := co.Domain if domain == "" { entry := discoveredDomains[nodomain] entry.AddPackage(pkg, data) co.Domain = nodomain entry.AddConst(pkg, co) discoveredDomains[nodomain] = entry continue } entry := discoveredDomains[domain] entry.AddPackage(pkg, data) entry.AddConst(pkg, co) discoveredDomains[domain] = entry } } type key struct { pkg string name string } func makeKey(pkg, name string) key { return key{ pkg: pkg, name: name, } } // Entry represents a discovered domain entry with associated functions, types, variables, and constants. type Entry struct { description string packages map[key]*model.AssertionPackage funcs map[key]*model.Function typeDecls map[key]*model.Ident varDecls map[key]*model.Ident constDecls map[key]*model.Ident } func makeEntry() Entry { return Entry{ packages: make(map[key]*model.AssertionPackage), funcs: make(map[key]*model.Function), typeDecls: make(map[key]*model.Ident), varDecls: make(map[key]*model.Ident), constDecls: make(map[key]*model.Ident), } } // AddPackage adds a package to the entry. func (e *Entry) AddPackage(pkg string, apkg *model.AssertionPackage) { if e.packages == nil { *e = makeEntry() } e.packages[makeKey(pkg, apkg.Package)] = apkg } // AddFunction adds a function to the entry. func (e *Entry) AddFunction(pkg string, fn model.Function) { val := fn if e.packages == nil { *e = makeEntry() } e.funcs[makeKey(pkg, fn.Name)] = &val } // AddType adds a type declaration to the entry. func (e *Entry) AddType(pkg string, id model.Ident) { val := id if e.packages == nil { *e = makeEntry() } e.typeDecls[makeKey(pkg, id.Name)] = &val } // AddVariable adds a variable declaration to the entry. func (e *Entry) AddVariable(pkg string, id model.Ident) { val := id if e.packages == nil { *e = makeEntry() } e.varDecls[makeKey(pkg, id.Name)] = &val } // AddConst adds a constant declaration to the entry. func (e *Entry) AddConst(pkg string, id model.Ident) { val := id if e.packages == nil { *e = makeEntry() } e.constDecls[makeKey(pkg, id.Name)] = &val } // ExtraPackages returns the list of extra packages (excluding the assertions package). // // This should correspond to the generated packages, e.g. "assert" and "require". func (e Entry) ExtraPackages() model.ExtraPackages { if len(e.packages) == 0 { return nil } result := make(model.ExtraPackages, 0, len(e.packages)-1) for _, pkg := range e.packages { if pkg == nil { // safeguard continue } if path.Base(pkg.Package) == assertions { continue } result = append(result, pkg) } slices.SortFunc(result, comparePackages) return result } // comparePackages compares two AssertionPackages by their Package field. func comparePackages(a, b *model.AssertionPackage) int { return strings.Compare(a.Package, b.Package) } // Functions returns the list of functions in this domain from the assertions package. func (e Entry) Functions() []model.Function { result := make([]model.Function, 0, len(e.funcs)) for key, fn := range e.funcs { if fn == nil { // safeguard continue } if path.Base(key.pkg) != assertions { continue } result = append(result, *fn) } slices.SortFunc(result, compareFunctions) return slices.Clip(result) } // Len returns the number of (filtered) functions for this entry. func (e Entry) Len() (l int) { for key, fn := range e.funcs { if fn == nil { // safeguard continue } if path.Base(key.pkg) != assertions { continue } l++ } return l } // compareFunctions compares two Functions by their Name field. func compareFunctions(a, b model.Function) int { return strings.Compare(a.Name, b.Name) } // Types returns the list of type declarations in this domain. func (e Entry) Types() []model.Ident { return e.sortedIdents(e.typeDecls) } // Vars returns the list of variable declarations in this domain. func (e Entry) Vars() []model.Ident { return e.sortedIdents(e.varDecls) } // Consts returns the list of constant declarations in this domain. func (e Entry) Consts() []model.Ident { return e.sortedIdents(e.constDecls) } // Description returns the description of this domain. func (e Entry) Description() string { return e.description } func (e Entry) sortedIdents(idents map[key]*model.Ident) []model.Ident { result := make([]model.Ident, 0, len(idents)) for key, id := range idents { if id == nil { // safeguard continue } if path.Base(key.pkg) != assertions { continue } result = append(result, *id) } slices.SortFunc(result, compareIdents) return result } // compareIdents compares two Idents by their Name field. func compareIdents(a, b model.Ident) int { return strings.Compare(a.Name, b.Name) } go-openapi-testify-c10ca71/codegen/internal/generator/domains/domains_test.go000066400000000000000000000352161520301377500275300ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package domains import ( "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) const ( testPackage = "github.com/go-openapi/testify/v2/internal/assertions" testRepo = "github.com/go-openapi/testify/v2" testAssertPackage = "github.com/go-openapi/testify/v2/assert" testRequirePackage = "github.com/go-openapi/testify/v2/require" ) // TestFlattenDocumentation verifies documentation flattening. func TestFlattenDocumentation(t *testing.T) { t.Parallel() for c := range flattenDocumentationCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() flattened := FlattenDocumentation(c.doc) if len(flattened) != c.expectedCount { t.Errorf("Expected %d packages, got %d", c.expectedCount, len(flattened)) } for _, pkg := range c.expectedPackages { if _, ok := flattened[pkg]; !ok { t.Errorf("Missing package %s", pkg) } } }) } } // TestMakeDomainIndex verifies domain index creation. func TestMakeDomainIndex(t *testing.T) { t.Parallel() for c := range makeDomainIndexCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() index := MakeDomainIndex(c.docs) // Verify metadata if c.checkMetadata != nil { c.checkMetadata(t, index) } // Verify domains if c.checkDomains != nil { c.checkDomains(t, index) } }) } } // TestDomainIndexSorting verifies that domains are sorted correctly. func TestDomainIndexSorting(t *testing.T) { t.Parallel() for c := range domainIndexSortingCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() index := MakeDomainIndex(c.docs) // Collect domain names in order const testDomains = 16 domains := make([]string, 0, testDomains) for domain := range index.Entries() { domains = append(domains, domain) } // Verify order if len(domains) != len(c.expectedOrder) { t.Fatalf("Expected %d domains, got %d: %v", len(c.expectedOrder), len(domains), domains) } for i, expected := range c.expectedOrder { if domains[i] != expected { t.Errorf("Position %d: expected %q, got %q", i, expected, domains[i]) } } }) } } // TestEntry_AddMethods verifies entry mutation methods. func TestEntry_AddMethods(t *testing.T) { t.Parallel() for c := range entryAddMethodsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() var entry Entry // Execute the add operation c.addFunc(&entry) // Verify the result c.checkFunc(t, &entry) }) } } // TestEntry_ExtraPackages verifies extra package filtering. func TestEntry_ExtraPackages(t *testing.T) { t.Parallel() for c := range entryExtraPackagesCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() var entry Entry // Add packages for _, pkg := range c.packages { entry.AddPackage(pkg.name, pkg.pkg) } extras := entry.ExtraPackages() // Verify count if len(extras) != c.expectedCount { t.Errorf("Expected %d extra packages, got %d", c.expectedCount, len(extras)) } // Verify order and content for i, expectedPkg := range c.expectedPackages { if i >= len(extras) { break } if extras[i].Package != expectedPkg { t.Errorf("Expected package[%d] to be %s, got %s", i, expectedPkg, extras[i].Package) } } }) } } // TestEntry_Functions verifies function filtering and sorting. func TestEntry_Functions(t *testing.T) { t.Parallel() for c := range entryFunctionsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() var entry Entry // Add functions for _, fn := range c.functions { entry.AddFunction(fn.pkg, fn.fn) } funcs := entry.Functions() // Verify count if len(funcs) != c.expectedCount { t.Errorf("Expected %d functions, got %d", c.expectedCount, len(funcs)) } // Verify order for i, expectedName := range c.expectedNames { if i >= len(funcs) { break } if funcs[i].Name != expectedName { t.Errorf("Expected function[%d] to be %s, got %s", i, expectedName, funcs[i].Name) } } }) } } /* Test case iterators */ type flattenDocumentationCase struct { name string doc model.Documentation expectedCount int expectedPackages []string } func flattenDocumentationCases() iter.Seq[flattenDocumentationCase] { return slices.Values([]flattenDocumentationCase{ { name: "nested documentation structure", doc: func() model.Documentation { pkg1 := model.New() pkg1.Package = "pkg1" pkg2 := model.New() pkg2.Package = "pkg2" pkg3 := model.New() pkg3.Package = "pkg3" doc := model.NewDocumentation() doc.Documents = []model.Document{ { Package: pkg1, Documents: []model.Document{ {Package: pkg2}, }, }, {Package: pkg3}, } return *doc }(), expectedCount: 3, expectedPackages: []string{"pkg1", "pkg2", "pkg3"}, }, }) } type makeDomainIndexCase struct { name string docs map[string]model.Document checkMetadata func(*testing.T, Index) checkDomains func(*testing.T, Index) } func makeDomainIndexCases() iter.Seq[makeDomainIndexCase] { return slices.Values([]makeDomainIndexCase{ { name: "complete domain index with metadata and domains", docs: func() map[string]model.Document { assertPkg := model.New() assertPkg.Package = testPackage assertPkg.Tool = "testify-codegen" assertPkg.Copyright = "Copyright 2025" assertPkg.Receiver = "Assertions" assertPkg.Imports = model.ImportMap{ "assertions": testPackage, } assertPkg.Functions = []model.Function{ {Name: "Equal", Domain: "equal"}, {Name: "True", Domain: "boolean"}, {Name: "Helper"}, // no domain } assertPkg.Types = []model.Ident{ {Name: "TestingT", Domain: "testing"}, {Name: "H"}, // no domain } assertPkg.Vars = []model.Ident{ {Name: "SomeVar", Domain: "boolean"}, {Name: "SomeOtherVar"}, // no domain } assertPkg.Consts = []model.Ident{ {Name: "SomeConst", Domain: "equal"}, {Name: "SomeOtherConst"}, // no domain } assertPkg.ExtraComments = []model.ExtraComment{ { Tag: model.CommentTagDomainDescription, Key: "equal", Text: "Equality assertions", }, { Tag: model.CommentTagDomainDescription, Key: "boolean", Text: "Boolean assertions", }, { Tag: model.CommentTagMaintainer, Key: "author", Text: "Some Author", // Should be ignored by domain indexer }, } return map[string]model.Document{ testPackage: {Package: assertPkg}, } }(), checkMetadata: checkCompleteIndexMetadata, checkDomains: checkCompleteIndexDomains, }, { name: "dangling domain description without declarations", docs: func() map[string]model.Document { pkg := model.New() pkg.Package = testPackage pkg.Imports = model.ImportMap{ "assertions": testPackage, } pkg.Functions = []model.Function{ {Name: "Equal", Domain: "equal"}, } pkg.ExtraComments = []model.ExtraComment{ { Tag: model.CommentTagDomainDescription, Key: "equal", Text: "Equality assertions", }, { Tag: model.CommentTagDomainDescription, Key: "phantom", Text: "This domain has no associated declarations", // Dangling description }, } return map[string]model.Document{ testPackage: {Package: pkg}, } }(), checkDomains: checkDanglingDomainExclusion, }, }) } /* Domain index assertion helpers */ func checkCompleteIndexMetadata(t *testing.T, index Index) { t.Helper() if index.Tool() != "testify-codegen" { t.Errorf("Expected tool 'testify-codegen', got %q", index.Tool()) } if index.Copyright() != "Copyright 2025" { t.Errorf("Expected copyright 'Copyright 2025', got %q", index.Copyright()) } if index.Receiver() != "Assertions" { t.Errorf("Expected receiver 'Assertions', got %q", index.Receiver()) } if index.RootPackage() != testRepo { t.Errorf("Expected root package '%s', got %q", testRepo, index.RootPackage()) } } func checkCompleteIndexDomains(t *testing.T, index Index) { t.Helper() checkers := map[string]func(*testing.T, Entry){ "equal": checkEqualDomain, "boolean": checkBooleanDomain, "testing": checkTestingDomain, "common": checkCommonDomain, } domainCount := 0 for domain, entry := range index.Entries() { domainCount++ if checker, ok := checkers[domain]; ok { checker(t, entry) } else { t.Errorf("Unexpected domain: %s", domain) } } if domainCount != 4 { t.Errorf("Expected 4 domains, got %d", domainCount) } } func checkEqualDomain(t *testing.T, entry Entry) { t.Helper() if entry.Description() != "Equality assertions" { t.Errorf("Expected description 'Equality assertions', got %q", entry.Description()) } if len(entry.Functions()) != 1 || entry.Functions()[0].Name != "Equal" { t.Error("Expected Equal function in equal domain") } if len(entry.Consts()) != 1 || entry.Consts()[0].Name != "SomeConst" { t.Error("Expected SomeConst in equal domain") } } func checkBooleanDomain(t *testing.T, entry Entry) { t.Helper() if entry.Description() != "Boolean assertions" { t.Errorf("Expected description 'Boolean assertions', got %q", entry.Description()) } if len(entry.Functions()) != 1 || entry.Functions()[0].Name != "True" { t.Error("Expected True function in boolean domain") } if len(entry.Vars()) != 1 || entry.Vars()[0].Name != "SomeVar" { t.Error("Expected SomeVar in boolean domain") } } func checkTestingDomain(t *testing.T, entry Entry) { t.Helper() if len(entry.Types()) != 1 || entry.Types()[0].Name != "TestingT" { t.Error("Expected TestingT type in testing domain") } } func checkCommonDomain(t *testing.T, entry Entry) { t.Helper() if entry.Description() != "Other uncategorized helpers" { t.Errorf("Expected description 'Other uncategorized helpers', got %q", entry.Description()) } if len(entry.Functions()) != 1 || entry.Functions()[0].Name != "Helper" { t.Error("Expected Helper function in common domain") } if len(entry.Types()) != 1 || entry.Types()[0].Name != "H" { t.Error("Expected H type in common domain") } if len(entry.Vars()) != 1 || entry.Vars()[0].Name != "SomeOtherVar" { t.Error("Expected SomeOtherVar in common domain") } if len(entry.Consts()) != 1 || entry.Consts()[0].Name != "SomeOtherConst" { t.Error("Expected SomeOtherConst in common domain") } } func checkDanglingDomainExclusion(t *testing.T, index Index) { t.Helper() domainCount := 0 for domain := range index.Entries() { domainCount++ if domain == "phantom" { t.Error("Dangling domain 'phantom' should not appear in entries") } } if domainCount != 1 { t.Errorf("Expected 1 domain (equal), got %d", domainCount) } } type domainIndexSortingCase struct { name string docs map[string]model.Document expectedOrder []string } func domainIndexSortingCases() iter.Seq[domainIndexSortingCase] { return slices.Values([]domainIndexSortingCase{ { name: "alphabetical with common last", docs: func() map[string]model.Document { pkg := model.New() pkg.Package = testPackage pkg.Imports = model.ImportMap{ "assertions": testPackage, } pkg.Functions = []model.Function{ {Name: "ZFunc", Domain: "zebra"}, {Name: "AFunc", Domain: "alpha"}, {Name: "MFunc", Domain: "middle"}, {Name: "NoFunc"}, // no domain -> common } return map[string]model.Document{ testPackage: {Package: pkg}, } }(), expectedOrder: []string{"alpha", "middle", "zebra", "common"}, }, }) } type entryAddMethodsCase struct { name string addFunc func(*Entry) checkFunc func(*testing.T, *Entry) } func entryAddMethodsCases() iter.Seq[entryAddMethodsCase] { return slices.Values([]entryAddMethodsCase{ { name: "add package", addFunc: func(e *Entry) { pkg := model.New() pkg.Package = "test" e.AddPackage("pkg", pkg) }, checkFunc: func(t *testing.T, e *Entry) { t.Helper() if len(e.packages) != 1 { t.Error("Package not added") } }, }, { name: "add function", addFunc: func(e *Entry) { fn := model.Function{Name: "TestFunc"} e.AddFunction("pkg", fn) }, checkFunc: func(t *testing.T, e *Entry) { t.Helper() if len(e.funcs) != 1 { t.Error("Function not added") } }, }, { name: "add type", addFunc: func(e *Entry) { ty := model.Ident{Name: "TestType"} e.AddType("pkg", ty) }, checkFunc: func(t *testing.T, e *Entry) { t.Helper() if len(e.typeDecls) != 1 { t.Error("Type not added") } }, }, { name: "add variable", addFunc: func(e *Entry) { vr := model.Ident{Name: "TestVar"} e.AddVariable("pkg", vr) }, checkFunc: func(t *testing.T, e *Entry) { t.Helper() if len(e.varDecls) != 1 { t.Error("Variable not added") } }, }, { name: "add const", addFunc: func(e *Entry) { co := model.Ident{Name: "TestConst"} e.AddConst("pkg", co) }, checkFunc: func(t *testing.T, e *Entry) { t.Helper() if len(e.constDecls) != 1 { t.Error("Const not added") } }, }, }) } type packageToAdd struct { name string pkg *model.AssertionPackage } type entryExtraPackagesCase struct { name string packages []packageToAdd expectedCount int expectedPackages []string } func entryExtraPackagesCases() iter.Seq[entryExtraPackagesCase] { return slices.Values([]entryExtraPackagesCase{ { name: "filter assertions package and sort", packages: func() []packageToAdd { assertionsPkg := model.New() assertionsPkg.Package = testPackage assertPkg := model.New() assertPkg.Package = testAssertPackage requirePkg := model.New() requirePkg.Package = testRequirePackage return []packageToAdd{ {name: "assertions", pkg: assertionsPkg}, {name: "assert", pkg: assertPkg}, {name: "require", pkg: requirePkg}, } }(), expectedCount: 2, expectedPackages: []string{ testAssertPackage, testRequirePackage, }, }, }) } type functionToAdd struct { pkg string fn model.Function } type entryFunctionsCase struct { name string functions []functionToAdd expectedCount int expectedNames []string } func entryFunctionsCases() iter.Seq[entryFunctionsCase] { return slices.Values([]entryFunctionsCase{ { name: "filter by assertions package and sort alphabetically", functions: []functionToAdd{ {pkg: "assertions", fn: model.Function{Name: "Zebra"}}, {pkg: "assertions", fn: model.Function{Name: "Alpha"}}, {pkg: "assert", fn: model.Function{Name: "Other"}}, // filtered out }, expectedCount: 2, expectedNames: []string{"Alpha", "Zebra"}, }, }) } go-openapi-testify-c10ca71/codegen/internal/generator/domains/sorting_test.go000066400000000000000000000133771520301377500275670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package domains import ( "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // TestCompareIdents verifies Ident comparison by Name. func TestCompareIdents(t *testing.T) { t.Parallel() for c := range compareIdentsCase() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := compareIdents(c.a, c.b) if (result < 0 && !c.aLessThanB) || (result > 0 && c.aLessThanB) || (result == 0 && c.aLessThanB) { t.Errorf("compareIdents(%q, %q) = %d, expected a < b: %v", c.a.Name, c.b.Name, result, c.aLessThanB) } }) } } // TestCompareFunctions verifies Function comparison by Name. func TestCompareFunctions(t *testing.T) { t.Parallel() for c := range compareFunctionsCase() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := compareFunctions(c.a, c.b) if (result < 0 && !c.aLessThanB) || (result > 0 && c.aLessThanB) || (result == 0 && c.aLessThanB) { t.Errorf("compareFunctions(%q, %q) = %d, expected a < b: %v", c.a.Name, c.b.Name, result, c.aLessThanB) } }) } } // TestComparePackages verifies AssertionPackage comparison by Package field. func TestComparePackages(t *testing.T) { t.Parallel() for c := range comparePackagesCase() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := comparePackages(c.a, c.b) if (result < 0 && !c.aLessThanB) || (result > 0 && c.aLessThanB) || (result == 0 && c.aLessThanB) { t.Errorf("comparePackages(%q, %q) = %d, expected a < b: %v", c.a.Package, c.b.Package, result, c.aLessThanB) } }) } } // TestCompareMapEntries verifies mapEntry comparison with special "common" domain handling. func TestCompareMapEntries(t *testing.T) { t.Parallel() for c := range compareMapEntriesCase() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := compareMapEntries(c.a, c.b) if (result < 0 && !c.aLessThanB) || (result > 0 && c.aLessThanB) || (result == 0 && c.aLessThanB) { t.Errorf("compareMapEntries(%q, %q) = %d, expected a < b: %v", c.a.key, c.b.key, result, c.aLessThanB) } }) } } /* Test case iterators */ type compareIdentsTestCase struct { name string a model.Ident b model.Ident aLessThanB bool } func compareIdentsCase() iter.Seq[compareIdentsTestCase] { return slices.Values([]compareIdentsTestCase{ { name: "a < b alphabetically", a: model.Ident{Name: "Alpha"}, b: model.Ident{Name: "Beta"}, aLessThanB: true, }, { name: "a > b alphabetically", a: model.Ident{Name: "Zebra"}, b: model.Ident{Name: "Alpha"}, aLessThanB: false, }, { name: "a == b", a: model.Ident{Name: "Equal"}, b: model.Ident{Name: "Equal"}, aLessThanB: false, }, }) } type compareFunctionsTestCase struct { name string a model.Function b model.Function aLessThanB bool } func compareFunctionsCase() iter.Seq[compareFunctionsTestCase] { return slices.Values([]compareFunctionsTestCase{ { name: "a < b alphabetically", a: model.Function{Name: "Contains"}, b: model.Function{Name: "Equal"}, aLessThanB: true, }, { name: "a > b alphabetically", a: model.Function{Name: "True"}, b: model.Function{Name: "False"}, aLessThanB: false, }, { name: "a == b", a: model.Function{Name: "Empty"}, b: model.Function{Name: "Empty"}, aLessThanB: false, }, }) } type comparePackagesTestCase struct { name string a *model.AssertionPackage b *model.AssertionPackage aLessThanB bool } func comparePackagesCase() iter.Seq[comparePackagesTestCase] { return slices.Values([]comparePackagesTestCase{ { name: "a < b alphabetically", a: &model.AssertionPackage{ Package: "github.com/go-openapi/testify/v2/assert", }, b: &model.AssertionPackage{ Package: "github.com/go-openapi/testify/v2/require", }, aLessThanB: true, }, { name: "a > b alphabetically", a: &model.AssertionPackage{ Package: "github.com/some/pkg/z", }, b: &model.AssertionPackage{ Package: "github.com/some/pkg/a", }, aLessThanB: false, }, { name: "a == b", a: &model.AssertionPackage{ Package: "github.com/go-openapi/testify/v2/assert", }, b: &model.AssertionPackage{ Package: "github.com/go-openapi/testify/v2/assert", }, aLessThanB: false, }, }) } type compareMapEntriesTestCase struct { name string a mapEntry b mapEntry aLessThanB bool } func compareMapEntriesCase() iter.Seq[compareMapEntriesTestCase] { return slices.Values([]compareMapEntriesTestCase{ { name: "both regular domains, a < b", a: mapEntry{key: "alpha"}, b: mapEntry{key: "beta"}, aLessThanB: true, }, { name: "both regular domains, a > b", a: mapEntry{key: "zebra"}, b: mapEntry{key: "alpha"}, aLessThanB: false, }, { name: "both regular domains, a == b", a: mapEntry{key: "equal"}, b: mapEntry{key: "equal"}, aLessThanB: false, }, { name: "a is common domain, b is regular (common sorts last)", a: mapEntry{key: "common"}, b: mapEntry{key: "alpha"}, aLessThanB: false, // common > alpha (sorts after) }, { name: "a is regular, b is common domain (common sorts last)", a: mapEntry{key: "zebra"}, b: mapEntry{key: "common"}, aLessThanB: true, // zebra < common (common sorts after) }, { name: "both are common domain", a: mapEntry{key: "common"}, b: mapEntry{key: "common"}, aLessThanB: false, }, }) } go-openapi-testify-c10ca71/codegen/internal/generator/funcmaps/000077500000000000000000000000001520301377500246635ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/generator/funcmaps/doc.go000066400000000000000000000010021520301377500257500ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package funcmaps provides template functions for code and documentation generation. // // This package contains all template helper functions used by both the code generator // (for assert/require packages) and the documentation generator (for markdown docs). // // The main entry point is [FuncMap], which returns a complete [template.FuncMap] // with all available functions registered. package funcmaps go-openapi-testify-c10ca71/codegen/internal/generator/funcmaps/funcmaps.go000066400000000000000000000242761520301377500270410ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package funcmaps import ( "fmt" "go/token" "net/url" "path" "path/filepath" "sort" "strings" "text/template" "time" "golang.org/x/text/cases" "golang.org/x/text/language" "github.com/go-openapi/testify/codegen/v2/internal/model" parser "github.com/go-openapi/testify/codegen/v2/internal/scanner/comments-parser" ) const ( pkgRequire = "require" pkgAssert = "assert" assertions = "assertions" ) // Titleize transform a string into a title, upper-casing the first letter. func Titleize(in string) string { return titleize(in) } // PrintReturns print a collection of [model.Parameters] as values returned by a function. func PrintReturns(vars model.Parameters) string { return printReturns(vars) } // FuncMap returns the complete template function map for code and documentation generation. func FuncMap() template.FuncMap { return map[string]any{ "comment": comment, "concat": concatStrings, "date": printDate, "debug": printDebug, "docStringFor": docStringFor, "docStringPackage": docStringPackage, "forward": forward, "godocbadge": godocbadge, "hasPrefix": strings.HasPrefix, "hasSuffix": strings.HasSuffix, "imports": printImports, "mdformat": FormatMarkdown, // From markdown.go "params": params, "pathparts": pathParts, "quote": quote, "relocate": relocate, "returns": PrintReturns, "sourceLink": sourceLink, "titleize": titleize, "slugize": Slugize, "blockquote": blockquote, "hopen": hugoopen, "hclose": hugoclose, "cr": shouldLineFeed, "testSetup": testSetup, } } func printImports(in model.ImportMap) string { list := make([]string, 0, len(in)) for k, v := range in { if k == path.Base(v) { // no alias list = append(list, "\t\""+v+"\"") continue } list = append(list, "\t"+k+"\t\""+v+"\"") } sort.Strings(list) return strings.Join(list, "\n") } func comment(str string) string { if str == "" { return "" } lines := rTrimEmpty(strings.Split(str, "\n")) return "// " + strings.Join(lines, "\n// ") } func rTrimEmpty(lines []string) []string { var i int for i = len(lines) - 1; i >= 0; i-- { if lines[i] != "" { break } } return lines[:i+1] } func params(args model.Parameters) string { var b strings.Builder l := len(args) if l == 0 { return "" } b.WriteString(args[0].Name) b.WriteByte(' ') if strings.HasSuffix(args[0].GoType, "Func") && args[0].Selector == assertions { b.WriteString(args[0].Selector + ".") // for xxxFunc types, backward-compatibility imposes to use the "assert-like" type definition } b.WriteString(args[0].GoType) for _, v := range args[1:] { b.WriteString(", ") b.WriteString(v.Name) b.WriteByte(' ') if strings.HasSuffix(v.GoType, "Func") && v.Selector == assertions { b.WriteString(v.Selector + ".") // for xxxFunc types, backward-compatibility imposes to use the "assert-like" type definition } b.WriteString(v.GoType) } return b.String() } func forward(args model.Parameters) string { var b strings.Builder l := len(args) if l == 0 { return "" } b.WriteString(args[0].Name) if args[0].IsVariadic { b.WriteString("...") } for _, v := range args[1:] { b.WriteString(", ") b.WriteString(v.Name) if v.IsVariadic { b.WriteString("...") } } return b.String() } func printReturns(vars model.Parameters) string { var b strings.Builder l := len(vars) if l == 0 { return "" } if l > 1 || vars[0].Name != "" { b.WriteByte('(') } if vars[0].Name != "" { b.WriteString(vars[0].Name) b.WriteByte(' ') } b.WriteString(vars[0].GoType) for _, v := range vars[1:] { b.WriteByte(',') b.WriteByte(' ') if v.Name != "" { b.WriteString(v.Name) b.WriteByte(' ') } b.WriteString(v.GoType) } if l > 1 || vars[0].Name != "" { b.WriteByte(')') } return b.String() } // docStringFor adds an extra comment with usage-specific context. // // Supported usage strings are: // // - format: usage with a format argument // - forward: usage as a method. func docStringFor(usage, name string) string { parts := strings.Split(name, ".") basename := parts[len(parts)-1] switch usage { case "format": return comment( fmt.Sprintf( "%sf is the same as [%s], but it accepts a format string to format arguments like [fmt.Printf].", basename, name, ), ) case "forward": return comment( fmt.Sprintf( "%s is the same as [%s], as a method rather than a package-level function.", basename, name), ) default: return "" } } // docStringPackage adds an additional comment specific to the target package. // // Supported pkg strings are: // // - assert // - require. func docStringPackage(pkg string) string { switch pkg { case pkgAssert: return `// Upon failure, the test [T] is marked as failed and continues execution.` case pkgRequire: return `// Upon failure, the test [T] is marked as failed and stops execution.` // NOTE(fredbi): // Proposal for enhancement: add more packages, e.g. for the generics-only API default: return "" } } func concatStrings(in ...string) string { return strings.Join(in, "") } func pathParts(in string) string { parts := strings.FieldsFunc(filepath.Clean(in), func(r rune) bool { return r == filepath.Separator }) for i := range parts { parts[i] = fmt.Sprintf("%q", parts[i]) } return strings.Join(parts, ",") } // relocate relocates already-parsed test values from the "assertions" package to the target package. // // It uses AST-based relocation to properly handle all Go expressions, replacing // package selectors and qualifying unqualified identifiers as needed. // // Examples: // - assertions.ErrTest → assert.ErrTest // - ErrTest → assert.ErrTest (adds qualifier) // - assertions.PanicTestFunc → assertions.PanicTestFunc (exception) func relocate(values []model.TestValue, pkg string) string { if len(values) == 0 { return "" } if pkg == "" { // Return original values as string parts := make([]string, 0, len(values)) for _, v := range values { parts = append(parts, v.Raw) } return strings.Join(parts, ", ") } // Relocate each value relocated := make([]string, 0, len(values)) for _, tv := range values { // If parse failed, use original (fallback) if tv.Error != nil { relocated = append(relocated, tv.Raw) continue } // Relocate from "assertions" to target package relocatedTV := parser.RelocateTestValue(tv, assertions, pkg) // If relocation failed, use original (fallback) if relocatedTV.Error != nil { relocated = append(relocated, tv.Raw) } else { relocated = append(relocated, relocatedTV.Raw) } } // Join back with comma-space return strings.Join(relocated, ", ") } // sourceLink recomposes a github URL to the source code. func sourceLink(baseGitHubURL string, pos *token.Position) string { if pos == nil { return "" } dir, file := filepath.Split(pos.Filename) l1 := filepath.Base(dir) l2 := filepath.Base(filepath.Dir(filepath.Dir(dir))) filename := path.Join(l2, l1, file) return fmt.Sprintf("%s/blob/master/%s#L%d", baseGitHubURL, filename, pos.Line, ) } func quote(in string) string { return fmt.Sprintf("%q", in) } func titleize(in string) string { caser := cases.Title(language.English, cases.NoLower) // the case is stateful: cannot declare it globally return caser.String(in) } // godocbadge produces a badge URL from https://pkg.go.dev. func godocbadge(pkggodevURL string) (string, error) { u, err := url.Parse(pkggodevURL) if err != nil { return "", err } u.Path = path.Join("badge", u.Path) return u.String(), nil } func printDebug(in any) string { return fmt.Sprintf("%#v", in) } func printDate() string { return time.Now().Format(time.DateOnly) } // Slugize converts a name into a markdown ref inside a document. func Slugize(in string) string { return strings.ToLower( strings.Map(func(r rune) rune { switch r { case '.', '_', ' ', '\t', ':': return '-' case '[', ']', ',', '~': return -1 default: return r } }, in, )) } // blockquote prints a text as a markdown blockquote. func blockquote(in string) string { result := make([]string, 0, sensiblePrealloc) for line := range strings.SplitSeq(in, "\n") { result = append(result, "> "+line) if strings.HasSuffix(line, ".") { result = append(result, ">") } } return strings.Join(result, "\n") } func hugoopen() string { return "{{" } func hugoclose() string { return "}}" } func shouldLineFeed(dontFeed bool) string { if dontFeed { return "" } return "\n" } // testSetup computes the test parameterization for a function variant. // // Supported variants: // - "assertions": package-level test (e.g., TestEqual) // - "format": format variant test (e.g., TestEqualf) // - "forward": forwarded method test (e.g., TestAssertionsEqual) // - "forward-format": forwarded format method test (e.g., TestAssertionsEqualf) func testSetup(fn model.Function, variant string, receiver string) model.Function { switch variant { case "assertions": fn.TestCall = fn.Name + fn.GenericSuffix() + "(mock, " fn.TestMock = fmt.Sprintf("mock := new(%s)", fn.UseMock) fn.TestErrorPrefix = fn.Name fn.TestPanicWrapper = "Panics(t, " fn.TestMockFailure = fn.FailMsg() case "format": fn.TestCall = fn.Name + "f" + fn.GenericSuffix() + "(mock, " fn.TestMock = fmt.Sprintf("mock := new(%s)", fn.UseMock) fn.TestErrorPrefix = fn.Name + "f" fn.TestPanicWrapper = "Panics(t, " fn.TestMockFailure = fn.FailMsg("f") fn.TestMsg = "test message" case "forward": fn.TestCall = "a." + fn.Name + "(" fn.TestMock = fmt.Sprintf("mock := new(%s)\na := New(mock)", fn.UseMock) fn.TestErrorPrefix = receiver + "." + fn.Name fn.TestPanicWrapper = "a.Panics(" fn.TestMockFailure = fn.FailMsg(receiver, "") case "forward-format": fn.TestCall = "a." + fn.Name + "f(" fn.TestMock = fmt.Sprintf("mock := new(%s)\na := New(mock)", fn.UseMock) fn.TestErrorPrefix = receiver + "." + fn.Name + "f" fn.TestPanicWrapper = "a.Panics(" fn.TestMockFailure = fn.FailMsg(receiver, "f") fn.TestMsg = "test message" } return fn } go-openapi-testify-c10ca71/codegen/internal/generator/funcmaps/funcmaps_test.go000066400000000000000000000531361520301377500300750ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package funcmaps import ( "fmt" "go/token" "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" parser "github.com/go-openapi/testify/codegen/v2/internal/scanner/comments-parser" ) const ( testPackage = "github.com/go-openapi/testify/v2/internal/assertions" testRepo = "github.com/go-openapi/testify/v2" testAssertPackage = "github.com/go-openapi/testify/v2/assert" testRequirePackage = "github.com/go-openapi/testify/v2/require" githubRepo = "https://github.com/go-openapi/testify" ) // TestFuncMap verifies that FuncMap returns all expected functions. func TestFuncMap(t *testing.T) { t.Parallel() fm := FuncMap() expectedFuncs := []string{ "imports", "comment", "date", "params", "forward", "docStringFor", "docStringPackage", "returns", "concat", "pathparts", "relocate", "hasSuffix", "sourceLink", "titleize", "quote", "mdformat", "godocbadge", "debug", } for _, name := range expectedFuncs { if _, ok := fm[name]; !ok { t.Errorf("FuncMap missing expected function: %q", name) } } } // TestPrintImports verifies import formatting. func TestPrintImports(t *testing.T) { t.Parallel() for c := range printImportsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := printImports(c.input) if result != c.expected { t.Errorf("Expected:\n%s\n\nGot:\n%s", c.expected, result) } }) } } // TestComment verifies comment formatting. func TestComment(t *testing.T) { t.Parallel() for c := range commentCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := comment(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestParams verifies parameter formatting. func TestParams(t *testing.T) { t.Parallel() for c := range paramsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := params(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestForward verifies forward parameter formatting. func TestForward(t *testing.T) { t.Parallel() for c := range forwardCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := forward(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestPrintReturns verifies return value formatting. func TestPrintReturns(t *testing.T) { t.Parallel() for c := range printReturnsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := PrintReturns(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestDocStringFor verifies usage-specific doc string generation. func TestDocStringFor(t *testing.T) { t.Parallel() for c := range docStringForCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := docStringFor(c.usage, c.funcName) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestDocStringPackage verifies package-specific doc string generation. func TestDocStringPackage(t *testing.T) { t.Parallel() for c := range docStringPackageCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := docStringPackage(c.pkg) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestConcatStrings verifies string concatenation. func TestConcatStrings(t *testing.T) { t.Parallel() for c := range concatStringsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := concatStrings(c.input...) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestPathParts verifies path splitting and quoting. func TestPathParts(t *testing.T) { t.Parallel() for c := range pathPartsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := pathParts(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestRelocate verifies special value relocation for testable examples. func TestRelocate(t *testing.T) { t.Parallel() for c := range relocateCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() // Parse input string into TestValue slice values := parser.ParseTestValues(c.input) result := relocate(values, c.pkg) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestSourceLink verifies GitHub source link generation. func TestSourceLink(t *testing.T) { t.Parallel() for c := range sourceLinkCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := sourceLink(c.baseURL, c.pos) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestQuote verifies string quoting. func TestQuote(t *testing.T) { t.Parallel() for c := range quoteCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := quote(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestTitleize verifies title casing. func TestTitleize(t *testing.T) { t.Parallel() for c := range titleizeCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := Titleize(c.input) if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } }) } } // TestGodocbadge verifies pkg.go.dev badge URL generation. func TestGodocbadge(t *testing.T) { t.Parallel() for c := range godocbadgeCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result, err := godocbadge(c.input) if c.expectError { if err == nil { t.Error("Expected error but got nil") } } else { if err != nil { t.Errorf("Unexpected error: %v", err) } if result != c.expected { t.Errorf("Expected: %q, Got: %q", c.expected, result) } } }) } } func TestDebug(t *testing.T) { t.Parallel() result := printDebug(1) const expected = "1" if result != expected { t.Errorf("Expected: %q, Got: %q", expected, result) } } func TestData(t *testing.T) { t.Parallel() result := printDate() if result == "" { t.Errorf("Expected a non-empty string, Got: %q", result) } } /* Test case iterators */ type printImportsCase struct { name string input model.ImportMap expected string } func printImportsCases() iter.Seq[printImportsCase] { return slices.Values([]printImportsCase{ { name: "empty imports", input: model.ImportMap{}, expected: "", }, { name: "single import without alias", input: model.ImportMap{ "testing": "testing", }, expected: "\t\"testing\"", }, { name: "single import with alias", input: model.ImportMap{ "req": testRequirePackage, }, expected: fmt.Sprintf("\treq\t%q", testRequirePackage), }, { name: "multiple imports sorted alphabetically", input: model.ImportMap{ "testing": "testing", "assertions": testPackage, "fmt": "fmt", }, // Sorted by the full formatted string, not package name // No alias for assertions since it matches path.Base expected: fmt.Sprintf("\t\"fmt\"\n\t%q\n\t\"testing\"", testPackage), }, }) } type commentCase struct { name string input string expected string } func commentCases() iter.Seq[commentCase] { return slices.Values([]commentCase{ { name: "empty string", input: "", expected: "", }, { name: "single line", input: "This is a comment", expected: "// This is a comment", }, { name: "multiline", input: "Line 1\nLine 2\nLine 3", expected: "// Line 1\n// Line 2\n// Line 3", }, { name: "trailing empty lines trimmed", input: "Line 1\nLine 2\n\n\n", expected: "// Line 1\n// Line 2", }, }) } type paramsCase struct { name string input model.Parameters expected string } func paramsCases() iter.Seq[paramsCase] { return slices.Values([]paramsCase{ { name: "empty parameters", input: model.Parameters{}, expected: "", }, { name: "single parameter", input: model.Parameters{ {Name: "t", GoType: "TestingT"}, }, expected: "t TestingT", }, { name: "multiple parameters", input: model.Parameters{ {Name: "expected", GoType: "any"}, {Name: "actual", GoType: "any"}, }, expected: "expected any, actual any", }, { name: "parameter with Func type and assertions selector", input: model.Parameters{ {Name: "fn", GoType: "ComparisonAssertionFunc", Selector: "assertions"}, }, expected: "fn assertions.ComparisonAssertionFunc", }, { name: "variadic parameter as last argument", input: model.Parameters{ {Name: "t", GoType: "TestingT"}, {Name: "expected", GoType: "any"}, {Name: "msgAndArgs", GoType: "...any", IsVariadic: true}, }, expected: "t TestingT, expected any, msgAndArgs ...any", }, { name: "variadic parameter as unique argument", input: model.Parameters{ {Name: "msgAndArgs", GoType: "...any", IsVariadic: true}, }, expected: "msgAndArgs ...any", }, { name: "special handling of xxxFunc type as unique argument", input: model.Parameters{ {Name: "fn", GoType: "PanicFunc", Selector: "assertions"}, }, expected: "fn assertions.PanicFunc", }, { name: "special handling of xxxFunc type with multiple arguments", input: model.Parameters{ {Name: "t", GoType: "TestingT"}, {Name: "fn", GoType: "PanicFunc", Selector: "assertions"}, }, expected: "t TestingT, fn assertions.PanicFunc", }, }) } type forwardCase struct { name string input model.Parameters expected string } func forwardCases() iter.Seq[forwardCase] { return slices.Values([]forwardCase{ { name: "empty parameters", input: model.Parameters{}, expected: "", }, { name: "single parameter", input: model.Parameters{ {Name: "expected"}, }, expected: "expected", }, { name: "multiple parameters", input: model.Parameters{ {Name: "expected"}, {Name: "actual"}, {Name: "msgAndArgs"}, }, expected: "expected, actual, msgAndArgs", }, { name: "variadic parameter as last argument", input: model.Parameters{ {Name: "expected"}, {Name: "msgAndArgs", IsVariadic: true}, }, expected: "expected, msgAndArgs...", }, { name: "variadic parameter as unique argument", input: model.Parameters{ {Name: "msgAndArgs", GoType: "...any", IsVariadic: true}, }, expected: "msgAndArgs...", }, }) } type printReturnsCase struct { name string input model.Parameters expected string } func printReturnsCases() iter.Seq[printReturnsCase] { return slices.Values([]printReturnsCase{ { name: "no returns", input: model.Parameters{}, expected: "", }, { name: "single unnamed return", input: model.Parameters{ {GoType: "bool"}, }, expected: "bool", }, { name: "single named return", input: model.Parameters{ {Name: "success", GoType: "bool"}, }, expected: "(success bool)", }, { name: "multiple unnamed returns", input: model.Parameters{ {GoType: "string"}, {GoType: "error"}, }, expected: "(string, error)", }, { name: "multiple named returns", input: model.Parameters{ {Name: "result", GoType: "string"}, {Name: "err", GoType: "error"}, }, expected: "(result string, err error)", }, }) } type docStringForCase struct { name string usage string funcName string expected string } func docStringForCases() iter.Seq[docStringForCase] { return slices.Values([]docStringForCase{ { name: "format usage", usage: "format", funcName: "assert.Equal", expected: "// Equalf is the same as [assert.Equal], but it accepts a format string to format arguments like [fmt.Printf].", }, { name: "forward usage", usage: "forward", funcName: "assert.Equal", expected: "// Equal is the same as [assert.Equal], as a method rather than a package-level function.", }, { name: "unknown usage", usage: "unknown", funcName: "assert.Equal", expected: "", }, }) } type docStringPackageCase struct { name string pkg string expected string } func docStringPackageCases() iter.Seq[docStringPackageCase] { return slices.Values([]docStringPackageCase{ { name: "assert package", pkg: "assert", expected: `// Upon failure, the test [T] is marked as failed and continues execution.`, }, { name: "require package", pkg: "require", expected: `// Upon failure, the test [T] is marked as failed and stops execution.`, }, { name: "unknown package", pkg: "unknown", expected: "", }, }) } type concatStringsCase struct { name string input []string expected string } func concatStringsCases() iter.Seq[concatStringsCase] { return slices.Values([]concatStringsCase{ { name: "empty strings", input: []string{}, expected: "", }, { name: "single string", input: []string{"hello"}, expected: "hello", }, { name: "multiple strings", input: []string{"hello", " ", "world"}, expected: "hello world", }, }) } type pathPartsCase struct { name string input string expected string } func pathPartsCases() iter.Seq[pathPartsCase] { return slices.Values([]pathPartsCase{ { name: "simple path", input: "foo/bar/baz", expected: `"foo","bar","baz"`, }, { name: "absolute path", input: "/usr/local/bin", expected: `"usr","local","bin"`, }, { name: "single component", input: "test", expected: `"test"`, }, }) } type relocateCase struct { name string input string pkg string expected string } func relocateCases() iter.Seq[relocateCase] { return slices.Values([]relocateCase{ { name: "empty package does nothing", input: "ErrTest{}, CollectT{}, True(c)", pkg: "", expected: "ErrTest{}, CollectT{}, True(c)", }, { name: "relocates ErrTest", input: "ErrTest{}", pkg: "assert", expected: "assert.ErrTest{}", }, { name: "relocates CollectT", input: "CollectT{}", pkg: "assert", expected: "assert.CollectT{}", }, { name: "relocates True(c)", input: "True(c)", pkg: "assert", expected: "assert.True(c)", }, { name: "does not relocate with dot prefix", input: "t.ErrTest{}", pkg: "assert", expected: "t.ErrTest{}", }, { name: "relocates all three", input: "ErrTest{}, CollectT{}, True(c)", pkg: "assert", expected: "assert.ErrTest{}, assert.CollectT{}, assert.True(c)", }, }) } type sourceLinkCase struct { name string baseURL string pos *token.Position expected string } func sourceLinkCases() iter.Seq[sourceLinkCase] { return slices.Values([]sourceLinkCase{ { name: "nil position", baseURL: githubRepo, pos: nil, expected: "", }, { name: "valid position", baseURL: githubRepo, pos: &token.Position{ Filename: "/home/user/go/src/github.com/go-openapi/testify/internal/assertions/equal.go", Line: 42, }, expected: githubRepo + "/blob/master/internal/assertions/equal.go#L42", }, }) } type quoteCase struct { name string input string expected string } func quoteCases() iter.Seq[quoteCase] { return slices.Values([]quoteCase{ { name: "simple string", input: "hello", expected: `"hello"`, }, { name: "string with quotes", input: `he said "hello"`, expected: `"he said \"hello\""`, }, { name: "empty string", input: "", expected: `""`, }, }) } type titleizeCase struct { name string input string expected string } func titleizeCases() iter.Seq[titleizeCase] { return slices.Values([]titleizeCase{ { name: "lowercase", input: "hello world", expected: "Hello World", }, { name: "mixed case keeps original case", input: "hELLo WoRLD", expected: "HELLo WoRLD", }, { name: "upper-cased keeps original case", input: "HTTP", expected: "HTTP", }, { name: "already titlecase", input: "Hello World", expected: "Hello World", }, { name: "empty string", input: "", expected: "", }, }) } type godocbadgeCase struct { name string input string expected string expectError bool } func godocbadgeCases() iter.Seq[godocbadgeCase] { return slices.Values([]godocbadgeCase{ { name: "valid URL", input: "https://pkg.go.dev/github.com/go-openapi/testify/v2", expected: "https://pkg.go.dev/badge/github.com/go-openapi/testify/v2", expectError: false, }, { name: "invalid URL", input: "://invalid", expected: "", expectError: true, }, }) } // TestSlugize verifies slugize converts names to markdown slugs. func TestSlugize(t *testing.T) { t.Parallel() for tt := range slugizeCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := Slugize(tt.input); got != tt.expected { t.Errorf("Slugize(%q) = %q, want %q", tt.input, got, tt.expected) } }) } } type slugizeCase struct { name string input string expected string } func slugizeCases() iter.Seq[slugizeCase] { return slices.Values([]slugizeCase{ { name: "simple name", input: "Equal", expected: "equal", }, { name: "with dots", input: "assert.Equal", expected: "assert-equal", }, { name: "with underscores", input: "my_function", expected: "my-function", }, { name: "with spaces", input: "My Function", expected: "my-function", }, { name: "with brackets", input: "Greater[T cmp.Ordered]", expected: "greatert-cmp-ordered", }, { name: "with colons and tabs", input: "section:\ttitle", expected: "section--title", }, { name: "with commas and tildes", input: "A, B~C", expected: "a-bc", }, }) } // TestBlockquote verifies blockquote formatting. func TestBlockquote(t *testing.T) { t.Parallel() for tt := range blockquoteCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := blockquote(tt.input); got != tt.expected { t.Errorf("blockquote(%q) = %q, want %q", tt.input, got, tt.expected) } }) } } type blockquoteCase struct { name string input string expected string } func blockquoteCases() iter.Seq[blockquoteCase] { return slices.Values([]blockquoteCase{ { name: "single line", input: "Hello world", expected: "> Hello world", }, { name: "line ending with period", input: "Hello world.", expected: "> Hello world.\n>", }, { name: "multi-line", input: "Line 1.\nLine 2", expected: "> Line 1.\n>\n> Line 2", }, }) } // TestHugoDelimiters verifies Hugo template delimiter functions. func TestHugoDelimiters(t *testing.T) { t.Parallel() if got := hugoopen(); got != "{{" { t.Errorf("hugoopen() = %q, want %q", got, "{{") } if got := hugoclose(); got != "}}" { t.Errorf("hugoclose() = %q, want %q", got, "}}") } } // TestShouldLineFeed verifies conditional line feed. func TestShouldLineFeed(t *testing.T) { t.Parallel() if got := shouldLineFeed(true); got != "" { t.Errorf("shouldLineFeed(true) = %q, want empty", got) } if got := shouldLineFeed(false); got != "\n" { t.Errorf(`shouldLineFeed(false) = %q, want "\n"`, got) } } // TestTestSetup verifies test parameterization for all variants. func TestTestSetup(t *testing.T) { t.Parallel() for tt := range testSetupCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() fn := model.Function{ Name: "Equal", UseMock: tt.useMock, } result := testSetup(fn, tt.variant, tt.receiver) if result.TestCall != tt.expectedCall { t.Errorf("TestCall = %q, want %q", result.TestCall, tt.expectedCall) } if result.TestMock != tt.expectedMock { t.Errorf("TestMock = %q, want %q", result.TestMock, tt.expectedMock) } if result.TestErrorPrefix != tt.expectedPrefix { t.Errorf("TestErrorPrefix = %q, want %q", result.TestErrorPrefix, tt.expectedPrefix) } if result.TestMsg != tt.expectedMsg { t.Errorf("TestMsg = %q, want %q", result.TestMsg, tt.expectedMsg) } }) } } type testSetupCase struct { name string variant string receiver string useMock string expectedCall string expectedMock string expectedPrefix string expectedMsg string } func testSetupCases() iter.Seq[testSetupCase] { return slices.Values([]testSetupCase{ { name: "assertions variant", variant: "assertions", useMock: "mockT", expectedCall: "Equal(mock, ", expectedMock: "mock := new(mockT)", expectedPrefix: "Equal", expectedMsg: "", }, { name: "format variant", variant: "format", useMock: "mockT", expectedCall: "Equalf(mock, ", expectedMock: "mock := new(mockT)", expectedPrefix: "Equalf", expectedMsg: "test message", }, { name: "forward variant", variant: "forward", receiver: "Assertions", useMock: "mockT", expectedCall: "a.Equal(", expectedMock: "mock := new(mockT)\na := New(mock)", expectedPrefix: "Assertions.Equal", expectedMsg: "", }, { name: "forward-format variant", variant: "forward-format", receiver: "Assertions", useMock: "mockT", expectedCall: "a.Equalf(", expectedMock: "mock := new(mockT)\na := New(mock)", expectedPrefix: "Assertions.Equalf", expectedMsg: "test message", }, }) } go-openapi-testify-c10ca71/codegen/internal/generator/funcmaps/markdown.go000066400000000000000000000212611520301377500270360ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package funcmaps import ( "fmt" "regexp" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" ) var ( refLinkPattern = regexp.MustCompile(`(?m)^\[([^\]]+)\]:\s+(.+)$`) // find godoc-style links: [word.word]. godocPattern = regexp.MustCompile(`\[([a-zA-Z0-9_]+\.[a-zA-Z0-9_.]+)\]`) // there is a dot godocPatternLocal = regexp.MustCompile(`\[([a-zA-Z0-9_]+)\]`) // no dot tabSectionPattern = regexp.MustCompile(`^#\s+(Examples?|Usage|Use)$`) sectionPattern = regexp.MustCompile(`^#\s+`) ) const ( sensiblePrealloc = 20 godocHost = "pkg.go.dev" repo = "github.com/go-openapi/testify/v2" ) // FormatMarkdown carries out a formatting of inline markdown in comments that handles: // // 1. Reference-style markdown links: [text]: url // 2. Godoc-style links: [errors.Is], [testing.T], etc. // 3. Rearrange godoc sections so we extract Usage and Examples. Examples are matched with the // generated testable examples and reinjected as go code in the "Testable Examples" tab. func FormatMarkdown(in string, object any) string { // 1. process godoc links processed, danglingRefs := markdownLinks(in) // 2. build pkg.go.dev links processed = godocLinks(processed) // 3. strip Usage and Example sections and render them as hugo tabs result, trailer := stripSections(processed, object) // Append dangling reference links as additional context if len(danglingRefs) > 0 { result = append(result, "") result = append(result, danglingRefs...) } return strings.Join(append(result, trailer...), "\n") } // markdownLinks processes links found in godoc strings to produce markdown links. // // If some references are defined, they are stashed and returned as a slice of "dangling references". func markdownLinks(in string) (string, []string) { // Step 1: Extract reference-style link definitions // Pattern: [text]: url (at start of line or after whitespace) refLinks := make(map[string]string) // Extract all reference links matches := refLinkPattern.FindAllStringSubmatch(in, -1) const expectedGroups = 2 for _, match := range matches { if len(match) == expectedGroups+1 { refLinks[match[1]] = match[2] } } // Remove reference link definitions from input processed := refLinkPattern.ReplaceAllString(in, "") // Convert reference-style links to inline links // Replace [text] with [text](url) where we have the reference usedRefs := make(map[string]bool) for refText, refURL := range refLinks { // Pattern: [text] not followed by ( or : pattern := regexp.MustCompile(`\[` + regexp.QuoteMeta(refText) + `\]([^(\[]|$)`) if pattern.MatchString(processed) { processed = pattern.ReplaceAllString(processed, fmt.Sprintf("[%s](%s)$1", refText, refURL)) usedRefs[refText] = true } } // Collect unused reference links to append later (dangling references) var danglingRefs []string for refText, refURL := range refLinks { if !usedRefs[refText] { danglingRefs = append(danglingRefs, fmt.Sprintf("[%s]: %s", refText, refURL)) } } return processed, danglingRefs } // godocLinks converts godoc-style links to pkg.go.dev URLs. // // Pattern: [package.Type] - must contain a dot and only valid identifier chars. // We process line by line to avoid matching already-converted links. func godocLinks(in string) string { lines := strings.Split(in, "\n") for i, line := range lines { // Skip if line already has inline links (contains ](http) if strings.Contains(line, "](") { continue } replacedStdLib := godocPattern.ReplaceAllStringFunc(line, func(match string) string { identifier := strings.Trim(match, "[]") // Has package qualifier - link to pkg.go.dev const expectedParts = 2 parts := strings.SplitN(identifier, ".", expectedParts) if len(parts) != expectedParts { // Defensive code: This should never happen with the current regex pattern, // which requires at least one dot. If this triggers, it indicates a bug in // the regex pattern that needs to be fixed. panic(fmt.Errorf("internal error: godoc pattern matched %q but split into %d parts instead of %d", identifier, len(parts), expectedParts)) } pkgPath := parts[0] symbol := parts[1] // Assume standard library for simple package names return fmt.Sprintf("[%s](https://%s/%s#%s)", identifier, godocHost, pkgPath, symbol) }) // check for links with current package, e.g. [Boolean]. Those don't have a dot. replacedLocal := godocPatternLocal.ReplaceAllStringFunc(replacedStdLib, func(match string) string { identifier := strings.Trim(match, "[]") return fmt.Sprintf("[%s](https://%s/%s/assert#%s)", identifier, godocHost, repo, identifier) }) lines[i] = replacedLocal } return strings.Join(lines, "\n") } // packageExamples groups testable examples by generated package (assert, require). type packageExamples struct { pkg string examples []model.Renderable } // stripSections renders Usage and Examples sections with Hugo shortcodes. // These are rendered as tabs. // // It returns a result of lines (indented markdown with sections), a trailer of lines (Usage and Examples tabs). func stripSections(in string, object any) (result, trailer []string) { result = make([]string, 0, sensiblePrealloc) trailer = make([]string, 0, sensiblePrealloc) var ( perPkgExamples []packageExamples funcName string ) if function, ok := (object).(model.Function); ok { funcName = function.Name // Testable examples live in the generated packages (assert, require), // not in the source assertions package. Look them up via the document // context which carries pointers to the generated packages. if function.Context != nil { for _, fwc := range function.Context.ExtraPackages.LookupFunction(funcName) { if len(fwc.Examples) > 0 { perPkgExamples = append(perPkgExamples, packageExamples{ pkg: fwc.Package, examples: fwc.Examples, }) } } } } // parse state tabsCollection := false expanded := false tab := false hasTestableExamples := false for line := range strings.SplitSeq(in, "\n") { if expanded && len(strings.TrimSpace(line)) == 0 { continue } matches := tabSectionPattern.FindStringSubmatch(line) const expectedCapture = 1 if len(matches) != expectedCapture+1 { // either a regular line or a section header that we don't want in the trailer tabs if sectionPattern.MatchString(line) { // found a new section expanded = false line = strings.ReplaceAll(line, "#", "####") // containing function heading is ### } if expanded { trailer = append(trailer, line) } else { result = append(result, line) } continue } // interesting section to catch as a tab section := matches[expectedCapture] expanded = true if !tabsCollection { trailer = append(trailer, `{{% expand title="Examples" %}}`) // the title of the collapsible section trailer = append(trailer, `{{< tabs >}}`) tabsCollection = true } if strings.EqualFold(section, "Examples") && len(perPkgExamples) > 0 { hasTestableExamples = true continue // skip : we'll add testable examples below } title := titleize(section) if tab { trailer = append(trailer, "```") trailer = append(trailer, `{{< /tab >}}`) } trailer = append(trailer, fmt.Sprintf(`{{%% tab title="%s" %%}}`, title)) trailer = append(trailer, "```go") tab = true } if tab { trailer = append(trailer, "```") trailer = append(trailer, `{{< /tab >}}`) } if hasTestableExamples { trailer = addExamples(trailer, funcName, perPkgExamples) } if tabsCollection { trailer = append(trailer, `{{< /tabs >}}`) trailer = append(trailer, `{{% /expand %}}`) } return result, trailer } func addExamples(trailer []string, funcName string, perPkgExamples []packageExamples) []string { for _, pe := range perPkgExamples { trailer = append(trailer, fmt.Sprintf(`{{%% tab title="Testable Examples (%s)" %%}}`, pe.pkg)) trailer = append(trailer, `{{% cards %}}`) for _, example := range pe.examples { trailer = append(trailer, `{{% card %}}`) trailer = append(trailer, "\n") trailer = append(trailer, `*[Copy and click to open Go Playground](https://go.dev/play/)*`) trailer = append(trailer, "\n") trailer = append(trailer, "```go") trailer = append(trailer, fmt.Sprintf("// real-world test would inject *testing.T from Test%s(t *testing.T)", funcName)) trailer = append(trailer, example.Render()) trailer = append(trailer, "```") trailer = append(trailer, `{{% /card %}}`) trailer = append(trailer, "\n") } trailer = append(trailer, `{{% /cards %}}`) trailer = append(trailer, `{{< /tab >}}`) trailer = append(trailer, "\n") } return trailer } go-openapi-testify-c10ca71/codegen/internal/generator/funcmaps/markdown_test.go000066400000000000000000000214251520301377500300770ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package funcmaps import ( "iter" "slices" "strings" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) func TestMarkdownFormatEnhanced(t *testing.T) { t.Parallel() for tt := range markdownTestCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() result := FormatMarkdown(tt.input, nil) for _, want := range tt.contains { if !strings.Contains(result, want) { t.Errorf("FormatMarkdown() missing expected string:\nwant: %q\ngot: %s", want, result) } } for _, notWant := range tt.notContains { if strings.Contains(result, notWant) { t.Errorf("FormatMarkdown() contains unexpected string:\ndon't want: %q\ngot: %s", notWant, result) } } }) } } // mockRenderable implements model.Renderable for testing. type mockRenderable struct { content string } func (m mockRenderable) Render() string { return m.content } // TestFormatMarkdownWithFunction verifies FormatMarkdown with a Function object // that has Context and testable examples - this exercises the stripSections // branch for Example sections and the addExamples path. func TestFormatMarkdownWithFunction(t *testing.T) { t.Parallel() // Build a Function with Context that has ExtraPackages containing examples assertPkg := &model.AssertionPackage{ Package: "assert", Receiver: "Assertions", Functions: model.Functions{ { Name: "Equal", Examples: []model.Renderable{ mockRenderable{content: "assert.Equal(t, 1, 1)"}, }, }, }, } doc := &model.Document{ ExtraPackages: model.ExtraPackages{assertPkg}, } fn := model.Function{ Name: "Equal", Context: doc, } input := `Equal asserts that two objects are equal. # Usage assertions.Equal(t, expected, actual) # Examples success: 123, 123 failure: 123, 456` result := FormatMarkdown(input, fn) // Should contain Hugo tab structure if !strings.Contains(result, `{{% expand title="Examples" %}}`) { t.Error("Expected expand shortcode in output") } if !strings.Contains(result, `{{< tabs >}}`) { t.Error("Expected tabs shortcode in output") } // Should contain testable examples tab if !strings.Contains(result, `Testable Examples (assert)`) { t.Error("Expected Testable Examples tab in output") } // Should contain the rendered example if !strings.Contains(result, "assert.Equal(t, 1, 1)") { t.Error("Expected rendered example code in output") } // Should contain cards shortcode if !strings.Contains(result, `{{% cards %}}`) { t.Error("Expected cards shortcode in output") } // Should contain Go Playground link if !strings.Contains(result, "go.dev/play") { t.Error("Expected Go Playground link in output") } } // TestFormatMarkdownWithFunctionNoExamples verifies the Examples section // is rendered as a regular code tab when no testable examples exist. func TestFormatMarkdownWithFunctionNoExamples(t *testing.T) { t.Parallel() fn := model.Function{ Name: "Equal", Context: nil, // no context } input := `Equal asserts that two objects are equal. # Examples success: 123, 123` result := FormatMarkdown(input, fn) // Should still have tab structure if !strings.Contains(result, `{{% expand title="Examples" %}}`) { t.Error("Expected expand shortcode in output") } // Should NOT have testable examples (no context with packages) if strings.Contains(result, "Testable Examples") { t.Error("Should not have Testable Examples when no packages available") } } // TestFormatMarkdownWithMultipleSections verifies sections beyond Usage/Examples // are rendered with increased heading depth. func TestFormatMarkdownWithMultipleSections(t *testing.T) { t.Parallel() input := `Some description. # Usage assertions.Something(t) # Details More info about the function. # Examples success: true` result := FormatMarkdown(input, nil) // "Details" is not a tab-captured section, it should stay in the result // but with increased heading depth (# → ####) if !strings.Contains(result, "#### Details") { t.Error("Non-tab section should have increased heading depth") } // The description should be in the main body if !strings.Contains(result, "Some description.") { t.Error("Description should be in main body") } } func TestFormatMarkdownWithMultiplePackages(t *testing.T) { t.Parallel() assertPkg := &model.AssertionPackage{ Package: "assert", Functions: model.Functions{ { Name: "Equal", Examples: []model.Renderable{ mockRenderable{content: "assert.Equal(t, 1, 1)"}, }, }, }, } requirePkg := &model.AssertionPackage{ Package: "require", Functions: model.Functions{ { Name: "Equal", Examples: []model.Renderable{ mockRenderable{content: "require.Equal(t, 1, 1)"}, }, }, }, } doc := &model.Document{ ExtraPackages: model.ExtraPackages{assertPkg, requirePkg}, } fn := model.Function{ Name: "Equal", Context: doc, } input := `Equal asserts equality. # Examples success: 123, 123` result := FormatMarkdown(input, fn) // Should have tabs for both packages if !strings.Contains(result, `Testable Examples (assert)`) { t.Error("Expected assert examples tab") } if !strings.Contains(result, `Testable Examples (require)`) { t.Error("Expected require examples tab") } // Both rendered examples should appear if !strings.Contains(result, "assert.Equal(t, 1, 1)") { t.Error("Expected assert rendered example") } if !strings.Contains(result, "require.Equal(t, 1, 1)") { t.Error("Expected require rendered example") } } func TestFormatMarkdownOutput_Debug(t *testing.T) { input := `Empty asserts that the given value is "empty". Zero values are "empty". Values can be of type [strings.Builder] or [Boolean]. # Usage assertions.Empty(t, obj) # Examples success: "" failure: "not empty" [Zero values]: https://go.dev/ref/spec#The_zero_value` result := FormatMarkdown(input, nil) t.Logf("Output:\n%s", result) } type markdownTestCase struct { name string input string contains []string // strings that should appear in output notContains []string // strings that should NOT appear } func markdownTestCases() iter.Seq[markdownTestCase] { return slices.Values([]markdownTestCase{ { name: "reference-style markdown link (dangling)", input: `Empty asserts that the given value is "empty". # Usage assertions.Empty(t, obj) # Examples success: "" failure: "not empty" [Zero values]: https://go.dev/ref/spec#The_zero_value`, contains: []string{ `[Zero values]: https://go.dev/ref/spec#The_zero_value`, // Dangling ref appears after expand `{{% expand title="Examples" %}}`, `{{< tabs >}}`, `{{% /expand %}}`, }, notContains: []string{ // The reference shouldn't appear inside code blocks }, }, { name: "godoc-style link", input: `EqualError asserts that a function returned an error equal to the provided error. This is a wrapper for [errors.Is]. # Usage assertions.EqualError(t, err, expectedErrorString)`, contains: []string{ `[errors.Is](https://pkg.go.dev/errors#Is)`, }, notContains: []string{ `[errors.Is].`, // Should be converted }, }, { name: "multiple godoc links", input: `See [testing.T] and [fmt.Printf] for details. # Usage assertions.Something(t)`, contains: []string{ `[testing.T](https://pkg.go.dev/testing#T)`, `[fmt.Printf](https://pkg.go.dev/fmt#Printf)`, }, }, { name: "mixed markdown and godoc links (dangling ref)", input: `Uses [errors.Is] to check errors. # Examples success: nil failure: ErrTest [error wrapping]: https://go.dev/blog/go1.13-errors`, contains: []string{ `[errors.Is](https://pkg.go.dev/errors#Is)`, `[error wrapping]: https://go.dev/blog/go1.13-errors`, // Dangling ref }, notContains: []string{ // Nothing }, }, { name: "used reference link", input: `Empty asserts values are empty. See [Zero values] for definition. # Examples success: "" [Zero values]: https://go.dev/ref/spec#The_zero_value`, contains: []string{ `[Zero values](https://go.dev/ref/spec#The_zero_value)`, // Converted to inline }, notContains: []string{ `[Zero values]:`, // Definition removed since it was used }, }, { name: "identifier without package qualifier unchanged", input: `This function uses [T] as a type parameter. It also references [TestingT] interface. # Usage assertions.Something[T TestingT](t)`, contains: []string{ `[T](https://pkg.go.dev/github.com/go-openapi`, // No package qualifier, link to the go doc for our lib `[TestingT](https://pkg.go.dev/github.com/go-openapi`, // No package qualifier, link to the go doc for our lib }, notContains: []string{ `[T](https://pkg.go.dev#T`, // Should not be appended directly `[TestingT](https://pkg.go.dev#T`, }, }, }) } go-openapi-testify-c10ca71/codegen/internal/generator/generator.go000066400000000000000000000260521520301377500253710ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import ( "embed" "errors" "fmt" "os" "path" "path/filepath" "text/template" "github.com/go-openapi/testify/codegen/v2/internal/model" ) const ( pkgRequire = "require" pkgAssert = "assert" assertions = "assertions" mockWithFailNow = "mockFailNowT" mock = "mockT" ) const ( dirPermissions = 0o750 filePermissions = 0o600 ) //go:embed templates/*.gotmpl var templatesFS embed.FS // Generator generates testify packages (assert, require) from the internal assertions package. type Generator struct { options source *model.AssertionPackage ctx *genCtx docs model.Documentation } type genCtx struct { generateOptions index map[string]string templates map[string]*template.Template target *model.AssertionPackage docs *model.Documentation targetBase string } func New(source *model.AssertionPackage, opts ...Option) *Generator { return &Generator{ options: optionsWithDefaults(opts), source: source, } } func (g *Generator) Generate(opts ...GenerateOption) error { if err := g.initContext(opts); err != nil { return err } defer func() { if g.ctx.docs != nil { g.docs = *g.ctx.docs // before we leave, stash the transformed documentation for the current package } g.ctx = nil }() { // prepare steps if err := g.loadTemplates(); err != nil { return err } if err := g.transformModel(); err != nil { return err } g.buildDocs() } { // auto-generated assertions if err := g.generateTypes(); err != nil { // assertion_types.gotmpl return err } if err := g.generateAssertions(); err != nil { return err } if err := g.generateFormatFuncs(); err != nil { // assertion_format.gotmpl // requirement_format.gotmpl return err } if err := g.generateForwardFuncs(); err != nil { // assertion_forward.gotmpl // requirement_forward.gotmpl return err } if err := g.generateHelpers(); err != nil { return err } } { // auto-generated tests if err := g.generateAssertionsTests(); err != nil { return err } if err := g.generateFormatTests(); err != nil { return err } if err := g.generateForwardTests(); err != nil { return err } if err := g.generateExampleTests(); err != nil { return err } if err := g.generateHelpersTests(); err != nil { return err } } return nil } // Documentation yields the transformed package model as a [model.Documentation] // usable by a generation step by the [DocGenerator]. func (g *Generator) Documentation() model.Documentation { return g.docs } func (g *Generator) initContext(opts []GenerateOption) error { // prepare options g.ctx = &genCtx{ generateOptions: generateOptionsWithDefaults(opts), } if g.ctx.targetPkg == "" { return errors.New("a target package is required") } g.ctx.targetBase = path.Base(g.ctx.targetPkg) // perhaps find a better name if g.ctx.targetBase != pkgAssert && g.ctx.targetBase != pkgRequire { return fmt.Errorf(`unsupported target package. Expect pkgAssert or pkgRequire but got: %q`, g.ctx.targetBase) } if err := os.MkdirAll(filepath.Join(g.ctx.targetRoot, g.ctx.targetBase), dirPermissions); err != nil { return fmt.Errorf("can't make target folder: %w", err) } return nil } func (g *Generator) loadTemplates() error { const ( tplExt = ".gotmpl" expectedTemplates = 10 ) index := make(map[string]string, expectedTemplates) switch g.ctx.targetBase { case pkgAssert: g.loadAssertTemplates(index) case pkgRequire: g.loadRequireTemplates(index) default: panic(fmt.Errorf("internal error: invalid targetBase: %q", g.ctx.targetBase)) } templates, err := loadTemplatesFromIndex(index, tplExt, templatesFS) if err != nil { return err } g.ctx.index = index g.ctx.templates = templates return nil } func (g *Generator) loadAssertTemplates(index map[string]string) { index["types"] = "assertion_types" index["assertions"] = "assertion_assertions" if g.ctx.generateTests { index["assertions_test"] = "assertion_assertions_test" } if g.ctx.generateHelpers { index["helpers"] = "assertion_helpers" if g.ctx.generateTests { index["helpers_test"] = "assertion_helpers_test" } } if g.ctx.generateExamples { index["examples_test"] = "assertion_examples_test" } if g.ctx.enableForward { index["forward"] = "assertion_forward" if g.ctx.generateTests { index["forward_test"] = "assertion_forward_test" } } if g.ctx.enableFormat { index["format"] = "assertion_format" if g.ctx.generateTests { index["format_test"] = "assertion_format_test" } } } func (g *Generator) loadRequireTemplates(index map[string]string) { index["types"] = "assertion_types" index["assertions"] = "requirement_assertions" if g.ctx.generateTests { index["assertions_test"] = "assertion_assertions_test" } if g.ctx.generateHelpers { index["helpers"] = "assertion_helpers" if g.ctx.generateTests { index["helpers_test"] = "assertion_helpers_test" } } if g.ctx.generateExamples { index["examples_test"] = "assertion_examples_test" } if g.ctx.enableForward { index["forward"] = "requirement_forward" if g.ctx.generateTests { index["forward_test"] = "assertion_forward_test" } } if g.ctx.enableFormat { index["format"] = "requirement_format" if g.ctx.generateTests { index["format_test"] = "assertion_format_test" } } } func (g *Generator) transformModel() error { tgt := g.source.Clone() tgt.Package = g.ctx.targetBase tgt.Receiver = "Assertions" tgt.EnableFormat = g.ctx.enableFormat tgt.EnableForward = g.ctx.enableForward tgt.EnableGenerics = g.ctx.enableGenerics tgt.EnableExamples = g.ctx.generateExamples tgt.RunnableExamples = g.ctx.runnableExamples /// instructs the doc generator to scan the generated packages to collect runnable examples if tgt.Imports == nil { tgt.Imports = make(model.ImportMap, 1) } tgt.Imports[assertions] = g.source.Package // add the import of our internal assertions package absRoot, err := filepath.Abs(g.ctx.targetRoot) if err != nil { return err } // NOTE: the use of [filepath.Rel] here imposes a constraint that the target resides on the same // drive as the source. This may cause issues, e.g. on windows (currently, our CI ensures any temp file // resides on the same drive as the source). testdata, err := filepath.Rel(filepath.Join(absRoot, g.ctx.targetBase), g.source.TestDataPath) if err != nil { return err } tgt.TestDataPath = testdata for i, fn := range tgt.Functions { tgt.Functions[i] = g.transformFunc(fn) } for i, vr := range tgt.Vars { if vr.Function == nil { continue } fn := g.transformFunc(*vr.Function) vr.Function = &fn tgt.Vars[i] = vr } for i, typ := range tgt.Types { if typ.Function == nil { continue } fn := g.transformFunc(*typ.Function) typ.Function = &fn tgt.Types[i] = typ } g.ctx.target = tgt return nil } func (g *Generator) transformFunc(fn model.Function) model.Function { fn.Params = g.transformArgs(fn.Params) fn.AllParams = g.transformArgs(fn.AllParams) fn.Returns = g.transformArgs(fn.Returns) if fn.Name == "FailNow" || g.ctx.targetBase == pkgRequire { fn.UseMock = mockWithFailNow } else { fn.UseMock = mock } for i, test := range fn.Tests { test.Pkg = g.ctx.targetBase // override the target package for this test fn.Tests[i] = test } return fn } func (g *Generator) generateTypes() error { file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_types.go") return g.render("types", file, g.ctx.target) } func (g *Generator) generateAssertions() error { file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_assertions.go") return g.render("assertions", file, g.ctx.target) } func (g *Generator) generateFormatFuncs() error { if !g.ctx.enableFormat { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_format.go") return g.render("format", file, g.ctx.target) } func (g *Generator) generateForwardFuncs() error { if !g.ctx.enableForward { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_forward.go") return g.render("forward", file, g.ctx.target) } func (g *Generator) generateHelpers() error { if !g.ctx.generateHelpers { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_helpers.go") return g.render("helpers", file, g.ctx.target) } func (g *Generator) generateAssertionsTests() error { if !g.ctx.generateTests { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_assertions_test.go") return g.render("assertions_test", file, g.ctx.target) } func (g *Generator) generateFormatTests() error { if !g.ctx.enableFormat || !g.ctx.generateTests { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_format_test.go") return g.render("format_test", file, g.ctx.target) } func (g *Generator) generateForwardTests() error { if !g.ctx.enableForward || !g.ctx.generateTests { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_forward_test.go") return g.render("forward_test", file, g.ctx.target) } func (g *Generator) generateExampleTests() error { if !g.ctx.generateExamples { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_examples_test.go") return g.render("examples_test", file, g.ctx.target) } func (g *Generator) generateHelpersTests() error { if !g.ctx.generateHelpers || !g.ctx.generateTests { return nil } file := filepath.Join(g.ctx.targetRoot, g.ctx.targetBase, g.ctx.targetBase+"_helpers_test.go") return g.render("helpers_test", file, g.ctx.target) } func (g *Generator) render(name string, target string, data any) error { return renderTemplate( g.ctx.index, g.ctx.templates, name, target, data, func(tpl *template.Template, target string, data any) error { return render(tpl, target, data, g.ctx.formatOptions) }, ) } func (g *Generator) transformArgs(in model.Parameters) model.Parameters { for j, arg := range in { if arg.Selector == "" { arg.Selector = assertions } in[j] = arg } return in } // buildDocs builds a hierarchy of documents for the current package. // // Documents are NOT rendered by the [Generator]. func (g *Generator) buildDocs() { if !g.ctx.generateDoc { return } docs := model.NewDocumentation() docs.Package = g.ctx.target // create a single document representing this package's contribution. // // This document will be reorganized by domain by the [DocGenerator] later on. // The [DocGenerator] carries out the heavy lifting when building a layout-ready Document. doc := model.Document{ Title: g.ctx.target.Package, Path: g.ctx.targetBase, // "assert" or "require" Kind: model.KindFolder, Package: g.ctx.target, } source := model.Document{ Title: g.source.Package, Path: assertions, Kind: model.KindFolder, Package: g.source, } docs.Documents = []model.Document{ doc, source, } g.ctx.docs = docs } go-openapi-testify-c10ca71/codegen/internal/generator/generator_smoke_test.go000066400000000000000000000355471520301377500276370ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import ( "os" "path/filepath" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // minimalSource builds a minimal but realistic AssertionPackage fixture // that satisfies all template requirements for code generation. // // testDataPath must be an absolute path so that filepath.Rel can compute // a relative path from the target directory. Use t.TempDir() or os.TempDir(). func minimalSource(testDataPath string) *model.AssertionPackage { pkg := model.New() pkg.Package = pkgSource pkg.Copyright = "Copyright 2025" pkg.Tool = "codegen-test" pkg.Header = "Test header" pkg.Receiver = "Assertions" pkg.TestDataPath = testDataPath pkg.Imports = model.ImportMap{} // A minimal assertion function. // Note: Params excludes 't' AND the variadic 'msgAndArgs' (used by format template). // AllParams includes everything for the main assertion template. pkg.Functions = model.Functions{ { Name: "Equal", SourcePackage: assertions, TargetPackage: assertions, DocString: "Equal asserts that two objects are equal.", Domain: "equality", AllParams: model.Parameters{ {Name: "t", GoType: "T"}, {Name: "expected", GoType: "any"}, {Name: "actual", GoType: "any"}, {Name: "msgAndArgs", GoType: "any", IsVariadic: true}, }, Params: model.Parameters{ {Name: "expected", GoType: "any"}, {Name: "actual", GoType: "any"}, }, Returns: model.Parameters{ {GoType: "bool"}, }, Tests: []model.Test{ { TestedValue: "123, 123", ExpectedOutcome: model.TestSuccess, IsFirst: true, }, { TestedValue: "123, 456", ExpectedOutcome: model.TestFailure, }, }, }, { Name: "CallerInfo", SourcePackage: assertions, TargetPackage: assertions, DocString: "CallerInfo returns caller information.", IsHelper: true, AllParams: model.Parameters{}, Params: model.Parameters{}, Returns: model.Parameters{ {GoType: "[]string"}, }, }, } pkg.Types = []model.Ident{ { Name: "T", SourcePackage: assertions, TargetPackage: assertions, DocString: "T is the testing interface.", }, { Name: "Assertions", SourcePackage: assertions, TargetPackage: assertions, DocString: "Assertions provides methods for asserting.", }, { Name: "ComparisonFunc", SourcePackage: assertions, TargetPackage: assertions, DocString: "ComparisonFunc is a comparison function.", Function: &model.Function{ AllParams: model.Parameters{ {Name: "a", GoType: "any"}, {Name: "b", GoType: "any"}, }, Returns: model.Parameters{ {GoType: "bool"}, }, }, }, } pkg.Consts = []model.Ident{ { Name: "TestConst", SourcePackage: assertions, TargetPackage: assertions, DocString: "TestConst is a test constant.", }, } pkg.Vars = []model.Ident{ { Name: "TestVar", SourcePackage: assertions, TargetPackage: assertions, DocString: "TestVar is a test variable.", }, } return pkg } // TestGenerateAssertSmokeTest exercises the full Generate pipeline for the assert package. func TestGenerateAssertSmokeTest(t *testing.T) { t.Parallel() tmpDir := t.TempDir() source := minimalSource(tmpDir) gen := New(source) err := gen.Generate( WithTargetPackage(pkgAssert), WithTargetRoot(tmpDir), WithIncludeFormatFuncs(true), WithIncludeForwardFuncs(true), WithIncludeTests(true), WithIncludeHelpers(true), WithIncludeDoc(true), ) if err != nil { t.Fatalf("Generate(assert) failed: %v", err) } // Verify expected files exist and are non-empty expectedFiles := []string{ "assert_types.go", "assert_assertions.go", "assert_format.go", "assert_forward.go", "assert_helpers.go", "assert_assertions_test.go", "assert_format_test.go", "assert_forward_test.go", "assert_helpers_test.go", } assertDir := filepath.Join(tmpDir, "assert") for _, file := range expectedFiles { path := filepath.Join(assertDir, file) info, err := os.Stat(path) if err != nil { t.Errorf("Expected file %s not found: %v", file, err) continue } if info.Size() == 0 { t.Errorf("File %s is empty", file) } } // Verify documentation was built docs := gen.Documentation() if len(docs.Documents) == 0 { t.Error("Documentation should have documents") } } // TestGenerateRequireSmokeTest exercises the full Generate pipeline for the require package. func TestGenerateRequireSmokeTest(t *testing.T) { t.Parallel() tmpDir := t.TempDir() source := minimalSource(tmpDir) gen := New(source) err := gen.Generate( WithTargetPackage(pkgRequire), WithTargetRoot(tmpDir), WithIncludeFormatFuncs(true), WithIncludeForwardFuncs(true), WithIncludeTests(true), WithIncludeHelpers(true), ) if err != nil { t.Fatalf("Generate(require) failed: %v", err) } expectedFiles := []string{ "require_types.go", "require_assertions.go", "require_format.go", "require_forward.go", "require_helpers.go", "require_assertions_test.go", "require_format_test.go", "require_forward_test.go", "require_helpers_test.go", } requireDir := filepath.Join(tmpDir, "require") for _, file := range expectedFiles { path := filepath.Join(requireDir, file) info, err := os.Stat(path) if err != nil { t.Errorf("Expected file %s not found: %v", file, err) continue } if info.Size() == 0 { t.Errorf("File %s is empty", file) } } } // TestGenerateMinimalNoOptionals exercises Generate with all optional generation disabled. func TestGenerateMinimalNoOptionals(t *testing.T) { t.Parallel() tmpDir := t.TempDir() source := minimalSource(tmpDir) gen := New(source) err := gen.Generate( WithTargetPackage(pkgAssert), WithTargetRoot(tmpDir), WithIncludeFormatFuncs(false), WithIncludeForwardFuncs(false), WithIncludeTests(false), WithIncludeHelpers(false), WithIncludeExamples(false), WithIncludeDoc(false), ) if err != nil { t.Fatalf("Generate(minimal) failed: %v", err) } assertDir := filepath.Join(tmpDir, "assert") // Types and assertions should exist for _, file := range []string{"assert_types.go", "assert_assertions.go"} { info, err := os.Stat(filepath.Join(assertDir, file)) if err != nil { t.Errorf("Expected file %s not found: %v", file, err) continue } if info.Size() == 0 { t.Errorf("File %s is empty", file) } } // Optional files should NOT exist for _, file := range []string{"assert_format.go", "assert_forward.go", "assert_helpers.go"} { if _, err := os.Stat(filepath.Join(assertDir, file)); err == nil { t.Errorf("File %s should not exist when disabled", file) } } } // TestGenerateDocSmokeTest exercises the DocGenerator pipeline with merged documentation. func TestGenerateDocSmokeTest(t *testing.T) { t.Parallel() tmpDir := t.TempDir() source := minimalSource(tmpDir) // Generate assert package with doc enabled assertGen := New(source) err := assertGen.Generate( WithTargetPackage(pkgAssert), WithTargetRoot(tmpDir), WithIncludeDoc(true), ) if err != nil { t.Fatalf("Generate(assert) failed: %v", err) } assertDocs := assertGen.Documentation() // Generate require package with doc enabled requireGen := New(source) err = requireGen.Generate( WithTargetPackage(pkgRequire), WithTargetRoot(tmpDir), WithIncludeDoc(true), ) if err != nil { t.Fatalf("Generate(require) failed: %v", err) } requireDocs := requireGen.Documentation() // Merge documentation assertDocs.Merge(requireDocs) // Run doc generator docDir := "docs" // relative to targetDir docGen := NewDocGenerator(assertDocs) err = docGen.Generate( WithTargetDoc(docDir), WithTargetRoot(tmpDir), WithIncludeFormatFuncs(true), WithIncludeForwardFuncs(true), ) if err != nil { t.Fatalf("DocGenerator.Generate() failed: %v", err) } // Verify index and at least one domain page were created indexPath := filepath.Join(tmpDir, docDir, "_index.md") info, err := os.Stat(indexPath) if err != nil { t.Fatalf("Index file not found: %v", err) } if info.Size() == 0 { t.Error("Index file is empty") } // Verify the domain page for "equality" exists equalityPath := filepath.Join(tmpDir, docDir, "equality.md") info, err = os.Stat(equalityPath) if err != nil { t.Fatalf("equality.md not found: %v", err) } if info.Size() == 0 { t.Error("equality.md is empty") } } // TestDocGeneratorErrors verifies error paths. func TestDocGeneratorErrors(t *testing.T) { t.Parallel() t.Run("missing target doc", func(t *testing.T) { t.Parallel() doc := model.Documentation{} gen := NewDocGenerator(doc) err := gen.Generate(WithTargetDoc("")) if err == nil { t.Error("Expected error for empty target doc") } }) } // TestGenerateErrors verifies error paths. func TestGenerateErrors(t *testing.T) { t.Parallel() t.Run("missing target package", func(t *testing.T) { t.Parallel() source := model.New() source.Package = pkgSource gen := New(source) err := gen.Generate() if err == nil { t.Error("Expected error for missing target package") } }) t.Run("invalid target package", func(t *testing.T) { t.Parallel() source := model.New() source.Package = pkgSource gen := New(source) err := gen.Generate( WithTargetPackage("invalid"), WithTargetRoot(t.TempDir()), ) if err == nil { t.Error("Expected error for invalid target package") } }) } // TestBuildDocs verifies doc building is skipped when disabled. func TestBuildDocsDisabled(t *testing.T) { t.Parallel() tmpDir := t.TempDir() source := minimalSource(tmpDir) gen := New(source) err := gen.initContext([]GenerateOption{ WithTargetPackage(pkgAssert), WithTargetRoot(tmpDir), WithIncludeDoc(false), }) if err != nil { t.Fatal(err) } _ = gen.loadTemplates() _ = gen.transformModel() gen.buildDocs() if gen.ctx.docs != nil { t.Error("buildDocs should not set docs when disabled") } } // TestBuildDocsEnabled verifies doc building when enabled. func TestBuildDocsEnabled(t *testing.T) { t.Parallel() tmpDir := t.TempDir() source := minimalSource(tmpDir) gen := New(source) err := gen.initContext([]GenerateOption{ WithTargetPackage(pkgAssert), WithTargetRoot(tmpDir), WithIncludeDoc(true), }) if err != nil { t.Fatal(err) } _ = gen.loadTemplates() _ = gen.transformModel() gen.buildDocs() if gen.ctx.docs == nil { t.Fatal("buildDocs should set docs when enabled") } if len(gen.ctx.docs.Documents) != 2 { t.Errorf("Expected 2 documents (target + source), got %d", len(gen.ctx.docs.Documents)) } } // TestRenderTemplateErrors verifies renderTemplate error handling. func TestRenderTemplateErrors(t *testing.T) { t.Parallel() t.Run("missing index entry", func(t *testing.T) { t.Parallel() err := renderTemplate( map[string]string{}, nil, "nonexistent", "target.go", nil, nil, ) if err == nil { t.Error("Expected error for missing index entry") } }) t.Run("missing template", func(t *testing.T) { t.Parallel() err := renderTemplate( map[string]string{"test": "test_template"}, nil, "test", "target.go", nil, nil, ) if err == nil { t.Error("Expected error for missing template") } }) } // TestPopulateFunctionExamples verifies example population logic. func TestPopulateFunctionExamples(t *testing.T) { t.Parallel() pkg := &model.AssertionPackage{ Functions: model.Functions{ {Name: "Equal"}, {Name: "True"}, }, } // No examples at all populateFunctionExamples(pkg, nil) if len(pkg.Functions[0].Examples) != 0 { t.Error("No examples should be set") } } // TestPopulateIdentExamples verifies ident example population. func TestPopulateIdentExamples(t *testing.T) { t.Parallel() idents := []model.Ident{ {Name: "T"}, {Name: "Assertions"}, } // No examples at all populateIdentExamples(idents, nil) if len(idents[0].Examples) != 0 { t.Error("No examples should be set") } } // TestTransformFunc verifies function transformation for assert vs require. func TestTransformFunc(t *testing.T) { t.Parallel() source := minimalSource(t.TempDir()) t.Run("assert package uses mockT", func(t *testing.T) { t.Parallel() gen := New(source) err := gen.initContext([]GenerateOption{ WithTargetPackage(pkgAssert), WithTargetRoot(t.TempDir()), }) if err != nil { t.Fatal(err) } fn := model.Function{ Name: "Equal", Params: model.Parameters{ {Name: "expected", GoType: "any"}, }, } result := gen.transformFunc(fn) if result.UseMock != mock { t.Errorf("Assert Equal should use mockT, got %q", result.UseMock) } }) t.Run("require package uses mockFailNowT", func(t *testing.T) { t.Parallel() gen := New(source) err := gen.initContext([]GenerateOption{ WithTargetPackage(pkgRequire), WithTargetRoot(t.TempDir()), }) if err != nil { t.Fatal(err) } fn := model.Function{ Name: "Equal", Params: model.Parameters{ {Name: "expected", GoType: "any"}, }, } result := gen.transformFunc(fn) if result.UseMock != mockWithFailNow { t.Errorf("Require Equal should use mockFailNowT, got %q", result.UseMock) } }) t.Run("FailNow always uses mockFailNowT", func(t *testing.T) { t.Parallel() gen := New(source) err := gen.initContext([]GenerateOption{ WithTargetPackage(pkgAssert), WithTargetRoot(t.TempDir()), }) if err != nil { t.Fatal(err) } fn := model.Function{Name: "FailNow"} result := gen.transformFunc(fn) if result.UseMock != mockWithFailNow { t.Errorf("FailNow in assert should use mockFailNowT, got %q", result.UseMock) } }) } // TestAllOptions exercises all option functions. func TestAllOptions(t *testing.T) { t.Parallel() tmpDir := t.TempDir() opts := generateOptionsWithDefaults([]GenerateOption{ WithTargetRoot(tmpDir), WithTargetPackage("assert"), WithTargetDoc("docs"), WithIncludeFormatFuncs(true), WithIncludeForwardFuncs(true), WithIncludeTests(true), WithIncludeGenerics(true), WithIncludeHelpers(true), WithIncludeExamples(true), WithRunnableExamples(true), WithIncludeDoc(true), }) if opts.targetRoot != tmpDir { t.Errorf("targetRoot: want %q, got %q", tmpDir, opts.targetRoot) } if opts.targetPkg != "assert" { t.Errorf("targetPkg: want assert, got %q", opts.targetPkg) } if opts.targetDoc != "docs" { t.Errorf("targetDoc: want docs, got %q", opts.targetDoc) } if !opts.enableFormat { t.Error("enableFormat should be true") } if !opts.enableForward { t.Error("enableForward should be true") } if !opts.generateTests { t.Error("generateTests should be true") } if !opts.enableGenerics { t.Error("enableGenerics should be true") } if !opts.generateHelpers { t.Error("generateHelpers should be true") } if !opts.generateExamples { t.Error("generateExamples should be true") } if !opts.runnableExamples { t.Error("runnableExamples should be true") } if !opts.generateDoc { t.Error("generateDoc should be true") } } go-openapi-testify-c10ca71/codegen/internal/generator/generator_test.go000066400000000000000000000124751520301377500264340ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import ( "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/generator/funcmaps" "github.com/go-openapi/testify/codegen/v2/internal/model" ) const ( pkgSource = "github.com/go-openapi/testify/v2/internal/assertions" ) // TestNew verifies that New creates a valid Generator instance. func TestNew(t *testing.T) { t.Parallel() source := model.New() source.Package = pkgSource source.DocString = "Package assertions provides test assertions" source.Copyright = "Copyright 2025" gen := New(source) if gen == nil { t.Fatal("New() returned nil") } if gen.source != source { t.Error("Generator source not set correctly") } } // TestGeneratorInitContext verifies context initialization. func TestGeneratorInitContext(t *testing.T) { t.Parallel() source := model.New() source.Package = pkgSource for tt := range generatorCases(t) { t.Run(tt.name, func(t *testing.T) { t.Parallel() gen := New(source) err := gen.initContext(tt.opts) if tt.expectError { if err == nil { t.Error("Expected error but got nil") } } else { if err != nil { t.Errorf("Unexpected error: %v", err) } if tt.checkFunc != nil { tt.checkFunc(t, gen) } } }) } } // TestTransformArgs verifies argument transformation for generation. func TestTransformArgs(t *testing.T) { t.Parallel() source := model.New() gen := New(source) for tt := range transformCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() params := gen.transformArgs(tt.input) result := funcmaps.PrintReturns(params) if result != tt.expected { t.Errorf("Expected %q, got %q", tt.expected, result) } }) } } // TestLoadTemplates verifies template loading for assert and require packages. func TestLoadTemplates(t *testing.T) { t.Parallel() tests := []struct { name string targetPkg string expectError bool checkFunc func(*testing.T, *Generator) }{ { name: "load assert templates", targetPkg: pkgAssert, expectError: false, checkFunc: func(t *testing.T, g *Generator) { t.Helper() if len(g.ctx.templates) == 0 { t.Error("No templates loaded") } if _, ok := g.ctx.templates["assertion_assertions"]; !ok { t.Error("Missing assertion_assertions template") } }, }, { name: "load require templates", targetPkg: pkgRequire, expectError: false, checkFunc: func(t *testing.T, g *Generator) { t.Helper() if len(g.ctx.templates) == 0 { t.Error("No templates loaded") } if _, ok := g.ctx.templates["requirement_assertions"]; !ok { t.Error("Missing requirement_assertions template") } }, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() source := model.New() source.Package = pkgSource gen := New(source) err := gen.initContext([]GenerateOption{ WithTargetPackage(tt.targetPkg), WithTargetRoot(t.TempDir()), }) if err != nil { t.Fatalf("Failed to init context: %v", err) } err = gen.loadTemplates() if tt.expectError { if err == nil { t.Error("Expected error but got nil") } } else { if err != nil { t.Errorf("Unexpected error: %v", err) } if tt.checkFunc != nil { tt.checkFunc(t, gen) } } }) } } type generatorCase struct { name string opts []GenerateOption expectError bool checkFunc func(*testing.T, *Generator) } func generatorCases(t *testing.T) iter.Seq[generatorCase] { t.Helper() return slices.Values([]generatorCase{ { name: "missing target package", opts: []GenerateOption{}, expectError: true, }, { name: "invalid target package", opts: []GenerateOption{ WithTargetPackage("invalid"), }, expectError: true, }, { name: "valid assert package", opts: []GenerateOption{ WithTargetPackage(pkgAssert), WithTargetRoot(t.TempDir()), }, expectError: false, checkFunc: func(t *testing.T, g *Generator) { t.Helper() if g.ctx.targetBase != pkgAssert { t.Errorf("Expected targetBase '%s', got %q", pkgAssert, g.ctx.targetBase) } }, }, { name: "valid require package", opts: []GenerateOption{ WithTargetPackage(pkgRequire), WithTargetRoot(t.TempDir()), }, expectError: false, checkFunc: func(t *testing.T, g *Generator) { t.Helper() if g.ctx.targetBase != pkgRequire { t.Errorf("Expected targetBase '%s', got %q", pkgRequire, g.ctx.targetBase) } }, }, }) } type transformCase struct { name string input model.Parameters expected string } func transformCases() iter.Seq[transformCase] { return slices.Values([]transformCase{ { name: "empty parameters", input: model.Parameters{}, expected: "", }, { name: "single parameter without name", input: model.Parameters{ {GoType: "bool"}, }, expected: "bool", }, { name: "single parameter with name", input: model.Parameters{ {Name: "value", GoType: "bool"}, }, expected: "(value bool)", }, { name: "multiple parameters", input: model.Parameters{ {Name: "expected", GoType: "any"}, {Name: "actual", GoType: "any"}, }, expected: "(expected any, actual any)", }, }) } go-openapi-testify-c10ca71/codegen/internal/generator/options.go000066400000000000000000000050021520301377500250660ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import "golang.org/x/tools/imports" // GenerateOption is an option for code generation. type GenerateOption func(*generateOptions) // Option is a general option for the generator. // // Currently unused (reserved for future use). type Option func(*options) func WithTargetRoot(dir string) GenerateOption { return func(o *generateOptions) { o.targetRoot = dir } } func WithTargetPackage(pkg string) GenerateOption { return func(o *generateOptions) { o.targetPkg = pkg } } func WithTargetDoc(dir string) GenerateOption { return func(o *generateOptions) { o.targetDoc = dir } } func WithIncludeFormatFuncs(enabled bool) GenerateOption { return func(o *generateOptions) { o.enableFormat = enabled } } func WithIncludeForwardFuncs(enabled bool) GenerateOption { return func(o *generateOptions) { o.enableForward = enabled } } func WithIncludeTests(enabled bool) GenerateOption { return func(o *generateOptions) { o.generateTests = enabled } } func WithIncludeGenerics(enabled bool) GenerateOption { return func(o *generateOptions) { o.enableGenerics = enabled } } func WithIncludeHelpers(enabled bool) GenerateOption { return func(o *generateOptions) { o.generateHelpers = enabled } } func WithIncludeExamples(enabled bool) GenerateOption { return func(o *generateOptions) { o.generateExamples = enabled } } func WithRunnableExamples(enabled bool) GenerateOption { return func(o *generateOptions) { o.runnableExamples = enabled } } func WithIncludeDoc(enabled bool) GenerateOption { return func(o *generateOptions) { o.generateDoc = enabled } } type generateOptions struct { targetPkg string targetRoot string targetDoc string enableForward bool enableFormat bool enableGenerics bool generateHelpers bool generateTests bool generateExamples bool runnableExamples bool generateDoc bool formatOptions *imports.Options } type options struct{} func generateOptionsWithDefaults(opts []GenerateOption) generateOptions { o := generateOptions{ targetRoot: ".", targetDoc: ".", enableForward: true, enableFormat: true, enableGenerics: false, generateHelpers: true, generateTests: false, generateExamples: false, generateDoc: false, runnableExamples: false, } for _, apply := range opts { apply(&o) } return o } func optionsWithDefaults(_ []Option) options { return options{} } go-openapi-testify-c10ca71/codegen/internal/generator/render.go000066400000000000000000000020311520301377500246510ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import ( "bytes" "fmt" "os" "text/template" "golang.org/x/tools/imports" ) func render(tpl *template.Template, target string, data any, o *imports.Options) error { var buffer bytes.Buffer if err := tpl.Execute(&buffer, data); err != nil { return fmt.Errorf("error executing template %q: %w", tpl.Name(), err) } formatted, err := imports.Process(target, buffer.Bytes(), o) if err != nil { _ = os.WriteFile(target, buffer.Bytes(), filePermissions) return fmt.Errorf("error formatting go code: %w:%w", err, fmt.Errorf("details available at: %v", target)) } return os.WriteFile(target, formatted, filePermissions) } func renderMD(tpl *template.Template, target string, data any) error { var buffer bytes.Buffer if err := tpl.Execute(&buffer, data); err != nil { return fmt.Errorf("error executing template %q: %w", tpl.Name(), err) } return os.WriteFile(target, buffer.Bytes(), filePermissions) } go-openapi-testify-c10ca71/codegen/internal/generator/templates.go000066400000000000000000000066041520301377500254020ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package generator import ( "embed" "fmt" "path" "sort" "strings" "text/template" "github.com/go-openapi/testify/codegen/v2/internal/generator/funcmaps" ) // buildTemplateIndex extracts template names from the index and returns them sorted. // // This helper reduces duplication between Generator and DocGenerator template loading. func buildTemplateIndex(index map[string]string) []string { needed := make([]string, 0, len(index)) for _, v := range index { needed = append(needed, v) } sort.Strings(needed) return needed } // loadTemplatesFromIndex loads templates from an embedded filesystem using a name-to-file index. // // Parameters: // - index: map from logical template name to template file name (without extension) // - tplExt: template file extension (e.g., ".gotmpl", ".md.gotmpl") // - fs: embedded filesystem containing template files in "templates/" directory // // Returns a map from template name to parsed template, or an error if any template fails to load. // // This function consolidates the common template loading logic shared between Generator // and DocGenerator, eliminating ~25 lines of duplicate code. func loadTemplatesFromIndex( index map[string]string, tplExt string, fs embed.FS, ) (map[string]*template.Template, error) { needed := buildTemplateIndex(index) templates := make(map[string]*template.Template, len(needed)) for _, name := range needed { file := name + tplExt files := []string{path.Join("templates", file)} if strings.Contains(name, "_test") { // test templates use a set of shared definitions files = append(files, path.Join("templates", "assertion_test_shared.gotmpl")) } tpl, err := template.New(file).Funcs(funcmaps.FuncMap()).ParseFS(fs, files...) if err != nil { return nil, fmt.Errorf("failed to load template %q from %q: %w", name, file, err) } templates[name] = tpl } return templates, nil } // renderFunc is a function that executes a template and writes the result to a file. type renderFunc func(*template.Template, string, any) error // renderTemplate executes a template by name and writes the result using the provided render function. // // Parameters: // - index: map from logical template name to template file name // - templates: map from template file name to parsed template // - name: logical template name to render // - target: output file path // - data: data to pass to the template // - renderFn: function that executes the template and writes output (e.g., render or renderMD) // // Returns an error if the template name is not found in the index, the template is not loaded, // or the render function fails. // // This function consolidates the common rendering logic shared between Generator and DocGenerator, // eliminating ~15 lines of duplicate code. The only difference between the two generators is // the final render function (render vs renderMD), which is passed as a parameter. func renderTemplate( index map[string]string, templates map[string]*template.Template, name string, target string, data any, renderFn renderFunc, ) error { tplName, ok := index[name] if !ok { return fmt.Errorf("internal error: expect template name %q", name) } tpl, ok := templates[tplName] if !ok { return fmt.Errorf("internal error: expect template %q", name) } return renderFn(tpl, target, data) } go-openapi-testify-c10ca71/codegen/internal/generator/templates/000077500000000000000000000000001520301377500250455ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_assertions.gotmpl000066400000000000000000000007651520301377500325620ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} {{- range .Functions.Scope "include-generics" . }} {{ comment .DocString }} // {{ docStringPackage $.Package }} func {{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }} { if h, ok := t.(H); ok { h.Helper() } return {{ .TargetPackage }}.{{ .GenericCallName }}({{ forward .AllParams }}) } {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_assertions_test.gotmpl000066400000000000000000000014751520301377500336200ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} import ( "net/http" "path/filepath" "testing" {{ imports .Imports }} ) {{- range .Functions.Scope "include-generics" . }} func Test{{ .Name }}(t *testing.T) { {{- template "test-case" (testSetup . "assertions" "") }} } {{- end }} // mockT is a mock testing.T for assertion tests type mockT struct { failed bool } func (m *mockT) Helper() {} func (m *mockT) Errorf(format string, args ...any) { m.failed = true } type mockFailNowT struct { failed bool } // Helper is like [testing.T.Helper] but does nothing. func (mockFailNowT) Helper() {} func (m *mockFailNowT) Errorf(format string, args ...any) { _ = format _ = args } func (m *mockFailNowT) FailNow() { m.failed = true } {{ template "test-helpers" . }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_examples_test.gotmpl000066400000000000000000000042111520301377500332330ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }}_test import ( "fmt" "net/http" "path/filepath" "runtime" "testing" "github.com/go-openapi/testify/v2/{{ .Package }}" {{ imports .Imports }} ) {{- $pkg := .Package }} {{- $runnable := .RunnableExamples }} {{- range .Functions.Scope "include-generics" . }} {{- if not .HasSuccessTest }} // func Example{{ .Name }}() { // no success example available. Please add some examples to produce a testable example. // } {{- else }} func Example{{ .Name }}() { {{- $fn := . }} {{- if eq $fn.Name "NoFileDescriptorLeak" }}{{/* handle special case for platform-specific assertion: example only runs on windows */}} if runtime.GOOS != "linux" { // This example is only runnable on linux. On other platforms, the assertion skips the test. // We force the expected output below, so that tests don't fail on other platforms. {{- if eq $pkg "require" }} fmt.Println("passed") {{- else }} fmt.Println("success: true") {{- end }} return } {{ cr false }} {{- end }} {{- range .Tests }} {{- if .IsSuccess }} {{- cr .IsFirst }} t := new(testing.T) // should come from testing, e.g. func Test{{ $fn.Name }}(t *testing.T) {{- if .IsKindRequire }} {{ $pkg }}.{{ $fn.Name }}{{ $fn.GenericSuffix }}(t, {{ relocate .TestedValues $pkg }}) fmt.Println("passed") {{- if $runnable }} // Output: passed {{- end }} {{- else }} success := {{ $pkg }}.{{ $fn.Name }}{{ $fn.GenericSuffix }}(t, {{ relocate .TestedValues $pkg }}) fmt.Printf("success: %t\n", success) {{- if $runnable }} // Output: success: true {{- end }} {{- end }} {{- end }} {{- break }}{{/* just produce one single example, in case there are multiple success examples */}} {{- end }} } {{- end }} {{- end }} // Test helpers (also in the tests for package {{ .Package }}. // // This code is duplicated because the current test is run as a separate test package: {{ .Package }}_test. {{ template "test-helpers" .WithTestPackage }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_format.gotmpl000066400000000000000000000013051520301377500316470ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} {{- range .Functions.Scope "include-generics" . }} {{ docStringFor "format" .Name }} // {{ docStringPackage $.Package }} func {{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) {{ returns .Returns }} { if h, ok := t.(H); ok { h.Helper() } return {{ .TargetPackage }}.{{ .GenericCallName }}(t, {{ forward .Params }}, forwardArgs(msg, args)...) } {{- end }} func forwardArgs(msg string, args []any) []any { result := make([]any, len(args)+1) result[0]=msg copy(result[1:], args) return result } go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_format_test.gotmpl000066400000000000000000000005331520301377500327100ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( "testing" {{ imports .Imports }} ) {{- end }} {{- range .Functions.Scope "include-generics" . }} func Test{{ .Name }}f(t *testing.T) { {{- template "test-case" (testSetup . "format" "") }} } {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_forward.gotmpl000066400000000000000000000025271520301377500320320ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} // {{ .Receiver }} exposes all assertion functions as methods. // // NOTE: assertion methods with parameterized types (generics) are not supported as methods. // {{ docStringPackage .Package }} type {{ .Receiver }} struct { T } // New makes a new [{{ .Receiver }}] object for the specified [T] (e.g. [testing.T]). func New(t T) *{{ .Receiver }} { return &{{ .Receiver }}{ T: t, } } {{- range .Functions.Scope "exclude-generics" . }}{{/* generics can't be added to the receiver */}} {{ docStringFor "forward" .Name }} // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}) {{.Name}}({{ params .Params }}, msgAndArgs ...any) {{ returns .Returns }} { if h, ok := a.T.(H); ok { h.Helper() } return {{ .TargetPackage }}.{{.Name}}(a.T, {{ forward .Params }}, msgAndArgs...) } {{- if $.EnableFormat }} {{ docStringFor "format" (concat $.Receiver "." .Name ) }} // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}){{ .Name }}f({{ params .Params }}, msg string, args ...any) {{ returns .Returns }} { if h, ok := a.T.(H); ok { h.Helper() } return {{ .TargetPackage }}.{{ .Name }}(a.T, {{ forward .Params }}, forwardArgs(msg, args)...) } {{- end }} {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_forward_test.gotmpl000066400000000000000000000011271520301377500330640ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( "testing" {{ imports .Imports }} ) {{- end }} {{- range .Functions.Scope "exclude-generics" . }} func Test{{ $.Receiver }}{{ .Name }}(t *testing.T) { {{- template "test-case" (testSetup . "forward" $.Receiver) }} } {{- end }} {{- if $.EnableFormat }} {{- range .Functions.Scope "exclude-generics" . }} func Test{{ $.Receiver }}{{ .Name }}f(t *testing.T) { {{- template "test-case" (testSetup . "forward-format" $.Receiver) }} } {{- end }} {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_helpers.gotmpl000066400000000000000000000006171520301377500320260ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} {{- range .Functions.Scope "only-helpers" . }} {{ comment .DocString }} func {{ .Name }}({{ params .AllParams }}) {{ returns .Returns }} { return {{ .TargetPackage }}.{{ .Name }}({{ forward .AllParams }}) } {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_helpers_test.gotmpl000066400000000000000000000006351520301377500330650ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( "testing" {{ imports .Imports }} ) {{- end }} {{- range .Functions }} {{- if .IsHelper }} func Test{{ .Name }}f(t *testing.T) { {{- if (not .HasTest) }} t.Skip() // this function doesn't have tests yet {{- else }} // TODO {{- end }} } {{- end }} {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_test_shared.gotmpl000066400000000000000000000064041520301377500326710ustar00rootroot00000000000000{{- define "test-helpers" }}{{/* common test helpers that need to be duplicated between the "x" and "x_test" packages */}} func testDataPath() string { return filepath.Join({{ pathparts .TestDataPath }}) } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } func httpError(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) } func httpRedirect(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusTemporaryRedirect) } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar dummyInterface {{ if .TestPackage }}{{ .Package }}.{{ end }}T ) func ptr[T any](value T) *T { p := value return &p } type dummyStruct struct { A string b int } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } var dummyChan = make(chan struct{}) type myType float64 {{- end }} {{- define "test-case" }}{{/* data: Function */}} {{- $mock := .TestMock }} {{- $prefix := .TestErrorPrefix }} {{- $panic := .TestPanicWrapper }} {{- $mockFailure := .TestMockFailure }} {{- $msg := .TestMsg }} t.Parallel() {{ if (not .HasTest) }}{{/* no testcases captured from the Example section of the original function */}} t.Skip() // this function doesn't have tests yet: feed the original function with examples to test. {{- else }} {{- $fn := . }} {{- range .Tests }} {{ $call := $fn.TestCall }} {{- if $msg }} {{- $call = ( printf "%s%v,%q)" $call .TestedValue $msg ) }} {{- else }} {{- $call = ( printf "%s%v)" $call .TestedValue ) }} {{- end }} {{- if .IsSuccess }} {{- cr .IsFirst }} t.Run("success", func(t *testing.T) { t.Parallel() {{ $mock }} {{- if .IsKindRequire }} {{ $call }} // require functions don't return a value {{- else }} result := {{ $call }} if !result { t.Error("{{ $prefix }} should return true on success") } {{- end }} }) {{- else if .IsFailure }} {{- cr .IsFirst }} t.Run("failure", func(t *testing.T) { t.Parallel() {{ $mock }} {{- if .IsKindRequire }} {{ $call }} // require functions don't return a value {{- else }} result := {{ $call }} if result { t.Error("{{ $prefix }} should return false on failure") } {{- end }} if !mock.failed { t.Error("{{ $mockFailure }}") } }) {{- else if .IsPanic }} {{- cr .IsFirst }} t.Run("panic", func(t *testing.T) { t.Parallel() {{ $mock }} {{- if .IsKindRequire }} {{ $panic }}func() { {{ $call }} }, "{{ .AssertionMessage }}") {{- else }} result := {{ $panic }}func() { {{ $call }} }, "{{ .AssertionMessage }}") if !result { t.Error("{{ $prefix }} should return true on panic") } {{- end }} if mock.failed { t.Error("{{ $prefix }} should panic as expected") } }) {{- end }} {{- end }} {{- end }} {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/assertion_types.gotmpl000066400000000000000000000033731520301377500315320ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} {{- with .Consts }} const ( {{- range $i,$v := . }} {{- cr (eq $i 0) }} {{- with $v }} {{ comment .DocString }} {{ .Name }} = {{ .TargetPackage }}.{{ .Name }} {{- end }} {{- end }} ) {{- end }} {{- with .Vars }} var ( {{- range $i,$v := . }} {{- cr (eq $i 0) }} {{- with $v }} {{ comment .DocString }} {{ .Name }} = {{ .TargetPackage }}.{{ .Name }} {{- end }} {{- end }} ) {{- end }} {{- with .Types }} type ( {{- $first := true }} {{- $isRequire := (eq $.Package "require") }} {{- range . }} {{- if not (eq .Name $.Receiver) }}{{/* we declare that type separately */}} {{- cr $first }}{{ $first = false }} {{ comment .DocString }} {{- if and (eq .Name "T") (eq $.Package "require") }}{{/* to use with require, T must support FailNow() */}} {{ .Name }} interface { {{ .TargetPackage }}.{{ .Name }} FailNow() } {{- else if and $isRequire .Function ( hasSuffix .Name "Func" ) (gt (len .Function.Returns) 0) }}{{/* special override with require of function types */}} {{ .Name }} func({{ params .Function.AllParams }}) {{- else }} {{ .Name }} = {{ .TargetPackage }}.{{ .Name }} {{- end }} {{- end }} {{- end }} ) {{- end }} // Type declarations for backward compatibility. type ( // TestingT is like [T] and is declared here to remain compatible with previous versions of this package. // // Most users should not be affected, as the implementation of [T] that is widely used is [testing.T]. // // Deprecated: use [T] as a more concise alternative. TestingT = T ) go-openapi-testify-c10ca71/codegen/internal/generator/templates/doc_index.md.gotmpl000066400000000000000000000024441520301377500306300ustar00rootroot00000000000000--- title: {{ .Title | quote }} description: | {{ titleize .Description }}. Find the assertion function you need for your data. weight: 1 --- **The v2 our tests wanted** The [`testify/v2`][testifyv2] package has a fairly large API surface. This is in part inherited from the original [`testify`][testifyv1] and also largely due to the many available variants of the same assertion function. The following sections organize the assertion API into usage domains, with all documented exported variants documented in a more concise form than the [reference godoc documentation]({{ .PkgGoDevURL }}). [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} ## Domains The `testify` API is organized in {{ .RefCount }} logical domains shown below. Each domain contains assertions regrouped by their use case (e.g. http, json, error). {{ hopen }}< children type="card" description="true" >{{ hclose }} --- {{ range .Index }} - [{{ .Title }}]({{ .Link }}) - {{ titleize .Description }} ({{ .RefCount }}) {{- end }} --- Generated with {{ .Package.Tool }} [godoc-badge]: {{ godocbadge .PkgGoDevURL }} [godoc-url]: {{ .PkgGoDevURL }} [testifyv2]: {{ .GitHubURL }} [testifyv1]: https://github.com/stretchr/testify go-openapi-testify-c10ca71/codegen/internal/generator/templates/doc_metrics.md.gotmpl000066400000000000000000000031621520301377500311650ustar00rootroot00000000000000--- title: {{ .Title | quote }} description: | {{ titleize .Description }}. weight: -1 --- {{- with .Metrics }} ## Domains All assertions are classified into **{{ .Domains }}** domains to help navigate the API, depending on your use case. ## API metrics Counts for core functionality, and generated variants (formatted, forward, forward-formatted). | Kind | Count | Note | | ------------------------- | ----------------- | ---- | | All core functions | {{ .Functions }} | Maintained core | | All core assertions | {{ .Assertions }} | Usage with `*testing.T` | | Generic assertions | {{ .Generics }} | Type-safe assertions ("T" suffix) | | Helpers (not assertions) | {{ .Helpers }} | General-purpose utilities, not assertions | | Others | {{ .Others }} | | | assert/require variants | {{ .PackageVariants }} | Generated variants | | Total assertions variants | {{ .TotalVariants }} | Available assertions API | | Total API surface | {{ .TotalFunctions }} | | {{- end }} ## Quick index Table of core assertions, excluding variants. Each function is side by side with its logical opposite (when available). | Assertion | Opposite | Domain | Kind | | ------------------------ | ----------------- | ------ | ---- | {{- range .QuickIndex }} | [{{ .Name }}]({{ .Domain }}/#{{ .Anchor }}){{ if .IsGeneric }} {{ hopen }}% icon icon="star" color=orange %{{ hclose }}{{ end }} | {{ if .Opposite }}[{{ .Opposite }}]({{ .Domain }}/#{{ .OppositeAnchor }}){{ end }} | {{ .Domain }} | {{ if .IsHelper }}helper{{ end }} | {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/doc_page.md.gotmpl000066400000000000000000000113131520301377500304300ustar00rootroot00000000000000{{- define "TOC" }}{{/* a tree-like TOC to introduce content */}} ```tree {{- with .Package }} {{- range .Functions.Scope "include-generics" . }}{{/* functions in internal/assertions annotated with "domain:" */}} - [{{ .GenericName }}](#{{ slugize .GenericName }}) | {{ if .IsGeneric}}star | orange{{ else }}angles-right{{ end }} {{- end }} {{- end }} ``` {{- end }} {{- define "TAB-INTERNAL" }}{{/* tab internal discloses internal maintainers notes - moved apart as this is heavier tab to construct */}} {{- $godoc := .Context.PkgGoDevURL }} {{- $pkg := .Context.Package.Package }} {{- $github := .Context.GitHubURL }} {{ hopen }}% tab title="internal" style="accent" icon="wrench" %{{ hclose }} | Signature | Usage | |--|--| | [`{{ $pkg }}.{{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }}`]({{ $godoc }}/internal/{{ $pkg }}#{{ .Name }}) | internal implementation | **Source:** [{{ .SourcePackage }}#{{ .Name }}]({{ sourceLink $github .SourceLink }}) {{- range .ExtraComments }} {{- if .IsTagMaintainer }}{{/* maintainers section */}} > **Maintainer Note** > {{ blockquote .Text }} {{- else if .IsTagNote }}{{/* notes section */}} > **Note** > {{ blockquote .Text }} {{- end }} {{- end }} {{ hopen }}% /tab %{{ hclose }} {{- end }} {{- define "TABS-SUBSECTION" }}{{/* render the signatures of all variant in a tab for each package */}} {{- $extraPackages := .Context.ExtraPackages }} {{- $godoc := .Context.PkgGoDevURL }} {{- $funcName := .Name }} {{- with $extraPackages }} {{- range .LookupFunction $funcName }} {{ hopen }}% tab title="{{ .Package }}" style="secondary" %{{ hclose }} | Signature | Usage | |--|--| | [`{{.Package }}.{{ .GenericName }}({{ params .AllParams }}) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Name }}) | package-level function | {{- if .EnableFormat }} | [`{{ .Package }}.{{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Name }}f) | formatted variant | {{- end }} {{- if and .EnableForward (not .IsGeneric) }} | [`{{ .Package}}.(*{{ .Receiver }}).{{ .Name }}({{ params .Params }}) {{ returns .Returns }}`]({{ $godoc }}/{{ .Package }}#{{ .Receiver }}.{{ .Name }}) | method variant | {{- end }} {{- if and .EnableForward .EnableFormat (not .IsGeneric) }} | [`{{ .Package }}.(*{{ .Receiver }}).{{ .Name }}f({{ params .Params }}, msg string, args ..any)`]({{ $godoc }}/{{ .Package }}#{{ .Receiver }}.{{ .Name }}f) | method formatted variant | {{- end }} {{ hopen }}% /tab %{{ hclose }} {{- end }} {{- end }} {{ template "TAB-INTERNAL" . }} {{- end }} {{- define "FUNC-SECTION" }}{{/* data: Function ; complete description of an assertion function */}} ### {{ .GenericName }}{{ if .IsGeneric }} {{ hopen }}% icon icon="star" color=orange %{{ hclose }}{{ end }}{#{{ slugize .GenericName }}} {{- if .IsDeprecated }} {{ hopen }}% expand title="{{ .Name }} [DEPRECATED]" %{{ hclose }}
{{/* like godoc TODO: use css */}} {{- end }} {{ mdformat .DocString . }}{{/* reformat inner markdown and extract testable examples */}} {{ hopen }}< tabs >{{ hclose }} {{ template "TABS-SUBSECTION" . }} {{ hopen }}< /tabs >{{ hclose }} {{- if .IsDeprecated }}
{{ hopen }}% /expand %{{ hclose }} {{- end }} {{- end -}} --- title: {{ .Title | quote }} description: {{ titleize .Description | quote }} weight: {{ .Weight }} domains: - {{ .Domain | quote }} keywords: {{- with .Package }} {{- range .Names }} - {{ . | quote }} {{- end }} {{- end }} --- {{ titleize .Description }}{{/* description of the domain, as annotated in internal/assertions/doc.go */}} ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to <{{ .PkgGoDevURL }}>_ This domain exposes {{ .RefCount }} functionalities. {{- if .HasGenerics }} Generic assertions are marked with a {{ hopen }}% icon icon="star" color=orange %{{ hclose }}. {{- end }} {{ template "TOC" . }} {{- with .Package }} {{- range .Functions.Scope "include-generics" . }}{{/* functions in internal/assertions annotated with "domain:" */}} {{ template "FUNC-SECTION" . }} {{- end }} {{- end }} --- {{- with .Package }} {{- if .HasHelpers }} ## Other helpers{{/* we don't need the New() function to appear in this list */}} {{- range .Functions.Scope "only-helpers" . }}{{/* functions in internal/assertions annotated with "domain:" (removed constructor) */}} {{ template "FUNC-SECTION" . }} {{- end }} {{- end }} {{- end }} --- Generated with {{ .Package.Tool }} [godoc-badge]: {{ godocbadge .PkgGoDevURL }} [godoc-url]: {{ .PkgGoDevURL }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/requirement_assertions.gotmpl000066400000000000000000000014721520301377500331070ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} {{- range .Functions }} {{- if or (not .IsGeneric) ($.EnableGenerics) }} {{- if and (not .IsHelper) (not .IsConstructor) }} {{ comment .DocString }} // {{ docStringPackage $.Package }} func {{ .GenericName }}({{ params .AllParams }}) { if h, ok := t.(H); ok { h.Helper() } {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{ .Name }}({{ forward .AllParams }}) {{- else }} if {{ .TargetPackage }}.{{ .GenericCallName }}({{ forward .AllParams }}) { return } {{- end }} t.FailNow() } {{- end }} {{- end }} {{- end }} go-openapi-testify-c10ca71/codegen/internal/generator/templates/requirement_format.gotmpl000066400000000000000000000020461520301377500322030ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} {{- range .Functions }} {{- if or (not .IsGeneric) ($.EnableGenerics) }} {{- if and (not .IsHelper) (not .IsConstructor) }} {{ docStringFor "format" .Name }} // {{ docStringPackage $.Package }} func {{ .GenericName "f" }}(t T, {{ params .Params }}, msg string, args ...any) { if h, ok := t.(H); ok { h.Helper() } {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special semantics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{ .Name }}(t, {{ forward .Params }}, forwardArgs(msg, args)...) {{- else }} if {{ .TargetPackage }}.{{ .GenericCallName }}(t, {{ forward .Params }}, forwardArgs(msg, args)...) { return } {{- end }} t.FailNow() } {{- end }} {{- end }} {{- end }} func forwardArgs(msg string, args []any) []any { result := make([]any, len(args)+1) result[0]=msg copy(result[1:], args) return result } go-openapi-testify-c10ca71/codegen/internal/generator/templates/requirement_forward.gotmpl000066400000000000000000000035641520301377500323650ustar00rootroot00000000000000{{ comment .Copyright }} // Code generated with {{ .Tool }}; DO NOT EDIT. package {{ .Package }} {{- if .Imports.HasImports }} import ( {{ imports .Imports }} ) {{- end }} // {{ .Receiver }} exposes all assertion functions as methods. // // NOTE: assertion methods with parameterized types (generics) are not supported as methods. // {{ docStringPackage .Package }} type {{ .Receiver }} struct { T } // New makes a new [{{ .Receiver }}] object for the specified [T] (e.g. [testing.T]). func New(t T) *{{ .Receiver }} { return &{{ .Receiver }}{ T: t, } } {{- range .Functions }} {{- if and (not .IsGeneric) (not .IsHelper) (not .IsConstructor) }}{{/* generics can't be added to the receiver */}} {{ docStringFor "forward" .Name }} // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}) {{.Name}}({{ params .Params }}, msgAndArgs ...any) { if h, ok := a.T.(H); ok { h.Helper() } {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special sema.Tics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{.Name}}(a.T, {{ forward .Params }}, msgAndArgs...) {{- else }} if {{ .TargetPackage }}.{{.Name}}(a.T, {{ forward .Params }}, msgAndArgs...) { return } {{- end }} a.T.FailNow() } {{- if $.EnableFormat }} {{ docStringFor "format" (concat $.Receiver "." .Name) }} // {{ docStringPackage $.Package }} func (a *{{ $.Receiver }}){{ .Name }}f({{ params .Params }}, msg string, args ...any) { if h, ok := a.T.(H); ok { h.Helper() } {{- if or (eq .Name "Fail") (eq .Name "FailNow") }}{{/* special sema.Tics for these two, which can only fail */}} _ = {{ .TargetPackage }}.{{ .Name }}(a.T, {{ forward .Params }}, forwardArgs(msg, args)...) {{- else }} if {{ .TargetPackage }}.{{ .Name }}(a.T, {{ forward .Params }}, forwardArgs(msg, args)...) { return } {{- end }} a.T.FailNow() } {{- end }} {{- end }} {{- end }} go-openapi-testify-c10ca71/codegen/internal/model/000077500000000000000000000000001520301377500221615ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/model/documentation.go000066400000000000000000000103051520301377500253600ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package model import ( "slices" ) type Documentation struct { Package *AssertionPackage // the complete source package being documented Documents []Document // Documents is a collection of markdown pages or folders } // NewDocumentation builds an empty [Documentation]. func NewDocumentation() *Documentation { return &Documentation{} } func (d *Documentation) Merge(doc Documentation) { if len(d.Documents) == 0 { // just replace *d = doc return } if len(d.Documents) == 1 { // creates a hierarchy current := Document{ Title: d.Package.Package, Path: d.Package.Package, Kind: KindFolder, Package: d.Package, Documents: d.Documents, } next := Document{ Title: doc.Package.Package, Path: doc.Package.Package, Kind: KindFolder, Package: doc.Package, Documents: doc.Documents, } d.Package = nil d.Documents = []Document{ current, next, } return } // simply append a new folder d.Documents = append(d.Documents, Document{ Title: doc.Package.Package, Path: doc.Package.Package, Kind: KindFolder, Package: doc.Package, Documents: doc.Documents, }) } type KindDoc uint8 const ( KindPage KindDoc = iota KindIndex KindFolder ) type Document struct { Title string Domain string Description string Path string // document folder, relative to its parent if any File string // document name, e.g. _index.md, string.md GitHubURL string PkgGoDevURL string Kind KindDoc // page or index or folder Documents []Document // for folders, their content [Document] Index []IndexEntry // for indexes, their entry list Package *AssertionPackage // subset of the package that pertains to this document ExtraPackages ExtraPackages RefCount int Weight int Metrics Metrics QuickIndex QuickIndex } func (d Document) HasGenerics() bool { if d.Package == nil { return false } for _, fn := range d.Package.Functions { if fn.IsGeneric { return true } } return false } type ExtraPackages []*AssertionPackage func (pkgs ExtraPackages) LookupFunction(name string) []FunctionWithContext { const defaultVariants = 8 result := make([]FunctionWithContext, 0, defaultVariants) for _, pkg := range pkgs { for _, fn := range pkg.Functions { if fn.Name == name { result = append(result, FunctionWithContext{ Function: fn, Package: pkg.Package, Receiver: pkg.Receiver, EnableFormat: pkg.EnableFormat, EnableForward: pkg.EnableForward, EnableGenerics: pkg.EnableGenerics, EnableExamples: pkg.EnableExamples, }) break } } } return slices.Clip(result) } type FunctionWithContext struct { Function Package string Receiver string EnableFormat bool EnableForward bool EnableGenerics bool EnableExamples bool } type IndexEntry struct { Name string Title string Description string Link string RefCount int Weight int } //nolint:tagliatelle // not using camelcase type Metrics struct { Domains int `yaml:"domains"` Functions int `yaml:"functions"` Assertions int `yaml:"assertions"` Generics int `yaml:"generics"` NonGenerics int `yaml:"nongeneric_assertions"` Helpers int `yaml:"helpers"` Others int `yaml:"others"` ByDomain map[string]DomainMetrics `yaml:"by_domain"` PackageVariants int `yaml:"package_variants"` TotalVariants int `yaml:"total_variants"` TotalFunctions int `yaml:"total_functions"` } type DomainMetrics struct { Name string `yaml:"name"` Count int `yaml:"count"` } type QuickIndex []QuickIndexEntry type QuickIndexEntry struct { Name string Anchor string Opposite string OppositeAnchor string Domain string IsGeneric bool IsHelper bool } go-openapi-testify-c10ca71/codegen/internal/model/documentation_test.go000066400000000000000000000120351520301377500264210ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package model import ( "testing" ) func TestNewDocumentation(t *testing.T) { t.Parallel() doc := NewDocumentation() if doc == nil { t.Fatal("NewDocumentation() returned nil") } if doc.Package != nil { t.Error("Package should be nil") } if doc.Documents != nil { t.Error("Documents should be nil") } } func TestDocumentationMerge_EmptyTarget(t *testing.T) { t.Parallel() target := NewDocumentation() incoming := Documentation{ Package: &AssertionPackage{Package: pkgAssert}, Documents: []Document{ {Title: "doc1"}, }, } target.Merge(incoming) if target.Package == nil || target.Package.Package != pkgAssert { t.Error("Merge into empty should replace Package") } if len(target.Documents) != 1 || target.Documents[0].Title != "doc1" { t.Error("Merge into empty should replace Documents") } } func TestDocumentationMerge_SingleDocument(t *testing.T) { t.Parallel() target := &Documentation{ Package: &AssertionPackage{Package: pkgAssert}, Documents: []Document{ {Title: "assertDoc"}, }, } incoming := Documentation{ Package: &AssertionPackage{Package: pkgRequire}, Documents: []Document{ {Title: "requireDoc"}, }, } target.Merge(incoming) // Should create a hierarchy with 2 folders if target.Package != nil { t.Error("Package should be nil after hierarchy creation") } if len(target.Documents) != 2 { t.Fatalf("Expected 2 documents, got %d", len(target.Documents)) } if target.Documents[0].Title != pkgAssert { t.Errorf("First folder title: want %q, got %q", pkgAssert, target.Documents[0].Title) } if target.Documents[0].Kind != KindFolder { t.Error("First document should be KindFolder") } if target.Documents[1].Title != pkgRequire { t.Errorf("Second folder title: want %q, got %q", pkgRequire, target.Documents[1].Title) } } func TestDocumentationMerge_MultipleDocuments(t *testing.T) { t.Parallel() // Start with a hierarchy already in place (2 folders) target := &Documentation{ Documents: []Document{ {Title: pkgAssert, Kind: KindFolder}, {Title: pkgRequire, Kind: KindFolder}, }, } incoming := Documentation{ Package: &AssertionPackage{Package: "extra"}, Documents: []Document{ {Title: "extraDoc"}, }, } target.Merge(incoming) if len(target.Documents) != 3 { t.Fatalf("Expected 3 documents, got %d", len(target.Documents)) } if target.Documents[2].Title != "extra" { t.Errorf("Third folder title: want %q, got %q", "extra", target.Documents[2].Title) } if target.Documents[2].Kind != KindFolder { t.Error("Third document should be KindFolder") } } func TestDocumentHasGenerics(t *testing.T) { t.Parallel() // nil package doc := Document{} if doc.HasGenerics() { t.Error("nil package should return false") } // no generic functions doc = Document{ Package: &AssertionPackage{ Functions: Functions{ {Name: "Equal"}, {Name: "True"}, }, }, } if doc.HasGenerics() { t.Error("No generic functions should return false") } // has generic function doc = Document{ Package: &AssertionPackage{ Functions: Functions{ {Name: "Equal"}, {Name: "Greater", IsGeneric: true}, }, }, } if !doc.HasGenerics() { t.Error("With generic function should return true") } } func TestExtraPackagesLookupFunction(t *testing.T) { t.Parallel() assertPkg := &AssertionPackage{ Package: pkgAssert, Receiver: "Assertions", EnableFormat: true, EnableForward: true, EnableGenerics: false, EnableExamples: true, Functions: Functions{ {Name: "Equal"}, {Name: "True"}, }, } requirePkg := &AssertionPackage{ Package: pkgRequire, Receiver: "Assertions", EnableFormat: true, EnableForward: true, EnableGenerics: true, EnableExamples: false, Functions: Functions{ {Name: "Equal"}, {Name: "False"}, }, } pkgs := ExtraPackages{assertPkg, requirePkg} // Found in both packages results := pkgs.LookupFunction("Equal") if len(results) != 2 { t.Fatalf("LookupFunction(Equal): want 2 results, got %d", len(results)) } if results[0].Package != pkgAssert { t.Errorf("First result package: want %q, got %q", pkgAssert, results[0].Package) } if results[1].Package != pkgRequire { t.Errorf("Second result package: want %q, got %q", pkgRequire, results[1].Package) } if !results[0].EnableExamples { t.Error("First result should have EnableExamples true") } if results[1].EnableExamples { t.Error("Second result should have EnableExamples false") } // Found in one package results = pkgs.LookupFunction("True") if len(results) != 1 { t.Fatalf("LookupFunction(True): want 1 result, got %d", len(results)) } // Not found results = pkgs.LookupFunction("NonExistent") if len(results) != 0 { t.Errorf("LookupFunction(NonExistent): want 0 results, got %d", len(results)) } // Empty extra packages empty := ExtraPackages{} results = empty.LookupFunction("Equal") if len(results) != 0 { t.Errorf("LookupFunction on empty: want 0 results, got %d", len(results)) } } go-openapi-testify-c10ca71/codegen/internal/model/model.go000066400000000000000000000260131520301377500236120ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package model import ( "errors" "fmt" "go/ast" "go/token" "iter" "maps" "slices" "strings" ) type Renderable interface { Render() string } // AssertionPackage describes the internal/assertions package. type AssertionPackage struct { Tool string Header string Package string DocString string Copyright string Receiver string Imports ImportMap EnableFormat bool EnableForward bool EnableGenerics bool EnableExamples bool RunnableExamples bool Functions Functions Types []Ident Consts []Ident Vars []Ident // extraneous information when scanning in collectDoc mode ExtraComments []ExtraComment Context *Document // Overridable context for test generation TestDataPath string TestPackage bool } func (a *AssertionPackage) WithTestPackage() *AssertionPackage { a.TestPackage = true return a } func (a *AssertionPackage) HasHelpers() (ok bool) { for _, fn := range a.Functions { if fn.IsHelper || fn.IsConstructor { return true } } return false } // New empty [AssertionPackage]. func New() *AssertionPackage { const ( allocatedFuncs = 100 allocatedIdents = 50 ) return &AssertionPackage{ // preallocate with sensible defaults for our package Functions: make(Functions, 0, allocatedFuncs), Types: make([]Ident, 0, allocatedIdents), Consts: make([]Ident, 0, allocatedIdents), Vars: make([]Ident, 0, allocatedIdents), } } func (a *AssertionPackage) Clone() *AssertionPackage { b := *a maps.Copy(b.Imports, a.Imports) // Deep-copy slices so the clone has its own backing arrays. // Without this, transformModel writes through the shared backing // array and clobbers the source data used by other generators. b.Functions = slices.Clone(a.Functions) b.Types = slices.Clone(a.Types) b.Consts = slices.Clone(a.Consts) b.Vars = slices.Clone(a.Vars) return &b } func (a *AssertionPackage) Names() iter.Seq[string] { return func(yield func(string) bool) { for _, fn := range a.Functions { if !yield(fn.Name) { return } if !a.EnableFormat { continue } if !yield(fn.Name + "f") { return } } } } // ImportMap represents the imports for the analyzed package. type ImportMap map[string]string func (m ImportMap) HasImports() bool { return len(m) > 0 } type ScopeKind string const ( ScopeKindWithGenerics ScopeKind = "include-generics" ScopeKindWithoutGenerics ScopeKind = "exclude-generics" ScopeKindHelpers ScopeKind = "only-helpers" ) type Functions []Function func (f Functions) Scope(scope ScopeKind, ctx *AssertionPackage) (iter.Seq[Function], error) { switch scope { case ScopeKindWithGenerics: if ctx == nil { return nil, errors.New(`nil package context passed to scope: please pass "." as context to the Scope function`) } return f.iterScopeWithGenerics(ctx), nil case ScopeKindWithoutGenerics: return f.iterScopeWithoutGenerics, nil case ScopeKindHelpers: return f.iterScopeHelpers, nil default: return nil, fmt.Errorf("invalid scope kind in template. Expected one of include-generics, only-helpers, but got %q", scope) } } func (f Functions) iterScopeWithGenerics(ctx *AssertionPackage) func(func(Function) bool) { return func(yield func(Function) bool) { for _, fn := range f { if fn.IsConstructor || fn.IsHelper || (fn.IsGeneric && !ctx.EnableGenerics) { continue } if !yield(fn) { return } } } } func (f Functions) iterScopeWithoutGenerics(yield func(Function) bool) { for _, fn := range f { if fn.IsConstructor || fn.IsHelper || fn.IsGeneric { continue } if !yield(fn) { return } } } func (f Functions) iterScopeHelpers(yield func(Function) bool) { for _, fn := range f { if !fn.IsHelper { continue } if !yield(fn) { return } } } // Function represents an assertion function extracted from the source package. type Function struct { TestData ID string Name string SourcePackage string TargetPackage string DocString string UseMock string Params Parameters AllParams Parameters Returns Parameters TypeParams []TypeParam IsGeneric bool IsHelper bool IsDeprecated bool IsConstructor bool Tests []Test // extraneous information when scanning in collectDoc mode Domain string SourceLink *token.Position ExtraComments []ExtraComment Examples []Renderable // testable examples as a collection of [Renderable] examples Context *Document } // TestData holds test variabilization parameters for templates. type TestData struct { TestCall string TestMock string TestErrorPrefix string TestMockFailure string TestPanicWrapper string TestMsg string } // GenericName renders the function name with one or more suffixes, // accounting for any type parameter for generic functions. func (f Function) GenericName(suffixes ...string) string { suffix := strings.Join(suffixes, "") if !f.IsGeneric { // means len(f.TypeParams) == 0 return f.Name + suffix } var w strings.Builder w.WriteString(f.Name) w.WriteString(suffix) w.WriteByte('[') c := f.TypeParams[0] w.WriteString(c.Name) if len(f.TypeParams) <= 1 || f.TypeParams[1].Constraint != c.Constraint { // constraint is elided if next param has the same constraint w.WriteByte(' ') w.WriteString(c.Constraint) } for i, p := range f.TypeParams[1:] { w.WriteString(", ") w.WriteString(p.Name) if len(f.TypeParams) <= i+1+1 || f.TypeParams[i+1+1].Constraint != p.Constraint { w.WriteByte(' ') w.WriteString(p.Constraint) } } w.WriteByte(']') return w.String() } // GenericCallName renders the function name with explicit type parameters. // This is used when forwarding type parameters, as all type parameters may not be always inferred from the arguments. func (f Function) GenericCallName(suffixes ...string) string { suffix := strings.Join(suffixes, "") if !f.IsGeneric { // means len(f.TypeParams) == 0 return f.Name + suffix } var w strings.Builder w.WriteString(f.Name) w.WriteString(suffix) w.WriteByte('[') c := f.TypeParams[0] w.WriteString(c.Name) for _, p := range f.TypeParams[1:] { w.WriteString(", ") w.WriteString(p.Name) } w.WriteByte(']') return w.String() } func (f Function) HasTest() bool { return len(f.Tests) > 0 } func (f Function) HasSuccessTest() bool { return slices.ContainsFunc(f.Tests, func(e Test) bool { return e.ExpectedOutcome == TestSuccess }) } // GenericSuffix provides a type parameter instantiation for methods which cannot infer // type parameters from their arguments. At this moment, only one such case exists: OfTypeT(). func (f Function) GenericSuffix() string { if strings.HasSuffix(f.Name, "OfTypeT") { return "[myType]" } return "" } // FailMsg returns an error message to report in tests. // // If the function should use the "mockFailNowT" mock, we should report that [testing.FailNow] should be // called, not just marking the test as failed. // // An optional suffix may be used to apply to the function name and express a formatted variant. // // If to arguments are passed, the first one is interpreted as a prefix, followed by "." and the second as a suffix. // Further passed args are ignored. func (f Function) FailMsg(args ...string) string { var prefix, suffix string const ( onlySuffix = 1 prefixAndSuffix = 2 ) switch len(args) { case onlySuffix: suffix = args[0] case prefixAndSuffix: prefix = args[0] + "." suffix = args[1] } if f.UseMock == "mockFailNowT" { return prefix + f.Name + suffix + " should call FailNow()" } return prefix + f.Name + suffix + " should mark test as failed" } type Parameters []Parameter // Parameter represents a function parameter or return value. type Parameter struct { Name string GoType string Selector string IsVariadic bool IsGeneric bool } // TypeParam represents a type parameter in a generic function. type TypeParam struct { Name string // type parameter name (e.g., "B") Constraint string // constraint type (e.g., "Boolean", "cmp.Ordered") } // Ident represents an exported identifier (type, constant, or variable) from the source package. type Ident struct { ID string Name string SourcePackage string TargetPackage string DocString string IsAlias bool IsDeprecated bool Function *Function // for function types (or vars) // extraneous information when scanning in collectDoc mode Domain string SourceLink *token.Position ExtraComments []ExtraComment Examples []Renderable // testable examples as a collection of [Renderable] examples } // TestValue represents a single parsed test value expression. // // It stores both the original string (for debugging/audit) and the parsed AST. type TestValue struct { Raw string // Original string from doc comment Expr ast.Expr // Parsed Go expression (nil if parse failed) Error error // Parse error if any } // Test captures test values to use with generated tests. // // Test values are parsed as Go expressions and stored with their AST representation. type Test struct { TestedValues []TestValue // Parsed test value expressions TestedValue string // Original raw string, kept for auditability ExpectedOutcome TestExpectedOutcome // Expected test outcome (success/failure/panic) AssertionMessage string // Optional assertion message for panic tests IsFirst bool Pkg string } func (t Test) IsSuccess() bool { return t.ExpectedOutcome == TestSuccess } func (t Test) IsFailure() bool { return t.ExpectedOutcome == TestFailure } func (t Test) IsPanic() bool { return t.ExpectedOutcome == TestPanic } func (t Test) IsKindRequire() bool { return t.Pkg == "require" } type TestExpectedOutcome uint8 const ( TestNone TestExpectedOutcome = iota TestSuccess TestFailure TestPanic ) type CommentTag uint8 // String representation of a comment tag, mostly useful for debugging purpose. func (t CommentTag) String() string { switch t { case CommentTagNone: return "comment-tag-none" case CommentTagDomain: return "comment-tag-domain" case CommentTagMaintainer: return "comment-tag-maintainer" case CommentTagMention: return "comment-tag-mention" case CommentTagNote: return "comment-tag-note" case CommentTagDomainDescription: return "comment-tag-domain-description" default: return "invalid-value" } } const ( CommentTagNone CommentTag = iota CommentTagDomain CommentTagMaintainer CommentTagMention CommentTagNote CommentTagDomainDescription CommentTagOpposite ) type ExtraComment struct { Tag CommentTag Key string Text string } func (c ExtraComment) IsTagMaintainer() bool { return c.Tag == CommentTagMaintainer } func (c ExtraComment) IsTagMention() bool { return c.Tag == CommentTagMention } func (c ExtraComment) IsTagNote() bool { return c.Tag == CommentTagNote } func (c ExtraComment) Opposite() string { if c.Tag != CommentTagOpposite { return "" } return c.Text } go-openapi-testify-c10ca71/codegen/internal/model/model_test.go000066400000000000000000000415001520301377500246470ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package model import ( "iter" "slices" "testing" ) const ( pkgAssertions = "assertions" pkgAssert = "assert" pkgRequire = "require" myType = "[myType]" ) func TestNew(t *testing.T) { t.Parallel() pkg := New() if pkg == nil { t.Fatal("New() returned nil") } if pkg.Functions == nil { t.Error("Functions should be initialized") } if pkg.Types == nil { t.Error("Types should be initialized") } if pkg.Consts == nil { t.Error("Consts should be initialized") } if pkg.Vars == nil { t.Error("Vars should be initialized") } if cap(pkg.Functions) != 100 { t.Errorf("Functions capacity: want 100, got %d", cap(pkg.Functions)) } } func TestWithTestPackage(t *testing.T) { t.Parallel() pkg := New() if pkg.TestPackage { t.Error("TestPackage should be false initially") } result := pkg.WithTestPackage() if result != pkg { t.Error("WithTestPackage should return the same pointer") } if !pkg.TestPackage { t.Error("TestPackage should be true after WithTestPackage") } } func TestHasHelpers(t *testing.T) { t.Parallel() for tt := range hasHelpersCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() pkg := New() pkg.Functions = tt.functions if got := pkg.HasHelpers(); got != tt.expected { t.Errorf("HasHelpers() = %v, want %v", got, tt.expected) } }) } } type hasHelpersCase struct { name string functions Functions expected bool } func hasHelpersCases() iter.Seq[hasHelpersCase] { return slices.Values([]hasHelpersCase{ { name: "empty functions", functions: Functions{}, expected: false, }, { name: "no helpers or constructors", functions: Functions{ {Name: "Equal"}, {Name: "True"}, }, expected: false, }, { name: "has helper", functions: Functions{ {Name: "Equal"}, {Name: "CallerInfo", IsHelper: true}, }, expected: true, }, { name: "has constructor", functions: Functions{ {Name: "New", IsConstructor: true}, }, expected: true, }, }) } func TestClone(t *testing.T) { t.Parallel() original := New() original.Package = pkgAssertions original.Imports = ImportMap{"foo": "bar"} original.Functions = Functions{ {Name: "Equal"}, {Name: "True"}, } original.Types = []Ident{{Name: "T"}} original.Consts = []Ident{{Name: "Const1"}} original.Vars = []Ident{{Name: "Var1"}} cloned := original.Clone() if cloned == original { t.Error("Clone should return a different pointer") } if cloned.Package != pkgAssertions { t.Error("Clone should preserve Package") } // Verify slice independence cloned.Functions = append(cloned.Functions, Function{Name: "False"}) if len(original.Functions) != 2 { t.Error("Modifying cloned Functions should not affect original") } cloned.Types = append(cloned.Types, Ident{Name: "T2"}) if len(original.Types) != 1 { t.Error("Modifying cloned Types should not affect original") } } func TestNames(t *testing.T) { t.Parallel() for tt := range namesCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() pkg := New() pkg.Functions = tt.functions pkg.EnableFormat = tt.enableFormat names := make([]string, 0, len(tt.expected)) for name := range pkg.Names() { names = append(names, name) } if len(names) != len(tt.expected) { t.Errorf("Names() yielded %d names, want %d", len(names), len(tt.expected)) return } for i, name := range names { if name != tt.expected[i] { t.Errorf("Names()[%d] = %q, want %q", i, name, tt.expected[i]) } } }) } } type namesCase struct { name string functions Functions enableFormat bool expected []string } func namesCases() iter.Seq[namesCase] { return slices.Values([]namesCase{ { name: "empty", functions: Functions{}, expected: nil, }, { name: "without format", functions: Functions{ {Name: "Equal"}, {Name: "True"}, }, enableFormat: false, expected: []string{"Equal", "True"}, }, { name: "with format", functions: Functions{ {Name: "Equal"}, {Name: "True"}, }, enableFormat: true, expected: []string{"Equal", "Equalf", "True", "Truef"}, }, }) } func TestNamesEarlyBreak(t *testing.T) { t.Parallel() pkg := New() pkg.Functions = Functions{ {Name: "Equal"}, {Name: "True"}, {Name: "False"}, } pkg.EnableFormat = true var names []string for name := range pkg.Names() { names = append(names, name) if len(names) == 2 { break } } if len(names) != 2 { t.Errorf("Early break: got %d names, want 2", len(names)) } } func TestImportMapHasImports(t *testing.T) { t.Parallel() empty := ImportMap{} if empty.HasImports() { t.Error("Empty map should return false") } nonEmpty := ImportMap{"foo": "bar"} if !nonEmpty.HasImports() { t.Error("Non-empty map should return true") } } func TestFunctionsScope(t *testing.T) { t.Parallel() for tt := range scopeCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() seq, err := tt.functions.Scope(tt.scope, tt.ctx) if tt.expectError { if err == nil { t.Fatal("Expected error but got nil") } return } if err != nil { t.Fatalf("Unexpected error: %v", err) } var names []string for fn := range seq { names = append(names, fn.Name) } if len(names) != len(tt.expected) { t.Errorf("Got %d functions %v, want %d %v", len(names), names, len(tt.expected), tt.expected) return } for i, name := range names { if name != tt.expected[i] { t.Errorf("Function[%d] = %q, want %q", i, name, tt.expected[i]) } } }) } } type scopeCase struct { name string functions Functions scope ScopeKind ctx *AssertionPackage expectError bool expected []string } func scopeCases() iter.Seq[scopeCase] { funcs := Functions{ {Name: "Equal"}, {Name: "GreaterOrEqual", IsGeneric: true}, {Name: "CallerInfo", IsHelper: true}, {Name: "New", IsConstructor: true}, {Name: "True"}, } ctxNoGenerics := &AssertionPackage{EnableGenerics: false} ctxWithGenerics := &AssertionPackage{EnableGenerics: true} return slices.Values([]scopeCase{ { name: "with-generics scope, generics disabled", functions: funcs, scope: ScopeKindWithGenerics, ctx: ctxNoGenerics, expected: []string{"Equal", "True"}, }, { name: "with-generics scope, generics enabled", functions: funcs, scope: ScopeKindWithGenerics, ctx: ctxWithGenerics, expected: []string{"Equal", "GreaterOrEqual", "True"}, }, { name: "without-generics scope", functions: funcs, scope: ScopeKindWithoutGenerics, expected: []string{"Equal", "True"}, }, { name: "helpers scope", functions: funcs, scope: ScopeKindHelpers, expected: []string{"CallerInfo"}, }, { name: "with-generics scope, nil context", functions: funcs, scope: ScopeKindWithGenerics, ctx: nil, expectError: true, }, { name: "invalid scope kind", functions: funcs, scope: ScopeKind("invalid"), expectError: true, }, }) } func TestScopeEarlyBreak(t *testing.T) { t.Parallel() funcs := Functions{ {Name: "A"}, {Name: "B"}, {Name: "C"}, } // Test early break on iterScopeWithoutGenerics seq, err := funcs.Scope(ScopeKindWithoutGenerics, nil) if err != nil { t.Fatal(err) } count := 0 for range seq { count++ if count == 1 { break } } if count != 1 { t.Errorf("Early break: got %d, want 1", count) } // Test early break on iterScopeHelpers helperFuncs := Functions{ {Name: "H1", IsHelper: true}, {Name: "H2", IsHelper: true}, } seq2, err := helperFuncs.Scope(ScopeKindHelpers, nil) if err != nil { t.Fatal(err) } count = 0 for range seq2 { count++ if count == 1 { break } } if count != 1 { t.Errorf("Early break helpers: got %d, want 1", count) } // Test early break on iterScopeWithGenerics ctx := &AssertionPackage{EnableGenerics: true} seq3, err := funcs.Scope(ScopeKindWithGenerics, ctx) if err != nil { t.Fatal(err) } count = 0 for range seq3 { count++ if count == 1 { break } } if count != 1 { t.Errorf("Early break with-generics: got %d, want 1", count) } } func TestGenericName(t *testing.T) { t.Parallel() for tt := range genericNameCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := tt.fn.GenericName(tt.suffixes...); got != tt.expected { t.Errorf("GenericName(%v) = %q, want %q", tt.suffixes, got, tt.expected) } }) } } type genericNameCase struct { name string fn Function suffixes []string expected string } func genericNameCases() iter.Seq[genericNameCase] { return slices.Values([]genericNameCase{ { name: "non-generic, no suffix", fn: Function{Name: "Equal"}, expected: "Equal", }, { name: "non-generic, with suffix", fn: Function{Name: "Equal"}, suffixes: []string{"f"}, expected: "Equalf", }, { name: "generic, single type param", fn: Function{ Name: "Greater", IsGeneric: true, TypeParams: []TypeParam{ {Name: "T", Constraint: "cmp.Ordered"}, }, }, expected: "Greater[T cmp.Ordered]", }, { name: "generic, with suffix", fn: Function{ Name: "Greater", IsGeneric: true, TypeParams: []TypeParam{ {Name: "T", Constraint: "cmp.Ordered"}, }, }, suffixes: []string{"f"}, expected: "Greaterf[T cmp.Ordered]", }, { name: "generic, two type params, same constraint elided", fn: Function{ Name: "Compare", IsGeneric: true, TypeParams: []TypeParam{ {Name: "A", Constraint: "cmp.Ordered"}, {Name: "B", Constraint: "cmp.Ordered"}, }, }, expected: "Compare[A, B cmp.Ordered]", }, { name: "generic, two type params, different constraints", fn: Function{ Name: "Compare", IsGeneric: true, TypeParams: []TypeParam{ {Name: "A", Constraint: "cmp.Ordered"}, {Name: "B", Constraint: "any"}, }, }, expected: "Compare[A cmp.Ordered, B any]", }, { name: "generic, three type params, mixed constraints", fn: Function{ Name: "Multi", IsGeneric: true, TypeParams: []TypeParam{ {Name: "A", Constraint: "cmp.Ordered"}, {Name: "B", Constraint: "cmp.Ordered"}, {Name: "C", Constraint: "any"}, }, }, expected: "Multi[A, B cmp.Ordered, C any]", }, }) } func TestGenericCallName(t *testing.T) { t.Parallel() for tt := range genericCallNameCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := tt.fn.GenericCallName(tt.suffixes...); got != tt.expected { t.Errorf("GenericCallName(%v) = %q, want %q", tt.suffixes, got, tt.expected) } }) } } type genericCallNameCase struct { name string fn Function suffixes []string expected string } func genericCallNameCases() iter.Seq[genericCallNameCase] { return slices.Values([]genericCallNameCase{ { name: "non-generic", fn: Function{Name: "Equal"}, expected: "Equal", }, { name: "non-generic, with suffix", fn: Function{Name: "Equal"}, suffixes: []string{"f"}, expected: "Equalf", }, { name: "generic, single type param", fn: Function{ Name: "Greater", IsGeneric: true, TypeParams: []TypeParam{ {Name: "T", Constraint: "cmp.Ordered"}, }, }, expected: "Greater[T]", }, { name: "generic, two type params", fn: Function{ Name: "Compare", IsGeneric: true, TypeParams: []TypeParam{ {Name: "A", Constraint: "cmp.Ordered"}, {Name: "B", Constraint: "any"}, }, }, expected: "Compare[A, B]", }, }) } func TestHasTest(t *testing.T) { t.Parallel() fn := Function{Name: "Equal"} if fn.HasTest() { t.Error("HasTest() should be false with no tests") } fn.Tests = []Test{{ExpectedOutcome: TestSuccess}} if !fn.HasTest() { t.Error("HasTest() should be true with tests") } } func TestHasSuccessTest(t *testing.T) { t.Parallel() fn := Function{ Name: "Equal", Tests: []Test{{ExpectedOutcome: TestFailure}}, } if fn.HasSuccessTest() { t.Error("HasSuccessTest() should be false with only failure tests") } fn.Tests = append(fn.Tests, Test{ExpectedOutcome: TestSuccess}) if !fn.HasSuccessTest() { t.Error("HasSuccessTest() should be true with a success test") } } func TestGenericSuffix(t *testing.T) { t.Parallel() fn := Function{Name: "Equal"} if got := fn.GenericSuffix(); got != "" { t.Errorf("GenericSuffix() = %q, want empty", got) } fn.Name = "IsOfTypeT" if got := fn.GenericSuffix(); got != myType { t.Errorf("GenericSuffix() = %q, want %q", got, myType) } fn.Name = "OfTypeT" if got := fn.GenericSuffix(); got != myType { t.Errorf("GenericSuffix() = %q, want %q", got, myType) } } func TestFailMsg(t *testing.T) { t.Parallel() for tt := range failMsgCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := tt.fn.FailMsg(tt.args...); got != tt.expected { t.Errorf("FailMsg(%v) = %q, want %q", tt.args, got, tt.expected) } }) } } type failMsgCase struct { name string fn Function args []string expected string } func failMsgCases() iter.Seq[failMsgCase] { return slices.Values([]failMsgCase{ { name: "mockT, no args", fn: Function{Name: "Equal", UseMock: "mockT"}, expected: "Equal should mark test as failed", }, { name: "mockFailNowT, no args", fn: Function{Name: "Equal", UseMock: "mockFailNowT"}, expected: "Equal should call FailNow()", }, { name: "mockT, with suffix", fn: Function{Name: "Equal", UseMock: "mockT"}, args: []string{"f"}, expected: "Equalf should mark test as failed", }, { name: "mockT, with prefix and suffix", fn: Function{Name: "Equal", UseMock: "mockT"}, args: []string{"Assertions", "f"}, expected: "Assertions.Equalf should mark test as failed", }, { name: "mockFailNowT, with prefix and suffix", fn: Function{Name: "Equal", UseMock: "mockFailNowT"}, args: []string{"Assertions", ""}, expected: "Assertions.Equal should call FailNow()", }, }) } func TestTestPredicates(t *testing.T) { t.Parallel() tests := []struct { name string test Test success bool failure bool panics bool isRequire bool }{ { name: "success test", test: Test{ExpectedOutcome: TestSuccess}, success: true, }, { name: "failure test", test: Test{ExpectedOutcome: TestFailure}, failure: true, }, { name: "panic test", test: Test{ExpectedOutcome: TestPanic}, panics: true, }, { name: "require test", test: Test{Pkg: pkgRequire}, isRequire: true, }, { name: "none outcome", test: Test{ExpectedOutcome: TestNone}, }, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := tt.test.IsSuccess(); got != tt.success { t.Errorf("IsSuccess() = %v, want %v", got, tt.success) } if got := tt.test.IsFailure(); got != tt.failure { t.Errorf("IsFailure() = %v, want %v", got, tt.failure) } if got := tt.test.IsPanic(); got != tt.panics { t.Errorf("IsPanic() = %v, want %v", got, tt.panics) } if got := tt.test.IsKindRequire(); got != tt.isRequire { t.Errorf("IsKindRequire() = %v, want %v", got, tt.isRequire) } }) } } func TestCommentTagString(t *testing.T) { t.Parallel() for tt := range commentTagCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() if got := tt.tag.String(); got != tt.expected { t.Errorf("String() = %q, want %q", got, tt.expected) } }) } } type commentTagCase struct { name string tag CommentTag expected string } func commentTagCases() iter.Seq[commentTagCase] { return slices.Values([]commentTagCase{ {name: "none", tag: CommentTagNone, expected: "comment-tag-none"}, {name: "domain", tag: CommentTagDomain, expected: "comment-tag-domain"}, {name: "maintainer", tag: CommentTagMaintainer, expected: "comment-tag-maintainer"}, {name: "mention", tag: CommentTagMention, expected: "comment-tag-mention"}, {name: "note", tag: CommentTagNote, expected: "comment-tag-note"}, {name: "domain-description", tag: CommentTagDomainDescription, expected: "comment-tag-domain-description"}, {name: "invalid", tag: CommentTag(99), expected: "invalid-value"}, }) } func TestExtraCommentPredicates(t *testing.T) { t.Parallel() maintainer := ExtraComment{Tag: CommentTagMaintainer} if !maintainer.IsTagMaintainer() { t.Error("Expected IsTagMaintainer() true") } if maintainer.IsTagMention() || maintainer.IsTagNote() { t.Error("Other predicates should be false") } mention := ExtraComment{Tag: CommentTagMention} if !mention.IsTagMention() { t.Error("Expected IsTagMention() true") } note := ExtraComment{Tag: CommentTagNote} if !note.IsTagNote() { t.Error("Expected IsTagNote() true") } } go-openapi-testify-c10ca71/codegen/internal/scanner/000077500000000000000000000000001520301377500225125ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/000077500000000000000000000000001520301377500256315ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/doc.go000066400000000000000000000012111520301377500267200ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package parser provides text parsing utilities for extracting structured information from comments. // // This package contains pure text parsing logic with no AST dependencies. It parses: // // - Test values from "Examples:" sections in doc comments // - Tagged comments (domain, maintainer, note, mention) // - Domain descriptions from package-level comments // // The parsers use regular expressions to identify sections and extract values. // All parsing is case-insensitive and supports multiple formatting styles. package parser go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/examples.go000066400000000000000000000066771520301377500300160ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "slices" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // ParseTestExamples extracts test examples from doc comment text. // // It looks for an "Examples:" or "Example:" section and parses lines like: // // - success: // - failure: // - panic: // // After each value line, an optional comment line can provide an assertion message. // Empty values or values marked "// NOT IMPLEMENTED" are skipped. func ParseTestExamples(text string) []model.Test { const usualNumberOfExamples = 2 tests := make([]model.Test, 0, usualNumberOfExamples) inExamplesSection := false // we expect an Example[s] or # Example[s] section inValueSection := false const ( successPrefix = "success" failurePrefix = "failure" panicPrefix = "panic" examplePrefix = "example" ) startExampleSection := StartSectionFunc(examplePrefix) // e.g. Example:, examples:, # Example, ... startValueSuccess := StartValueFunc(successPrefix) // e.g. success: , Success:, "SuCCess :" startValueFailure := StartValueFunc(failurePrefix) startValuePanic := StartValueFunc(panicPrefix) startExampleValue := func(line string) (string, model.TestExpectedOutcome, bool) { val, ok := startValueSuccess(line) if ok { return val, model.TestSuccess, true } val, ok = startValueFailure(line) if ok { return val, model.TestFailure, true } val, ok = startValuePanic(line) if ok { return val, model.TestPanic, true } return "", model.TestNone, false } for line := range strings.SplitSeq(text, "\n") { line = strings.TrimSpace(line) value, outcome, isExampleValue := startExampleValue(line) if inValueSection && len(tests) > 0 && line != "" && !StartAnotherSection(line) && !isExampleValue { // check if a comment line follows an example value: this would be the assertion message tests[len(tests)-1].AssertionMessage = line inValueSection = false continue } inValueSection = false // check if we're entering the Examples section if startExampleSection(line) { inExamplesSection = true continue } // skip until we find the Examples section if !inExamplesSection { continue } // stop if we hit another section (starts with capital letter and ends with colon) if StartAnotherSection(line) { break } // parse test lines: "success: { values to put in the test }", "failure: ..." or "panic: ..." // after each value line, we may found an additional text to be used as an assertion message. if !isExampleValue { continue } inValueSection = true // an extra comment may appear right after the value line testcase, ok := parseTestValue(outcome, value) if !ok { continue // there is a value, but it's empty or marked "// NOT IMPLEMENTED" } tests = append(tests, testcase) } return slices.Clip(tests) } func parseTestValue(outcome model.TestExpectedOutcome, value string) (model.Test, bool) { value = strings.TrimSpace(value) _, isTodo := strings.CutPrefix(value, "// NOT IMPLEMENTED") if value == "" || isTodo { return model.Test{}, false } // Parse test values as Go expressions parsedValues := ParseTestValues(value) return model.Test{ TestedValue: value, // Keep original for backward compatibility TestedValues: parsedValues, // New parsed representation ExpectedOutcome: outcome, }, true } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/examples_test.go000066400000000000000000000212671520301377500310450ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) func TestParseTestExamples(t *testing.T) { t.Parallel() for c := range parseTestExamplesCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := ParseTestExamples(c.input) if len(result) != len(c.expected) { t.Fatalf("ParseTestExamples() returned %d tests, expected %d\nGot: %+v\nExpected: %+v", len(result), len(c.expected), result, c.expected) } for i, expected := range c.expected { got := result[i] if got.TestedValue != expected.TestedValue { t.Errorf("Test[%d].TestedValue = %q, expected %q", i, got.TestedValue, expected.TestedValue) } if got.ExpectedOutcome != expected.ExpectedOutcome { t.Errorf("Test[%d].ExpectedOutcome = %v, expected %v", i, got.ExpectedOutcome, expected.ExpectedOutcome) } if got.AssertionMessage != expected.AssertionMessage { t.Errorf("Test[%d].AssertionMessage = %q, expected %q", i, got.AssertionMessage, expected.AssertionMessage) } } }) } } /* Test case iterators */ type parseTestExamplesCase struct { name string input string expected []model.Test } func parseTestExamplesCases() iter.Seq[parseTestExamplesCase] { return slices.Values([]parseTestExamplesCase{ { name: "success and failure examples", input: `Some function description. Examples: success: 123, 456 failure: 789, 012`, expected: []model.Test{ {TestedValue: "123, 456", ExpectedOutcome: model.TestSuccess}, {TestedValue: "789, 012", ExpectedOutcome: model.TestFailure}, }, }, { name: "panic example", input: `Examples: panic: nil, "something" should panic with nil pointer`, expected: []model.Test{ { TestedValue: "nil, \"something\"", ExpectedOutcome: model.TestPanic, AssertionMessage: "should panic with nil pointer", }, }, }, { name: "mixed examples with assertion messages", input: `Examples: success: "hello", "hello" failure: "hello", "world" strings don't match panic: invalid, args Expected to panic`, expected: []model.Test{ {TestedValue: "\"hello\", \"hello\"", ExpectedOutcome: model.TestSuccess}, { TestedValue: "\"hello\", \"world\"", ExpectedOutcome: model.TestFailure, AssertionMessage: "strings don't match", }, { TestedValue: "invalid, args", ExpectedOutcome: model.TestPanic, AssertionMessage: "Expected to panic", }, }, }, { name: "case insensitive success/failure/panic", input: `Examples: SUCCESS: 1, 1 FAILURE: 1, 2 PANIC: bad`, expected: []model.Test{ {TestedValue: "1, 1", ExpectedOutcome: model.TestSuccess}, {TestedValue: "1, 2", ExpectedOutcome: model.TestFailure}, {TestedValue: "bad", ExpectedOutcome: model.TestPanic}, }, }, { name: "markdown header style", input: `# Examples success: true, true failure: true, false`, expected: []model.Test{ {TestedValue: "true, true", ExpectedOutcome: model.TestSuccess}, {TestedValue: "true, false", ExpectedOutcome: model.TestFailure}, }, }, { name: "plural form", input: `Examples: success: 42`, expected: []model.Test{ {TestedValue: "42", ExpectedOutcome: model.TestSuccess}, }, }, { name: "singular form", input: `Example: success: 100`, expected: []model.Test{ {TestedValue: "100", ExpectedOutcome: model.TestSuccess}, }, }, { name: "skip NOT IMPLEMENTED values", input: `Examples: success: 123 failure: // NOT IMPLEMENTED success: 456`, expected: []model.Test{ {TestedValue: "123", ExpectedOutcome: model.TestSuccess}, {TestedValue: "456", ExpectedOutcome: model.TestSuccess}, }, }, { name: "skip empty values", input: `Examples: success: 123 failure: success: 456`, expected: []model.Test{ {TestedValue: "123", ExpectedOutcome: model.TestSuccess}, {TestedValue: "456", ExpectedOutcome: model.TestSuccess}, }, }, { name: "skip whitespace-only values", input: `Examples: success: 123 failure: success: 456`, expected: []model.Test{ {TestedValue: "123", ExpectedOutcome: model.TestSuccess}, {TestedValue: "456", ExpectedOutcome: model.TestSuccess}, }, }, { name: "stop at next section", input: `Examples: success: 123 failure: 456 Usage: success: this should not be parsed`, expected: []model.Test{ {TestedValue: "123", ExpectedOutcome: model.TestSuccess}, {TestedValue: "456", ExpectedOutcome: model.TestFailure}, }, }, { name: "complex values with quotes and commas", input: `Examples: success: "hello, world", "hello, world" failure: map[string]int{"a": 1}, map[string]int{"b": 2}`, expected: []model.Test{ {TestedValue: `"hello, world", "hello, world"`, ExpectedOutcome: model.TestSuccess}, {TestedValue: `map[string]int{"a": 1}, map[string]int{"b": 2}`, ExpectedOutcome: model.TestFailure}, }, }, { name: "assertion message on line after value", input: `Examples: success: 1, 1 Both values are equal failure: 1, 2 Values differ`, expected: []model.Test{ { TestedValue: "1, 1", ExpectedOutcome: model.TestSuccess, AssertionMessage: "Both values are equal", }, { TestedValue: "1, 2", ExpectedOutcome: model.TestFailure, AssertionMessage: "Values differ", }, }, }, { name: "no assertion message when next line is value", input: `Examples: success: 1, 1 failure: 1, 2`, expected: []model.Test{ {TestedValue: "1, 1", ExpectedOutcome: model.TestSuccess}, {TestedValue: "1, 2", ExpectedOutcome: model.TestFailure}, }, }, { name: "assertion message not consumed by next value", input: `Examples: success: 1, 1 Equal values success: 2, 2`, expected: []model.Test{ { TestedValue: "1, 1", ExpectedOutcome: model.TestSuccess, AssertionMessage: "Equal values", }, {TestedValue: "2, 2", ExpectedOutcome: model.TestSuccess}, }, }, { name: "no examples section", input: `This is just a description without examples.`, expected: []model.Test{}, }, { name: "empty examples section", input: `Examples: Next section.`, expected: []model.Test{}, }, { name: "examples section with no valid values", input: `Examples: Just some text here. No actual test values.`, expected: []model.Test{}, }, { name: "leading and trailing whitespace in values", input: `Examples: success: 123, 456 failure: "hello" `, expected: []model.Test{ {TestedValue: "123, 456", ExpectedOutcome: model.TestSuccess}, {TestedValue: `"hello"`, ExpectedOutcome: model.TestFailure}, }, }, { name: "multiple assertion messages only first consumed", input: `Examples: success: 1 First message Second message should not be consumed failure: 2`, expected: []model.Test{ { TestedValue: "1", ExpectedOutcome: model.TestSuccess, AssertionMessage: "First message", }, {TestedValue: "2", ExpectedOutcome: model.TestFailure}, }, }, { name: "assertion message with special characters", input: `Examples: failure: nil, "test" Expected: non-nil value, got: `, expected: []model.Test{ { TestedValue: `nil, "test"`, ExpectedOutcome: model.TestFailure, AssertionMessage: "Expected: non-nil value, got: ", }, }, }, { name: "case insensitive example header", input: `EXAMPLES: success: 1`, expected: []model.Test{ {TestedValue: "1", ExpectedOutcome: model.TestSuccess}, }, }, { name: "values with leading whitespace", input: `Examples: success: 123 failure: 456`, expected: []model.Test{ {TestedValue: "123", ExpectedOutcome: model.TestSuccess}, {TestedValue: "456", ExpectedOutcome: model.TestFailure}, }, }, { name: "TODO with description", input: `Examples: success: // NOT IMPLEMENTED: implement this test failure: 456`, expected: []model.Test{ {TestedValue: "456", ExpectedOutcome: model.TestFailure}, }, }, { name: "empty line between examples", input: `Examples: success: 1 failure: 2`, expected: []model.Test{ {TestedValue: "1", ExpectedOutcome: model.TestSuccess}, {TestedValue: "2", ExpectedOutcome: model.TestFailure}, }, }, { name: "assertion message separated by empty line", input: `Examples: success: 1 Some text here should not be an assertion message failure: 2`, expected: []model.Test{ {TestedValue: "1", ExpectedOutcome: model.TestSuccess}, {TestedValue: "2", ExpectedOutcome: model.TestFailure}, }, }, }) } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/expressions.go000066400000000000000000000076651520301377500305600ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "fmt" "go/ast" "go/format" "go/parser" "go/token" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // ParseTestValues parses a comma-separated list of Go expressions. // // Examples: // // - "123, 456" → two integer literals // - "assertions.ErrTest, nil" → selector expression and nil // - "[]int{1,2,3}, []int{4,5,6}" → two composite literals // - `"a,b", "c,d"` → two string literals with commas inside // // Implementation: Wraps input in []any{...} and parses as composite literal, // then extracts the elements. This leverages Go's parser to handle all edge cases // (strings, runes, comments, nesting, etc.) correctly. func ParseTestValues(input string) []model.TestValue { input = strings.TrimSpace(input) if input == "" { return nil } // Wrap in composite literal to let Go's parser handle comma splitting wrapped := "[]any{" + input + "}" // Parse the wrapped expression expr, err := parser.ParseExpr(wrapped) if err != nil { // If parsing fails, return a single TestValue with the error return []model.TestValue{{ Raw: input, Expr: nil, Error: fmt.Errorf("invalid Go expression list %q: %w", input, err), }} } // Extract elements from the composite literal compositeLit, ok := expr.(*ast.CompositeLit) if !ok { // Should never happen if parser succeeded return []model.TestValue{{ Raw: input, Expr: nil, Error: fmt.Errorf("internal error: expected composite literal, got %T", expr), }} } // Convert each element to TestValue result := make([]model.TestValue, 0, len(compositeLit.Elts)) // We need to extract the original source text for each element // Since we don't have position info for the original input, we'll format the AST fset := token.NewFileSet() for _, elt := range compositeLit.Elts { // Format the expression back to source code var buf strings.Builder if err := format.Node(&buf, fset, elt); err != nil { result = append(result, model.TestValue{ Raw: "", Expr: elt, Error: fmt.Errorf("failed to format expression: %w", err), }) continue } result = append(result, model.TestValue{ Raw: buf.String(), Expr: elt, Error: nil, }) } return result } // ParseExprWithFileSet is like [ParseTestValues] but uses a provided FileSet // for better error reporting with file/line/column information. // // The filename parameter is used for error messages (e.g., "assertion.go:42"). func ParseExprWithFileSet(fset *token.FileSet, filename string, input string) ([]model.TestValue, error) { input = strings.TrimSpace(input) if input == "" { return nil, nil } // Wrap in composite literal to let Go's parser handle comma splitting wrapped := "[]any{" + input + "}" // Parse with FileSet for better error reporting expr, err := parser.ParseExprFrom(fset, filename, wrapped, 0) if err != nil { return []model.TestValue{{ Raw: input, Expr: nil, Error: fmt.Errorf("invalid Go expression list %q: %w", input, err), }}, err } // Extract elements from the composite literal compositeLit, ok := expr.(*ast.CompositeLit) if !ok { err := fmt.Errorf("internal error: expected composite literal, got %T", expr) return []model.TestValue{{ Raw: input, Expr: nil, Error: err, }}, err } // Convert each element to TestValue result := make([]model.TestValue, 0, len(compositeLit.Elts)) for _, elt := range compositeLit.Elts { // Format the expression back to source code var buf strings.Builder if err := format.Node(&buf, fset, elt); err != nil { result = append(result, model.TestValue{ Raw: "", Expr: elt, Error: fmt.Errorf("failed to format expression: %w", err), }) continue } result = append(result, model.TestValue{ Raw: buf.String(), Expr: elt, Error: nil, }) } return result, nil } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/expressions_test.go000066400000000000000000000145271520301377500316120ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "go/token" "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // TestParseTestValues tests parsing of test value expressions. func TestParseTestValues(t *testing.T) { t.Parallel() for c := range parseTestValuesTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := ParseTestValues(c.input) if len(result) != c.expectedCount { t.Errorf("Expected %d values, got %d", c.expectedCount, len(result)) return } for i, val := range result { t.Run("check single parsed test value", testSingleTestValue(c, val, i)) } }) } } func testSingleTestValue(c parseTestCase, val model.TestValue, i int) func(*testing.T) { return func(t *testing.T) { // Check raw string is preserved expectedRaw := c.expectedRaw[i] if val.Raw != expectedRaw { t.Errorf("Value %d: expected raw %q, got %q", i, expectedRaw, val.Raw) } // Check parse success/failure shouldParse := true // Default: expect successful parse if c.shouldParsePerVal != nil && i < len(c.shouldParsePerVal) { shouldParse = c.shouldParsePerVal[i] } if shouldParse { if val.Error != nil { t.Errorf("Value %d: unexpected parse error: %v", i, val.Error) } if val.Expr == nil { t.Errorf("Value %d: expected parsed expression, got nil", i) } } else if val.Error == nil { t.Errorf("Value %d: expected parse error, got none", i) } } } // TestParseExprWithFileSet tests parsing with FileSet for error reporting. func TestParseExprWithFileSet(t *testing.T) { t.Parallel() fset := token.NewFileSet() for c := range parseExprWithFileSetTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result, err := ParseExprWithFileSet(fset, "test.go", c.input) if c.shouldError { if err == nil { t.Error("Expected error, got none") } } else { if err != nil { t.Errorf("Unexpected error: %v", err) } } if len(result) != c.expectedCount { t.Errorf("Expected %d values, got %d", c.expectedCount, len(result)) } }) } } /* Test case iterators */ type parseTestCase struct { name string input string expectedCount int expectedRaw []string shouldParsePerVal []bool // per-value parse expectations (nil means all should parse) } func parseTestValuesTestCases() iter.Seq[parseTestCase] { return slices.Values([]parseTestCase{ { name: "two integer literals", input: "123, 456", expectedCount: 2, expectedRaw: []string{"123", "456"}, }, { name: "string literals", input: `"hello", "world"`, expectedCount: 2, expectedRaw: []string{`"hello"`, `"world"`}, }, { name: "string literals with commas inside", input: `"a,b", "c,d"`, expectedCount: 2, expectedRaw: []string{`"a,b"`, `"c,d"`}, }, { name: "mixed strings and values with commas", input: `"hello, world", 123, "foo,bar,baz"`, expectedCount: 3, expectedRaw: []string{`"hello, world"`, `123`, `"foo,bar,baz"`}, }, { name: "nil and boolean", input: "nil, true, false", expectedCount: 3, expectedRaw: []string{"nil", "true", "false"}, }, { name: "selector expressions", input: "assertions.ErrTest, pkg.Type", expectedCount: 2, expectedRaw: []string{"assertions.ErrTest", "pkg.Type"}, }, { name: "composite literals", input: "[]int{1,2,3}, []int{4,5,6}", expectedCount: 2, expectedRaw: []string{"[]int{1, 2, 3}", "[]int{4, 5, 6}"}, // go/format adds spaces }, { name: "struct literals", input: "pkg.Type{Field: value}, pkg.Type{}", expectedCount: 2, expectedRaw: []string{"pkg.Type{Field: value}", "pkg.Type{}"}, }, { name: "function calls", input: "fn(a, b), fn(c)", expectedCount: 2, expectedRaw: []string{"fn(a, b)", "fn(c)"}, }, { name: "unary expressions", input: "&x, *ptr, -123", expectedCount: 3, expectedRaw: []string{"&x", "*ptr", "-123"}, }, { name: "type assertions", input: "x.(int), y.(string)", expectedCount: 2, expectedRaw: []string{"x.(int)", "y.(string)"}, }, { name: "empty string", input: "", expectedCount: 0, expectedRaw: []string{}, }, { name: "whitespace only", input: " ", expectedCount: 0, expectedRaw: []string{}, }, { name: "single value", input: "123", expectedCount: 1, expectedRaw: []string{"123"}, }, { name: "invalid expression", input: "123 +", expectedCount: 1, expectedRaw: []string{"123 +"}, shouldParsePerVal: []bool{false}, }, { name: "unclosed paren", input: "(123", expectedCount: 1, expectedRaw: []string{"(123"}, shouldParsePerVal: []bool{false}, }, { name: "invalid characters", input: "@#$%", expectedCount: 1, expectedRaw: []string{"@#$%"}, shouldParsePerVal: []bool{false}, }, { name: "mixed valid and invalid", input: "123, @invalid", expectedCount: 1, // Parse fails for whole input, returns single error expectedRaw: []string{"123, @invalid"}, shouldParsePerVal: []bool{false}, // Entire input fails to parse }, }) } type parseWithFileSetTestCase struct { name string input string expectedCount int shouldError bool // true if ParseExprWithFileSet should return an error } func parseExprWithFileSetTestCases() iter.Seq[parseWithFileSetTestCase] { return slices.Values([]parseWithFileSetTestCase{ { name: "valid expressions", input: "123, 456", expectedCount: 2, shouldError: false, }, { name: "invalid expression returns error", input: "123 +", expectedCount: 1, shouldError: true, }, { name: "first error is returned", input: "123, @invalid, 789", expectedCount: 1, // Parse fails for whole input shouldError: true, }, { name: "empty input", input: "", expectedCount: 0, shouldError: false, }, }) } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/matchers.go000066400000000000000000000031751520301377500277740ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "fmt" "regexp" "strings" ) // StartSectionFunc creates a matcher function for section headers. // // A section header can be: // // - "# Placeholder" or "# Placeholders" (markdown-style) // - "Placeholder:" or "Placeholders:" (simple colon-style) // // Matching is case-insensitive. func StartSectionFunc(placeholder string) func(string) bool { sectionRex := regexp.MustCompile( fmt.Sprintf(`(?i)^\s*(#\s+%[1]ss?\s*$|%[1]ss?\s*:)`, regexp.QuoteMeta(placeholder)), ) return sectionRex.MatchString } // StartValueFunc creates a matcher function for key-value lines. // // A value line has the format: "placeholder: value" or "placeholders: value" (plural form). // Returns the value part (everything after the colon) and true if matched. // Matching is case-insensitive. func StartValueFunc(placeholder string) func(string) (string, bool) { valueRex := regexp.MustCompile( fmt.Sprintf(`(?i)^\s*%[1]ss?\s*:\s*`, regexp.QuoteMeta(placeholder)), ) return func(text string) (string, bool) { if valueRex.MatchString(text) { return valueRex.ReplaceAllLiteralString(text, ""), true } return "", false } } // StartAnotherSection checks if a line starts a new section. // // A line starts a new section if it either: // // - Starts with a capital letter and ends with a colon (e.g., "Usage:") // - Starts with "# " (markdown header) func StartAnotherSection(text string) bool { return len(text) > 0 && (text[0] >= 'A' && text[0] <= 'Z' && strings.HasSuffix(text, ":")) || strings.HasPrefix(text, "# ") } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/matchers_test.go000066400000000000000000000101251520301377500310240ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "fmt" "iter" "slices" "testing" ) func TestStartSectionFunc(t *testing.T) { t.Parallel() for c := range startSectionCases() { t.Run(fmt.Sprintf("%q matches %q", c.placeholder, c.input), func(t *testing.T) { t.Parallel() matcher := StartSectionFunc(c.placeholder) result := matcher(c.input) if result != c.expected { t.Errorf("StartSectionFunc(%q)(%q) = %v, expected %v", c.placeholder, c.input, result, c.expected) } }) } } func TestStartValueFunc(t *testing.T) { t.Parallel() for c := range startValueCases() { t.Run(fmt.Sprintf("%q extracts from %q", c.placeholder, c.input), func(t *testing.T) { t.Parallel() matcher := StartValueFunc(c.placeholder) value, ok := matcher(c.input) if ok != c.expectedOk { t.Errorf("StartValueFunc(%q)(%q) ok = %v, expected %v", c.placeholder, c.input, ok, c.expectedOk) } if value != c.expectedValue { t.Errorf("StartValueFunc(%q)(%q) value = %q, expected %q", c.placeholder, c.input, value, c.expectedValue) } }) } } func TestStartAnotherSection(t *testing.T) { t.Parallel() for c := range startAnotherSectionCases() { t.Run(c.input, func(t *testing.T) { t.Parallel() result := StartAnotherSection(c.input) if result != c.expected { t.Errorf("StartAnotherSection(%q) = %v, expected %v", c.input, result, c.expected) } }) } } /* Test case iterators */ type startSectionCase struct { placeholder string input string expected bool } func startSectionCases() iter.Seq[startSectionCase] { return slices.Values([]startSectionCase{ // Markdown-style headers {"Example", "# Example", true}, {"Example", "# Examples", true}, {"Example", "# EXAMPLES", true}, {"Example", " # Examples", true}, {"Example", "# Examples", true}, // Colon-style headers {"Example", "Example:", true}, {"Example", "Examples:", true}, {"Example", "EXAMPLES:", true}, {"Example", " Examples:", true}, {"Example", "Examples :", true}, // Domain examples {"Domain", "# Domains", true}, {"Domain", "Domains:", true}, // Negative cases {"Example", "# Usage", false}, {"Example", "Examples", false}, {"Example", "# Examples are here", false}, {"Example", "", false}, {"Example", "Some Examples:", false}, // Special characters in placeholder {"Pre-commit", "# Pre-commits", true}, {"Test(s)", "# Test(s)", true}, }) } type startValueCase struct { placeholder string input string expectedValue string expectedOk bool } func startValueCases() iter.Seq[startValueCase] { return slices.Values([]startValueCase{ // Basic matching {"success", "success: 123, 456", "123, 456", true}, {"success", "SUCCESS: value", "value", true}, {"failure", " failure: value", "value", true}, {"panic", "panic : value", "value", true}, {"domain", "domain:", "", true}, {"note", "note: Error: something failed", "Error: something failed", true}, // Domain tag examples {"domain", "domain: string", "string", true}, {"maintainer", "maintainer: @username", "@username", true}, // Negative cases {"success", "failure: value", "", false}, {"success", "success value", "", false}, {"success", "", "", false}, {"success", "not success: value", "", false}, // Special characters {"pre-commit", "pre-commit: hook", "hook", true}, }) } type startAnotherSectionCase struct { input string expected bool } func startAnotherSectionCases() iter.Seq[startAnotherSectionCase] { return slices.Values([]startAnotherSectionCase{ // Capital letter + colon (section headers) {"Examples:", true}, {"Usage:", true}, {"Notes:", true}, {"Parameters:", true}, // Markdown headers {"# Section", true}, {"# Header", true}, {"# examples", true}, // markdown header still matches // Negative cases {"examples:", false}, {"Examples", false}, {"Examples are here", false}, {"", false}, {"This is a description line.", false}, {"success: 123", false}, {" Examples:", false}, // indented section header }) } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/relocation.go000066400000000000000000000137611520301377500303270ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "go/ast" "go/format" "go/token" "strings" "unicode" "github.com/go-openapi/testify/codegen/v2/internal/model" ) const ( // Exception: PanicTestFunc should always use "assertions" package. panicTestFunc = "PanicTestFunc" assertions = "assertions" ) // RelocateTestValue relocates a test value expression from one package to another. // // It walks the AST and: // // 1. Changes qualified identifiers: fromPkg.X → toPkg.X (except exceptions) // 2. Qualifies unqualified exported identifiers: X → toPkg.X // // Exception: PanicTestFunc always stays with assertions package. func RelocateTestValue(tv model.TestValue, fromPkg, toPkg string) model.TestValue { if tv.Expr == nil || fromPkg == "" || toPkg == "" { return tv } // Handle root-level unqualified identifier specially expr := tv.Expr if ident, ok := expr.(*ast.Ident); ok && shouldQualify(ident) { // Special case for PanicTestFunc: always use assertions pkg := toPkg if ident.Name == panicTestFunc { pkg = assertions } expr = &ast.SelectorExpr{ X: &ast.Ident{Name: pkg}, Sel: ident, } } // Walk the AST and modify selectors in place ast.Inspect(expr, func(n ast.Node) bool { if node, ok := n.(*ast.SelectorExpr); ok { // Handle qualified identifiers: pkg.Identifier relocateSelectorExpr(node, fromPkg, toPkg) } return true }) // Second pass: qualify unqualified exported identifiers in sub-expressions qualifyUnqualifiedIdents(expr, toPkg) // Re-render the modified AST to string fset := token.NewFileSet() var buf strings.Builder if err := format.Node(&buf, fset, expr); err != nil { return model.TestValue{ Raw: tv.Raw, Expr: tv.Expr, Error: err, } } return model.TestValue{ Raw: buf.String(), Expr: expr, Error: nil, } } // relocateSelectorExpr handles selector expressions: // 1. Package selectors: assertions.CollectT → assert.CollectT // 2. Method calls on unqualified types: ErrTest.Error() → assert.ErrTest.Error() func relocateSelectorExpr(sel *ast.SelectorExpr, fromPkg, toPkg string) { ident, ok := sel.X.(*ast.Ident) if !ok { return } // Exception: PanicTestFunc always uses assertions package if sel.Sel.Name == panicTestFunc { ident.Name = assertions return } // Case 1: Package selector (assertions.CollectT → assert.CollectT) if ident.Name == fromPkg { ident.Name = toPkg return } // Case 2: Unqualified identifier in selector (ErrTest.Error() → assert.ErrTest.Error()) // Need to wrap the identifier in a SelectorExpr if shouldQualify(ident) { sel.X = qualifyIdent(ident, toPkg) } } // qualifyUnqualifiedIdents adds package qualifiers to unqualified exported identifiers. // // This recursively processes all type expressions in the AST, replacing unqualified // identifiers with qualified SelectorExpr nodes. func qualifyUnqualifiedIdents(expr ast.Expr, pkg string) { ast.Inspect(expr, func(n ast.Node) bool { switch parent := n.(type) { case *ast.CallExpr: // Qualify function name if ident, ok := parent.Fun.(*ast.Ident); ok && shouldQualify(ident) { parent.Fun = qualifyIdent(ident, pkg) } // Qualify arguments for i, arg := range parent.Args { if ident, ok := arg.(*ast.Ident); ok && shouldQualify(ident) { parent.Args[i] = qualifyIdent(ident, pkg) } } case *ast.CompositeLit: // Qualify type in composite literal parent.Type = qualifyTypeExpr(parent.Type, pkg) case *ast.TypeAssertExpr: // Qualify type in type assertion parent.Type = qualifyTypeExpr(parent.Type, pkg) case *ast.StarExpr: // Qualify type inside pointer: *T → *pkg.T parent.X = qualifyTypeExpr(parent.X, pkg) case *ast.ArrayType: // Qualify element type in array/slice: []T → []pkg.T parent.Elt = qualifyTypeExpr(parent.Elt, pkg) case *ast.MapType: // Qualify key and value types in map: map[K]V → map[pkg.K]pkg.V parent.Key = qualifyTypeExpr(parent.Key, pkg) parent.Value = qualifyTypeExpr(parent.Value, pkg) case *ast.ChanType: // Qualify type in channel: chan T → chan pkg.T parent.Value = qualifyTypeExpr(parent.Value, pkg) case *ast.Field: // Qualify type in function parameters, struct fields, etc. parent.Type = qualifyTypeExpr(parent.Type, pkg) } return true }) } // qualifyTypeExpr recursively qualifies unqualified identifiers in a type expression. func qualifyTypeExpr(typ ast.Expr, pkg string) ast.Expr { if typ == nil { return nil } switch t := typ.(type) { case *ast.Ident: if shouldQualify(t) { return qualifyIdent(t, pkg) } return t case *ast.StarExpr: t.X = qualifyTypeExpr(t.X, pkg) return t case *ast.ArrayType: t.Elt = qualifyTypeExpr(t.Elt, pkg) return t case *ast.MapType: t.Key = qualifyTypeExpr(t.Key, pkg) t.Value = qualifyTypeExpr(t.Value, pkg) return t case *ast.ChanType: t.Value = qualifyTypeExpr(t.Value, pkg) return t default: return typ } } // qualifyIdent wraps an identifier in a SelectorExpr with the appropriate package. func qualifyIdent(ident *ast.Ident, pkg string) *ast.SelectorExpr { // Special case for PanicTestFunc: always use assertions targetPkg := pkg if ident.Name == panicTestFunc { targetPkg = assertions } return &ast.SelectorExpr{ X: &ast.Ident{Name: targetPkg}, Sel: ident, } } // shouldQualify returns true if an identifier should be qualified with a package name. // // Qualifies exported identifiers (uppercase start) except: // - Language keywords and built-ins func shouldQualify(ident *ast.Ident) bool { name := ident.Name if name == "" { return false } // Don't qualify if not exported (lowercase start) if !unicode.IsUpper(rune(name[0])) { return false } // Don't qualify language built-ins switch name { case "bool", "byte", "complex64", "complex128", "error", "float32", "float64", "int", "int8", "int16", "int32", "int64", "rune", "string", "uint", "uint8", "uint16", "uint32", "uint64", "uintptr": return false } return true } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/relocation_test.go000066400000000000000000000133701520301377500313620ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "iter" "slices" "testing" ) // TestRelocateTestValue tests AST-based relocation of test values. func TestRelocateTestValue(t *testing.T) { t.Parallel() for c := range relocateTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() // Parse the input values := ParseTestValues(c.input) if len(values) != 1 { t.Fatalf("Expected 1 value, got %d", len(values)) } original := values[0] if original.Error != nil { t.Fatalf("Parse error: %v", original.Error) } // Relocate relocated := RelocateTestValue(original, c.fromPkg, c.toPkg) if relocated.Error != nil { t.Fatalf("Relocation error: %v", relocated.Error) } if relocated.Raw != c.expected { t.Errorf("Expected %q, got %q", c.expected, relocated.Raw) } }) } } /* Test case iterators */ type relocateTestCase struct { name string input string fromPkg string toPkg string expected string } func relocateTestCases() iter.Seq[relocateTestCase] { return slices.Values([]relocateTestCase{ { name: "qualified selector: assertions.ErrTest → assert.ErrTest", input: "assertions.ErrTest", fromPkg: "assertions", toPkg: "assert", expected: "assert.ErrTest", }, { name: "qualified selector: assertions.CollectT → assert.CollectT", input: "assertions.CollectT", fromPkg: "assertions", toPkg: "assert", expected: "assert.CollectT", }, { name: "unqualified type: ErrTest → assert.ErrTest", input: "ErrTest", fromPkg: "assertions", toPkg: "assert", expected: "assert.ErrTest", // Should qualify unqualified exported ident }, { name: "unqualified type: CollectT → assert.CollectT", input: "CollectT", fromPkg: "assertions", toPkg: "assert", expected: "assert.CollectT", }, { name: "exception: assertions.PanicTestFunc stays assertions.PanicTestFunc", input: "assertions.PanicTestFunc", fromPkg: "assertions", toPkg: "assert", expected: "assertions.PanicTestFunc", // Exception: keep assertions }, { name: "exception: PanicTestFunc → assertions.PanicTestFunc", input: "PanicTestFunc", fromPkg: "assertions", toPkg: "assert", expected: "assertions.PanicTestFunc", // Exception even when unqualified }, { name: "composite literal: assertions.ErrTest{} → assert.ErrTest{}", input: "assertions.ErrTest{}", fromPkg: "assertions", toPkg: "assert", expected: "assert.ErrTest{}", }, { name: "composite literal unqualified: CollectT{} → assert.CollectT{}", input: "CollectT{}", fromPkg: "assertions", toPkg: "assert", expected: "assert.CollectT{}", }, { name: "function call: assertions.NewT() → assert.NewT()", input: "assertions.NewT()", fromPkg: "assertions", toPkg: "assert", expected: "assert.NewT()", }, { name: "function call unqualified: NewT() → assert.NewT()", input: "NewT()", fromPkg: "assertions", toPkg: "assert", expected: "assert.NewT()", }, { name: "lowercase identifier unchanged: true", input: "true", fromPkg: "assertions", toPkg: "assert", expected: "true", // Don't qualify lowercase/built-ins }, { name: "nil unchanged", input: "nil", fromPkg: "assertions", toPkg: "assert", expected: "nil", }, { name: "string literal unchanged", input: `"hello"`, fromPkg: "assertions", toPkg: "assert", expected: `"hello"`, }, { name: "integer literal unchanged", input: "123", fromPkg: "assertions", toPkg: "assert", expected: "123", }, { name: "complex expression with multiple relocations", input: "func(t assertions.TestingT, fn assertions.PanicTestFunc) bool { return true }", fromPkg: "assertions", toPkg: "assert", expected: "func(t assert.TestingT, fn assertions.PanicTestFunc) bool {\n\treturn true\n}", // go/format adds newlines }, { name: "pointer type: *CollectT → *assert.CollectT", input: "*CollectT", fromPkg: "assertions", toPkg: "assert", expected: "*assert.CollectT", }, { name: "function with pointer parameter: func(c *CollectT) → func(c *assert.CollectT)", input: "func(c *CollectT) { }", fromPkg: "assertions", toPkg: "assert", expected: "func(c *assert.CollectT) {\n}", // go/format adds newline }, { name: "slice type: []TestingT → []assert.TestingT", input: "[]TestingT", fromPkg: "assertions", toPkg: "assert", expected: "[]assert.TestingT", }, { name: "map type: map[string]TestingT → map[string]assert.TestingT", input: "map[string]TestingT", fromPkg: "assertions", toPkg: "assert", expected: "map[string]assert.TestingT", }, { name: "channel type: chan CollectT → chan assert.CollectT", input: "chan CollectT", fromPkg: "assertions", toPkg: "assert", expected: "chan assert.CollectT", }, { name: "method call on unqualified variable: ErrTest.Error() → assert.ErrTest.Error()", input: "ErrTest.Error()", fromPkg: "assertions", toPkg: "assert", expected: "assert.ErrTest.Error()", }, { name: "field access on unqualified variable: Config.Value → assert.Config.Value", input: "Config.Value", fromPkg: "assertions", toPkg: "assert", expected: "assert.Config.Value", }, { name: "function call with unqualified argument: panic(ErrTest) → panic(assert.ErrTest)", input: "panic(ErrTest)", fromPkg: "assertions", toPkg: "assert", expected: "panic(assert.ErrTest)", }, }) } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/tags.go000066400000000000000000000076361520301377500271320ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "regexp" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // ParseTaggedComments extracts tagged comments from text. // // Recognized tags: // // - domain: - single-line domain tag // - maintainer: - multi-line maintainer note // - note: - multi-line note // - mention: - single-line mention // - opposite: - name of the logical opposite assertion (if any) // // Multi-line tags continue until the next tagged line or end of text. func ParseTaggedComments(text string) []model.ExtraComment { const ( domainPrefix = "domain" maintainerPrefix = "maintainer" notePrefix = "note" mentionPrefix = "mention" oppositePrefix = "opposite" ) inValue := false startValueDomain := StartValueFunc(domainPrefix) startValueMaintainer := StartValueFunc(maintainerPrefix) startValueNote := StartValueFunc(notePrefix) startValueMention := StartValueFunc(mentionPrefix) startValueOpposite := StartValueFunc(oppositePrefix) startTaggedValue := func(line string) (key string, val string, tag model.CommentTag, multiline bool, ok bool) { val, ok = startValueDomain(line) if ok { return val, "", model.CommentTagDomain, false, true } val, ok = startValueMaintainer(line) if ok { return "", val, model.CommentTagMaintainer, true, true } val, ok = startValueNote(line) if ok { return "", val, model.CommentTagNote, true, true } val, ok = startValueMention(line) if ok { return "", val, model.CommentTagMention, false, true } val, ok = startValueOpposite(line) if ok { return "", val, model.CommentTagOpposite, false, true } return "", "", model.CommentTagNone, false, false } result := make([]model.ExtraComment, 0) for line := range strings.SplitSeq(text, "\n") { key, val, tag, multiline, ok := startTaggedValue(line) if inValue && len(result) > 0 && len(line) > 0 && !ok { result[len(result)-1].Text += "\n" + line continue } if ok { if multiline { inValue = true } result = append(result, model.ExtraComment{ Tag: tag, Key: key, Text: val, }) continue } inValue = false } return result } // ParseDomainDescriptions extracts domain descriptions from package-level comment text. // // It looks for a "Domains:" or "Domain:" section with entries like: // // - string: assertions on strings // - json: assertions on JSON documents // // The domain name is normalized to lowercase. func ParseDomainDescriptions(text string) []model.ExtraComment { const allocatedDomains = 20 result := make([]model.ExtraComment, 0, allocatedDomains) const ( domainPrefix = "domain" // e.g. domain:, domains:, # domain, # Domains rexCapturedGroups = 2 ) domainRex := regexp.MustCompile(`^\s*(?:(?:[\-\*])|(?:\d+\.))\s+(\w+)\s*:\s+(.+)`) startDomainSection := StartSectionFunc(domainPrefix) inDomainSection := false for line := range strings.SplitSeq(text, "\n") { if startDomainSection(line) { inDomainSection = true continue } if !inDomainSection { continue } if StartAnotherSection(line) { break } // parse domain descriptions matches := domainRex.FindStringSubmatch(line) if len(matches) < rexCapturedGroups+1 { continue } domain := strings.ToLower(matches[1]) description := strings.TrimSpace(matches[2]) result = append(result, model.ExtraComment{ Tag: model.CommentTagDomainDescription, Key: domain, Text: description, }) } return result } // DomainFromExtraComments retrieves the domain from preparsed extra (tagged) comment. func DomainFromExtraComments(taggedComments []model.ExtraComment) string { for _, taggedComment := range taggedComments { if taggedComment.Tag == model.CommentTagDomain { return taggedComment.Key } } return "" } go-openapi-testify-c10ca71/codegen/internal/scanner/comments-parser/tags_test.go000066400000000000000000000266331520301377500301670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "iter" "slices" "testing" "github.com/go-openapi/testify/codegen/v2/internal/model" ) func TestParseTaggedComments(t *testing.T) { t.Parallel() for c := range parseTaggedCommentsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := ParseTaggedComments(c.input) if len(result) != len(c.expected) { t.Fatalf("ParseTaggedComments() returned %d comments, expected %d\nGot: %+v\nExpected: %+v", len(result), len(c.expected), result, c.expected) } for i, expected := range c.expected { got := result[i] if got.Tag != expected.Tag { t.Errorf("Comment[%d].Tag = %v, expected %v", i, got.Tag, expected.Tag) } if got.Key != expected.Key { t.Errorf("Comment[%d].Key = %q, expected %q", i, got.Key, expected.Key) } if got.Text != expected.Text { t.Errorf("Comment[%d].Text = %q, expected %q", i, got.Text, expected.Text) } } }) } } func TestParseDomainDescriptions(t *testing.T) { t.Parallel() for c := range parseDomainDescriptionsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := ParseDomainDescriptions(c.input) if len(result) != len(c.expected) { t.Fatalf("ParseDomainDescriptions() returned %d domains, expected %d\nGot: %+v\nExpected: %+v", len(result), len(c.expected), result, c.expected) } for i, expected := range c.expected { got := result[i] if got.Tag != expected.Tag { t.Errorf("Domain[%d].Tag = %v, expected %v", i, got.Tag, expected.Tag) } if got.Key != expected.Key { t.Errorf("Domain[%d].Key = %q, expected %q", i, got.Key, expected.Key) } if got.Text != expected.Text { t.Errorf("Domain[%d].Text = %q, expected %q", i, got.Text, expected.Text) } } }) } } func TestDomainFromExtraComments(t *testing.T) { t.Parallel() for c := range domainFromExtraCommentsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := DomainFromExtraComments(c.comments) if result != c.expected { t.Errorf("DomainFromExtraComments() = %q, expected %q", result, c.expected) } }) } } /* Test case iterators */ type parseTaggedCommentsCase struct { name string input string expected []model.ExtraComment } func parseTaggedCommentsCases() iter.Seq[parseTaggedCommentsCase] { return slices.Values([]parseTaggedCommentsCase{ { name: "single domain tag", input: `Some description. domain: string More text.`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomain, Key: "string", Text: ""}, }, }, { name: "multiple domain tags", input: `domain: string domain: boolean`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomain, Key: "string", Text: ""}, {Tag: model.CommentTagDomain, Key: "boolean", Text: ""}, }, }, { name: "maintainer tag single line", input: `maintainer: @username`, expected: []model.ExtraComment{ {Tag: model.CommentTagMaintainer, Key: "", Text: "@username"}, }, }, { name: "maintainer tag multiline", input: `maintainer: This function needs review. It has some edge cases that should be documented. The implementation could be improved.`, expected: []model.ExtraComment{ {Tag: model.CommentTagMaintainer, Key: "", Text: "This function needs review.\nIt has some edge cases that should be documented.\nThe implementation could be improved."}, }, }, { name: "note tag multiline", input: `note: This is important. This continues the note. And this too.`, expected: []model.ExtraComment{ {Tag: model.CommentTagNote, Key: "", Text: "This is important.\nThis continues the note.\nAnd this too."}, }, }, { name: "mention tag", input: `mention: Related to issue #123`, expected: []model.ExtraComment{ {Tag: model.CommentTagMention, Key: "", Text: "Related to issue #123"}, }, }, { name: "mixed tags", input: `domain: error maintainer: @alice This needs attention. note: Check performance impact. mention: See also HTTPError`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomain, Key: "error", Text: ""}, {Tag: model.CommentTagMaintainer, Key: "", Text: "@alice\nThis needs attention."}, {Tag: model.CommentTagNote, Key: "", Text: "Check performance impact."}, {Tag: model.CommentTagMention, Key: "", Text: "See also HTTPError"}, }, }, { name: "multiline interrupted by new tag", input: `maintainer: First maintainer note. This continues. note: New section starts here. And continues.`, expected: []model.ExtraComment{ {Tag: model.CommentTagMaintainer, Key: "", Text: "First maintainer note.\nThis continues."}, {Tag: model.CommentTagNote, Key: "", Text: "New section starts here.\nAnd continues."}, }, }, { name: "case insensitive tags", input: `DOMAIN: string Maintainer: @bob NOTE: Important info`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomain, Key: "string", Text: ""}, {Tag: model.CommentTagMaintainer, Key: "", Text: "@bob"}, {Tag: model.CommentTagNote, Key: "", Text: "Important info"}, }, }, { name: "no tags", input: `Just regular text without any tags.`, expected: []model.ExtraComment{}, }, { name: "empty input", input: "", expected: []model.ExtraComment{}, }, { name: "tags with extra whitespace", input: ` domain : string maintainer : @user `, expected: []model.ExtraComment{ {Tag: model.CommentTagDomain, Key: "string", Text: ""}, {Tag: model.CommentTagMaintainer, Key: "", Text: "@user "}, }, }, { name: "multiline ends with empty line", input: `note: First line. Second line. domain: string`, expected: []model.ExtraComment{ {Tag: model.CommentTagNote, Key: "", Text: "First line.\nSecond line."}, {Tag: model.CommentTagDomain, Key: "string", Text: ""}, }, }, }) } type parseDomainDescriptionsCase struct { name string input string expected []model.ExtraComment } func parseDomainDescriptionsCases() iter.Seq[parseDomainDescriptionsCase] { return slices.Values([]parseDomainDescriptionsCase{ { name: "domains section with bullet points", input: `Package assertions provides testing utilities. Domains: - string: assertions on strings - json: assertions on JSON documents - yaml: assertions on YAML documents`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "string", Text: "assertions on strings"}, {Tag: model.CommentTagDomainDescription, Key: "json", Text: "assertions on JSON documents"}, {Tag: model.CommentTagDomainDescription, Key: "yaml", Text: "assertions on YAML documents"}, }, }, { name: "domains section with asterisks", input: `Domains: * boolean: true/false assertions * number: numeric comparisons`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "boolean", Text: "true/false assertions"}, {Tag: model.CommentTagDomainDescription, Key: "number", Text: "numeric comparisons"}, }, }, { name: "domains section with numbered list", input: `Domains: 1. error: error handling assertions 2. http: HTTP response assertions`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "error", Text: "error handling assertions"}, {Tag: model.CommentTagDomainDescription, Key: "http", Text: "HTTP response assertions"}, }, }, { name: "markdown header style", input: `# Domains - file: file system assertions - time: time-based assertions`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "file", Text: "file system assertions"}, {Tag: model.CommentTagDomainDescription, Key: "time", Text: "time-based assertions"}, }, }, { name: "case insensitive domain header", input: `DOMAINS: - panic: panic assertions`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "panic", Text: "panic assertions"}, }, }, { name: "singular form", input: `Domain: - collection: collection assertions`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "collection", Text: "collection assertions"}, }, }, { name: "stops at next section", input: `Domains: - string: string assertions - json: JSON assertions Usage: - boolean: this should not be parsed`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "string", Text: "string assertions"}, {Tag: model.CommentTagDomainDescription, Key: "json", Text: "JSON assertions"}, }, }, { name: "normalizes domain names to lowercase", input: `Domains: - String: string assertions - JSON: JSON assertions`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "string", Text: "string assertions"}, {Tag: model.CommentTagDomainDescription, Key: "json", Text: "JSON assertions"}, }, }, { name: "handles extra whitespace", input: `Domains: - string : assertions on strings `, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "string", Text: "assertions on strings"}, }, }, { name: "no domains section", input: `This package has no domains section.`, expected: []model.ExtraComment{}, }, { name: "domains section with no entries", input: `Domains: Usage follows.`, expected: []model.ExtraComment{}, }, { name: "skip non-matching lines", input: `Domains: Some intro text. - string: string assertions Not a domain line. - json: JSON assertions`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "string", Text: "string assertions"}, {Tag: model.CommentTagDomainDescription, Key: "json", Text: "JSON assertions"}, }, }, { name: "description with colons", input: `Domains: - http: HTTP assertions: status codes, headers, bodies`, expected: []model.ExtraComment{ {Tag: model.CommentTagDomainDescription, Key: "http", Text: "HTTP assertions: status codes, headers, bodies"}, }, }, }) } type domainFromExtraCommentsCase struct { name string comments []model.ExtraComment expected string } func domainFromExtraCommentsCases() iter.Seq[domainFromExtraCommentsCase] { return slices.Values([]domainFromExtraCommentsCase{ { name: "find domain tag", comments: []model.ExtraComment{ {Tag: model.CommentTagNote, Key: "", Text: "Some note"}, {Tag: model.CommentTagDomain, Key: "string", Text: ""}, {Tag: model.CommentTagMaintainer, Key: "", Text: "@user"}, }, expected: "string", }, { name: "first domain wins", comments: []model.ExtraComment{ {Tag: model.CommentTagDomain, Key: "boolean", Text: ""}, {Tag: model.CommentTagDomain, Key: "string", Text: ""}, }, expected: "boolean", }, { name: "no domain tag", comments: []model.ExtraComment{ {Tag: model.CommentTagNote, Key: "", Text: "Note"}, {Tag: model.CommentTagMaintainer, Key: "", Text: "@user"}, }, expected: "", }, { name: "empty comments", comments: []model.ExtraComment{}, expected: "", }, { name: "nil comments", comments: nil, expected: "", }, { name: "domain at end", comments: []model.ExtraComment{ {Tag: model.CommentTagMaintainer, Key: "", Text: "@user"}, {Tag: model.CommentTagNote, Key: "", Text: "Note"}, {Tag: model.CommentTagDomain, Key: "error", Text: ""}, }, expected: "error", }, }) } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/000077500000000000000000000000001520301377500243375ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/comments/bridge.go000066400000000000000000000034771520301377500261350ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package comments import ( "go/ast" "go/token" "go/types" "golang.org/x/tools/go/ast/astutil" "golang.org/x/tools/go/packages" ) // BuildFilesMap constructs a lookup index to help bridge token position vs [ast.File]. // // This creates a map from [token.File] (file metadata) to [ast.File] (syntax tree), // enabling O(1) lookup after O(n) construction. func BuildFilesMap(pkg *packages.Package) map[*token.File]*ast.File { filesMap := make(map[*token.File]*ast.File, len(pkg.Syntax)) for _, astFile := range pkg.Syntax { tokenFile := pkg.Fset.File(astFile.Pos()) // o(log n) lookup filesMap[tokenFile] = astFile } return filesMap } // resolveDeclFromObject finds the AST declaration for a types.Object using position-based lookup. // // Returns both the declaration and the file containing it, or (nil, nil) if not found. func resolveDeclFromObject(object types.Object, fileSet *token.FileSet, filesMap map[*token.File]*ast.File) (ast.Decl, *ast.File) { pos := object.Pos() if !pos.IsValid() { return nil, nil } tokenFile := fileSet.File(pos) // o(log n) lookup if tokenFile == nil { return nil, nil } astFile := filesMap[tokenFile] // o(1) lookup if astFile == nil { return nil, nil } path, _ := astutil.PathEnclosingInterval(astFile, pos, pos) for _, node := range path { declaration, ok := node.(ast.Decl) if !ok { continue } return declaration, astFile } return nil, astFile } // findCommentInFile finds a comment group within a position range. func findCommentInFile(astFile *ast.File, minPos, maxPos token.Pos) *ast.CommentGroup { for _, commentGroup := range astFile.Comments { pos := commentGroup.Pos() if pos > minPos && pos < maxPos { return commentGroup } } return nil } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/doc.go000066400000000000000000000013321520301377500254320ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package comments provides utilities for extracting comments from Go source code. // // This package handles the bridge between go/types (semantic view) and go/[ast] (syntactic view) // using position-based lookup. It extracts: // // - Package-level comments and copyright headers // - Doc comments for functions, types, constants, and variables // - Body comments from within functions and type declarations // - Domain descriptions from package comments // // The Extractor type maintains the context needed for position-based lookup, // including the file set, files map, and declaration cache. package comments go-openapi-testify-c10ca71/codegen/internal/scanner/comments/extractor.go000066400000000000000000000174411520301377500267100ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package comments import ( "go/ast" "go/token" "go/types" "regexp" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" parser "github.com/go-openapi/testify/codegen/v2/internal/scanner/comments-parser" ) var copyrightRex = regexp.MustCompile(`(?i)copyright`) type cacheEntry struct { Decl ast.Decl File *ast.File } // Extractor extracts comments from Go source code using position-based AST/types bridging. type Extractor struct { syntaxPackage []*ast.File fileSet *token.FileSet filesMap map[*token.File]*ast.File declCache map[types.Object]cacheEntry } // New creates a comment extractor. // // The syntaxPackage provides access to all AST files in the package. // The fileSet and filesMap enable position-based lookup to bridge [types.Object] to [ast.Decl]. func New(syntaxPackage []*ast.File, fileSet *token.FileSet, filesMap map[*token.File]*ast.File) *Extractor { return &Extractor{ syntaxPackage: syntaxPackage, fileSet: fileSet, filesMap: filesMap, declCache: make(map[types.Object]cacheEntry), } } // ExtractPackageComments finds the package-level comment that describes the source package. // // This is a single comment group from the first file that has package documentation. // Never returns nil. func (e *Extractor) ExtractPackageComments() *ast.CommentGroup { for _, file := range e.syntaxPackage { if file.Doc == nil { continue } return file.Doc } // safeguard: never returns nil return &ast.CommentGroup{} } // ExtractCopyright scans source files and expects that at least one contains // a leading comment line with the string "copyright". // // Never returns nil. func (e *Extractor) ExtractCopyright() *ast.CommentGroup { FILE: for _, file := range e.syntaxPackage { if len(file.Comments) == 0 { continue } // Check all comment groups before the package declaration // (needed for files with build tags, where copyright is not the first group) for _, group := range file.Comments { // Skip comment groups that come after the package declaration if group.Pos() >= file.Package { continue FILE } // Check if this group contains a copyright notice for _, line := range group.List { if copyrightRex.MatchString(line.Text) { return group } } } } // safeguard: never returns nil return &ast.CommentGroup{} } // ExtractComments retrieves the docstring for an object. // // Never returns nil: if no comment is present an empty but valid [ast.CommentGroup] is returned. // // NOTE: comment prioritization rule is: // // 1. GenDecl.Doc = leading comment for the entire declaration block // 2. Spec.Doc = individual doc for that specific spec func (e *Extractor) ExtractComments(object types.Object) *ast.CommentGroup { entry, ok := e.declCache[object] if ok { // short-circuit already resolved declarations if entry.Decl == nil { return &ast.CommentGroup{} } return extractCommentFromDecl(entry.Decl, object) } declaration, astFile := resolveDeclFromObject(object, e.fileSet, e.filesMap) if declaration == nil { e.declCache[object] = cacheEntry{} return &ast.CommentGroup{} } e.declCache[object] = cacheEntry{ Decl: declaration, File: astFile, } return extractCommentFromDecl(declaration, object) } // ExtractExtraComments retrieves body comments (inside functions or type declarations). // // Returns nil if no extra comments are found. func (e *Extractor) ExtractExtraComments(object types.Object) []model.ExtraComment { entry, ok := e.declCache[object] if ok { // short-circuit already resolved declarations if entry.Decl == nil { return nil } return extractExtraCommentsFromDecl(entry.Decl, entry.File, object) } declaration, astFile := resolveDeclFromObject(object, e.fileSet, e.filesMap) if declaration == nil || astFile == nil { e.declCache[object] = cacheEntry{} return nil } e.declCache[object] = cacheEntry{ Decl: declaration, File: astFile, } return extractExtraCommentsFromDecl(declaration, astFile, object) } // ExtractDomainDescriptions extracts domain descriptions from package-level comments. // // Returns nil if no domain descriptions are found. func (e *Extractor) ExtractDomainDescriptions() []model.ExtraComment { // find the documented file (e.g. doc.go) that contains package-level comments var documentedFile *ast.File for _, file := range e.syntaxPackage { if file.Doc == nil { continue } documentedFile = file break } if documentedFile == nil { return nil } // collect all comment text from the documented file var allComments strings.Builder for _, group := range documentedFile.Comments { if group != nil { allComments.WriteString(group.Text()) allComments.WriteString("\n") } } return parser.ParseDomainDescriptions(allComments.String()) } func extractCommentFromDecl(declaration ast.Decl, object types.Object) *ast.CommentGroup { switch typedDeclaration := declaration.(type) { case *ast.GenDecl: // const, var, type declaration for _, spec := range typedDeclaration.Specs { comment := extractCommentFromTypeSpec(typedDeclaration, spec, object) if comment != nil { return comment } } case *ast.FuncDecl: // a function if typedDeclaration.Doc != nil { return typedDeclaration.Doc } } return &ast.CommentGroup{} } func extractExtraCommentsFromDecl(declaration ast.Decl, astFile *ast.File, object types.Object) []model.ExtraComment { var comments *ast.CommentGroup switch typedDeclaration := declaration.(type) { case *ast.GenDecl: // const, var, type declaration for _, spec := range typedDeclaration.Specs { comments = extractExtraCommentFromTypeSpec(astFile, spec, object) if comments != nil { break } } case *ast.FuncDecl: // a function bodyStart := typedDeclaration.Body.Lbrace bodyEnd := typedDeclaration.Body.Rbrace comments = findCommentInFile(astFile, bodyStart, bodyEnd) } if comments == nil { return nil } return parser.ParseTaggedComments(comments.Text()) } func extractCommentFromTypeSpec(typedDeclaration *ast.GenDecl, spec ast.Spec, object types.Object) *ast.CommentGroup { switch typedSpec := spec.(type) { case *ast.TypeSpec: // for TypeSpec, check if it matches (though usually only 1 spec per GenDecl for types) if typedSpec.Name.Name != object.Name() { return nil } if typedSpec.Doc != nil { return typedSpec.Doc } // return Doc, checking both GenDecl.Doc and TypeSpec.Doc if typedDeclaration.Doc != nil { return typedDeclaration.Doc } case *ast.ValueSpec: for _, ident := range typedSpec.Names { if ident.Name != object.Name() { return nil } // return Doc, checking both GenDecl.Doc and ValueSpec.Doc if typedDeclaration.Doc != nil { return typedDeclaration.Doc } if typedSpec.Doc != nil { return typedSpec.Doc } } } return nil } func extractExtraCommentFromTypeSpec(astFile *ast.File, spec ast.Spec, object types.Object) *ast.CommentGroup { typedSpec, isTypeSpec := spec.(*ast.TypeSpec) if !isTypeSpec { // case *ast.ValueSpec: // not supported for now: only types return nil } // for TypeSpec, check if it matches (though usually only 1 spec per GenDecl for types) if typedSpec.Name.Name != object.Name() { return nil } var bodyStart, bodyEnd token.Pos switch kind := typedSpec.Type.(type) { case *ast.StructType: bodyStart = kind.Fields.Opening // position of '{' bodyEnd = kind.Fields.Closing // position of '}' case *ast.InterfaceType: bodyStart = kind.Methods.Opening // position of '{' bodyEnd = kind.Methods.Closing // position of '}' // other type declarations (maps, functions, ...): not supported for now } if bodyEnd > bodyStart { comments := findCommentInFile(astFile, bodyStart, bodyEnd) if comments != nil { return comments } } return nil } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/extractor_test.go000066400000000000000000000314521520301377500277450ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package comments import ( "strings" "sync" "testing" "golang.org/x/tools/go/packages" "github.com/go-openapi/testify/codegen/v2/internal/model" ) //nolint:gochecknoglobals // we use [sync.Once] to cache the parsed test source and run the tests faster var ( loadTestPackageOnce sync.Once cachedTestPackage *packages.Package ) // loadTestPackage loads the testdata/assertions package for testing. func loadTestPackage(t *testing.T) (*packages.Package, *Extractor) { t.Helper() loadTestPackageOnce.Do(func() { cfg := &packages.Config{ Mode: packages.NeedName | packages.NeedFiles | packages.NeedCompiledGoFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo, BuildFlags: []string{"-tags", "integrationtest"}, } pkgs, err := packages.Load(cfg, "./testdata/assertions") if err != nil { t.Fatalf("Failed to load test package: %v", err) } if len(pkgs) != 1 { t.Fatalf("Expected 1 package, got %d", len(pkgs)) } pkg := pkgs[0] if len(pkg.Errors) > 0 { t.Fatalf("Package has errors: %v", pkg.Errors) } cachedTestPackage = pkg }) pkg := cachedTestPackage filesMap := BuildFilesMap(pkg) extractor := New(pkg.Syntax, pkg.Fset, filesMap) return pkg, extractor } func TestNew(t *testing.T) { _, extractor := loadTestPackage(t) if extractor == nil { t.Fatal("New() returned nil") } if extractor.syntaxPackage == nil { t.Error("syntaxPackage is nil") } if extractor.fileSet == nil { t.Error("fileSet is nil") } if extractor.filesMap == nil { t.Error("filesMap is nil") } if extractor.declCache == nil { t.Error("declCache is nil") } } func TestExtractPackageComments(t *testing.T) { _, extractor := loadTestPackage(t) comments := extractor.ExtractPackageComments() if comments == nil { t.Fatal("ExtractPackageComments() returned nil") } text := comments.Text() if text == "" { t.Error("Package comments are empty") } // Should contain package description from doc.go if !strings.Contains(text, "Package assertions") { t.Errorf("Package comments should mention 'Package assertions', got: %s", text) } } func TestExtractCopyright(t *testing.T) { _, extractor := loadTestPackage(t) copyright := extractor.ExtractCopyright() if copyright == nil { t.Fatal("ExtractCopyright() returned nil") } text := copyright.Text() if text == "" { t.Error("Copyright is empty") } // Should contain copyright notice if !strings.Contains(strings.ToLower(text), "copyright") { t.Errorf("Copyright should contain 'copyright', got: %s", text) } // Should contain SPDX header if !strings.Contains(text, "SPDX-FileCopyrightText") { t.Errorf("Copyright should contain SPDX header, got: %s", text) } } func TestExtractDomainDescriptions(t *testing.T) { _, extractor := loadTestPackage(t) domains := extractor.ExtractDomainDescriptions() if domains == nil { t.Fatal("ExtractDomainDescriptions() returned nil") } if len(domains) == 0 { t.Error("Expected domain descriptions, got none") } // Verify we found expected domains expectedDomains := map[string]bool{ "boolean": false, "collection": false, "compare": false, "equal": false, "error": false, } for _, domain := range domains { if domain.Tag != model.CommentTagDomainDescription { t.Errorf("Expected tag %v, got %v", model.CommentTagDomainDescription, domain.Tag) } if _, exists := expectedDomains[domain.Key]; exists { expectedDomains[domain.Key] = true } // Verify description is not empty if domain.Text == "" { t.Errorf("Domain %q has empty description", domain.Key) } } // Check that we found at least some expected domains foundCount := 0 for domain, found := range expectedDomains { if found { foundCount++ } else { t.Logf("Domain %q not found (may be OK if doc.go doesn't list it)", domain) } } if foundCount == 0 { t.Error("Expected to find at least one domain from the expected list") } } func TestExtractComments(t *testing.T) { pkg, extractor := loadTestPackage(t) // Find the "True" function scope := pkg.Types.Scope() trueObj := scope.Lookup("True") if trueObj == nil { t.Fatal("Could not find 'True' function in package scope") } comments := extractor.ExtractComments(trueObj) if comments == nil { t.Fatal("ExtractComments() returned nil") } text := comments.Text() if text == "" { t.Error("Function comments are empty") } // Should contain function description if !strings.Contains(text, "True asserts") { t.Errorf("Expected function description, got: %s", text) } // Should contain Examples section if !strings.Contains(text, "Examples:") { t.Errorf("Expected Examples section, got: %s", text) } } func TestExtractComments_Caching(t *testing.T) { pkg, extractor := loadTestPackage(t) scope := pkg.Types.Scope() trueObj := scope.Lookup("True") if trueObj == nil { t.Fatal("Could not find 'True' function") } // First call - should populate cache comments1 := extractor.ExtractComments(trueObj) // Second call - should use cache comments2 := extractor.ExtractComments(trueObj) // Should return same content if comments1.Text() != comments2.Text() { t.Error("Cached comments differ from original") } // Verify cache was populated if _, cached := extractor.declCache[trueObj]; !cached { t.Error("Object not found in cache after ExtractComments()") } } func TestExtractExtraComments(t *testing.T) { pkg, extractor := loadTestPackage(t) // Find the "True" function scope := pkg.Types.Scope() trueObj := scope.Lookup("True") if trueObj == nil { t.Fatal("Could not find 'True' function in package scope") } extraComments := extractor.ExtractExtraComments(trueObj) // The True function has "// Domain: boolean" inside it if len(extraComments) == 0 { t.Fatal("Expected extra comments (domain tag), got none") } // Should have parsed the domain tag foundDomain := false for _, comment := range extraComments { if comment.Tag == model.CommentTagDomain && comment.Key == "boolean" { foundDomain = true break } } if !foundDomain { t.Errorf("Expected to find domain tag with key 'boolean', got: %+v", extraComments) } } func TestExtractComments_NoComment(t *testing.T) { pkg, extractor := loadTestPackage(t) // Try to get comments for a type that exists but might not have comments scope := pkg.Types.Scope() // Look for the "T" type (testing interface) tObj := scope.Lookup("T") if tObj == nil { // If T doesn't exist in testdata, that's fine - skip this test t.Skip("Type 'T' not found in test package") } comments := extractor.ExtractComments(tObj) // Should never return nil, even if no comments if comments == nil { t.Error("ExtractComments() returned nil for object without comments") } } func TestExtractComments_MultipleFunctions(t *testing.T) { pkg, extractor := loadTestPackage(t) scope := pkg.Types.Scope() testCases := []struct { funcName string shouldContain string }{ { funcName: "True", shouldContain: "True asserts", }, { funcName: "False", shouldContain: "False asserts", }, } for _, tc := range testCases { t.Run(tc.funcName, func(t *testing.T) { obj := scope.Lookup(tc.funcName) if obj == nil { t.Fatalf("Could not find function %q", tc.funcName) } comments := extractor.ExtractComments(obj) text := comments.Text() if !strings.Contains(text, tc.shouldContain) { t.Errorf("Expected comment to contain %q, got: %s", tc.shouldContain, text) } }) } } func TestExtractComments_TypeDeclaration(t *testing.T) { pkg, extractor := loadTestPackage(t) // Find the "CollectT" type scope := pkg.Types.Scope() collectTObj := scope.Lookup("CollectT") if collectTObj == nil { t.Fatal("Could not find 'CollectT' type in package scope") } comments := extractor.ExtractComments(collectTObj) if comments == nil { t.Fatal("ExtractComments() returned nil for type") } text := comments.Text() if text == "" { t.Error("Type comments are empty") } // Should contain type description if !strings.Contains(text, "CollectT") { t.Errorf("Expected type description, got: %s", text) } } func TestExtractExtraComments_TypeWithBodyComments(t *testing.T) { pkg, extractor := loadTestPackage(t) // Find the "CollectT" type which has maintainer comments inside scope := pkg.Types.Scope() collectTObj := scope.Lookup("CollectT") if collectTObj == nil { t.Fatal("Could not find 'CollectT' type") } extraComments := extractor.ExtractExtraComments(collectTObj) // The CollectT struct has "// Maintainer:" comments inside it if len(extraComments) == 0 { t.Fatal("Expected extra comments (maintainer tag), got none") } // Should have parsed the maintainer tag foundMaintainer := false for _, comment := range extraComments { if comment.Tag == model.CommentTagMaintainer { foundMaintainer = true // Verify maintainer text is not empty if comment.Text == "" { t.Error("Maintainer comment has empty text") } // Should mention the maintainer notes if !strings.Contains(comment.Text, "runtime.GoExit") && !strings.Contains(comment.Text, "deprecated") { t.Logf("Maintainer comment: %q", comment.Text) } break } } if !foundMaintainer { t.Errorf("Expected to find maintainer tag, got: %+v", extraComments) } } func TestExtractExtraComments_Caching(t *testing.T) { pkg, extractor := loadTestPackage(t) scope := pkg.Types.Scope() collectTObj := scope.Lookup("CollectT") if collectTObj == nil { t.Fatal("Could not find 'CollectT' type") } // First call - should populate cache extraComments1 := extractor.ExtractExtraComments(collectTObj) // Second call - should use cache extraComments2 := extractor.ExtractExtraComments(collectTObj) // Should return same number of comments if len(extraComments1) != len(extraComments2) { t.Error("Cached extra comments differ from original") } // Verify cache was populated if _, cached := extractor.declCache[collectTObj]; !cached { t.Error("Object not found in cache after ExtractExtraComments()") } } func TestExtractComments_VariableDeclaration(t *testing.T) { pkg, extractor := loadTestPackage(t) // Find the "ErrTest" variable scope := pkg.Types.Scope() errTestObj := scope.Lookup("ErrTest") if errTestObj == nil { t.Fatal("Could not find 'ErrTest' variable in package scope") } comments := extractor.ExtractComments(errTestObj) if comments == nil { t.Fatal("ExtractComments() returned nil for variable") } text := comments.Text() if text == "" { t.Error("Variable comments are empty") } // Should contain variable description if !strings.Contains(text, "ErrTest") { t.Errorf("Expected variable description, got: %s", text) } if !strings.Contains(text, "error instance") { t.Errorf("Expected description 'error instance', got: %s", text) } } func TestExtractExtraComments_InterfaceWithBodyComments(t *testing.T) { pkg, extractor := loadTestPackage(t) // Find the "T" interface which has maintainer comments inside scope := pkg.Types.Scope() tObj := scope.Lookup("T") if tObj == nil { t.Fatal("Could not find 'T' interface") } extraComments := extractor.ExtractExtraComments(tObj) // Note: Comments attached to interface methods (not floating) may not be // extracted as "extra comments" - they're associated with the method. // This test verifies the ExtractExtraComments path for interfaces, // even if the result is empty for interfaces with only method-attached comments. if extraComments == nil { // nil is acceptable for interfaces without floating comments t.Log("No extra comments found for interface (expected if comments are method-attached)") return } // If we do have comments, verify they're parsed correctly if len(extraComments) > 0 { foundMaintainer := false for _, comment := range extraComments { if comment.Tag == model.CommentTagMaintainer { foundMaintainer = true // Verify maintainer text is not empty if comment.Text == "" { t.Error("Maintainer comment has empty text") } t.Logf("Found maintainer comment: %q", comment.Text) break } } if !foundMaintainer { t.Logf("Extra comments found but no maintainer tag: %+v", extraComments) } } } func TestBuildFilesMap(t *testing.T) { pkg, _ := loadTestPackage(t) filesMap := BuildFilesMap(pkg) if filesMap == nil { t.Fatal("BuildFilesMap() returned nil") } if len(filesMap) == 0 { t.Error("BuildFilesMap() returned empty map") } // Should have entries for each syntax file if len(filesMap) != len(pkg.Syntax) { t.Errorf("Expected %d entries in filesMap, got %d", len(pkg.Syntax), len(filesMap)) } // Verify map structure for tokenFile, astFile := range filesMap { if tokenFile == nil { t.Error("Found nil token.File key in filesMap") } if astFile == nil { t.Error("Found nil ast.File value in filesMap") } } } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/000077500000000000000000000000001520301377500261505ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/assertions/000077500000000000000000000000001520301377500303425ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/assertions/boolean.go000066400000000000000000000015361520301377500323150ustar00rootroot00000000000000//go:build integrationtest // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions // True asserts that the specified value is true. // // Usage: // // assertions.True(t, myBool) // // Examples: // // success: 1 == 1 // failure: 1 == 0 func True(t T, value bool, msgAndArgs ...any) bool { // Domain: boolean if !value { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Should be true", msgAndArgs...) } return true } // False asserts that the specified value is false. // // Usage: // // assertions.False(t, myBool) // // Examples: // // success: 1 == 0 // failure: 1 == 1 func False(t T, value bool, msgAndArgs ...any) bool { // Domain: boolean if value { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Should be false", msgAndArgs...) } return true } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/assertions/condition.go000066400000000000000000000140101520301377500326530ustar00rootroot00000000000000//go:build integrationtest // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "runtime" "time" ) // Condition uses a Comparison to assert a complex condition. // // Examples: // // success: func() bool { return true } // failure: func() bool { return false } func Condition(t T, comp Comparison, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } result := comp() if !result { Fail(t, "Condition failed!", msgAndArgs...) } return result } // Eventually asserts that given condition will be met in waitFor time, // periodically checking target function each tick. // // assert.Eventually(t, func() bool { return true; }, time.Second, 10*time.Millisecond) // // Examples: // // success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond func Eventually(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } ch := make(chan bool, 1) checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() var tickC <-chan time.Time // Check the condition once first on the initial call. go checkCond() for { select { case <-timer.C: return Fail(t, "Condition never satisfied", msgAndArgs...) case <-tickC: tickC = nil go checkCond() case v := <-ch: if v { return true } tickC = ticker.C } } } // CollectT implements the [T] interface and collects all errors. type CollectT struct { // Maintainer: // 1. we should verify the the use of runtime.GoExit is correct in this context. // 2. we should remove the deprecated methods. // // A slice of errors. Non-nil slice denotes a failure. // If it's non-nil but len(c.errors) == 0, this is also a failure // obtained by direct c.FailNow() call. errors []error } // Helper is like [testing.T.Helper] but does nothing. func (CollectT) Helper() {} // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...any) { c.errors = append(c.errors, fmt.Errorf(format, args...)) } // FailNow stops execution by calling runtime.Goexit. func (c *CollectT) FailNow() { c.fail() runtime.Goexit() } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. func (*CollectT) Reset() { panic("Reset() is deprecated") } // Deprecated: That was a method for internal usage that should not have been published. Now just panics. func (*CollectT) Copy(T) { panic("Copy() is deprecated") } func (c *CollectT) fail() { if !c.failed() { c.errors = []error{} // Make it non-nil to mark a failure. } } func (c *CollectT) failed() bool { return c.errors != nil } // EventuallyWithT asserts that given condition will be met in waitFor time, // periodically checking target function each tick. In contrast to Eventually, // it supplies a CollectT to the condition function, so that the condition // function can use the CollectT to call other assertions. // The condition is considered "met" if no errors are raised in a tick. // The supplied CollectT collects all errors from one tick (if there are any). // If the condition is not met before waitFor, the collected errors of // the last tick are copied to t. // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // assert.EventuallyWithT(t, func(c *assert.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assert.True(c, externalValue, "expected 'externalValue' to be true") // }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false") // // Examples: // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond // failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond func EventuallyWithT(t T, condition func(collect *CollectT), waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } var lastFinishedTickErrs []error ch := make(chan *CollectT, 1) checkCond := func() { collect := new(CollectT) defer func() { ch <- collect }() condition(collect) } timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() var tickC <-chan time.Time // Check the condition once first on the initial call. go checkCond() for { select { case <-timer.C: for _, err := range lastFinishedTickErrs { t.Errorf("%v", err) } return Fail(t, "Condition never satisfied", msgAndArgs...) case <-tickC: tickC = nil go checkCond() case collect := <-ch: if !collect.failed() { return true } // Keep the errors from the last ended condition, so that they can be copied to t if timeout is reached. lastFinishedTickErrs = collect.errors tickC = ticker.C } } } // Never asserts that the given condition doesn't satisfy in waitFor time, // periodically checking the target function each tick. // // assert.Never(t, func() bool { return false; }, time.Second, 10*time.Millisecond) // // Examples: // // success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond func Never(t T, condition func() bool, waitFor time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } ch := make(chan bool, 1) checkCond := func() { ch <- condition() } timer := time.NewTimer(waitFor) defer timer.Stop() ticker := time.NewTicker(tick) defer ticker.Stop() var tickC <-chan time.Time // Check the condition once first on the initial call. go checkCond() for { select { case <-timer.C: return true case <-tickC: tickC = nil go checkCond() case v := <-ch: if v { return Fail(t, "Condition satisfied", msgAndArgs...) } tickC = ticker.C } } } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/assertions/doc.go000066400000000000000000000021161520301377500314360ustar00rootroot00000000000000//go:build integrationtest // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package assertions holds the internal implementation // of all the helper functions exposed by testify. // // It serves as a base to develop and test testify, // whereas the publicly exposed API (in packages assert and require) // is generated. // // # Domains // // - boolean: asserting boolean values // - collection: asserting slices and maps // - compare: comparing ordered values // - condition: expressing assertions using conditions // - equal: asserting two things are equal // - error: asserting errors // - file: asserting os files // - helpers: extra helpers that are not assertions // - http: asserting http response and body // - json: asserting JSON documents // - number: asserting numbers // - order: asserting how collection are ordered // - panic: asserting a panic behavior // - string: asserting strings // - time: asserting times and durations // - type: asserting types rather than values // - yaml: asserting yaml documents package assertions go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/assertions/helpers.go000066400000000000000000000013511520301377500323330ustar00rootroot00000000000000//go:build integrationtest // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions // T is the minimal interface for test assertions (stub for testdata). type T interface { // Maintainers: we should clarify when we need to impose FailNow on the interface and when we don't. Errorf(format string, args ...any) FailNow() } // H is the interface for test helpers (stub for testdata). type H interface { Helper() } // Comparison is a function type for custom assertions (stub for testdata). type Comparison func() bool // Fail reports a failure (stub for testdata). func Fail(t T, failureMessage string, msgAndArgs ...any) bool { t.Errorf(failureMessage) return false } go-openapi-testify-c10ca71/codegen/internal/scanner/comments/testdata/assertions/vars.go000066400000000000000000000007171520301377500316510ustar00rootroot00000000000000//go:build integrationtest // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import "errors" // 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 // the test code more readable. var ErrTest = errors.New("assert.ErrTest general error for testing") go-openapi-testify-c10ca71/codegen/internal/scanner/doc.go000066400000000000000000000005161520301377500236100ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package scanner exposes a golang source [Scanner]. // // The [Scanner] scans the internal/assertions package. // // It extracts all testify helpers methods, types, and // package-level variables and constants. package scanner go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/000077500000000000000000000000001520301377500256225ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/doc.go000066400000000000000000000007231520301377500267200ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package parser scans a package of go source to extract testable examples. // // The outcome of the parser is an index of testable examples by exported function or type. // // Provided testable examples are a structure that may be rendered as go code. // // This package is freely inspired by go team's pkgsite tool (github.com/golang/pkgsite). package parser go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/helpers_test.go000066400000000000000000000313411520301377500306540ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "go/ast" "go/parser" "go/token" "iter" "slices" "strings" "testing" ) func TestExampleFuncName(t *testing.T) { t.Parallel() for c := range exampleFuncNameCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := exampleFuncName(c.input) if got != c.expected { t.Errorf("exampleFuncName(%q) = %q, expected %q", c.input, got, c.expected) } }) } } type exampleFuncNameCase struct { name string input string expected string } func exampleFuncNameCases() iter.Seq[exampleFuncNameCase] { return slices.Values([]exampleFuncNameCase{ { name: "empty string", input: "", expected: "", }, { name: "simple function name", input: "Equal", expected: "Equal", }, { name: "function with suffix", input: "Equal_basic", expected: "Equal", }, { name: "underscore followed by uppercase is part of name", input: "T_Method", expected: "T_Method", }, { name: "multiple underscores with suffix", input: "Foo_Bar_baz", expected: "Foo_Bar", }, { name: "trailing underscore without next char", input: "Foo_", expected: "Foo_", }, { name: "single character", input: "X", expected: "X", }, { name: "underscore only", input: "_", expected: "_", }, { name: "suffix starts immediately after underscore", input: "Contains_slice", expected: "Contains", }, { name: "multiple suffixes only first split applies", input: "HTTPStatusCode_redirect_permanent", expected: "HTTPStatusCode", }, }) } func TestExampleSuffix(t *testing.T) { t.Parallel() for c := range exampleSuffixCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := exampleSuffix(c.exampleName, c.funcName) if got != c.expected { t.Errorf("exampleSuffix(%q, %q) = %q, expected %q", c.exampleName, c.funcName, got, c.expected) } }) } } type exampleSuffixCase struct { name string exampleName string funcName string expected string } func exampleSuffixCases() iter.Seq[exampleSuffixCase] { return slices.Values([]exampleSuffixCase{ { name: "no suffix", exampleName: "Equal", funcName: "Equal", expected: "", }, { name: "simple suffix", exampleName: "Equal_basic", funcName: "Equal", expected: "basic", }, { name: "suffix with uppercase name", exampleName: "Foo_Bar_baz", funcName: "Foo_Bar", expected: "baz", }, { name: "multi-word suffix", exampleName: "Contains_with_custom_message", funcName: "Contains", expected: "with_custom_message", }, }) } func TestStripOutputComments(t *testing.T) { t.Parallel() for c := range stripOutputCommentsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := stripOutputComments(c.input) if got != c.expected { t.Errorf("stripOutputComments() =\n%q\nexpected:\n%q", got, c.expected) } }) } } type stripOutputCommentsCase struct { name string input string expected string } func stripOutputCommentsCases() iter.Seq[stripOutputCommentsCase] { return slices.Values([]stripOutputCommentsCase{ { name: "no output comment", input: "x := 1\ny := 2", expected: "x := 1\ny := 2", }, { name: "trailing output comment", input: "fmt.Println(x)\n// Output: 42", expected: "fmt.Println(x)", }, { name: "lowercase output comment", input: "fmt.Println(x)\n// output: 42", expected: "fmt.Println(x)", }, { name: "output comment followed by blank lines", input: "fmt.Println(x)\n// Output: 42\n\n", expected: "fmt.Println(x)", }, { name: "only trailing blanks stripped", input: "// Output: first\nfmt.Println(x)\n// Output: 42", expected: "// Output: first\nfmt.Println(x)", }, { name: "empty string", input: "", expected: "", }, { name: "only output comment", input: "// Output: 42", expected: "", }, { name: "multiple trailing output lines", input: "x := 1\n// Output:\n// output: result", expected: "x := 1", }, }) } func TestExtractFuncBody(t *testing.T) { t.Parallel() for c := range extractFuncBodyCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := extractFuncBody(c.input) if got != c.expected { t.Errorf("extractFuncBody() =\n%q\nexpected:\n%q", got, c.expected) } }) } } type extractFuncBodyCase struct { name string input string expected string } func extractFuncBodyCases() iter.Seq[extractFuncBodyCase] { return slices.Values([]extractFuncBodyCase{ { name: "standard synthetic file", input: "package p\n\nfunc f() {\n\tx := 1\n\ty := 2\n}\n", expected: "x := 1\ny := 2", }, { name: "no function marker", input: "package p\n\nfunc g() {\n\tx := 1\n}\n", expected: "package p\n\nfunc g() {\n\tx := 1\n}", }, { name: "empty function body", input: "package p\n\nfunc f() {\n}\n", expected: "", }, { name: "single statement", input: "package p\n\nfunc f() {\n\treturn\n}\n", expected: "return", }, { name: "no closing brace", input: "package p\n\nfunc f() {\n\tx := 1\n", expected: "x := 1", }, }) } func TestExtractWholeFileBody(t *testing.T) { t.Parallel() for c := range extractWholeFileBodyCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := extractWholeFileBody(c.input, c.funcName) if got != c.expected { t.Errorf("extractWholeFileBody() =\n%q\nexpected:\n%q", got, c.expected) } }) } } type extractWholeFileBodyCase struct { name string input string funcName string expected string } func extractWholeFileBodyCases() iter.Seq[extractWholeFileBodyCase] { return slices.Values([]extractWholeFileBodyCase{ { name: "strips package imports and renames main", input: `package main import "fmt" func main() { fmt.Println("hello") }`, funcName: "ExampleFoo", expected: `func ExampleFoo() { fmt.Println("hello") }`, }, { name: "strips grouped imports", input: `package main import ( "fmt" "strings" ) func main() { fmt.Println(strings.ToUpper("hello")) }`, funcName: "ExampleBar", expected: `func ExampleBar() { fmt.Println(strings.ToUpper("hello")) }`, }, { name: "preserves supporting declarations", input: `package main import "fmt" type myStruct struct { value int } func main() { s := myStruct{value: 42} fmt.Println(s.value) } func helper() int { return 1 }`, funcName: "ExampleBaz", expected: `type myStruct struct { value int } func ExampleBaz() { s := myStruct{value: 42} fmt.Println(s.value) } func helper() int { return 1 }`, }, { name: "handles single-line import", input: `package main import "fmt" func main() { fmt.Println("ok") }`, funcName: "ExampleSingle", expected: `func ExampleSingle() { fmt.Println("ok") }`, }, { name: "empty input", input: "", funcName: "ExampleEmpty", expected: "", }, }) } func TestIsWholeFileExample(t *testing.T) { t.Parallel() for c := range isWholeFileExampleCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := isWholeFileExample(c.file) if got != c.expected { t.Errorf("isWholeFileExample() = %v, expected %v", got, c.expected) } }) } } type isWholeFileExampleCase struct { name string file *ast.File expected bool } func isWholeFileExampleCases() iter.Seq[isWholeFileExampleCase] { return slices.Values([]isWholeFileExampleCase{ { name: "nil file", file: nil, expected: false, }, { name: "main only with imports", file: parseFile(`package main; import "fmt"; func main() { fmt.Println() }`), expected: false, }, { name: "has helper function", file: parseFile(`package main; func main() {}; func helper() {}`), expected: true, }, { name: "has type declaration", file: parseFile(`package main; type Foo struct{}; func main() {}`), expected: true, }, { name: "has const declaration", file: parseFile(`package main; const x = 1; func main() {}`), expected: true, }, { name: "has var declaration", file: parseFile(`package main; var x int; func main() {}`), expected: true, }, { name: "only main and imports", file: parseFile(`package main; import "os"; func main() { _ = os.Args }`), expected: false, }, }) } func TestCollectExportedSymbols(t *testing.T) { t.Parallel() for c := range collectExportedSymbolsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() exported := make(map[string]bool) file := parseFile(c.src) collectExportedSymbols(file, exported) for _, name := range c.want { if !exported[name] { t.Errorf("expected %q to be exported, but it was not collected", name) } } for _, name := range c.dontWant { if exported[name] { t.Errorf("expected %q to NOT be collected, but it was", name) } } if len(exported) != len(c.want) { t.Errorf("collected %d symbols, expected %d: %v", len(exported), len(c.want), exported) } }) } } type collectExportedSymbolsCase struct { name string src string want []string dontWant []string } func collectExportedSymbolsCases() iter.Seq[collectExportedSymbolsCase] { return slices.Values([]collectExportedSymbolsCase{ { name: "exported functions", src: `package p; func Exported() {}; func another() {}`, want: []string{"Exported"}, dontWant: []string{"another"}, }, { name: "methods are skipped", src: `package p; type T struct{}; func (t T) Method() {}; func Standalone() {}`, want: []string{"T", "Standalone"}, dontWant: []string{"Method"}, }, { name: "exported types", src: `package p; type Public struct{}; type private struct{}`, want: []string{"Public"}, dontWant: []string{"private"}, }, { name: "mixed functions and types", src: `package p; func Alpha() {}; type Beta int; func gamma() {}; type delta struct{}`, want: []string{"Alpha", "Beta"}, dontWant: []string{"gamma", "delta"}, }, { name: "no exported symbols", src: `package p; func hidden() {}; type secret struct{}`, want: nil, dontWant: []string{"hidden", "secret"}, }, { name: "multiple types in one declaration", src: `package p; type ( Foo int; Bar string; baz float64 )`, want: []string{"Foo", "Bar"}, dontWant: []string{"baz"}, }, }) } // parseFile is a test helper that parses a Go source string into an *ast.File. func parseFile(src string) *ast.File { fset := token.NewFileSet() file, err := parser.ParseFile(fset, "test.go", src, 0) if err != nil { // Only used in test case setup; a parse error indicates a broken test case. panic("parseFile: " + err.Error()) } return file } func TestRenderBody_FormatsCode(t *testing.T) { t.Parallel() // Parse a real example from the assert package and verify Render // produces output that: // 1. does not contain outer braces // 2. does not contain "// Output:" lines // 3. is non-empty for c := range renderBodyPropertyCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() rendered := c.example.Render() if rendered == "" { t.Fatal("Render() returned empty string") } t.Run("should not start with outer braces", func(t *testing.T) { trimmed := strings.TrimSpace(rendered) if strings.HasPrefix(trimmed, "{") { t.Errorf("got:\n%s", rendered) } }) t.Run("should not contain output comments", func(t *testing.T) { if strings.Contains(rendered, "// Output:") { t.Errorf("got:\n%s", rendered) } }) t.Run("should not contain package clause", func(t *testing.T) { if strings.Contains(rendered, "package ") { t.Errorf("got:\n%s", rendered) } }) }) } } type renderBodyPropertyCase struct { name string example TestableExample } func renderBodyPropertyCases() iter.Seq[renderBodyPropertyCase] { // Build simple AST examples programmatically to avoid depending on the // full package loader in unit tests. fset := token.NewFileSet() src := `package p func Example() { x := 1 _ = x // Output: 1 } ` file, err := parser.ParseFile(fset, "ex_test.go", src, parser.ParseComments) if err != nil { panic("renderBodyPropertyCases: " + err.Error()) } fn, ok := file.Decls[0].(*ast.FuncDecl) if !ok { panic("renderBodyPropertyCases: expected *ast.FuncDecl") } return slices.Values([]renderBodyPropertyCase{ { name: "simple body example", example: TestableExample{ Name: "Example", code: fn.Body, fset: fset, }, }, }) } go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/options.go000066400000000000000000000010101520301377500276340ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser // Option configures the [Extractor]. type Option func(*Extractor) // WithWorkDir sets the working directory for package resolution. func WithWorkDir(dir string) Option { return func(e *Extractor) { e.dir = dir } } // WithBuildTags sets build tags for package loading (e.g. "integrationtest"). func WithBuildTags(tags ...string) Option { return func(e *Extractor) { e.buildTags = tags } } go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/parser.go000066400000000000000000000302731520301377500274520ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser import ( "bytes" "fmt" "go/ast" "go/doc" "go/format" "go/printer" "go/token" "strings" "unicode" "golang.org/x/tools/go/packages" ) const tabWidth = 4 // Extractor explores a go package (including test code) and looks for testable examples. type Extractor struct { dir string pkg string buildTags []string } // New [Extractor] for a given source package to be scanned. // // pkg is an import path or a relative pattern (e.g., "./assert") resolved from the working directory. func New(pkg string, opts ...Option) *Extractor { e := &Extractor{ pkg: pkg, dir: ".", } for _, opt := range opts { opt(e) } return e } // Parse source code including test code. // // Builds an index of exported symbols (functions and types). // // Attaches all identified testable examples to the index. // // It may fail if the code doesn't compile. func (e *Extractor) Parse() (Examples, error) { cfg := &packages.Config{ Dir: e.dir, Mode: packages.NeedName | packages.NeedSyntax | packages.NeedCompiledGoFiles, Tests: true, } if len(e.buildTags) > 0 { cfg.BuildFlags = []string{"-tags", strings.Join(e.buildTags, ",")} } pkgs, err := packages.Load(cfg, e.pkg) if err != nil { return nil, err } if len(pkgs) == 0 { return nil, fmt.Errorf("package not found: %s", e.pkg) } // packages.Load reports resolution failures as package-level errors. for _, pkg := range pkgs { for _, pkgErr := range pkg.Errors { return nil, fmt.Errorf("loading %s: %s", pkg.ID, pkgErr.Msg) } } // With Tests: true, packages.Load returns: // - the production package (ID = import path) // - internal test variant (ID contains "[", Name = pkg name) // - external test variant (ID contains "[", Name = pkg_test) var ( exported = make(map[string]bool) testFiles []*ast.File fset *token.FileSet ) for _, pkg := range pkgs { if fset == nil { fset = pkg.Fset } isTestVariant := strings.Contains(pkg.ID, "[") // From the production package, collect exported symbol names // (functions and types, so examples for types are linked too). if !isTestVariant { for _, file := range pkg.Syntax { collectExportedSymbols(file, exported) } } // From test variant packages, collect _test.go files. if isTestVariant { for i, file := range pkg.Syntax { if strings.HasSuffix(pkg.CompiledGoFiles[i], "_test.go") { testFiles = append(testFiles, file) } } } } if len(testFiles) == 0 { return make(Examples), nil } // Extract examples using go/doc. docExamples := doc.Examples(testFiles...) // Build the index: match examples to exported symbols. index := make(Examples) for _, ex := range docExamples { funcName := exampleFuncName(ex.Name) if funcName == "" { continue // package-level example, skip } if !exported[funcName] { continue // no matching exported symbol } te := TestableExample{ Name: ex.Name, Suffix: exampleSuffix(ex.Name, funcName), Doc: ex.Doc, Output: ex.Output, WholeFile: isWholeFileExample(ex.Play), code: ex.Code, play: ex.Play, fset: fset, } index[funcName] = append(index[funcName], te) } return index, nil } // collectExportedSymbols walks a file's declarations and records exported function // and type names into the provided set. func collectExportedSymbols(file *ast.File, exported map[string]bool) { for _, decl := range file.Decls { switch d := decl.(type) { case *ast.FuncDecl: if d.Recv != nil { continue // skip methods } if d.Name.IsExported() { exported[d.Name.Name] = true } case *ast.GenDecl: for _, spec := range d.Specs { ts, ok := spec.(*ast.TypeSpec) if !ok { continue } if ts.Name.IsExported() { exported[ts.Name.Name] = true } } } } } // exampleFuncName extracts the symbol name from a testable example name. // // Go naming convention: // // "Equal" -> "Equal" (ExampleEqual) // "Equal_basic" -> "Equal" (ExampleEqual_basic) // "T_Method" -> "T" (type method, if T is a type) // // We take the leading identifier segment: everything before the first '_' // that is followed by a lowercase letter (which marks a suffix). func exampleFuncName(name string) string { if name == "" { return "" } // Find the first '_' that separates the identifier from the suffix. // The suffix must start with a lowercase letter (Go convention). for i, r := range name { if r == '_' && i+1 < len(name) { next := rune(name[i+1]) if unicode.IsLower(next) { return name[:i] } } } return name } // exampleSuffix extracts the suffix part from a testable example name // given the already-identified function name. func exampleSuffix(name, funcName string) string { rest := strings.TrimPrefix(name, funcName) rest = strings.TrimPrefix(rest, "_") return rest } // isWholeFileExample reports whether a Play AST represents a whole-file example. // // A whole-file example has top-level declarations beyond the package clause, // imports, and the main function (i.e., supporting types, helpers, etc.). func isWholeFileExample(play *ast.File) bool { if play == nil { return false } for _, decl := range play.Decls { switch d := decl.(type) { case *ast.FuncDecl: if d.Name.Name != "main" { return true } case *ast.GenDecl: if d.Tok != token.IMPORT { return true } } } return false } // Examples is an index of [TestableExample]. // // Keys are exported symbol names (not fully qualified names). // // Each key may have 1 or several examples attached to it. // // Example: // // for function {package}.MyFunction, the key is "MyFunction". type Examples map[string][]TestableExample // TestableExample describes a go testable example and knows how to render as formatted go code. type TestableExample struct { // Name is the full example name after the "Example" prefix. // // For ExampleEqual, Name is "Equal". // For ExampleEqual_basic, Name is "Equal_basic". Name string // Suffix is the example suffix, without leading '_'. // // For ExampleEqual, Suffix is "". // For ExampleEqual_basic, Suffix is "basic". Suffix string // Doc is the doc comment on the example function. Doc string // Output is the expected output string (from "// Output:" comments). Output string // WholeFile indicates this is a whole-file example with supporting // declarations (types, helper functions) outside the example function. WholeFile bool // unexported fields for rendering code ast.Node play *ast.File fset *token.FileSet } // Render the example as a formatted go code snippet. // // For whole-file examples, the complete file is rendered (minus package and imports), // preserving all supporting declarations. // // For regular examples, only the function body is rendered. // // In both cases, the code is formatted with [format.Source]. func (x TestableExample) Render() string { // Render the full Play file as a standalone main program when available. if x.play != nil { return x.renderPlay() } return x.renderBody() // Previous routing: stripped package/imports/main scaffolding. // if x.WholeFile && x.play != nil { // return x.renderWholeFile() // } // return x.renderBody() } // renderPlay renders the Play AST as-is: // a complete runnable program with package clause, imports, and func main(). func (x TestableExample) renderPlay() string { var buf bytes.Buffer p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth} if err := p.Fprint(&buf, x.fset, x.play); err != nil { return "" } raw := buf.String() // make sure the output is well formatted formatted, err := format.Source([]byte(raw)) if err != nil { return raw } return string(formatted) } // renderBody renders the example function body only. func (x TestableExample) renderBody() string { if x.code == nil { return "" } // Print the raw AST node. var buf bytes.Buffer p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth} if err := p.Fprint(&buf, x.fset, x.code); err != nil { return "" } body := buf.String() // Strip outer braces: the Code node is a *ast.BlockStmt. body = strings.TrimSpace(body) if strings.HasPrefix(body, "{") && strings.HasSuffix(body, "}") { body = body[1 : len(body)-1] } // Remove trailing "// Output:" comment lines before formatting. body = stripOutputComments(body) // Wrap in a synthetic file so format.Source can handle formatting. synthetic := "package p\n\nfunc f() {\n" + body + "\n}\n" formatted, err := format.Source([]byte(synthetic)) if err != nil { return strings.TrimSpace(body) } return extractFuncBody(string(formatted)) } /* // renderWholeFile renders a whole-file example, stripping the package clause // and imports, and renaming "func main()" back to the example function name. func (x TestableExample) renderWholeFile() string { // Print the entire Play file. var buf bytes.Buffer p := printer.Config{Mode: printer.UseSpaces, Tabwidth: tabWidth} if err := p.Fprint(&buf, x.fset, x.play); err != nil { return "" } raw := buf.String() // Remove "// Output:" comments. raw = stripOutputComments(raw) // Format with goimports. formatted, err := imports.Process("example.go", []byte(raw), &imports.Options{ Fragment: true, FormatOnly: true, }) if err != nil { formatted = []byte(raw) } // Strip package clause and imports, rename main -> Example function. return extractWholeFileBody(string(formatted), "Example"+x.Name) } */ // extractWholeFileBody strips the package clause and import blocks from a // formatted Go file, and renames "func main()" to the given example function name. func extractWholeFileBody(src, exampleFuncName string) string { var result []string lines := strings.Split(src, "\n") inImportBlock := false for _, line := range lines { trimmed := strings.TrimSpace(line) // Skip package declaration. if strings.HasPrefix(trimmed, "package ") { continue } // Skip import blocks. if trimmed == "import (" { inImportBlock = true continue } if inImportBlock { if trimmed == ")" { inImportBlock = false } continue } // Skip single-line imports. if strings.HasPrefix(trimmed, "import ") { continue } // Rename "func main()" to the example function name. if strings.HasPrefix(trimmed, "func main()") { line = strings.Replace(line, "func main()", "func "+exampleFuncName+"()", 1) } result = append(result, line) } // Trim leading/trailing blank lines. for len(result) > 0 && strings.TrimSpace(result[0]) == "" { result = result[1:] } for len(result) > 0 && strings.TrimSpace(result[len(result)-1]) == "" { result = result[:len(result)-1] } return strings.Join(result, "\n") } // stripOutputComments removes trailing "// Output:" lines and any // blank lines that follow them at the end of the code block. func stripOutputComments(code string) string { lines := strings.Split(code, "\n") for len(lines) > 0 { trimmed := strings.TrimSpace(lines[len(lines)-1]) if trimmed == "" || strings.HasPrefix(trimmed, "// Output:") || strings.HasPrefix(trimmed, "// output:") { lines = lines[:len(lines)-1] continue } break } return strings.Join(lines, "\n") } // extractFuncBody extracts the body of "func f()" from a formatted synthetic file, // stripping the wrapper and dedenting. func extractFuncBody(src string) string { // Find "func f() {" and the closing "}". const openMarker = "func f() {" _, body, found := strings.Cut(src, openMarker) if !found { return strings.TrimSpace(src) } // Find the last "}" which closes the function. closeIdx := strings.LastIndex(body, "}") if closeIdx < 0 { return strings.TrimSpace(body) } body = body[:closeIdx] // Dedent by one tab (goimports uses tabs). lines := strings.Split(body, "\n") // Skip leading/trailing empty lines. start := 0 for start < len(lines) && strings.TrimSpace(lines[start]) == "" { start++ } end := len(lines) for end > start && strings.TrimSpace(lines[end-1]) == "" { end-- } lines = lines[start:end] // Remove one leading tab from each line (goimports indents function body by one tab). for i, line := range lines { if strings.HasPrefix(line, "\t") { lines[i] = line[1:] } } return strings.Join(lines, "\n") } go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/parser_test.go000066400000000000000000000136121520301377500305070ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package parser_test import ( "flag" "strings" "sync" "testing" parser "github.com/go-openapi/testify/codegen/v2/internal/scanner/examples-parser" ) //nolint:gochecknoglobals // it is okay to use globals for test flag and [sync.Once] cache for parsed testdata var ( render = flag.Bool("render", false, "log rendered example output") parseTestExamplesOnce sync.Once cachedTestExamples parser.Examples ) const buildTag = "integrationtest" func TestParse(t *testing.T) { t.Parallel() examples := loadTestExamples(t) if len(examples) == 0 { t.Fatal("expected examples, got none") } t.Run("Verify known function examples exist", func(t *testing.T) { for _, funcName := range []string{"Greet", "Add"} { t.Run("with "+funcName, func(t *testing.T) { exs, ok := examples[funcName] if !ok { t.Errorf("expected examples for %q, found none", funcName) return } if len(exs) == 0 { t.Errorf("expected at least one example for %q", funcName) } }) } }) t.Run("NoExample is exported but has no example", func(t *testing.T) { if _, ok := examples["NoExample"]; ok { t.Error("expected no examples for NoExample") } }) t.Run("unexported must not appear", func(t *testing.T) { if _, ok := examples["unexported"]; ok { t.Error("expected no examples for unexported") } }) if *render { t.Logf("found %d symbols with examples", len(examples)) } } func TestParse_TypeExamples(t *testing.T) { t.Parallel() examples := loadTestExamples(t) exs, ok := examples["Formatter"] if !ok { t.Fatal("expected example for type Formatter, found none") } if len(exs) != 1 { t.Fatalf("expected 1 example for Formatter, got %d", len(exs)) } if !exs[0].WholeFile { t.Error("expected WholeFile=true for Formatter") } } func TestParse_ExampleDetails(t *testing.T) { t.Parallel() examples := loadTestExamples(t) greetExamples, ok := examples["Greet"] if !ok || len(greetExamples) == 0 { t.Fatal("expected examples for Greet") } ex := greetExamples[0] if ex.Name != "Greet" { t.Errorf("expected Name=%q, got %q", "Greet", ex.Name) } if ex.Suffix != "" { t.Errorf("expected empty Suffix, got %q", ex.Suffix) } if ex.Output != "Hello, World!\n" { t.Errorf("expected Output=%q, got %q", "Hello, World!\n", ex.Output) } if ex.WholeFile { t.Error("expected WholeFile=false for ExampleGreet") } } func TestParse_SuffixedExample(t *testing.T) { t.Parallel() examples := loadTestExamples(t) addExamples, ok := examples["Add"] if !ok { t.Fatal("expected examples for Add") } // Add has two examples: ExampleAdd and ExampleAdd_negative. if len(addExamples) != 2 { t.Fatalf("expected 2 examples for Add, got %d", len(addExamples)) } var found bool for _, ex := range addExamples { if ex.Suffix == "negative" { found = true break } } if !found { t.Error("expected an example with Suffix=\"negative\" for Add") } } func TestParse_Render(t *testing.T) { t.Parallel() examples := loadTestExamples(t) greetExamples := examples["Greet"] if len(greetExamples) == 0 { t.Fatal("expected examples for Greet") } rendered := greetExamples[0].Render() if rendered == "" { t.Fatal("Render() returned empty string") } t.Run("Should contain the function call", func(t *testing.T) { if !strings.Contains(rendered, "Greet") { t.Errorf("expected rendered code to contain 'Greet', got:\n%s", rendered) } }) t.Run(`Should NOT contain "// Output:" lines`, func(t *testing.T) { if strings.Contains(rendered, "// Output:") { t.Errorf("expected rendered code to NOT contain '// Output:', got:\n%s", rendered) } }) t.Run("Should NOT have outer braces", func(t *testing.T) { trimmed := strings.TrimSpace(rendered) if strings.HasPrefix(trimmed, "{") { t.Errorf("expected rendered code without outer braces, got:\n%s", rendered) } }) if *render { t.Logf("Rendered ExampleGreet:\n%s", rendered) } } func TestParse_RenderWholeFile(t *testing.T) { t.Parallel() examples := loadTestExamples(t) exs := examples["Formatter"] if len(exs) == 0 { t.Fatal("expected example for Formatter") } rendered := exs[0].Render() if rendered == "" { t.Fatal("Render() returned empty string") } t.Run("Should contain a main function", func(t *testing.T) { if !strings.Contains(rendered, "func main()") { t.Errorf("expected rendered code to contain main function declaration, got:\n%s", rendered) } }) t.Run("Should contain the supporting type declaration", func(t *testing.T) { if !strings.Contains(rendered, "type helper struct") { t.Errorf("expected rendered code to contain 'type helper struct', got:\n%s", rendered) } }) t.Run("Should contain package clause or imports", func(t *testing.T) { if !strings.Contains(rendered, "package ") { t.Errorf("expected rendered code without package clause, got:\n%s", rendered) } if !strings.Contains(rendered, "import ") { t.Errorf("expected rendered code without imports, got:\n%s", rendered) } }) if *render { t.Logf("Rendered ExampleFormatter:\n%s", rendered) } } func TestParse_NonExistentPackage(t *testing.T) { t.Parallel() ext := parser.New("./nonexistent/package/path") _, err := ext.Parse() if err == nil { t.Error("expected error for non-existent package") } } func TestRender_NilCode(t *testing.T) { t.Parallel() ex := parser.TestableExample{} if got := ex.Render(); got != "" { t.Errorf("expected empty string for nil code, got %q", got) } } // loadTestExamples parses the testdata package once and caches the result. func loadTestExamples(t *testing.T) parser.Examples { t.Helper() parseTestExamplesOnce.Do(func() { examples, err := parser.New( "./testdata/examplespkg", parser.WithBuildTags(buildTag), ).Parse() if err != nil { t.Fatalf("Parse() error: %v", err) } cachedTestExamples = examples }) return cachedTestExamples } go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/testdata/000077500000000000000000000000001520301377500274335ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/testdata/examplespkg/000077500000000000000000000000001520301377500317535ustar00rootroot00000000000000examplespkg.go000066400000000000000000000011441520301377500345430ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/testdata/examplespkg//go:build integrationtest // Package examplespkg is a test fixture for the examples-parser unit tests. package examplespkg import "fmt" // Greet returns a greeting for the given name. func Greet(name string) string { return fmt.Sprintf("Hello, %s!", name) } // Add returns the sum of two integers. func Add(a, b int) int { return a + b } // NoExample is exported but has no testable example. func NoExample() {} func unexported() {} //nolint:unused // fixture: verifies unexported symbols are skipped // Formatter is an exported type with a whole-file example. type Formatter struct { Prefix string } examplespkg_test.go000066400000000000000000000004241520301377500356020ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/testdata/examplespkg//go:build integrationtest package examplespkg import "fmt" func ExampleGreet() { fmt.Println(Greet("World")) // Output: Hello, World! } func ExampleAdd() { fmt.Println(Add(1, 2)) // Output: 3 } func ExampleAdd_negative() { fmt.Println(Add(-1, -2)) // Output: -3 } wholefile_test.go000066400000000000000000000003671520301377500352460ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/examples-parser/testdata/examplespkg//go:build integrationtest package examplespkg import "fmt" type helper struct { value string } func ExampleFormatter() { f := Formatter{Prefix: ">>"} h := helper{value: "test"} fmt.Printf("%s %s", f.Prefix, h.value) // Output: >> test } go-openapi-testify-c10ca71/codegen/internal/scanner/imports.go000066400000000000000000000026521520301377500245430ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package scanner import ( "strings" "golang.org/x/tools/go/packages" ) // buildImportAliases scans import declarations to find aliases used in the source. // // This bridges the AST view (import aliases) with the types view (package paths). func buildImportAliases(pkg *packages.Package) map[string]string { aliases := make(map[string]string) for _, astFile := range pkg.Syntax { for _, importSpec := range astFile.Imports { // get the import path (remove quotes) importPath := strings.Trim(importSpec.Path.Value, `"`) var alias string if importSpec.Name != nil { // explicit alias: import foo "bar/baz" alias = importSpec.Name.Name // skip blank imports and dot imports for type qualification if alias == "_" || alias == "." { continue } } else { // no explicit alias - need to determine the package name // try to find it in the loaded imports for _, imported := range pkg.Imports { if imported.PkgPath == importPath { alias = imported.Name break } } // fallback: use last segment of import path if alias == "" { parts := strings.Split(importPath, "/") alias = parts[len(parts)-1] } } // store the mapping (later imports override earlier ones if there are conflicts) aliases[importPath] = alias } } return aliases } go-openapi-testify-c10ca71/codegen/internal/scanner/module.go000066400000000000000000000036471520301377500243400ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package scanner import ( "bytes" "context" "fmt" "log" "os/exec" "runtime/debug" "time" "unicode" ) // header returns a codegen header, more detailed than the Tool information. func header() string { moduleVersion := moduleVersion() repoVersion := gitDescribe() ts := time.Now().Format(time.DateOnly) return fmt.Sprintf( "Generated on %s (version %s) using codegen version %s", ts, repoVersion, moduleVersion, ) } func gitDescribe() string { const cmdTimeout = 10 * time.Second ctx, cancel := context.WithTimeout(context.Background(), cmdTimeout) defer cancel() gitDesc, err := exec.CommandContext(ctx, "git", "rev-parse", "--short", "HEAD").CombinedOutput() if err != nil { log.Printf("warning: attempted to run git describe, but got: %v. Skipped", err) return "" } return string(bytes.TrimRightFunc(gitDesc, func(r rune) bool { return r == '\n' || r == '\r' || unicode.IsSpace(r) })) } // moduleName returns the main module name of the caller. // // This identifies the tool currently running the analysis. // // NOTE: no versioning here - the header provides more information. func moduleName() string { info, ok := debug.ReadBuildInfo() if !ok { return "" } if info.Main.Path != "" { return info.Main.Path } // default when not running in module mode, e.g. go run/go generate return "github.com/go-openapi/testify/codegen/v2" } func moduleVersion() string { info, ok := debug.ReadBuildInfo() if !ok { return "" } var ( modVersion string gitVersion string ) for _, setting := range info.Settings { if setting.Key == "vcs.revision" { gitVersion = setting.Value } } if info.Main.Version == "(devel)" { modVersion = "master" } else { modVersion = info.Main.Version } final := modVersion if gitVersion != "" { final += " [sha: " + gitVersion + "]" } return final } go-openapi-testify-c10ca71/codegen/internal/scanner/module_test.go000066400000000000000000000012061520301377500253640ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package scanner import "testing" func TestModuleName(t *testing.T) { const expected = "github.com/go-openapi/testify/codegen/v2" result := moduleName() if result != expected { t.Errorf("Expected %q but got %q", expected, result) } } func TestGitDescribe(t *testing.T) { result := gitDescribe() if result == "" { t.Errorf("Expected a non-empty string but got %q", result) } } func TestModuleVersion(t *testing.T) { result := moduleVersion() if result == "" { t.Errorf("Expected a non-empty string but got %q", result) } } go-openapi-testify-c10ca71/codegen/internal/scanner/options.go000066400000000000000000000016401520301377500245350ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package scanner const stdTarget = "github.com/go-openapi/testify/v2/internal/assertions" type Option func(*options) type options struct { dir string pkg string collectDoc bool } // WithWorkDir gives a workdir to the code scanner. func WithWorkDir(dir string) Option { return func(o *options) { o.dir = dir } } // WithPackage indicates which package is scanned. func WithPackage(pkg string) Option { return func(o *options) { o.pkg = pkg } } // WithCollectDoc indicates that the scanner should extract more information from comments. func WithCollectDoc(enabled bool) Option { return func(o *options) { o.collectDoc = enabled } } func optionsWithDefaults(opts []Option) options { o := options{ dir: "..", pkg: stdTarget, } for _, apply := range opts { apply(&o) } return o } go-openapi-testify-c10ca71/codegen/internal/scanner/scanner.go000066400000000000000000000207471520301377500245040ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package scanner import ( "cmp" "errors" "fmt" "go/ast" "go/token" "go/types" "path/filepath" "regexp" "slices" "golang.org/x/tools/go/packages" "github.com/go-openapi/testify/codegen/v2/internal/model" "github.com/go-openapi/testify/codegen/v2/internal/scanner/comments" parser "github.com/go-openapi/testify/codegen/v2/internal/scanner/comments-parser" "github.com/go-openapi/testify/codegen/v2/internal/scanner/signature" ) // Scanner scans the internal/assertions package and extracts all functions, types, and metadata. type Scanner struct { options scanConfig *packages.Config syntaxPackage []*ast.File typedPackage *types.Package typesInfo *types.Info filesMap map[*token.File]*ast.File fileSet *token.FileSet importAliases map[string]string // import path -> alias name used in source sigExtractor *signature.Extractor commentExtractor *comments.Extractor result *model.AssertionPackage } // New [Scanner] ready to [Scanner.Scan] code. func New(opts ...Option) *Scanner { o := optionsWithDefaults(opts) const pkgLoadMode = packages.NeedName | packages.NeedFiles | packages.NeedImports | packages.NeedDeps | packages.NeedTypes | packages.NeedSyntax | packages.NeedTypesInfo cfg := &packages.Config{ Dir: o.dir, Mode: pkgLoadMode, Tests: false, // we infer generated tests from production code and ignore internal tests } return &Scanner{ options: o, scanConfig: cfg, result: model.New(), } } // Scan a source package an returns a [model.AssertionPackage] data structure suited for code generation. func (s *Scanner) Scan() (*model.AssertionPackage, error) { pkgs, err := packages.Load(s.scanConfig, s.pkg) if err != nil { return nil, err } if len(pkgs) == 0 { return nil, fmt.Errorf("could not parse package: %v", s.pkg) } // we consider only one package pkg := pkgs[0] s.syntaxPackage = pkg.Syntax s.typedPackage = pkg.Types if s.typedPackage == nil { return nil, fmt.Errorf("something is wrong. Could not parse packages: %v", s.pkg) } // stash everything we need from [packages.Package] s.typesInfo = pkg.TypesInfo s.filesMap = comments.BuildFilesMap(pkg) s.fileSet = pkg.Fset s.importAliases = buildImportAliases(pkg) s.sigExtractor = signature.New(s.typedPackage, s.importAliases) s.commentExtractor = comments.New(s.syntaxPackage, s.fileSet, s.filesMap) if err := s.resolveScope(); err != nil { return nil, err } // post-collection enrichments s.result.Tool = moduleName() s.result.Header = header() s.result.Copyright = s.commentExtractor.ExtractCopyright().Text() s.result.DocString = s.commentExtractor.ExtractPackageComments().Text() s.result.TestDataPath = filepath.Join(pkg.Dir, "testdata") if s.collectDoc { // extract domain descriptions from package comments s.result.ExtraComments = s.commentExtractor.ExtractDomainDescriptions() } return s.result, nil } func (s *Scanner) resolveScope() error { s.result.Package = s.typedPackage.Path() for _, pkg := range s.typedPackage.Imports() { s.addImport(pkg) } scope := s.typedPackage.Scope() for _, symbol := range scope.Names() { object := scope.Lookup(symbol) if object == nil { // something went wrong continue } if !object.Exported() { continue } switch typedObject := object.(type) { case *types.Func: s.addFunction(typedObject) case *types.Const: s.addConst(typedObject) case *types.Var: s.addVar(typedObject) case *types.TypeName: s.addNamedType(typedObject) // NOTE: known limitation this does not support type or var aliases in the source package default: continue } } // Accumulate parse errors from test values var parseErrors []error for _, fn := range s.result.Functions { for _, test := range fn.Tests { for _, val := range test.TestedValues { if val.Error != nil { parseErrors = append(parseErrors, fmt.Errorf("function %s: invalid test value %q: %w", fn.Name, val.Raw, val.Error)) } } } } if len(parseErrors) > 0 { return errors.Join(parseErrors...) } return nil } func (s *Scanner) addImport(pkg *types.Package) { if s.result.Imports == nil { s.result.Imports = make(model.ImportMap) } // use the alias if one exists, otherwise use the package name. // (this ensures the key matches what elidedQualifier() returns) key := pkg.Name() // default to package name if alias, ok := s.importAliases[pkg.Path()]; ok { key = alias // use the alias from source } s.result.Imports[key] = pkg.Path() } func (s *Scanner) addFunction(object *types.Func) { docComment := s.commentExtractor.ExtractComments(object) // extract the base function signature function := s.sigExtractor.ExtractFunctionSignature(object.Signature(), object.Name()) // add function-specific metadata function.ID = object.Id() function.SourcePackage = object.Pkg().Path() // full package name function.TargetPackage = object.Pkg().Name() // short package name function.DocString = docComment.Text() function.Tests = parser.ParseTestExamples(docComment.Text()) slices.SortStableFunc(function.Tests, func(t1, t2 model.Test) int { return cmp.Compare(t1.ExpectedOutcome, t2.ExpectedOutcome) // sort tests with success first }) for i, test := range function.Tests { test.Pkg = function.TargetPackage // add context of the parent package test.IsFirst = i == 0 function.Tests[i] = test } if s.collectDoc { function.ExtraComments = s.commentExtractor.ExtractExtraComments(object) pos := s.fileSet.Position(object.Pos()) function.SourceLink = &pos function.Domain = parser.DomainFromExtraComments(function.ExtraComments) } function.IsDeprecated = isDeprecated(function.DocString) s.result.Functions = append(s.result.Functions, function) } func (s *Scanner) addConst(object *types.Const) { constant := model.Ident{ ID: object.Id(), Name: object.Name(), SourcePackage: object.Pkg().Path(), TargetPackage: object.Pkg().Name(), // short package name DocString: s.commentExtractor.ExtractComments(object).Text(), } constant.IsDeprecated = isDeprecated(constant.DocString) if s.collectDoc { pos := s.fileSet.Position(object.Pos()) constant.SourceLink = &pos } s.result.Consts = append(s.result.Consts, constant) } func (s *Scanner) addVar(object *types.Var) { variable := model.Ident{ ID: object.Id(), Name: object.Name(), SourcePackage: object.Pkg().Path(), TargetPackage: object.Pkg().Name(), // short package name DocString: s.commentExtractor.ExtractComments(object).Text(), } variable.IsDeprecated = isDeprecated(variable.DocString) if s.collectDoc { pos := s.fileSet.Position(object.Pos()) variable.SourceLink = &pos } // Check if this variable is a function type if sig, ok := object.Type().(*types.Signature); ok { fn := s.sigExtractor.ExtractFunctionSignature(sig, object.Name()) variable.Function = &fn } s.result.Vars = append(s.result.Vars, variable) } func (s *Scanner) addNamedType(object *types.TypeName) { namedType := model.Ident{ ID: object.Id(), Name: object.Name(), SourcePackage: object.Pkg().Path(), TargetPackage: object.Pkg().Name(), // short package name IsAlias: object.IsAlias(), DocString: s.commentExtractor.ExtractComments(object).Text(), } namedType.IsDeprecated = isDeprecated(namedType.DocString) // check if this named type is a function type. // For named types, we need to check the underlying type if named, ok := object.Type().(*types.Named); ok { if sig, ok := named.Underlying().(*types.Signature); ok { fn := s.sigExtractor.ExtractFunctionSignature(sig, object.Name()) namedType.Function = &fn } } else if sig, ok := object.Type().(*types.Signature); ok { // type alias to a function type fn := s.sigExtractor.ExtractFunctionSignature(sig, object.Name()) namedType.Function = &fn } if s.collectDoc { pos := s.fileSet.Position(object.Pos()) namedType.SourceLink = &pos namedType.ExtraComments = s.commentExtractor.ExtractExtraComments(object) namedType.Domain = parser.DomainFromExtraComments(namedType.ExtraComments) } s.result.Types = append(s.result.Types, namedType) } // This is the regexp used by pkgsite. // See: https://cs.opensource.google/go/x/pkgsite/+/master:internal/godoc/dochtml/deprecated.go // // "Deprecated:" at the start of a paragraph. var deprecatedRx = regexp.MustCompile(`(^|\n\s*\n)\s*Deprecated:`) func isDeprecated(comment string) bool { return deprecatedRx.MatchString(comment) } go-openapi-testify-c10ca71/codegen/internal/scanner/scanner_test.go000066400000000000000000000010771520301377500255360ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package scanner import ( "os" "testing" ) // TestScanner exercises the [Scanner] without asserting anything else than running without error. // // You may use this smoke test to verify the content of the constructed model. func TestScanner(t *testing.T) { s := New(WithCollectDoc(true)) r, err := s.Scan() if err != nil { t.Error(err) t.FailNow() } if isCI := os.Getenv("CI"); isCI != "" { // skip output when run on CI return } t.Logf("%#v", r) } go-openapi-testify-c10ca71/codegen/internal/scanner/signature/000077500000000000000000000000001520301377500245135ustar00rootroot00000000000000go-openapi-testify-c10ca71/codegen/internal/scanner/signature/doc.go000066400000000000000000000011161520301377500256060ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package signature provides utilities for extracting and formatting Go function signatures and types. // // This package handles: // // - Extracting function signatures from [types.Signature] // - Type qualification using import aliases // - Converting types to strings with proper package selectors // // The Extractor type maintains the context needed for proper type qualification, // including the current package and import aliases from the source code. package signature go-openapi-testify-c10ca71/codegen/internal/scanner/signature/extractor.go000066400000000000000000000120671520301377500270630ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package signature import ( "go/types" "strings" "github.com/go-openapi/testify/codegen/v2/internal/model" ) // Extractor extracts function signatures and formats types with proper package qualification. type Extractor struct { currentPackage *types.Package // the package being scanned importAliases map[string]string // import path -> alias name used in source } // New creates a signature extractor. // // The currentPackage is used to determine when type qualification is needed. // The importAliases map provides the mapping from import paths to the aliases // used in the source code (e.g., "net/http/httputil" -> "httputil"). func New(currentPackage *types.Package, importAliases map[string]string) *Extractor { return &Extractor{ currentPackage: currentPackage, importAliases: importAliases, } } // ExtractFunctionSignature extracts function signature details from a [types.Signature]. // // This is used for both regular functions and function types (var/type declarations). func (e *Extractor) ExtractFunctionSignature(signature *types.Signature, name string) model.Function { function := model.Function{ Name: name, } // check if generic and extract type parameters typeParams := signature.TypeParams() function.IsGeneric = typeParams != nil if typeParams != nil { function.TypeParams = e.extractTypeParams(typeParams) } // extract parameters params := signature.Params() function.AllParams = make(model.Parameters, 0, params.Len()) function.Params = make(model.Parameters, 0, params.Len()) for param := range params.Variables() { p := model.Parameter{ Name: param.Name(), GoType: e.ElidedType(param.Type()), Selector: e.ElidedQualifier(param.Type()), IsGeneric: isTypeParam(param.Type()), } function.AllParams = append(function.AllParams, p) if p.Name != "t" && p.Name != "msgAndArgs" { // filtered params function.Params = append(function.Params, p) } } // handle variadic parameters l := params.Len() if signature.Variadic() && l > 0 { // introduce ellipsis notation lastParam := params.At(l - 1) lastType := lastParam.Type() if asSlice, ok := lastType.(*types.Slice); ok { // should be always ok elemGoType := e.ElidedType(asSlice.Elem()) function.AllParams[len(function.AllParams)-1].GoType = "..." + elemGoType function.AllParams[len(function.AllParams)-1].IsVariadic = true // check filtered params if len(function.Params) > 0 && function.Params[len(function.Params)-1].Name == function.AllParams[len(function.AllParams)-1].Name { function.Params[len(function.Params)-1].GoType = "..." + elemGoType function.Params[len(function.Params)-1].IsVariadic = true } } } // extract return values results := signature.Results() function.Returns = make(model.Parameters, 0, results.Len()) for result := range results.Variables() { function.Returns = append(function.Returns, model.Parameter{ Name: result.Name(), GoType: e.ElidedType(result.Type()), Selector: e.ElidedQualifier(result.Type()), }) } // detect helpers (not assertions) function.IsHelper = l == 0 || (len(function.AllParams) > 0 && function.AllParams[0].Name != "t") function.IsConstructor = name == "New" return function } // Qualifier returns the appropriate package name for type qualification. // // It uses import aliases from the source (AST) rather than the package's actual name. func (e *Extractor) Qualifier(pkg *types.Package) string { if pkg == nil { return "" } // if it's the current package being scanned, no qualification needed if pkg == e.currentPackage { return "" } // look up the alias used in source imports if alias, ok := e.importAliases[pkg.Path()]; ok { return alias } // fallback to the package's actual name return pkg.Name() } // ElidedType returns a string representation of the type with package names as they appear in source. // // Uses import aliases when available (e.g., "httputil.Handler" if imported as "httputil"). func (e *Extractor) ElidedType(t types.Type) string { return types.TypeString(t, e.Qualifier) } // ElidedQualifier returns the selector used for a type, as its import package alias used in source, // or the empty string if this is a local declaration. func (e *Extractor) ElidedQualifier(t types.Type) string { const maxInterestingParts = 2 parts := strings.SplitN(types.TypeString(t, e.Qualifier), ".", maxInterestingParts) if len(parts) > 1 { return parts[0] } return "" } // isTypeParam checks if a type is a type parameter (generic type variable). func isTypeParam(t types.Type) bool { _, ok := t.(*types.TypeParam) return ok } // extractTypeParams extracts type parameter names and constraints from a TypeParamList. func (e *Extractor) extractTypeParams(typeParams *types.TypeParamList) []model.TypeParam { result := make([]model.TypeParam, typeParams.Len()) for i := range typeParams.Len() { tp := typeParams.At(i) result[i] = model.TypeParam{ Name: tp.Obj().Name(), Constraint: e.ElidedType(tp.Constraint()), } } return result } go-openapi-testify-c10ca71/codegen/internal/scanner/signature/extractor_test.go000066400000000000000000000343011520301377500301150ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package signature import ( "go/types" "iter" "slices" "testing" ) func TestQualifier(t *testing.T) { t.Parallel() // Create test packages currentPkg := types.NewPackage("github.com/example/test", "test") otherPkg := types.NewPackage("net/http", "http") aliasedPkg := types.NewPackage("net/http/httputil", "httputil") for c := range qualifierCases(currentPkg, otherPkg, aliasedPkg) { t.Run(c.name, func(t *testing.T) { t.Parallel() e := New(c.currentPkg, c.importAliases) result := e.Qualifier(c.pkg) if result != c.expected { t.Errorf("Qualifier() = %q, expected %q", result, c.expected) } }) } } func TestElidedQualifier(t *testing.T) { t.Parallel() currentPkg := types.NewPackage("github.com/example/test", "test") otherPkg := types.NewPackage("net/http", "http") for c := range elidedQualifierCases(currentPkg, otherPkg) { t.Run(c.name, func(t *testing.T) { t.Parallel() e := New(c.currentPkg, c.importAliases) result := e.ElidedQualifier(c.typ) if result != c.expected { t.Errorf("ElidedQualifier() = %q, expected %q", result, c.expected) } }) } } func TestElidedType(t *testing.T) { t.Parallel() currentPkg := types.NewPackage("github.com/example/test", "test") otherPkg := types.NewPackage("net/http", "http") for c := range elidedTypeCases(currentPkg, otherPkg) { t.Run(c.name, func(t *testing.T) { t.Parallel() e := New(c.currentPkg, c.importAliases) result := e.ElidedType(c.typ) if result != c.expected { t.Errorf("ElidedType() = %q, expected %q", result, c.expected) } }) } } func TestExtractFunctionSignature(t *testing.T) { t.Parallel() currentPkg := types.NewPackage("github.com/example/test", "test") // Helper to create a signature makeSig := func(params, results []*types.Var, variadic bool) *types.Signature { return types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), types.NewTuple(results...), variadic) } for c := range extractFunctionSignatureCases(currentPkg, makeSig) { t.Run(c.name, func(t *testing.T) { t.Parallel() e := New(currentPkg, map[string]string{}) result := e.ExtractFunctionSignature(c.signature, c.funcName) if result.Name != c.expectedName { t.Errorf("Name = %q, expected %q", result.Name, c.expectedName) } if result.IsHelper != c.expectedHelper { t.Errorf("IsHelper = %v, expected %v", result.IsHelper, c.expectedHelper) } if result.IsConstructor != c.expectedCtor { t.Errorf("IsConstructor = %v, expected %v", result.IsConstructor, c.expectedCtor) } if len(result.Params) != c.paramsLen { t.Errorf("len(Params) = %d, expected %d", len(result.Params), c.paramsLen) } if len(result.Returns) != c.returnsLen { t.Errorf("len(Returns) = %d, expected %d", len(result.Returns), c.returnsLen) } if len(result.AllParams) != c.allParamsLen { t.Errorf("len(AllParams) = %d, expected %d", len(result.AllParams), c.allParamsLen) } }) } } func TestExtractFunctionSignature_Variadic(t *testing.T) { currentPkg := types.NewPackage("github.com/example/test", "test") // Create variadic signature params := []*types.Var{ types.NewVar(0, currentPkg, "t", types.NewInterfaceType(nil, nil)), types.NewVar(0, currentPkg, "values", types.NewSlice(types.Typ[types.String])), } results := []*types.Var{ types.NewVar(0, currentPkg, "", types.Typ[types.Bool]), } sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), types.NewTuple(results...), true) // variadic e := New(currentPkg, map[string]string{}) result := e.ExtractFunctionSignature(sig, "TestFunc") // Check that last param has ellipsis notation if len(result.AllParams) != 2 { t.Fatalf("Expected 2 AllParams, got %d", len(result.AllParams)) } lastParam := result.AllParams[len(result.AllParams)-1] if lastParam.GoType != "...string" { t.Errorf("Last param GoType = %q, expected %q", lastParam.GoType, "...string") } if !lastParam.IsVariadic { t.Errorf("Last param IsVariadic = false, expected true") } // Check filtered params also have ellipsis if len(result.Params) != 1 { t.Fatalf("Expected 1 filtered Param, got %d", len(result.Params)) } if result.Params[0].GoType != "...string" { t.Errorf("Filtered param GoType = %q, expected %q", result.Params[0].GoType, "...string") } } func TestExtractFunctionSignature_GenericFunction(t *testing.T) { currentPkg := types.NewPackage("github.com/example/test", "test") // Create a generic signature with type parameters typeParam := types.NewTypeParam(types.NewTypeName(0, currentPkg, "T", nil), types.NewInterfaceType(nil, nil).Complete()) params := []*types.Var{ types.NewVar(0, currentPkg, "t", types.NewInterfaceType(nil, nil)), types.NewVar(0, currentPkg, "value", typeParam), } results := []*types.Var{ types.NewVar(0, currentPkg, "", types.Typ[types.Bool]), } sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(params...), types.NewTuple(results...), false) // Manually set type params on signature using reflection or accept that // we can't easily test generics without more complex setup. // For now, test non-generic case and verify IsGeneric is false. e := New(currentPkg, map[string]string{}) result := e.ExtractFunctionSignature(sig, "GenericFunc") // Without type params attached, this will be false if result.IsGeneric { t.Errorf("IsGeneric = true, expected false (cannot easily construct generic sig in test)") } } /* Test case iterators */ type qualifierCase struct { name string currentPkg *types.Package importAliases map[string]string pkg *types.Package expected string } func qualifierCases(currentPkg, otherPkg, aliasedPkg *types.Package) iter.Seq[qualifierCase] { return slices.Values([]qualifierCase{ { name: "nil package", currentPkg: currentPkg, importAliases: map[string]string{}, pkg: nil, expected: "", }, { name: "current package - no qualification", currentPkg: currentPkg, importAliases: map[string]string{}, pkg: currentPkg, expected: "", }, { name: "other package - use package name", currentPkg: currentPkg, importAliases: map[string]string{}, pkg: otherPkg, expected: "http", }, { name: "aliased package - use alias", currentPkg: currentPkg, importAliases: map[string]string{ "net/http/httputil": "httputil", }, pkg: aliasedPkg, expected: "httputil", }, { name: "aliased package with different alias", currentPkg: currentPkg, importAliases: map[string]string{ "net/http/httputil": "util", }, pkg: aliasedPkg, expected: "util", }, { name: "package with alias but using actual name as fallback", currentPkg: currentPkg, importAliases: map[string]string{ "some/other/path": "other", }, pkg: otherPkg, expected: "http", // falls back to package name }, }) } type elidedQualifierCase struct { name string currentPkg *types.Package importAliases map[string]string typ types.Type expected string } func elidedQualifierCases(currentPkg, otherPkg *types.Package) iter.Seq[elidedQualifierCase] { return slices.Values([]elidedQualifierCase{ { name: "basic type - no qualifier", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.Typ[types.String], expected: "", }, { name: "named type from other package", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewNamed(types.NewTypeName(0, otherPkg, "Request", nil), types.NewStruct(nil, nil), nil), expected: "http", }, { name: "pointer to basic type", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewPointer(types.Typ[types.Int]), expected: "", }, { name: "pointer to named type", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewPointer(types.NewNamed(types.NewTypeName(0, otherPkg, "Request", nil), types.NewStruct(nil, nil), nil)), expected: "*http", // includes pointer in qualifier }, { name: "aliased package qualifier", currentPkg: currentPkg, importAliases: map[string]string{ "net/http": "h", }, typ: types.NewNamed(types.NewTypeName(0, otherPkg, "Request", nil), types.NewStruct(nil, nil), nil), expected: "h", }, }) } type elidedTypeCase struct { name string currentPkg *types.Package importAliases map[string]string typ types.Type expected string } func elidedTypeCases(currentPkg, otherPkg *types.Package) iter.Seq[elidedTypeCase] { return slices.Values([]elidedTypeCase{ { name: "string type", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.Typ[types.String], expected: "string", }, { name: "int type", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.Typ[types.Int], expected: "int", }, { name: "bool type", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.Typ[types.Bool], expected: "bool", }, { name: "pointer to string", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewPointer(types.Typ[types.String]), expected: "*string", }, { name: "slice of int", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewSlice(types.Typ[types.Int]), expected: "[]int", }, { name: "named type from other package", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewNamed(types.NewTypeName(0, otherPkg, "Request", nil), types.NewStruct(nil, nil), nil), expected: "http.Request", }, { name: "named type with alias", currentPkg: currentPkg, importAliases: map[string]string{ "net/http": "h", }, typ: types.NewNamed(types.NewTypeName(0, otherPkg, "Request", nil), types.NewStruct(nil, nil), nil), expected: "h.Request", }, { name: "interface type", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewInterfaceType(nil, nil), expected: "interface{}", }, { name: "empty interface (any)", currentPkg: currentPkg, importAliases: map[string]string{}, typ: types.NewInterfaceType(nil, nil).Complete(), expected: "interface{}", }, }) } type extractFunctionSignatureCase struct { name string funcName string signature *types.Signature expectedName string expectedHelper bool expectedCtor bool paramsLen int returnsLen int allParamsLen int } func extractFunctionSignatureCases(currentPkg *types.Package, makeSig func([]*types.Var, []*types.Var, bool) *types.Signature) iter.Seq[extractFunctionSignatureCase] { return slices.Values([]extractFunctionSignatureCase{ { name: "simple assertion function", funcName: "Equal", signature: makeSig( []*types.Var{ types.NewVar(0, currentPkg, "t", types.NewInterfaceType(nil, nil)), types.NewVar(0, currentPkg, "expected", types.NewInterfaceType(nil, nil)), types.NewVar(0, currentPkg, "actual", types.NewInterfaceType(nil, nil)), }, []*types.Var{ types.NewVar(0, currentPkg, "", types.Typ[types.Bool]), }, false, ), expectedName: "Equal", expectedHelper: false, expectedCtor: false, paramsLen: 2, // filtered (excludes 't') returnsLen: 1, allParamsLen: 3, }, { name: "variadic assertion function", funcName: "True", signature: makeSig( []*types.Var{ types.NewVar(0, currentPkg, "t", types.NewInterfaceType(nil, nil)), types.NewVar(0, currentPkg, "value", types.Typ[types.Bool]), types.NewVar(0, currentPkg, "msgAndArgs", types.NewSlice(types.NewInterfaceType(nil, nil))), }, []*types.Var{ types.NewVar(0, currentPkg, "", types.Typ[types.Bool]), }, true, ), expectedName: "True", expectedHelper: false, expectedCtor: false, paramsLen: 1, // filtered (excludes 't' and 'msgAndArgs') returnsLen: 1, allParamsLen: 3, }, { name: "helper function - no params", funcName: "Helper", signature: makeSig( []*types.Var{}, []*types.Var{ types.NewVar(0, currentPkg, "", types.Typ[types.String]), }, false, ), expectedName: "Helper", expectedHelper: true, expectedCtor: false, paramsLen: 0, returnsLen: 1, allParamsLen: 0, }, { name: "constructor function", funcName: "New", signature: makeSig( []*types.Var{ types.NewVar(0, currentPkg, "t", types.NewInterfaceType(nil, nil)), }, []*types.Var{ types.NewVar(0, currentPkg, "", types.NewPointer(types.NewNamed( types.NewTypeName(0, currentPkg, "Assertions", nil), types.NewStruct(nil, nil), nil))), }, false, ), expectedName: "New", expectedHelper: false, expectedCtor: true, paramsLen: 0, // filtered (excludes 't') returnsLen: 1, allParamsLen: 1, }, { name: "helper - first param not 't'", funcName: "CalcValue", signature: makeSig( []*types.Var{ types.NewVar(0, currentPkg, "x", types.Typ[types.Int]), types.NewVar(0, currentPkg, "y", types.Typ[types.Int]), }, []*types.Var{ types.NewVar(0, currentPkg, "", types.Typ[types.Int]), }, false, ), expectedName: "CalcValue", expectedHelper: true, // first param is not 't' expectedCtor: false, paramsLen: 2, // not filtered returnsLen: 1, allParamsLen: 2, }, { name: "no return values", funcName: "FailNow", signature: makeSig( []*types.Var{ types.NewVar(0, currentPkg, "t", types.NewInterfaceType(nil, nil)), }, []*types.Var{}, false, ), expectedName: "FailNow", expectedHelper: false, expectedCtor: false, paramsLen: 0, // filtered (excludes 't') returnsLen: 0, allParamsLen: 1, }, }) } go-openapi-testify-c10ca71/codegen/main.go000066400000000000000000000106141520301377500205220ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package main provides the testify code generator. // // This program reads all assertion functions from the internal/assertions package and // automatically generates the corresponding packages assert and require with all variants. package main import ( "flag" "log" "strings" "github.com/go-openapi/testify/codegen/v2/internal/generator" "github.com/go-openapi/testify/codegen/v2/internal/model" "github.com/go-openapi/testify/codegen/v2/internal/scanner" ) type config struct { dir string targetRoot string targetDoc string inputPkg string outputPkgs string includeFmt bool includeFwd bool includeTst bool includeGen bool includeHlp bool includeExa bool includeDoc bool runExa bool } func main() { cfg := new(config) registerFlags(cfg) flag.Parse() // exits if invalid flags if err := execute(cfg); err != nil { log.Fatal(err) } } func registerFlags(cfg *config) { flag.StringVar(&cfg.dir, "work-dir", "..", "working directory to scan for package (default is the parent folder)") flag.StringVar(&cfg.inputPkg, "input-package", "github.com/go-openapi/testify/v2/internal/assertions", "source package to scan to produce generated output") flag.StringVar(&cfg.outputPkgs, "output-packages", "assert,require", "package(s) to generate") flag.StringVar(&cfg.targetRoot, "target-root", "..", "where to place the generated packages (default is in the parent folder)") flag.StringVar(&cfg.targetDoc, "target-doc", "docs/doc-site/api", "where to place the generated documentation, relative to the target root") flag.BoolVar(&cfg.includeFmt, "include-format-funcs", true, "include format functions such as Errorf and Equalf") flag.BoolVar(&cfg.includeFwd, "include-forward-funcs", true, "include forward assertions functions as methods of the Assertions object") flag.BoolVar(&cfg.includeTst, "include-tests", true, "generate the tests in the target package(s)") flag.BoolVar(&cfg.includeGen, "include-generics", true, "include generic functions") flag.BoolVar(&cfg.includeHlp, "include-helpers", true, "include helper functions that are not assertions") flag.BoolVar(&cfg.includeExa, "include-examples", true, "include generated testable examples") flag.BoolVar(&cfg.runExa, "runnable-examples", true, "include Output to generated testable examples, so they are run as tests") flag.BoolVar(&cfg.includeDoc, "include-doc", true, "include generated markdown documentation") } func execute(cfg *config) error { // 1. Scan internal/assertions scanner := scanner.New( scanner.WithWorkDir(cfg.dir), scanner.WithPackage(cfg.inputPkg), scanner.WithCollectDoc(cfg.includeDoc), // collects extra documentation from comments ) scanned, err := scanner.Scan() if err != nil { return err } // 2. Generate assert and require packages builder := generator.New(scanned) var doc model.Documentation for targetPkg := range strings.SplitSeq(cfg.outputPkgs, ",") { err = builder.Generate( // where to generate generator.WithTargetRoot(cfg.targetRoot), generator.WithTargetPackage(targetPkg), // what to generate generator.WithIncludeFormatFuncs(cfg.includeFmt), generator.WithIncludeForwardFuncs(cfg.includeFwd), generator.WithIncludeGenerics(cfg.includeGen), generator.WithIncludeTests(cfg.includeTst), generator.WithIncludeHelpers(cfg.includeHlp), generator.WithIncludeExamples(cfg.includeExa), generator.WithRunnableExamples(cfg.runExa), generator.WithIncludeDoc(cfg.includeDoc), ) if err != nil { return err } if cfg.includeDoc { // stash the transformed doc doc.Merge(builder.Documentation()) } } if !cfg.includeDoc { // we're done with codegen return nil } // ... and now for something completely different: generating the documentation // // 3. Generate API documentation in docs/doc-site/api documentalist := generator.NewDocGenerator(doc) err = documentalist.Generate( // where to generate generator.WithTargetRoot(cfg.targetRoot), generator.WithTargetDoc(cfg.targetDoc), // what to generate generator.WithIncludeFormatFuncs(cfg.includeFmt), generator.WithIncludeForwardFuncs(cfg.includeFwd), generator.WithIncludeGenerics(cfg.includeGen), generator.WithIncludeHelpers(cfg.includeHlp), generator.WithRunnableExamples(cfg.runExa), // extracts runnable examples from generated sourc ) if err != nil { return err } return nil } go-openapi-testify-c10ca71/codegen/main_test.go000066400000000000000000000137431520301377500215670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "iter" "os" "os/exec" "path/filepath" "slices" "testing" ) // TestExecute verifies the execute function works with valid configuration. func TestExecute(t *testing.T) { // Skip if we're in a short test run if testing.Short() { t.Skip("Skipping integration test in short mode") } tmpDir := t.TempDir() targetRoot := filepath.Join(tmpDir, "output") t.Run("should prepare go.mod to build in tmpDir", func(t *testing.T) { if err := os.MkdirAll(targetRoot, 0o700); err != nil { t.Fatalf("could not create working dir %s: %v", targetRoot, err) } cwd, err := os.Getwd() if err != nil { t.Fatalf("could not retrieve current dir: %v", err) } // NOTE: source formatting & imports check requires a proper go mod when building outside of the current GOROOT. goModInit(t, targetRoot, filepath.Join(cwd, "..")) }) cfg := &config{ dir: "..", inputPkg: "github.com/go-openapi/testify/v2/internal/assertions", outputPkgs: "assert", targetRoot: targetRoot, targetDoc: "doc", includeFmt: true, includeFwd: true, includeTst: true, includeGen: true, includeHlp: true, includeExa: true, includeDoc: true, runExa: true, } err := execute(cfg) if err != nil { t.Fatalf("Execute failed: %v", err) } // Verify that the assert package was generated assertPkg := filepath.Join(targetRoot, "assert") if _, err := os.Stat(assertPkg); os.IsNotExist(err) { t.Error("Assert package directory was not created") } t.Run("generated source files should exist", func(t *testing.T) { expectedFiles := []string{ "assert_types.go", "assert_assertions.go", "assert_format.go", "assert_forward.go", "assert_helpers.go", } for _, file := range expectedFiles { pth := filepath.Join(assertPkg, file) if _, err := os.Stat(pth); os.IsNotExist(err) { t.Errorf("Expected file %s was not generated", file) } } }) t.Run("generated doc files should exist", func(t *testing.T) { docLocation := filepath.Join(tmpDir, "output", "doc") if _, err := os.Stat(docLocation); os.IsNotExist(err) { t.Error("doc directory was not created") } pth := filepath.Join(docLocation, "number.md") // only check one sample if _, err := os.Stat(pth); os.IsNotExist(err) { t.Errorf("Expected file %s was not generated", pth) } }) } // TestExecuteMultiplePackages verifies that generating multiple packages works. func TestExecuteMultiplePackages(t *testing.T) { if testing.Short() { t.Skip("Skipping integration test in short mode") } tmpDir := t.TempDir() targetRoot := filepath.Join(tmpDir, "output") // NOTE: we don't need all the go.mod preparation work in this simpler test case cfg := &config{ dir: "..", inputPkg: "github.com/go-openapi/testify/v2/internal/assertions", outputPkgs: "assert,require", targetRoot: targetRoot, includeFmt: false, includeFwd: false, includeTst: false, includeGen: false, includeHlp: false, includeExa: false, runExa: false, } err := execute(cfg) if err != nil { t.Fatalf("Execute failed: %v", err) } // Verify both packages were generated for _, pkg := range []string{"assert", "require"} { pkgDir := filepath.Join(targetRoot, pkg) if _, err := os.Stat(pkgDir); os.IsNotExist(err) { t.Errorf("Package %s was not created", pkg) } // Verify at least the main assertions file exists assertionsFile := filepath.Join(pkgDir, pkg+"_assertions.go") if _, err := os.Stat(assertionsFile); os.IsNotExist(err) { t.Errorf("Assertions file for %s was not generated", pkg) } } } // TestExecuteInvalidConfig verifies error handling for invalid configuration. func TestExecuteInvalidConfig(t *testing.T) { t.Parallel() dir := t.TempDir() for tt := range invalidConfigCases(dir) { t.Run(tt.name, func(t *testing.T) { t.Parallel() err := execute(tt.cfg) if err == nil { t.Error("Expected error but got nil") } }) } } // TestSmokeRegisterFlags merely passes over the flag registration step to ensure // we don't have a panic due to flags registered several times. // // There is no real assertion to be made. func TestSmokeRegisterFlags(_ *testing.T) { registerFlags(&config{}) } type invalidConfigCase struct { name string cfg *config } func invalidConfigCases(dir string) iter.Seq[invalidConfigCase] { return slices.Values([]invalidConfigCase{ { name: "invalid input package", cfg: &config{ dir: "..", inputPkg: "invalid/package/path", outputPkgs: "assert", targetRoot: dir, }, }, { name: "nonexistent work dir", cfg: &config{ dir: "/nonexistent/directory", inputPkg: "github.com/go-openapi/testify/v2/internal/assertions", outputPkgs: "assert", targetRoot: dir, }, }, }) } //nolint:gosec // "tainted" args exec is actually okay in tests func goModInit(t *testing.T, location, source string) { t.Run("should init go.mod", func(t *testing.T) { mod := exec.CommandContext(t.Context(), "go", "mod", "init", filepath.Base(location)) mod.Dir = location output, err := mod.CombinedOutput() if err != nil { t.Fatalf("go mod init at %s returned: %v: %s", location, err, string(output)) } t.Run("should replace testify/v2 in go.mod by local references", func(t *testing.T) { replace := exec.CommandContext(t.Context(), "go", "mod", "edit", "-replace=github.com/go-openapi/testify/v2="+source, ) replace.Dir = location output, err := replace.CombinedOutput() if err != nil { t.Fatalf("go mod edit at %s returned: %v: %s", location, err, string(output)) } t.Run("should load required modules", func(t *testing.T) { get := exec.CommandContext(t.Context(), "go", "get", "github.com/go-openapi/testify/v2/assert", "github.com/go-openapi/testify/v2/require", ) get.Dir = location output, err := get.CombinedOutput() if err != nil { t.Fatalf("go get at %s returned: %v: %s", location, err, string(output)) } }) }) }) } go-openapi-testify-c10ca71/doc.go000066400000000000000000000045551520301377500167460ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package testify provides comprehensive assertion packages for Go testing. // // # Overview // // This is the go-openapi fork of testify, designed with zero external dependencies // and a focus on maintainability. The codebase uses code generation more extensively // than the original testify. This helps maintain consistency across all assertion variants. // // # Packages // // The [assert] package provides a comprehensive set of assertion functions that // integrate with Go's testing framework. Assertions return boolean values allowing // tests to continue after failures. // // The [require] package provides the same assertions but with fatal checks that stop // test execution immediately on failure via [testing.T.FailNow]. // // # Key Differences from stretchr/testify // // This fork prioritizes: // // - Zero external dependencies: [spew] and [difflib] are internalized // - Removed mock and suite packages (favor the use [mockery] or similar specialized tools instead) // - Optional features via enable packages (e.g., enable/yaml for YAML assertions) // - Code generation ensures consistency across many assertion functions × 4 to 8 variants // // # Optional Features // // YAML assertions require importing the enable package: // // import _ "github.com/go-openapi/testify/v2/enable/yaml" // // Without this import, [assert.YAMLEq] and [require.YAMLEq] will panic loudly with a helpful error message. // // # Note on testifylint // // [testifylint] is a linter compatible with golangci-lint. // It is designed for stretchr/testify and will not work with this fork as it checks only for the original dependency. // // [assert]: https://pkg.go.dev/github.com/go-openapi/testify/v2/assert // [assert.YAMLEq]: https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq // [difflib]: https://github.com/go-openapi/testify/tree/master/internal/difflib // [mockery]: https://github.com/vektra/mockery // [require]: https://pkg.go.dev/github.com/go-openapi/testify/v2/require // [require.YAMLEq]: https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEq // [spew]: https://github.com/go-openapi/testify/tree/master/internal/spew // [testifylint]: https://github.com/Antonboom/testifylint package testify //go:generate go run ./codegen/main.go -target-root . -work-dir . go-openapi-testify-c10ca71/docs/000077500000000000000000000000001520301377500165715ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/LINTING.md000066400000000000000000000167141520301377500202300ustar00rootroot00000000000000# Linting Directives Audit This document catalogs all `//nolint` directives in the codebase and their justifications. Last reviewed: 2026-02-07 ## Summary | Category | Count | Actionable | |---|---|---| | Internalized third-party (`internal/spew/`) | 17 | No (inherited) | | Enable pattern (`init()` + globals) | 8 | No (architectural) | | Generated code (shared test pointer) | 4+1 template | No (generated) | | Linter false positives | 4 | No (linter limitation) | | Intentional code patterns | 7 | No (by design) | | Intentional test environment | 4 | No (test-specific) | | Scanner test infrastructure | 3 | No (test caching) | | Inherent type-switch complexity | 2 | No (irreducible) | All directives are justified. No actionable items remain. ## Internalized Third-Party: `internal/spew/` The `internal/spew/` package is an internalized copy of `go-spew`. Its 17 `nolint` directives are inherited and appropriate for a low-level reflection library: - `bypass.go:42,71,84` - `gochecknoglobals` - reflect internals, set once during init - `bypass.go:120` - `gochecknoinits` - validates reflect internals at startup - `spew.go:80,92,104` - `forbidigo` - public API wrapping `fmt.Print/Printf/Println` - `config.go:111` - `gochecknoglobals` - global configuration (backward-compatible) - `config.go:184,196,208` - `forbidigo` - public API wrapping `fmt.Print/Printf/Println` - `common.go:43` - `gochecknoglobals` - immutable byte literals - `common.go:82` - `gochecknoglobals` - immutable lookup table - `common.go:221` - `unparam` - `base` param kept for consistency with `FormatInt` - `dump.go:31` - `gochecknoglobals` - immutable reflect types and compiled regexps - `dump.go:216` - `gosec` - uint8 conversion is safe (original type is uint8) - `format_test.go:175,310` - `dupl` - int/uint test data follows same pattern by design - `format_test.go:1564,1599` - `dupl` - panic/error test data follows same pattern - `dump_test.go:215,290` - `dupl` - int/uint test data follows same pattern - `dump_test.go:1235` - `unconvert` - intentional: proves aliased types don't matter ## Enable Pattern: `init()` + Globals The "enable" pattern activates optional features via blank imports (`import _ "github.com/go-openapi/testify/v2/enable/yaml"`). By design, this requires `init()` functions and package-level globals. | File | Directive | Reason | |---|---|---| | `enable/colors/enable.go:24` | `gochecknoglobals` | CLI flags state | | `enable/colors/enable.go:32` | `gochecknoinits` | Blank-import activation | | `enable/yaml/enable_yaml.go:12` | `gochecknoinits` | Blank-import activation | | `internal/assertions/enable/colors/colors.go:55` | `gochecknoglobals` | Package-level colorizers | | `internal/assertions/enable/colors/colors.go:68` | `gochecknoglobals` | Package-level printer builders | | `internal/assertions/enable/colors/enable_colors.go:12` | `gochecknoglobals` | Cross-module feature flag | | `internal/assertions/enable/yaml/enable_yaml.go:10` | `gochecknoglobals` | Cross-module feature flag | | `internal/leak/leak.go:22` | `gochecknoinits` | Volatile internal API check | ## Generated Code: Shared Test Pointer A single template generates a package-level `var` used to share a common pointer across generated test cases. The 4 output files all originate from one template: | File | Directive | |---|---| | `codegen/.../assertion_test_shared.gotmpl:23` | Template source | | `assert/assert_assertions_test.go:3210` | `gochecknoglobals` | | `assert/assert_examples_test.go:1030` | `gochecknoglobals` | | `require/require_assertions_test.go:2738` | `gochecknoglobals` | | `require/require_examples_test.go:1031` | `gochecknoglobals` | ## Linter False Positives | File | Directive | Reason | |---|---|---| | `internal/assertions/error.go:273` | `errorlint` | Type switch checks for interfaces, not unwrapping errors | | `internal/assertions/compare.go:496` | `ireturn` | Generic function returning `V`; linter doesn't understand type parameter | | `codegen/.../examples-parser/parser_test.go:15` | `gochecknoglobals` | `sync.Once` cache for parsed test data | | `codegen/.../comments/extractor_test.go:16` | `gochecknoglobals` | `sync.Once` cache for parsed test data | Note: the `thelper` linter was disabled entirely (see History) — it previously accounted for 12 false positives on test case factories returning `func(*testing.T)`. ## Intentional Code Patterns These directives suppress warnings on code that is correct and intentional. | File | Directive | Reason | |---|---|---| | `internal/assertions/number.go:482` | `gocritic` | `f != f` is the standard NaN check idiom | | `internal/assertions/compare.go:516` | `forcetypeassert` | Type guaranteed by prior reflection | | `internal/assertions/json.go:4` | `dupl` | JSON/YAML implementations intentionally parallel | | `internal/assertions/yaml.go:4` | `dupl` | JSON/YAML implementations intentionally parallel | | `internal/assertions/panic_test.go:43` | `nilness` | Deliberate `panic(nil)` edge case test | | `internal/assertions/mock_test.go:64` | `containedctx` | Context injection for test mocks | | `internal/assertions/file_test.go:126` | `errcheck` | Best-effort `os.Chmod` restore in `t.Cleanup` | ## Intentional Test Environment These directives are required because the tests intentionally exercise locale-specific or escape-sequence behavior. | File | Directive | Reason | |---|---|---| | `enable/colors/assertions_test.go:84` | `staticcheck` | Testing ANSI escape sequences | | `internal/difflib/options_test.go:63` | `staticcheck` | Testing escape sequences | | `internal/assertions/object_test.go:127` | `gosmopolitan` | `time.Local` is precisely what's being tested | | `internal/assertions/compare_impl_test.go:178,179` | `gosmopolitan` | `time.Local` is precisely what's being tested | ## Scanner Test Infrastructure | File | Directive | Reason | |---|---|---| | `codegen/.../examples-parser/parser_test.go:15` | `gochecknoglobals` | `sync.Once` cache for parsed test data | | `codegen/.../comments/extractor_test.go:16` | `gochecknoglobals` | `sync.Once` cache for parsed test data | | `codegen/.../testdata/examplespkg/examplespkg.go:21` | `unused` | Fixture: verifies unexported symbols are skipped | ## Inherent Type-Switch Complexity These two test functions contain exhaustive type switches over all Go types (`int`, `int8`, `int16`, `int32`, `int64`, `float32`, `float64`, `string`, etc.). Unlike the map-based dispatcher pattern (see `.claude/skills/refactor-tests.md` pattern 15), there is no dispatch key to route over -- just a flat enumeration of types. Extracting per-type helpers would create dozens of trivial one-liner functions with no readability gain. | File | Directive | Reason | |---|---|---| | `internal/assertions/equal_test.go:130` | `gocognit,gocyclo,cyclop` | Big type switch over all Go types | | `internal/assertions/collection_test.go:272` | `gocognit,gocyclo,cyclop` | Type dispatch over all Go collection types | ## History - **2026-02-07**: Disabled `thelper` linter entirely. Removed 12 `//nolint:thelper` directives across 6 test files (`yaml_test.go`, `json_test.go`, `number_test.go`, `string_test.go`, `compare_test.go`). The linter produces persistent false positives on test case factories returning `func(*testing.T)` and enforces unwanted naming conventions. Issue reported upstream. - **2026-02-07**: Removed `//nolint:gocognit,gocyclo,cyclop` from `codegen/internal/generator/domains/domains_test.go` by extracting inline check closures into named helper functions with a map-based dispatcher (see `.claude/skills/refactor-tests.md` pattern 15). go-openapi-testify-c10ca71/docs/doc-site/000077500000000000000000000000001520301377500203005ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/doc-site/_index.md000066400000000000000000000101301520301377500220630ustar00rootroot00000000000000--- title: "Testify v2" type: home description: 'The v2 our tests wanted' weight: 1 --- {{% notice info %}} This is the home of `github.com/go-openapi/testify/v2`, an active, opinionated fork of `github.com/stretchr/testify`. {{% /notice %}} ## Testify v2 - The v2 our tests wanted A set of `go` packages that provide tools for _testifying_ (verifying) that your code behaves as you intended. This is the go-openapi fork of the great [testify](https://github.com/stretchr/testify) package. ### Status {{% button href="https://github.com/go-openapi/testify/fork" hint="fork me on github" style=primary icon=code-fork %}}Fork me{{% /button %}} Design and exploration phase. Feedback, contributions and proposals are welcome. See our [ROADMAP](./project/maintainers/ROADMAP.md). ### Motivation See [why we wanted a v2](./MOTIVATION.md). ### Getting started Import this library in your project like so. ```cmd go get github.com/go-openapi/testify/v2 ``` ... and start writing tests. Look at our [examples][doc-examples]. ### Basic usage `testify` simplifies your test assertions like so. {{< cards >}} {{% card title="Standard library" %}} ```go import ( "testing" ) ... const expected = "expected result" result := printImports(input) if result != expected { t.Errorf( "Expected: %s. Got: %s", expected, result, ) return } ``` {{% /card %}} {{% card title="testify" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/require" ) ... const expected = "expected result" result := printImports(input) require.Equalf(t, expected, result, "Expected: %s. Got: %s", expected, result, ) ``` {{% /card %}} {{< /cards >}} ### Usage with generics Assertion functions that support go generic types are suffixed with `T` (for "Type safety"). A formatted variant suffixed with `Tf` is also exposed. Obviously, the `Assertion` type cannot be extended with generic methods, as of `go1.25`. {{< cards >}} {{% card title="EqualT" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/require" ) ... const expected = "Hello World" var input := "World" result := someRamblingTextGeneration(input) require.EqualT(t, expected, result) ``` {{% /card %}} {{% card title="InDeltaT" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/require" ) ... const ( expected = 1.00 delta = 1E-6 ) var input = 1.01 result := someComplexComputation(input) require.InDeltaT(t, expected, input, delta) ``` {{% /card %}} {{< /cards >}} ## Licensing `SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers` This library ships under the [SPDX-License-Identifier: Apache-2.0](./project/LICENSE.md). See the license [NOTICE](./project/NOTICE.md), which recalls the licensing terms of all the pieces of software distributed with this fork, including internalized libraries. ## Contributing Feel free to submit issues, fork the repository and send pull requests! {{% notice style="primary" title="Info" icon="info" %}} Code generation is used. Run `go generate ./...` to update generated files. {{% /notice %}} See also our [CONTRIBUTING guidelines](./project/contributing/CONTRIBUTING.md). --- ## See Also **Getting Started:** - [Usage Guide](./usage/USAGE.md) - API conventions and how to navigate the documentation - [Tutorial](./usage/TUTORIAL.md) - Best practices and patterns for writing great tests - [Examples](./usage/EXAMPLES.md) - Practical code examples for common testing scenarios **Advanced Topics:** - [Generics Guide](./usage/GENERICS.md) - Type-safe assertions with generic functions - [Migration Guide](./usage/MIGRATION.md) - Migrating from stretchr/testify v1 - [Changes from v1](./usage/CHANGES.md) - All changes and improvements in v2 - [Benchmarks](./project/maintainers/benchmarks.md) - Performance improvements in v2 **Reference:** - [API Reference](./api/_index.md) - Complete assertion catalog organized by domain --- {{< children type="card" description="true" >}} [doc-examples]: ./usage/EXAMPLES.md go-openapi-testify-c10ca71/docs/doc-site/api/000077500000000000000000000000001520301377500210515ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/doc-site/api/_index.md000066400000000000000000000047731520301377500226540ustar00rootroot00000000000000--- title: "Assertions index" description: | Index Of Assertion Domains. Find the assertion function you need for your data. weight: 1 --- **The v2 our tests wanted** The [`testify/v2`][testifyv2] package has a fairly large API surface. This is in part inherited from the original [`testify`][testifyv1] and also largely due to the many available variants of the same assertion function. The following sections organize the assertion API into usage domains, with all documented exported variants documented in a more concise form than the [reference godoc documentation](https://pkg.go.dev/github.com/go-openapi/testify/v2). [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} ## Domains The `testify` API is organized in 19 logical domains shown below. Each domain contains assertions regrouped by their use case (e.g. http, json, error). {{< children type="card" description="true" >}} --- - [Boolean](./boolean.md) - Asserting Boolean Values (4) - [Collection](./collection.md) - Asserting Slices And Maps (23) - [Comparison](./comparison.md) - Comparing Ordered Values (12) - [Condition](./condition.md) - Expressing Assertions Using Conditions (9) - [Equality](./equality.md) - Asserting Two Things Are Equal (16) - [Error](./error.md) - Asserting Errors (8) - [File](./file.md) - Asserting OS Files (6) - [Http](./http.md) - Asserting HTTP Response And Body (7) - [Json](./json.md) - Asserting JSON Documents (5) - [Number](./number.md) - Asserting Numbers (9) - [Ordering](./ordering.md) - Asserting How Collections Are Ordered (10) - [Panic](./panic.md) - Asserting A Panic Behavior (4) - [Safety](./safety.md) - Checks Against Leaked Resources (Goroutines, File Descriptors) (2) - [String](./string.md) - Asserting Strings (4) - [Testing](./testing.md) - Mimics Methods From The Testing Standard Library (2) - [Time](./time.md) - Asserting Times And Durations (2) - [Type](./type.md) - Asserting Types Rather Than Values (10) - [Yaml](./yaml.md) - Asserting Yaml Documents (5) - [Common](./common.md) - Other Uncategorized Helpers (3) --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 [testifyv2]: https://github.com/go-openapi/testify [testifyv1]: https://github.com/stretchr/testify go-openapi-testify-c10ca71/docs/doc-site/api/boolean.md000066400000000000000000000315421520301377500230170ustar00rootroot00000000000000--- title: "Boolean" description: "Asserting Boolean Values" weight: 1 domains: - "boolean" keywords: - "False" - "Falsef" - "FalseT" - "FalseTf" - "True" - "Truef" - "TrueT" - "TrueTf" --- Asserting Boolean Values ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 4 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [False](#false) | angles-right - [FalseT[B Boolean]](#falsetb-boolean) | star | orange - [True](#true) | angles-right - [TrueT[B Boolean]](#truetb-boolean) | star | orange ``` ### False{#false} False asserts that the specified value is false. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.False(t, myBool) success: 1 == 0 failure: 1 == 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFalse(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T) success := assert.False(t, 1 == 0) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFalse(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFalse(t *testing.T) require.False(t, 1 == 0) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.False(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#False) | package-level function | | [`assert.Falsef(t T, value bool, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Falsef) | formatted variant | | [`assert.(*Assertions).False(value bool) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.False) | method variant | | [`assert.(*Assertions).Falsef(value bool, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Falsef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.False(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#False) | package-level function | | [`require.Falsef(t T, value bool, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Falsef) | formatted variant | | [`require.(*Assertions).False(value bool) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.False) | method variant | | [`require.(*Assertions).Falsef(value bool, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Falsef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.False(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#False) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#False](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L67) {{% /tab %}} {{< /tabs >}} ### FalseT[B Boolean] {{% icon icon="star" color=orange %}}{#falsetb-boolean} FalseT asserts that the specified value is false. The type constraint [Boolean](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Boolean) accepts any type which underlying type is bool. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go type B bool var b B = true assertions.FalseT(t, b) success: 1 == 0 failure: 1 == 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFalseT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T) success := assert.FalseT(t, 1 == 0) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFalseT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFalseT(t *testing.T) require.FalseT(t, 1 == 0) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FalseT) | package-level function | | [`assert.FalseTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FalseTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FalseT) | package-level function | | [`require.FalseTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FalseTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FalseT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FalseT](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L94) {{% /tab %}} {{< /tabs >}} ### True{#true} True asserts that the specified value is true. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.True(t, myBool) success: 1 == 1 failure: 1 == 0 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestTrue(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T) success := assert.True(t, 1 == 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestTrue(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestTrue(t *testing.T) require.True(t, 1 == 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.True(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#True) | package-level function | | [`assert.Truef(t T, value bool, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Truef) | formatted variant | | [`assert.(*Assertions).True(value bool) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.True) | method variant | | [`assert.(*Assertions).Truef(value bool, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Truef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.True(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#True) | package-level function | | [`require.Truef(t T, value bool, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Truef) | formatted variant | | [`require.(*Assertions).True(value bool) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.True) | method variant | | [`require.(*Assertions).Truef(value bool, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Truef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.True(t T, value bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#True) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#True](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L16) {{% /tab %}} {{< /tabs >}} ### TrueT[B Boolean] {{% icon icon="star" color=orange %}}{#truetb-boolean} TrueT asserts that the specified value is true. The type constraint [Boolean](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Boolean) accepts any type which underlying type is bool. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go type B bool var b B = true assertions.True(t, b) success: 1 == 1 failure: 1 == 0 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestTrueT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T) success := assert.TrueT(t, 1 == 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestTrueT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestTrueT(t *testing.T) require.TrueT(t, 1 == 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#TrueT) | package-level function | | [`assert.TrueTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#TrueTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#TrueT) | package-level function | | [`require.TrueTf[B Boolean](t T, value B, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#TrueTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#TrueT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#TrueT](https://github.com/go-openapi/testify/blob/master/internal/assertions/boolean.go#L44) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/collection.md000066400000000000000000002460551520301377500235420ustar00rootroot00000000000000--- title: "Collection" description: "Asserting Slices And Maps" weight: 2 domains: - "collection" keywords: - "Contains" - "Containsf" - "ElementsMatch" - "ElementsMatchf" - "ElementsMatchT" - "ElementsMatchTf" - "Len" - "Lenf" - "MapContainsT" - "MapContainsTf" - "MapEqualT" - "MapEqualTf" - "MapNotContainsT" - "MapNotContainsTf" - "MapNotEqualT" - "MapNotEqualTf" - "NotContains" - "NotContainsf" - "NotElementsMatch" - "NotElementsMatchf" - "NotElementsMatchT" - "NotElementsMatchTf" - "NotSubset" - "NotSubsetf" - "SeqContainsT" - "SeqContainsTf" - "SeqNotContainsT" - "SeqNotContainsTf" - "SliceContainsT" - "SliceContainsTf" - "SliceEqualT" - "SliceEqualTf" - "SliceNotContainsT" - "SliceNotContainsTf" - "SliceNotEqualT" - "SliceNotEqualTf" - "SliceNotSubsetT" - "SliceNotSubsetTf" - "SliceSubsetT" - "SliceSubsetTf" - "StringContainsT" - "StringContainsTf" - "StringNotContainsT" - "StringNotContainsTf" - "Subset" - "Subsetf" --- Asserting Slices And Maps ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 23 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [Contains](#contains) | angles-right - [ElementsMatch](#elementsmatch) | angles-right - [ElementsMatchT[E comparable]](#elementsmatchte-comparable) | star | orange - [Len](#len) | angles-right - [MapContainsT[Map ~map[K]V, K comparable, V any]](#mapcontainstmap-mapkv-k-comparable-v-any) | star | orange - [MapEqualT[K, V comparable]](#mapequaltk-v-comparable) | star | orange - [MapNotContainsT[Map ~map[K]V, K comparable, V any]](#mapnotcontainstmap-mapkv-k-comparable-v-any) | star | orange - [MapNotEqualT[K, V comparable]](#mapnotequaltk-v-comparable) | star | orange - [NotContains](#notcontains) | angles-right - [NotElementsMatch](#notelementsmatch) | angles-right - [NotElementsMatchT[E comparable]](#notelementsmatchte-comparable) | star | orange - [NotSubset](#notsubset) | angles-right - [SeqContainsT[E comparable]](#seqcontainste-comparable) | star | orange - [SeqNotContainsT[E comparable]](#seqnotcontainste-comparable) | star | orange - [SliceContainsT[Slice ~[]E, E comparable]](#slicecontainstslice-e-e-comparable) | star | orange - [SliceEqualT[E comparable]](#sliceequalte-comparable) | star | orange - [SliceNotContainsT[Slice ~[]E, E comparable]](#slicenotcontainstslice-e-e-comparable) | star | orange - [SliceNotEqualT[E comparable]](#slicenotequalte-comparable) | star | orange - [SliceNotSubsetT[Slice ~[]E, E comparable]](#slicenotsubsettslice-e-e-comparable) | star | orange - [SliceSubsetT[Slice ~[]E, E comparable]](#slicesubsettslice-e-e-comparable) | star | orange - [StringContainsT[ADoc, EDoc Text]](#stringcontainstadoc-edoc-text) | star | orange - [StringNotContainsT[ADoc, EDoc Text]](#stringnotcontainstadoc-edoc-text) | star | orange - [Subset](#subset) | angles-right ``` ### Contains{#contains} Contains asserts that the specified string, list(array, slice...) or map contains the specified substring or element. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Contains(t, "Hello World", "World") assertions.Contains(t, []string{"Hello", "World"}, "World") assertions.Contains(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "World"}, "Hello") success: []string{"A","B"}, "A" failure: []string{"A","B"}, "C" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestContains(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T) success := assert.Contains(t, []string{"A", "B"}, "A") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestContains(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestContains(t *testing.T) require.Contains(t, []string{"A", "B"}, "A") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Contains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Contains) | package-level function | | [`assert.Containsf(t T, s any, contains any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Containsf) | formatted variant | | [`assert.(*Assertions).Contains(s any, contains any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Contains) | method variant | | [`assert.(*Assertions).Containsf(s any, contains any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Containsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Contains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Contains) | package-level function | | [`require.Containsf(t T, s any, contains any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Containsf) | formatted variant | | [`require.(*Assertions).Contains(s any, contains any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Contains) | method variant | | [`require.(*Assertions).Containsf(s any, contains any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Containsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Contains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Contains) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Contains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L64) {{% /tab %}} {{< /tabs >}} ### ElementsMatch{#elementsmatch} ElementsMatch asserts that the specified listA(array, slice...) is equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should match. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} failure: []int{1, 2, 3}, []int{1, 2, 4} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestElementsMatch(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T) success := assert.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestElementsMatch(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestElementsMatch(t *testing.T) require.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatch) | package-level function | | [`assert.ElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatchf) | formatted variant | | [`assert.(*Assertions).ElementsMatch(listA any, listB any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ElementsMatch) | method variant | | [`assert.(*Assertions).ElementsMatchf(listA any, listB any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ElementsMatchf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ElementsMatch) | package-level function | | [`require.ElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ElementsMatchf) | formatted variant | | [`require.(*Assertions).ElementsMatch(listA any, listB any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ElementsMatch) | method variant | | [`require.(*Assertions).ElementsMatchf(listA any, listB any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ElementsMatchf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.ElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L546) {{% /tab %}} {{< /tabs >}} ### ElementsMatchT[E comparable] {{% icon icon="star" color=orange %}}{#elementsmatchte-comparable} ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should match. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} failure: []int{1, 2, 3}, []int{1, 2, 4} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestElementsMatchT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T) success := assert.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestElementsMatchT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestElementsMatchT(t *testing.T) require.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatchT) | package-level function | | [`assert.ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatchTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ElementsMatchT) | package-level function | | [`require.ElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ElementsMatchTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.ElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#ElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L620) {{% /tab %}} {{< /tabs >}} ### Len{#len} Len asserts that the specified object has specific length. Len also fails if the object has a type that len() does not accept. The asserted object can be a string, a slice, a map, an array, pointer to array or a channel. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Len(t, mySlice, 3) assertions.Len(t, myString, 4) assertions.Len(t, myMap, 5) success: []string{"A","B"}, 2 failure: []string{"A","B"}, 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLen(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T) success := assert.Len(t, []string{"A", "B"}, 2) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLen(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLen(t *testing.T) require.Len(t, []string{"A", "B"}, 2) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Len(t T, object any, length int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Len) | package-level function | | [`assert.Lenf(t T, object any, length int, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Lenf) | formatted variant | | [`assert.(*Assertions).Len(object any, length int) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Len) | method variant | | [`assert.(*Assertions).Lenf(object any, length int, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Lenf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Len(t T, object any, length int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Len) | package-level function | | [`require.Lenf(t T, object any, length int, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Lenf) | formatted variant | | [`require.(*Assertions).Len(object any, length int) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Len) | method variant | | [`require.(*Assertions).Lenf(object any, length int, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Lenf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Len(t T, object any, length int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Len) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Len](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L32) > **Note** > > (proposal for enhancement) this does not currently support iterators, or collection objects that have a Len() method. > {{% /tab %}} {{< /tabs >}} ### MapContainsT[Map ~map[K]V, K comparable, V any] {{% icon icon="star" color=orange %}}{#mapcontainstmap-mapkv-k-comparable-v-any} MapContainsT asserts that the specified map contains a key. Go native comparable types are explained there: [comparable-types](https://go.dev/blog/comparable). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.MapContainsT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "x","World": "y"}, "World") success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A" failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T) success := assert.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapContainsT(t *testing.T) require.MapContainsT(t, map[string]string{"A": "B"}, "A") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapContainsT) | package-level function | | [`assert.MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapContainsT) | package-level function | | [`require.MapContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L182) {{% /tab %}} {{< /tabs >}} ### MapEqualT[K, V comparable] {{% icon icon="star" color=orange %}}{#mapequaltk-v-comparable} MapEqualT asserts that 2 maps of comparable elements are equal, that is have the same length and contain the keys with the same elements. See also [maps.Equal](https://pkg.go.dev/maps#Equal). [comparable-types]: https://go.dev/blog/comparable {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.MapEqualT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}) success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"2": "Hello", "1": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) success := assert.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapEqualT(t *testing.T) require.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapEqualT) | package-level function | | [`assert.MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapEqualT) | package-level function | | [`require.MapEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.MapEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L743) {{% /tab %}} {{< /tabs >}} ### MapNotContainsT[Map ~map[K]V, K comparable, V any] {{% icon icon="star" color=orange %}}{#mapnotcontainstmap-mapkv-k-comparable-v-any} MapNotContainsT asserts that the specified map does not contain a key. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.MapNotContainsT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"Hello": "x","World": "y"}, "hi") success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "C" failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"A": "B"}, "A" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) success := assert.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapNotContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapNotContainsT(t *testing.T) require.MapNotContainsT(t, map[string]string{"A": "B"}, "C") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotContainsT) | package-level function | | [`assert.MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotContainsT) | package-level function | | [`require.MapNotContainsTf[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L314) {{% /tab %}} {{< /tabs >}} ### MapNotEqualT[K, V comparable] {{% icon icon="star" color=orange %}}{#mapnotequaltk-v-comparable} MapNotEqualT asserts that 2 maps of comparable elements are not equal. See also [MapEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapEqualT). [comparable-types]: https://go.dev/blog/comparable {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.MapNotEqualT(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"2": "Hello", "1": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}) success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"2": "Hello", "1": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)string{"1": "Hello", "2": "World"} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapNotEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) success := assert.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestMapNotEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestMapNotEqualT(t *testing.T) require.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotEqualT) | package-level function | | [`assert.MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#MapNotEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotEqualT) | package-level function | | [`require.MapNotEqualTf[K, V comparable](t T, listA map[K]V, listB map[K]V, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#MapNotEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.MapNotEqualT[K, V comparable](t T, listA map[K]V, listB map[K]V, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#MapNotEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#MapNotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L768) {{% /tab %}} {{< /tabs >}} ### NotContains{#notcontains} NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the specified substring or element. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotContains(t, "Hello World", "Earth") assertions.NotContains(t, ["Hello", "World"], "Earth") assertions.NotContains(t, {"Hello": "World"}, "Earth") success: []string{"A","B"}, "C" failure: []string{"A","B"}, "B" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotContains(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T) success := assert.NotContains(t, []string{"A", "B"}, "C") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotContains(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotContains(t *testing.T) require.NotContains(t, []string{"A", "B"}, "C") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotContains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotContains) | package-level function | | [`assert.NotContainsf(t T, s any, contains any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotContainsf) | formatted variant | | [`assert.(*Assertions).NotContains(s any, contains any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotContains) | method variant | | [`assert.(*Assertions).NotContainsf(s any, contains any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotContainsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotContains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotContains) | package-level function | | [`require.NotContainsf(t T, s any, contains any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotContainsf) | formatted variant | | [`require.(*Assertions).NotContains(s any, contains any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotContains) | method variant | | [`require.(*Assertions).NotContainsf(s any, contains any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotContainsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotContains(t T, s any, contains any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotContains) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L210) {{% /tab %}} {{< /tabs >}} ### NotElementsMatch{#notelementsmatch} NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should not match. This is an inverse of [ElementsMatch](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatch). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true assertions.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true success: []int{1, 2, 3}, []int{1, 2, 4} failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T) success := assert.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotElementsMatch(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatch(t *testing.T) require.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotElementsMatch) | package-level function | | [`assert.NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotElementsMatchf) | formatted variant | | [`assert.(*Assertions).NotElementsMatch(listA any, listB any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotElementsMatch) | method variant | | [`assert.(*Assertions).NotElementsMatchf(listA any, listB any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotElementsMatchf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotElementsMatch) | package-level function | | [`require.NotElementsMatchf(t T, listA any, listB any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotElementsMatchf) | formatted variant | | [`require.(*Assertions).NotElementsMatch(listA any, listB any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotElementsMatch) | method variant | | [`require.(*Assertions).NotElementsMatchf(listA any, listB any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotElementsMatchf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotElementsMatch(t T, listA any, listB any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatch](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L584) {{% /tab %}} {{< /tabs >}} ### NotElementsMatchT[E comparable] {{% icon icon="star" color=orange %}}{#notelementsmatchte-comparable} NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, the number of appearances of each of them in both lists should not match. This is an inverse of [ElementsMatch](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ElementsMatch). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true assertions.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true success: []int{1, 2, 3}, []int{1, 2, 4} failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T) success := assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotElementsMatchT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotElementsMatchT(t *testing.T) require.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotElementsMatchT) | package-level function | | [`assert.NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotElementsMatchTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotElementsMatchT) | package-level function | | [`require.NotElementsMatchTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotElementsMatchTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotElementsMatchT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotElementsMatchT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L657) {{% /tab %}} {{< /tabs >}} ### NotSubset{#notsubset} NotSubset asserts that the list (array, slice, or map) does NOT contain all elements given in the subset (array, slice, or map). Map elements are key-value pairs unless compared with an array or slice where only the map key is evaluated. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotSubset(t, [1, 3, 4], [1, 2]) assertions.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) assertions.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) assertions.NotSubset(t, {"x": 1, "y": 2}, ["z"]) success: []int{1, 2, 3}, []int{4, 5} failure: []int{1, 2, 3}, []int{1, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSubset(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T) success := assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSubset(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSubset(t *testing.T) require.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotSubset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSubset) | package-level function | | [`assert.NotSubsetf(t T, list any, subset any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSubsetf) | formatted variant | | [`assert.(*Assertions).NotSubset(list any, subset any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotSubset) | method variant | | [`assert.(*Assertions).NotSubsetf(list any, subset any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotSubsetf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotSubset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSubset) | package-level function | | [`require.NotSubsetf(t T, list any, subset any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSubsetf) | formatted variant | | [`require.(*Assertions).NotSubset(list any, subset any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotSubset) | method variant | | [`require.(*Assertions).NotSubsetf(list any, subset any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotSubsetf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotSubset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSubset) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSubset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L452) {{% /tab %}} {{< /tabs >}} ### SeqContainsT[E comparable] {{% icon icon="star" color=orange %}}{#seqcontainste-comparable} SeqContainsT asserts that the specified iterator contains a comparable element. The sequence may not be consumed entirely: the iteration stops as soon as the specified element is found. Go native comparable types are explained there: [comparable-types](https://go.dev/blog/comparable). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") success: slices.Values([]string{"A","B"}), "A" failure: slices.Values([]string{"A","B"}), "C" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSeqContainsT(t *testing.T) package main import ( "fmt" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T) success := assert.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSeqContainsT(t *testing.T) package main import ( "fmt" "slices" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSeqContainsT(t *testing.T) require.SeqContainsT(t, slices.Values([]string{"A", "B"}), "A") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqContainsT) | package-level function | | [`assert.SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqContainsT) | package-level function | | [`require.SeqContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L152) {{% /tab %}} {{< /tabs >}} ### SeqNotContainsT[E comparable] {{% icon icon="star" color=orange %}}{#seqnotcontainste-comparable} SeqNotContainsT asserts that the specified iterator does not contain a comparable element. See [SeqContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqContainsT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") success: slices.Values([]string{"A","B"}), "C" failure: slices.Values([]string{"A","B"}), "A" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T) package main import ( "fmt" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T) success := assert.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSeqNotContainsT(t *testing.T) package main import ( "fmt" "slices" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSeqNotContainsT(t *testing.T) require.SeqNotContainsT(t, slices.Values([]string{"A", "B"}), "C") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqNotContainsT) | package-level function | | [`assert.SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SeqNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqNotContainsT) | package-level function | | [`require.SeqNotContainsTf[E comparable](t T, iter iter.Seq[E], element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SeqNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SeqNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L289) {{% /tab %}} {{< /tabs >}} ### SliceContainsT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicecontainstslice-e-e-comparable} SliceContainsT asserts that the specified slice contains a comparable element. Go native comparable types are explained there: [comparable-types](https://go.dev/blog/comparable). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SliceContainsT(t, []{"Hello","World"}, "World") success: []string{"A","B"}, "A" failure: []string{"A","B"}, "C" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T) success := assert.SliceContainsT(t, []string{"A", "B"}, "A") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceContainsT(t *testing.T) require.SliceContainsT(t, []string{"A", "B"}, "A") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceContainsT) | package-level function | | [`assert.SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceContainsT) | package-level function | | [`require.SliceContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L122) {{% /tab %}} {{< /tabs >}} ### SliceEqualT[E comparable] {{% icon icon="star" color=orange %}}{#sliceequalte-comparable} SliceEqualT asserts that 2 slices of comparable elements are equal, that is have the same length and contain the same elements in the same order. See also [slices.Equal](https://pkg.go.dev/slices#Equal). [comparable-types]: https://go.dev/blog/comparable {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) success: []string{"Hello","World"}, []string{"Hello","World"} failure: []string{"Hello","World"}, []string{"Hello"} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) success := assert.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceEqualT(t *testing.T) require.SliceEqualT(t, []string{"Hello", "World"}, []string{"Hello", "World"}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceEqualT) | package-level function | | [`assert.SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceEqualT) | package-level function | | [`require.SliceEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SliceEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L693) {{% /tab %}} {{< /tabs >}} ### SliceNotContainsT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicenotcontainstslice-e-e-comparable} SliceNotContainsT asserts that the specified slice does not contain a comparable element. See [SliceContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceContainsT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") success: []string{"A","B"}, "C" failure: []string{"A","B"}, "A" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) success := assert.SliceNotContainsT(t, []string{"A", "B"}, "C") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceNotContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotContainsT(t *testing.T) require.SliceNotContainsT(t, []string{"A", "B"}, "C") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotContainsT) | package-level function | | [`assert.SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotContainsT) | package-level function | | [`require.SliceNotContainsTf[Slice ~[]E, E comparable](t T, s Slice, element E, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L264) {{% /tab %}} {{< /tabs >}} ### SliceNotEqualT[E comparable] {{% icon icon="star" color=orange %}}{#slicenotequalte-comparable} SliceNotEqualT asserts that 2 slices of comparable elements are not equal. See also [SliceEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceEqualT). [comparable-types]: https://go.dev/blog/comparable {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) success: []string{"Hello","World"}, []string{"Hello"} failure: []string{"Hello","World"}, []string{"Hello","World"} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceNotEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) success := assert.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceNotEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotEqualT(t *testing.T) require.SliceNotEqualT(t, []string{"Hello", "World"}, []string{"Hello"}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotEqualT) | package-level function | | [`assert.SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotEqualT) | package-level function | | [`require.SliceNotEqualTf[E comparable](t T, listA []E, listB []E, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SliceNotEqualT[E comparable](t T, listA []E, listB []E, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L718) {{% /tab %}} {{< /tabs >}} ### SliceNotSubsetT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicenotsubsettslice-e-e-comparable} SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) success: []int{1, 2, 3}, []int{4, 5} failure: []int{1, 2, 3}, []int{1, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) success := assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceNotSubsetT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceNotSubsetT(t *testing.T) require.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotSubsetT) | package-level function | | [`assert.SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceNotSubsetTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotSubsetT) | package-level function | | [`require.SliceNotSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceNotSubsetTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SliceNotSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceNotSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L519) {{% /tab %}} {{< /tabs >}} ### SliceSubsetT[Slice ~[]E, E comparable] {{% icon icon="star" color=orange %}}{#slicesubsettslice-e-e-comparable} SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) success: []int{1, 2, 3}, []int{1, 2} failure: []int{1, 2, 3}, []int{4, 5} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T) success := assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSliceSubsetT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSliceSubsetT(t *testing.T) require.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceSubsetT) | package-level function | | [`assert.SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SliceSubsetTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceSubsetT) | package-level function | | [`require.SliceSubsetTf[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SliceSubsetTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SliceSubsetT[Slice ~[]E, E comparable](t T, list Slice, subset Slice, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SliceSubsetT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L420) {{% /tab %}} {{< /tabs >}} ### StringContainsT[ADoc, EDoc Text] {{% icon icon="star" color=orange %}}{#stringcontainstadoc-edoc-text} StringContainsT asserts that a string contains the specified substring. Strings may be go strings or []byte according to the type constraint [Text](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Text). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.StringContainsT(t, "Hello World", "World") success: "AB", "A" failure: "AB", "C" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestStringContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T) success := assert.StringContainsT(t, "AB", "A") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestStringContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestStringContainsT(t *testing.T) require.StringContainsT(t, "AB", "A") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringContainsT) | package-level function | | [`assert.StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringContainsT) | package-level function | | [`require.StringContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L94) {{% /tab %}} {{< /tabs >}} ### StringNotContainsT[ADoc, EDoc Text] {{% icon icon="star" color=orange %}}{#stringnotcontainstadoc-edoc-text} StringNotContainsT asserts that a string does not contain the specified substring. See [StringContainsT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringContainsT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.StringNotContainsT(t, "Hello World", "hi") success: "AB", "C" failure: "AB", "A" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T) success := assert.StringNotContainsT(t, "AB", "C") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestStringNotContainsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestStringNotContainsT(t *testing.T) require.StringNotContainsT(t, "AB", "C") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringNotContainsT) | package-level function | | [`assert.StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#StringNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringNotContainsT) | package-level function | | [`require.StringNotContainsTf[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#StringNotContainsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#StringNotContainsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L239) {{% /tab %}} {{< /tabs >}} ### Subset{#subset} Subset asserts that the list (array, slice, or map) contains all elements given in the subset (array, slice, or map). Map elements are key-value pairs unless compared with an array or slice where only the map key is evaluated. nil values are considered as empty sets. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) assertions.Subset(t, []int{1, 2, 3}, map[int](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#int)string{1: "one", 2: "two"}) assertions.Subset(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)int{"x": 1, "y": 2}, []string{"x"}) success: []int{1, 2, 3}, []int{1, 2} failure: []int{1, 2, 3}, []int{4, 5} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSubset(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T) success := assert.Subset(t, []int{1, 2, 3}, []int{1, 2}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSubset(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSubset(t *testing.T) require.Subset(t, []int{1, 2, 3}, []int{1, 2}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Subset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Subset) | package-level function | | [`assert.Subsetf(t T, list any, subset any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Subsetf) | formatted variant | | [`assert.(*Assertions).Subset(list any, subset any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Subset) | method variant | | [`assert.(*Assertions).Subsetf(list any, subset any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Subsetf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Subset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Subset) | package-level function | | [`require.Subsetf(t T, list any, subset any, msg string, args ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Subsetf) | formatted variant | | [`require.(*Assertions).Subset(list any, subset any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Subset) | method variant | | [`require.(*Assertions).Subsetf(list any, subset any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Subsetf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Subset(t T, list any, subset any, msgAndArgs ...any) (ok bool)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Subset) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Subset](https://github.com/go-openapi/testify/blob/master/internal/assertions/collection.go#L349) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/common.md000066400000000000000000000171571520301377500226760ustar00rootroot00000000000000--- title: "Common" description: "Other Uncategorized Helpers" weight: 19 domains: - "common" keywords: - "CallerInfo" - "CallerInfof" - "ObjectsAreEqual" - "ObjectsAreEqualf" - "ObjectsAreEqualValues" - "ObjectsAreEqualValuesf" --- Other Uncategorized Helpers ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 3 functionalities. ```tree ``` --- ## Other helpers ### CallerInfo{#callerinfo} CallerInfo returns an array of strings containing the file and line number of each stack frame leading from the current test to the assert call that failed. {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.CallerInfo() []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#CallerInfo) | package-level function | | [`assert.CallerInfof(t T, , msg string, args ...any) []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#CallerInfof) | formatted variant | | [`assert.(*Assertions).CallerInfo() []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.CallerInfo) | method variant | | [`assert.(*Assertions).CallerInfof(, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.CallerInfof) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.CallerInfo() []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#CallerInfo) | package-level function | | [`require.CallerInfof(t T, , msg string, args ...any) []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#CallerInfof) | formatted variant | | [`require.(*Assertions).CallerInfo() []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.CallerInfo) | method variant | | [`require.(*Assertions).CallerInfof(, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.CallerInfof) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.CallerInfo() []string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#CallerInfo) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#CallerInfo](https://github.com/go-openapi/testify/blob/master/internal/assertions/testing.go#L70) > **Maintainer Note** > > it is not necessary to export CallerInfo. This should remain an internal implementation detail. > {{% /tab %}} {{< /tabs >}} ### ObjectsAreEqual{#objectsareequal} ObjectsAreEqual determines if two objects are considered equal. This function does no assertion of any kind. {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ObjectsAreEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ObjectsAreEqual) | package-level function | | [`assert.ObjectsAreEqualf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ObjectsAreEqualf) | formatted variant | | [`assert.(*Assertions).ObjectsAreEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ObjectsAreEqual) | method variant | | [`assert.(*Assertions).ObjectsAreEqualf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ObjectsAreEqualf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ObjectsAreEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ObjectsAreEqual) | package-level function | | [`require.ObjectsAreEqualf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ObjectsAreEqualf) | formatted variant | | [`require.(*Assertions).ObjectsAreEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ObjectsAreEqual) | method variant | | [`require.(*Assertions).ObjectsAreEqualf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ObjectsAreEqualf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.ObjectsAreEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ObjectsAreEqual) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#ObjectsAreEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/object.go#L14) {{% /tab %}} {{< /tabs >}} ### ObjectsAreEqualValues{#objectsareequalvalues} ObjectsAreEqualValues gets whether two objects are equal, or if their values are equal. {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ObjectsAreEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ObjectsAreEqualValues) | package-level function | | [`assert.ObjectsAreEqualValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ObjectsAreEqualValuesf) | formatted variant | | [`assert.(*Assertions).ObjectsAreEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ObjectsAreEqualValues) | method variant | | [`assert.(*Assertions).ObjectsAreEqualValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ObjectsAreEqualValuesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ObjectsAreEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ObjectsAreEqualValues) | package-level function | | [`require.ObjectsAreEqualValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ObjectsAreEqualValuesf) | formatted variant | | [`require.(*Assertions).ObjectsAreEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ObjectsAreEqualValues) | method variant | | [`require.(*Assertions).ObjectsAreEqualValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ObjectsAreEqualValuesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.ObjectsAreEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#ObjectsAreEqualValues) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#ObjectsAreEqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/object.go#L38) {{% /tab %}} {{< /tabs >}} --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/comparison.md000066400000000000000000001302411520301377500235460ustar00rootroot00000000000000--- title: "Comparison" description: "Comparing Ordered Values" weight: 3 domains: - "comparison" keywords: - "Greater" - "Greaterf" - "GreaterOrEqual" - "GreaterOrEqualf" - "GreaterOrEqualT" - "GreaterOrEqualTf" - "GreaterT" - "GreaterTf" - "Less" - "Lessf" - "LessOrEqual" - "LessOrEqualf" - "LessOrEqualT" - "LessOrEqualTf" - "LessT" - "LessTf" - "Negative" - "Negativef" - "NegativeT" - "NegativeTf" - "Positive" - "Positivef" - "PositiveT" - "PositiveTf" --- Comparing Ordered Values ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 12 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [Greater](#greater) | angles-right - [GreaterOrEqual](#greaterorequal) | angles-right - [GreaterOrEqualT[Orderable Ordered]](#greaterorequaltorderable-ordered) | star | orange - [GreaterT[Orderable Ordered]](#greatertorderable-ordered) | star | orange - [Less](#less) | angles-right - [LessOrEqual](#lessorequal) | angles-right - [LessOrEqualT[Orderable Ordered]](#lessorequaltorderable-ordered) | star | orange - [LessT[Orderable Ordered]](#lesstorderable-ordered) | star | orange - [Negative](#negative) | angles-right - [NegativeT[SignedNumber SignedNumeric]](#negativetsignednumber-signednumeric) | star | orange - [Positive](#positive) | angles-right - [PositiveT[SignedNumber SignedNumeric]](#positivetsignednumber-signednumeric) | star | orange ``` ### Greater{#greater} Greater asserts that the first element is strictly greater than the second. Both elements must be of the same type in the [reflect.Kind](https://pkg.go.dev/reflect#Kind) sense. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Greater(t, 2, 1) assertions.Greater(t, float64(2), float64(1)) assertions.Greater(t, "b", "a") success: 2, 1 failure: 1, 2 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreater(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T) success := assert.Greater(t, 2, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreater(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreater(t *testing.T) require.Greater(t, 2, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Greater) | package-level function | | [`assert.Greaterf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Greaterf) | formatted variant | | [`assert.(*Assertions).Greater(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Greater) | method variant | | [`assert.(*Assertions).Greaterf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Greaterf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Greater) | package-level function | | [`require.Greaterf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Greaterf) | formatted variant | | [`require.(*Assertions).Greater(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Greater) | method variant | | [`require.(*Assertions).Greaterf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Greaterf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Greater) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Greater](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L30) {{% /tab %}} {{< /tabs >}} ### GreaterOrEqual{#greaterorequal} GreaterOrEqual asserts that the first element is greater than or equal to the second. See also [Greater](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Greater). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.GreaterOrEqual(t, 2, 1) assertions.GreaterOrEqual(t, 2, 2) assertions.GreaterOrEqual(t, "b", "a") assertions.GreaterOrEqual(t, "b", "b") success: 2, 1 failure: 1, 2 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T) success := assert.GreaterOrEqual(t, 2, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreaterOrEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqual(t *testing.T) require.GreaterOrEqual(t, 2, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqual) | package-level function | | [`assert.GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualf) | formatted variant | | [`assert.(*Assertions).GreaterOrEqual(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.GreaterOrEqual) | method variant | | [`assert.(*Assertions).GreaterOrEqualf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.GreaterOrEqualf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterOrEqual) | package-level function | | [`require.GreaterOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterOrEqualf) | formatted variant | | [`require.(*Assertions).GreaterOrEqual(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.GreaterOrEqual) | method variant | | [`require.(*Assertions).GreaterOrEqualf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.GreaterOrEqualf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqual) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L93) {{% /tab %}} {{< /tabs >}} ### GreaterOrEqualT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#greaterorequaltorderable-ordered} GreaterOrEqualT asserts that for two elements of the same type, the first element is greater than or equal to the second. The [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), []byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). Notice that pointers are not [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered), but uintptr are. So you can't call [GreaterOrEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualT) with [*time.Time]. [GreaterOrEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualT) ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [GreaterOrEqual](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqual) instead. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.GreaterOrEqualT(t, 2, 1) assertions.GreaterOrEqualT(t, 2, 2) assertions.GreaterOrEqualT(t, "b", "a") assertions.GreaterOrEqualT(t, "b", "b") success: 2, 1 failure: 1, 2 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T) success := assert.GreaterOrEqualT(t, 2, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreaterOrEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreaterOrEqualT(t *testing.T) require.GreaterOrEqualT(t, 2, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualT) | package-level function | | [`assert.GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterOrEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterOrEqualT) | package-level function | | [`require.GreaterOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterOrEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.GreaterOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterOrEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L127) {{% /tab %}} {{< /tabs >}} ### GreaterT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#greatertorderable-ordered} GreaterT asserts that for two elements of the same type, the first element is strictly greater than the second. The [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), []byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). Notice that pointers are not [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered), but uintptr are. So you can't call [GreaterT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterT) with [*time.Time]. [GreaterT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterT) ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Greater) instead. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.GreaterT(t, 2, 1) assertions.GreaterT(t, float64(2), float64(1)) assertions.GreaterT(t, "b", "a") assertions.GreaterT(t, time.Date(2026,1,1,0,0,0,0,nil), time.Now()) success: 2, 1 failure: 1, 2 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreaterT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T) success := assert.GreaterT(t, 2, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestGreaterT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestGreaterT(t *testing.T) require.GreaterT(t, 2, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterT) | package-level function | | [`assert.GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#GreaterTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterT) | package-level function | | [`require.GreaterTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#GreaterTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.GreaterT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#GreaterT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#GreaterT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L63) {{% /tab %}} {{< /tabs >}} ### Less{#less} Less asserts that the first element is strictly less than the second. Both elements must be of the same type in the [reflect.Kind](https://pkg.go.dev/reflect#Kind) sense. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Less(t, 1, 2) assertions.Less(t, float64(1), float64(2)) assertions.Less(t, "a", "b") success: 1, 2 failure: 2, 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLess(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T) success := assert.Less(t, 1, 2) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLess(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLess(t *testing.T) require.Less(t, 1, 2) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Less(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Less) | package-level function | | [`assert.Lessf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Lessf) | formatted variant | | [`assert.(*Assertions).Less(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Less) | method variant | | [`assert.(*Assertions).Lessf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Lessf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Less(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Less) | package-level function | | [`require.Lessf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Lessf) | formatted variant | | [`require.(*Assertions).Less(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Less) | method variant | | [`require.(*Assertions).Lessf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Lessf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Less(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Less) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Less](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L157) {{% /tab %}} {{< /tabs >}} ### LessOrEqual{#lessorequal} LessOrEqual asserts that the first element is less than or equal to the second. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.LessOrEqual(t, 1, 2) assertions.LessOrEqual(t, 2, 2) assertions.LessOrEqual(t, "a", "b") assertions.LessOrEqual(t, "b", "b") success: 1, 2 failure: 2, 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLessOrEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T) success := assert.LessOrEqual(t, 1, 2) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLessOrEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLessOrEqual(t *testing.T) require.LessOrEqual(t, 1, 2) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqual) | package-level function | | [`assert.LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualf) | formatted variant | | [`assert.(*Assertions).LessOrEqual(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.LessOrEqual) | method variant | | [`assert.(*Assertions).LessOrEqualf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.LessOrEqualf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessOrEqual) | package-level function | | [`require.LessOrEqualf(t T, e1 any, e2 any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessOrEqualf) | formatted variant | | [`require.(*Assertions).LessOrEqual(e1 any, e2 any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.LessOrEqual) | method variant | | [`require.(*Assertions).LessOrEqualf(e1 any, e2 any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.LessOrEqualf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessOrEqual) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessOrEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L215) {{% /tab %}} {{< /tabs >}} ### LessOrEqualT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#lessorequaltorderable-ordered} LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. The [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), []byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). Notice that pointers are not [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered), but uintptr are. So you can't call [LessOrEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualT) with [*time.Time]. [LessOrEqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualT) ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [LessOrEqual](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqual) instead. To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqual) instead. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.LessOrEqualT(t, 1, 2) assertions.LessOrEqualT(t, 2, 2) assertions.LessOrEqualT(t, "a", "b") assertions.LessOrEqualT(t, "b", "b") success: 1, 2 failure: 2, 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T) success := assert.LessOrEqualT(t, 1, 2) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLessOrEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLessOrEqualT(t *testing.T) require.LessOrEqualT(t, 1, 2) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualT) | package-level function | | [`assert.LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessOrEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessOrEqualT) | package-level function | | [`require.LessOrEqualTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessOrEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.LessOrEqualT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessOrEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessOrEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L247) {{% /tab %}} {{< /tabs >}} ### LessT[Orderable Ordered] {{% icon icon="star" color=orange %}}{#lesstorderable-ordered} LessT asserts that for two elements of the same type, the first element is strictly less than the second. The [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) type can be any of Go's [cmp.Ordered](https://pkg.go.dev/cmp#Ordered) (strings, numeric types), []byte (uses [bytes.Compare](https://pkg.go.dev/bytes#Compare)) and [time.Time](https://pkg.go.dev/time#Time) (uses [time.Time.Compare](https://pkg.go.dev/time#Time.Compare). Notice that pointers are not [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered), but uintptr are. So you can't call [LessT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessT) with [*time.Time]. [LessT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessT) ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Less](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Less) instead. To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.LessT(t, 1, 2) assertions.LessT(t, float64(1), float64(2)) assertions.LessT(t, "a", "b") success: 1, 2 failure: 2, 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLessT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T) success := assert.LessT(t, 1, 2) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestLessT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestLessT(t *testing.T) require.LessT(t, 1, 2) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessT) | package-level function | | [`assert.LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#LessTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessT) | package-level function | | [`require.LessTf[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#LessTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.LessT[Orderable Ordered](t T, e1 Orderable, e2 Orderable, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#LessT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#LessT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L188) {{% /tab %}} {{< /tabs >}} ### Negative{#negative} Negative asserts that the specified element is strictly negative. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Negative(t, -1) assertions.Negative(t, -1.23) success: -1 failure: 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNegative(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) success := assert.Negative(t, -1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNegative(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNegative(t *testing.T) require.Negative(t, -1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Negative(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Negative) | package-level function | | [`assert.Negativef(t T, e any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Negativef) | formatted variant | | [`assert.(*Assertions).Negative(e any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Negative) | method variant | | [`assert.(*Assertions).Negativef(e any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Negativef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Negative(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Negative) | package-level function | | [`require.Negativef(t T, e any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Negativef) | formatted variant | | [`require.(*Assertions).Negative(e any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Negative) | method variant | | [`require.(*Assertions).Negativef(e any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Negativef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Negative(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Negative) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Negative](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L320) {{% /tab %}} {{< /tabs >}} ### NegativeT[SignedNumber SignedNumeric] {{% icon icon="star" color=orange %}}{#negativetsignednumber-signednumeric} NegativeT asserts that the specified element of a signed numeric type is strictly negative. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NegativeT(t, -1) assertions.NegativeT(t, -1.23) success: -1 failure: 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNegativeT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T) success := assert.NegativeT(t, -1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNegativeT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNegativeT(t *testing.T) require.NegativeT(t, -1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NegativeT) | package-level function | | [`assert.NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NegativeTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NegativeT) | package-level function | | [`require.NegativeTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NegativeTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NegativeT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NegativeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L341) {{% /tab %}} {{< /tabs >}} ### Positive{#positive} Positive asserts that the specified element is strictly positive. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Positive(t, 1) assertions.Positive(t, 1.23) success: 1 failure: -1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPositive(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T) success := assert.Positive(t, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPositive(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPositive(t *testing.T) require.Positive(t, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Positive(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Positive) | package-level function | | [`assert.Positivef(t T, e any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Positivef) | formatted variant | | [`assert.(*Assertions).Positive(e any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Positive) | method variant | | [`assert.(*Assertions).Positivef(e any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Positivef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Positive(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Positive) | package-level function | | [`require.Positivef(t T, e any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Positivef) | formatted variant | | [`require.(*Assertions).Positive(e any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Positive) | method variant | | [`require.(*Assertions).Positivef(e any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Positivef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Positive(t T, e any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Positive) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Positive](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L272) {{% /tab %}} {{< /tabs >}} ### PositiveT[SignedNumber SignedNumeric] {{% icon icon="star" color=orange %}}{#positivetsignednumber-signednumeric} PositiveT asserts that the specified element of a signed numeric type is strictly positive. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.PositiveT(t, 1) assertions.PositiveT(t, 1.23) success: 1 failure: -1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPositiveT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T) success := assert.PositiveT(t, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPositiveT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPositiveT(t *testing.T) require.PositiveT(t, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PositiveT) | package-level function | | [`assert.PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PositiveTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PositiveT) | package-level function | | [`require.PositiveTf[SignedNumber SignedNumeric](t T, e SignedNumber, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PositiveTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#PositiveT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#PositiveT](https://github.com/go-openapi/testify/blob/master/internal/assertions/compare.go#L294) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/condition.md000066400000000000000000001615571520301377500234000ustar00rootroot00000000000000--- title: "Condition" description: "Expressing Assertions Using Conditions" weight: 4 domains: - "condition" keywords: - "Blocked" - "Blockedf" - "BlockedT" - "BlockedTf" - "Condition" - "Conditionf" - "Consistently" - "Consistentlyf" - "Eventually" - "Eventuallyf" - "EventuallyWith" - "EventuallyWithf" - "Never" - "Neverf" - "NotBlocked" - "NotBlockedf" - "NotBlockedT" - "NotBlockedTf" --- Expressing Assertions Using Conditions ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 9 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [Blocked](#blocked) | angles-right - [BlockedT[E any, CHAN ~chan E]](#blockedte-any-chan-chan-e) | star | orange - [Condition](#condition) | angles-right - [Consistently[C Conditioner]](#consistentlyc-conditioner) | star | orange - [Eventually[C Conditioner]](#eventuallyc-conditioner) | star | orange - [EventuallyWith[C CollectibleConditioner]](#eventuallywithc-collectibleconditioner) | star | orange - [Never[C NeverConditioner]](#neverc-neverconditioner) | star | orange - [NotBlocked](#notblocked) | angles-right - [NotBlockedT[E any, CHAN ~chan E]](#notblockedte-any-chan-chan-e) | star | orange ``` ### Blocked{#blocked} Blocked asserts that a channel is blocked on receive. It always fails if the operand is not a channel, or if the channel is send-only. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go ch := make(chan struct{}) assertions.Blocked(t, ch) success: make(chan struct{}) failure: sendChanMessage() ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestBlocked(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestBlocked(t *testing.T) success := assert.Blocked(t, make(chan struct{})) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestBlocked(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestBlocked(t *testing.T) require.Blocked(t, make(chan struct{})) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Blocked(t T, ch any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Blocked) | package-level function | | [`assert.Blockedf(t T, ch any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Blockedf) | formatted variant | | [`assert.(*Assertions).Blocked(ch any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Blocked) | method variant | | [`assert.(*Assertions).Blockedf(ch any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Blockedf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Blocked(t T, ch any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Blocked) | package-level function | | [`require.Blockedf(t T, ch any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Blockedf) | formatted variant | | [`require.(*Assertions).Blocked(ch any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Blocked) | method variant | | [`require.(*Assertions).Blockedf(ch any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Blockedf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Blocked(t T, ch any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Blocked) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Blocked](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L56) {{% /tab %}} {{< /tabs >}} ### BlockedT[E any, CHAN ~chan E] {{% icon icon="star" color=orange %}}{#blockedte-any-chan-chan-e} BlockedT asserts that a channel is blocked on receive. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go ch := make(chan struct{}) assertions.BlockedT(t, ch) success: make(chan struct{}) failure: sendChanMessage() ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestBlockedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestBlockedT(t *testing.T) success := assert.BlockedT(t, make(chan struct{})) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestBlockedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestBlockedT(t *testing.T) require.BlockedT(t, make(chan struct{})) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.BlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#BlockedT) | package-level function | | [`assert.BlockedTf[E any, CHAN ~chan E](t T, ch CHAN, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#BlockedTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.BlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#BlockedT) | package-level function | | [`require.BlockedTf[E any, CHAN ~chan E](t T, ch CHAN, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#BlockedTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.BlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#BlockedT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#BlockedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L104) {{% /tab %}} {{< /tabs >}} ### Condition{#condition} Condition uses a comparison function to assert a complex condition. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Condition(t, func() bool { return myCondition }) success: func() bool { return true } failure: func() bool { return false } ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestCondition(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T) success := assert.Condition(t, func() bool { return true }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestCondition(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestCondition(t *testing.T) require.Condition(t, func() bool { return true }) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Condition(t T, comp func() bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Condition) | package-level function | | [`assert.Conditionf(t T, comp func() bool, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Conditionf) | formatted variant | | [`assert.(*Assertions).Condition(comp func() bool) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Condition) | method variant | | [`assert.(*Assertions).Conditionf(comp func() bool, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Conditionf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Condition(t T, comp func() bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Condition) | package-level function | | [`require.Conditionf(t T, comp func() bool, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Conditionf) | formatted variant | | [`require.(*Assertions).Condition(comp func() bool) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Condition) | method variant | | [`require.(*Assertions).Conditionf(comp func() bool, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Conditionf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Condition(t T, comp func() bool, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Condition) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Condition](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L29) {{% /tab %}} {{< /tabs >}} ### Consistently[C Conditioner] {{% icon icon="star" color=orange %}}{#consistentlyc-conditioner} Consistently asserts that the given condition is always satisfied until timeout, periodically checking the target function at each tick. [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) ("always") imposes a stronger constraint than [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) ("at least once"): it checks at every tick that every occurrence of the condition is satisfied, whereas [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) succeeds on the first occurrence of a successful condition. #### Alternative condition signature The simplest form of condition is: func() bool The semantics of the assertion are "always returns true". To build more complex cases, a condition may also be defined as: func(context.Context) error It fails as soon as an error is returned before timeout expressing "always returns no error (nil)" This is consistent with [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) expressing "eventually returns no error (nil)". It will be executed with the context of the assertion, which inherits the [testing.T.Context](https://pkg.go.dev/testing#T.Context) and is cancelled on timeout. #### Panic recovery A panicking condition is treated as an error, causing [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) to fail immediately. See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details. #### Concurrency See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually). #### Attention point See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually). #### Synctest (opt-in) Wrap the condition with [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) (or [WithSynctestContext](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctestContext)) to run the polling loop inside a [testing/synctest] bubble, which uses a fake clock. This eliminates timing-induced flakiness and makes the tick count deterministic. See [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) for the constraints (no real I/O in the condition, requires [*testing.T]). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Consistently(t, func() bool { return true }, time.Second, 10*time.Millisecond) See also [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details about using context, concurrency, and panic recovery. success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T) success := assert.Consistently(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "context" "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // Simulate a service that stays healthy. healthCheck := func(_ context.Context) error { return nil // always healthy } result := assert.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("consistently healthy: %t", result) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // A counter that stays within bounds during the test. var counter atomic.Int32 counter.Store(5) result := assert.Consistently(t, func() bool { return counter.Load() < 10 }, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("consistently under limit: %t", result) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // An invariant that must hold throughout the observation period. var counter atomic.Int32 counter.Store(5) invariant := func() bool { return counter.Load() < 10 } result := assert.Consistently(t, assert.WithSynctest(invariant), 1*time.Hour, 1*time.Minute) fmt.Printf("invariant held: %t", result) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestConsistently(t *testing.T) require.Consistently(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) fmt.Println("passed") } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "context" "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // Simulate a service that stays healthy. healthCheck := func(_ context.Context) error { return nil // always healthy } require.Consistently(t, healthCheck, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("consistently healthy: %t", !t.Failed()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // A counter that stays within bounds during the test. var counter atomic.Int32 counter.Store(5) require.Consistently(t, func() bool { return counter.Load() < 10 }, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("consistently under limit: %t", !t.Failed()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestConsistently(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // An invariant that must hold throughout the observation period. var counter atomic.Int32 counter.Store(5) invariant := func() bool { return counter.Load() < 10 } require.Consistently(t, require.WithSynctest(invariant), 1*time.Hour, 1*time.Minute) fmt.Printf("invariant held: %t", !t.Failed()) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Consistently[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) | package-level function | | [`assert.Consistentlyf[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistentlyf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Consistently[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Consistently) | package-level function | | [`require.Consistentlyf[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Consistentlyf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Consistently[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Consistently) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Consistently](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L414) {{% /tab %}} {{< /tabs >}} ### Eventually[C Conditioner] {{% icon icon="star" color=orange %}}{#eventuallyc-conditioner} Eventually asserts that the given condition will be met before timeout, periodically checking the target function on each tick. [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) waits until the condition returns true, at most until timeout, or until the parent context of the test is cancelled. If the condition takes longer than the timeout to complete, [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) fails but waits for the current condition execution to finish before returning. For long-running conditions to be interrupted early, check [testing.T.Context](https://pkg.go.dev/testing#T.Context) which is cancelled on test failure. #### Alternative condition signature The simplest form of condition is: func() bool To build more complex cases, a condition may also be defined as: func(context.Context) error It fails when an error has always been returned up to timeout (equivalent semantics to func() bool returns false), expressing "eventually returns no error (nil)". It will be executed with the context of the assertion, which inherits the [testing.T.Context](https://pkg.go.dev/testing#T.Context) and is cancelled on timeout. The semantics of the three available async assertions read as follows. - [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) (func() bool) : "eventually returns true" - [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) (func() bool) : "never returns true" - [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) (func() bool): "always returns true" - [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) (func(ctx) error) : "eventually returns nil" - [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) (func(ctx) error) : not supported, use [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) instead (avoids confusion with double negation) - [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) (func(ctx) error): "always returns nil" #### Concurrency The condition function is always executed serially by a single goroutine. It is always executed at least once. It may thus write to variables outside its scope without triggering race conditions. A blocking condition will cause [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) to hang until it returns. Notice that time ticks may be skipped if the condition takes longer than the tick interval. #### Panic recovery If the condition panics, the panic is recovered and treated as a failed tick (equivalent to returning false or a non-nil error). For [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually), this means the poller retries on the next tick — if a later tick succeeds, the assertion succeeds. For [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) and [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently), a panic is treated as the condition erroring, which causes immediate failure. The recovered panic is wrapped as an error with the sentinel [errConditionPanicked](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#errConditionPanicked), detectable with [errors.Is](https://pkg.go.dev/errors#Is). #### Attention point Time-based tests may be flaky in a resource-constrained environment such as a CI runner and may produce counter-intuitive results, such as ticks or timeouts not firing in time as expected. To avoid flaky tests, always make sure that ticks and timeouts differ by at least an order of magnitude (tick << timeout). #### Synctest (opt-in) Wrap the condition with [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) (or [WithSynctestContext](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctestContext)) to run the polling loop inside a [testing/synctest] bubble, which uses a fake clock. This eliminates timing-induced flakiness and makes the tick count deterministic. See [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) for the constraints (no real I/O in the condition, requires `*testing.T`). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T) success := assert.Eventually(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // Simulate an async operation that completes after a short delay. var ready atomic.Bool go func() { time.Sleep(30 * time.Millisecond) ready.Store(true) }() result := assert.Eventually(t, ready.Load, 200*time.Millisecond, 10*time.Millisecond) fmt.Printf("eventually ready: %t", result) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "context" "errors" "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // Simulate a service that becomes healthy after a few attempts. var attempts atomic.Int32 healthCheck := func(_ context.Context) error { if attempts.Add(1) < 3 { return errors.New("service not ready") } return nil } result := assert.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond) fmt.Printf("eventually healthy: %t", result) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "context" "errors" "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test var attempts atomic.Int32 healthCheck := func(_ context.Context) error { if attempts.Add(1) < 3 { return errors.New("service not ready") } return nil } result := assert.Eventually(t, assert.WithSynctestContext(healthCheck), 1*time.Hour, 1*time.Minute) fmt.Printf("healthy: %t, attempts: %d", result, attempts.Load()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // A counter that converges on the 5th poll — no external time pressure. var attempts atomic.Int32 cond := func() bool { return attempts.Add(1) == 5 } // 1-hour/1-minute: under fake time this is instantaneous and // deterministic — exactly 5 calls to the condition. result := assert.Eventually(t, assert.WithSynctest(cond), 1*time.Hour, 1*time.Minute) fmt.Printf("ready: %t, attempts: %d", result, attempts.Load()) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEventually(t *testing.T) require.Eventually(t, func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond) fmt.Println("passed") } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // Simulate an async operation that completes after a short delay. var ready atomic.Bool go func() { time.Sleep(30 * time.Millisecond) ready.Store(true) }() require.Eventually(t, ready.Load, 200*time.Millisecond, 10*time.Millisecond) fmt.Printf("eventually ready: %t", !t.Failed()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "context" "errors" "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // Simulate a service that becomes healthy after a few attempts. var attempts atomic.Int32 healthCheck := func(_ context.Context) error { if attempts.Add(1) < 3 { return errors.New("service not ready") } return nil } require.Eventually(t, healthCheck, 200*time.Millisecond, 10*time.Millisecond) fmt.Printf("eventually healthy: %t", !t.Failed()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "context" "errors" "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test var attempts atomic.Int32 healthCheck := func(_ context.Context) error { if attempts.Add(1) < 3 { return errors.New("service not ready") } return nil } require.Eventually(t, require.WithSynctestContext(healthCheck), 1*time.Hour, 1*time.Minute) fmt.Printf("healthy: %t, attempts: %d", !t.Failed(), attempts.Load()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventually(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // A counter that converges on the 5th poll — no external time pressure. var attempts atomic.Int32 cond := func() bool { return attempts.Add(1) == 5 } // 1-hour/1-minute: under fake time this is instantaneous and // deterministic — exactly 5 calls to the condition. require.Eventually(t, require.WithSynctest(cond), 1*time.Hour, 1*time.Minute) fmt.Printf("ready: %t, attempts: %d", !t.Failed(), attempts.Load()) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Eventually[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) | package-level function | | [`assert.Eventuallyf[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventuallyf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Eventually[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Eventually) | package-level function | | [`require.Eventuallyf[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Eventuallyf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Eventually[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Eventually) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Eventually](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L290) {{% /tab %}} {{< /tabs >}} ### EventuallyWith[C CollectibleConditioner] {{% icon icon="star" color=orange %}}{#eventuallywithc-collectibleconditioner} EventuallyWith asserts that the given condition will be met before the timeout, periodically checking the target function at each tick. In contrast to [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually), the condition function is supplied with a [CollectT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#CollectT) to accumulate errors from calling other assertions. The condition is considered "met" if no errors are raised in a tick. The supplied [CollectT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#CollectT) collects all errors from one tick. If the condition is not met before the timeout, the collected errors from the last tick are copied to t. Calling [CollectT.FailNow](https://pkg.go.dev/CollectT#FailNow) (directly, or transitively through [require](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#require) assertions) fails the current tick only: the poller will retry on the next tick. This means [require](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#require)-style assertions inside [EventuallyWith](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWith) behave naturally — they abort the current evaluation and let the polling loop converge. To abort the whole assertion immediately (e.g. when the condition can no longer be expected to succeed), call [CollectT.Cancel](https://pkg.go.dev/CollectT#Cancel). #### Concurrency The condition function is never executed in parallel: only one goroutine executes it. It may write to variables outside its scope without triggering race conditions. The condition is wrapped in its own goroutine, so a call to [runtime.Goexit](https://pkg.go.dev/runtime#Goexit) (e.g. via [require](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#require) assertions or [CollectT.FailNow](https://pkg.go.dev/CollectT#FailNow)) cleanly aborts only the current tick. #### Panic recovery If the condition panics, the panic is recovered and recorded as an error in the [CollectT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#CollectT) for that tick. The poller treats it as a failed tick and retries on the next one. If the assertion times out, the panic error is included in the collected errors reported on the parent t. See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for the general panic recovery semantics. #### Synctest (opt-in) Wrap the condition with [WithSynctestCollect](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctestCollect) (or [WithSynctestCollectContext](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctestCollectContext)) to run the polling loop inside a [testing/synctest] bubble, which uses a fake clock. This eliminates timing-induced flakiness and makes the tick count deterministic. See [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) for the constraints (no real I/O in the condition, requires [*testing.T]). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go externalValue := false go func() { time.Sleep(8*time.Second) externalValue = true }() assertions.EventuallyWith(t, func(c *assertions.CollectT) { // add assertions as needed; any assertion failure will fail the current tick assertions.True(c, externalValue, "expected 'externalValue' to be true") }, 10*time.Second, 1*time.Second, "external state has not changed to 'true'; still false", ) success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond failure: func(c *CollectT) { c.Cancel() }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T) success := assert.EventuallyWith(t, func(c *assert.CollectT) { assert.True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test var attempts atomic.Int32 cond := func(c *assert.CollectT) { n := attempts.Add(1) assert.Equal(c, int32(3), n, "not yet converged") } result := assert.EventuallyWith(t, assert.WithSynctestCollect(cond), 1*time.Hour, 1*time.Minute) fmt.Printf("converged: %t, attempts: %d", result, attempts.Load()) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEventuallyWith(t *testing.T) require.EventuallyWith(t, func(c *assert.CollectT) { assert.True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) fmt.Println("passed") } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEventuallyWith(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test var attempts atomic.Int32 cond := func(c *require.CollectT) { n := attempts.Add(1) require.Equal(c, int32(3), n, "not yet converged") } require.EventuallyWith(t, require.WithSynctestCollect(cond), 1*time.Hour, 1*time.Minute) fmt.Printf("converged: %t, attempts: %d", !t.Failed(), attempts.Load()) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.EventuallyWith[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWith) | package-level function | | [`assert.EventuallyWithf[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EventuallyWithf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.EventuallyWith[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EventuallyWith) | package-level function | | [`require.EventuallyWithf[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EventuallyWithf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.EventuallyWith[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EventuallyWith) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#EventuallyWith](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L491) {{% /tab %}} {{< /tabs >}} ### Never[C NeverConditioner] {{% icon icon="star" color=orange %}}{#neverc-neverconditioner} Never asserts that the given condition is never satisfied until timeout, periodically checking the target function at each tick. [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) is the opposite of [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) ("at least once"). It succeeds if the timeout is reached without the condition ever returning true. If the parent context is cancelled before the timeout, [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) fails. #### Alternative condition signature The simplest form of condition is: func() bool Use [Consistently](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Consistently) instead if you want to use a condition returning an error. #### Panic recovery A panicking condition is treated as an error, causing [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) to fail immediately. See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details. #### Concurrency See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually). #### Attention point See [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually). #### Synctest (opt-in) Wrap the condition with [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) to run the polling loop inside a [testing/synctest] bubble, which uses a fake clock. This eliminates timing-induced flakiness and makes the tick count deterministic. See [WithSynctest](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) for the constraints (no real I/O in the condition, requires [*testing.T]). Note: [Never](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) does not accept the context/error form of condition, so [WithSynctestContext](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctestContext) does not apply here. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) See also [Eventually](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Eventually) for details about using context, concurrency, and panic recovery. success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNever(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T) success := assert.Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNever(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // A channel that should remain empty during the test. events := make(chan struct{}, 1) result := assert.Never(t, func() bool { select { case <-events: return true // event received = condition becomes true = Never fails default: return false } }, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("never received: %t", result) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNever(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test // A flag that should remain false across the whole observation period. var flipped atomic.Bool result := assert.Never(t, assert.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute) fmt.Printf("never flipped: %t", result) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNever(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNever(t *testing.T) require.Never(t, func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond) fmt.Println("passed") } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNever(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // A channel that should remain empty during the test. events := make(chan struct{}, 1) require.Never(t, func() bool { select { case <-events: return true // event received = condition becomes true = Never fails default: return false } }, 100*time.Millisecond, 10*time.Millisecond) fmt.Printf("never received: %t", !t.Failed()) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNever(t *testing.T) package main import ( "fmt" "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // normally provided by test // A flag that should remain false across the whole observation period. var flipped atomic.Bool require.Never(t, require.WithSynctest(flipped.Load), 1*time.Hour, 1*time.Minute) fmt.Printf("never flipped: %t", !t.Failed()) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Never[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Never) | package-level function | | [`assert.Neverf[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Neverf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Never[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Never) | package-level function | | [`require.Neverf[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Neverf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Never[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Never) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Never](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L348) {{% /tab %}} {{< /tabs >}} ### NotBlocked{#notblocked} NotBlocked asserts that a channel is not blocked on receive. It always fails if the operand is not a channel, or if the channel is send-only. A closed channel doesn't block and returns true. Notice that this consumes any message available in the channel. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go ch := make(chan struct{}, 1) ch <- struct{}{} assertions.NotBlocked(t, ch) success: sendChanMessage() failure: make(chan struct{}) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotBlocked(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotBlocked(t *testing.T) success := assert.NotBlocked(t, sendChanMessage()) fmt.Printf("success: %t\n", success) } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotBlocked(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotBlocked(t *testing.T) require.NotBlocked(t, sendChanMessage()) fmt.Println("passed") } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotBlocked(t T, ch any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotBlocked) | package-level function | | [`assert.NotBlockedf(t T, ch any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotBlockedf) | formatted variant | | [`assert.(*Assertions).NotBlocked(ch any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotBlocked) | method variant | | [`assert.(*Assertions).NotBlockedf(ch any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotBlockedf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotBlocked(t T, ch any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotBlocked) | package-level function | | [`require.NotBlockedf(t T, ch any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotBlockedf) | formatted variant | | [`require.(*Assertions).NotBlocked(ch any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotBlocked) | method variant | | [`require.(*Assertions).NotBlockedf(ch any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotBlockedf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotBlocked(t T, ch any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotBlocked) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotBlocked](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L141) {{% /tab %}} {{< /tabs >}} ### NotBlockedT[E any, CHAN ~chan E] {{% icon icon="star" color=orange %}}{#notblockedte-any-chan-chan-e} NotBlockedT asserts that a channel is not blocked on receive. A closed channel doesn't block and returns true. Notice that this consumes any message available in the channel. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go ch := make(chan struct{}, 1) ch <- struct{}{} assertions.NotBlockedT(t, ch) success: sendChanMessage() failure: make(chan struct{}) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotBlockedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotBlockedT(t *testing.T) success := assert.NotBlockedT(t, sendChanMessage()) fmt.Printf("success: %t\n", success) } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotBlockedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotBlockedT(t *testing.T) require.NotBlockedT(t, sendChanMessage()) fmt.Println("passed") } func sendChanMessage() chan struct{} { ch := make(chan struct{}, 1) ch <- struct{}{} return ch } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotBlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotBlockedT) | package-level function | | [`assert.NotBlockedTf[E any, CHAN ~chan E](t T, ch CHAN, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotBlockedTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotBlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotBlockedT) | package-level function | | [`require.NotBlockedTf[E any, CHAN ~chan E](t T, ch CHAN, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotBlockedTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotBlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotBlockedT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotBlockedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/condition.go#L188) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/equality.md000066400000000000000000001626041520301377500232410ustar00rootroot00000000000000--- title: "Equality" description: "Asserting Two Things Are Equal" weight: 5 domains: - "equality" keywords: - "Empty" - "Emptyf" - "Equal" - "Equalf" - "EqualExportedValues" - "EqualExportedValuesf" - "EqualT" - "EqualTf" - "EqualValues" - "EqualValuesf" - "Exactly" - "Exactlyf" - "Nil" - "Nilf" - "NotEmpty" - "NotEmptyf" - "NotEqual" - "NotEqualf" - "NotEqualT" - "NotEqualTf" - "NotEqualValues" - "NotEqualValuesf" - "NotNil" - "NotNilf" - "NotSame" - "NotSamef" - "NotSameT" - "NotSameTf" - "Same" - "Samef" - "SameT" - "SameTf" --- Asserting Two Things Are Equal ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 16 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [Empty](#empty) | angles-right - [Equal](#equal) | angles-right - [EqualExportedValues](#equalexportedvalues) | angles-right - [EqualT[V comparable]](#equaltv-comparable) | star | orange - [EqualValues](#equalvalues) | angles-right - [Exactly](#exactly) | angles-right - [Nil](#nil) | angles-right - [NotEmpty](#notempty) | angles-right - [NotEqual](#notequal) | angles-right - [NotEqualT[V comparable]](#notequaltv-comparable) | star | orange - [NotEqualValues](#notequalvalues) | angles-right - [NotNil](#notnil) | angles-right - [NotSame](#notsame) | angles-right - [NotSameT[P any]](#notsametp-any) | star | orange - [Same](#same) | angles-right - [SameT[P any]](#sametp-any) | star | orange ``` ### Empty{#empty} Empty asserts that the given value is "empty". Zero values are "empty". Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). Slices, maps and channels with zero length are "empty". Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". [Zero values]: https://go.dev/ref/spec#The_zero_value {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Empty(t, obj) success: "" failure: "not empty" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEmpty(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T) success := assert.Empty(t, "") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEmpty(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEmpty(t *testing.T) require.Empty(t, "") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Empty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Empty) | package-level function | | [`assert.Emptyf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Emptyf) | formatted variant | | [`assert.(*Assertions).Empty(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Empty) | method variant | | [`assert.(*Assertions).Emptyf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Emptyf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Empty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Empty) | package-level function | | [`require.Emptyf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Emptyf) | formatted variant | | [`require.(*Assertions).Empty(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Empty) | method variant | | [`require.(*Assertions).Emptyf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Emptyf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L74) {{% /tab %}} {{< /tabs >}} ### Equal{#equal} Equal asserts that two objects are equal. Pointer variable equality is determined based on the equality of the referenced values (as opposed to the memory addresses). Function equality cannot be determined and will always fail. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Equal(t, 123, 123) success: 123, 123 failure: 123, 456 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T) success := assert.Equal(t, 123, 123) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqual(t *testing.T) require.Equal(t, 123, 123) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Equal(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) | package-level function | | [`assert.Equalf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equalf) | formatted variant | | [`assert.(*Assertions).Equal(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Equal) | method variant | | [`assert.(*Assertions).Equalf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Equalf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Equal(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Equal) | package-level function | | [`require.Equalf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Equalf) | formatted variant | | [`require.(*Assertions).Equal(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Equal) | method variant | | [`require.(*Assertions).Equalf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Equalf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Equal(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Equal) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Equal](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L30) {{% /tab %}} {{< /tabs >}} ### EqualExportedValues{#equalexportedvalues} EqualExportedValues asserts that the types of two objects are equal and their public fields are also equal. This is useful for comparing structs that have private fields that could potentially differ. Function equality cannot be determined and will always fail. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go type S struct { Exported int notExported int } assertions.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true assertions.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false success: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2} failure: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualExportedValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T) success := assert.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) fmt.Printf("success: %t\n", success) } type dummyStruct struct { A string b int } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualExportedValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualExportedValues(t *testing.T) require.EqualExportedValues(t, &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2}) fmt.Println("passed") } type dummyStruct struct { A string b int } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualExportedValues) | package-level function | | [`assert.EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualExportedValuesf) | formatted variant | | [`assert.(*Assertions).EqualExportedValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualExportedValues) | method variant | | [`assert.(*Assertions).EqualExportedValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualExportedValuesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualExportedValues) | package-level function | | [`require.EqualExportedValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualExportedValuesf) | formatted variant | | [`require.(*Assertions).EqualExportedValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualExportedValues) | method variant | | [`require.(*Assertions).EqualExportedValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualExportedValuesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.EqualExportedValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualExportedValues) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualExportedValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L217) {{% /tab %}} {{< /tabs >}} ### EqualT[V comparable] {{% icon icon="star" color=orange %}}{#equaltv-comparable} EqualT asserts that two objects of the same comparable type are equal. Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal), but like [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same)). Functions, slices and maps are not comparable. See also [ComparisonOperators](https://go.dev/ref/spec#Comparison_operators.). If you need to compare values of non-comparable types, or compare pointers by the value they point to, use [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) instead. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.EqualT(t, 123, 123) success: 123, 123 failure: 123, 456 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T) success := assert.EqualT(t, 123, 123) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualT(t *testing.T) require.EqualT(t, 123, 123) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualT) | package-level function | | [`assert.EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualT) | package-level function | | [`require.EqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.EqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L68) {{% /tab %}} {{< /tabs >}} ### EqualValues{#equalvalues} EqualValues asserts that two objects are equal or convertible to the larger type and equal. Function equality cannot be determined and will always fail. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.EqualValues(t, uint32(123), int32(123)) success: uint32(123), int32(123) failure: uint32(123), int32(456) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T) success := assert.EqualValues(t, uint32(123), int32(123)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualValues(t *testing.T) require.EqualValues(t, uint32(123), int32(123)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.EqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualValues) | package-level function | | [`assert.EqualValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualValuesf) | formatted variant | | [`assert.(*Assertions).EqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualValues) | method variant | | [`assert.(*Assertions).EqualValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualValuesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.EqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualValues) | package-level function | | [`require.EqualValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualValuesf) | formatted variant | | [`require.(*Assertions).EqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualValues) | method variant | | [`require.(*Assertions).EqualValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualValuesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.EqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#EqualValues) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#EqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L144) {{% /tab %}} {{< /tabs >}} ### Exactly{#exactly} Exactly asserts that two objects are equal in value and type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Exactly(t, int32(123), int64(123)) success: int32(123), int32(123) failure: int32(123), int64(123) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestExactly(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T) success := assert.Exactly(t, int32(123), int32(123)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestExactly(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestExactly(t *testing.T) require.Exactly(t, int32(123), int32(123)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Exactly(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Exactly) | package-level function | | [`assert.Exactlyf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Exactlyf) | formatted variant | | [`assert.(*Assertions).Exactly(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Exactly) | method variant | | [`assert.(*Assertions).Exactlyf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Exactlyf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Exactly(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Exactly) | package-level function | | [`require.Exactlyf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Exactlyf) | formatted variant | | [`require.(*Assertions).Exactly(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Exactly) | method variant | | [`require.(*Assertions).Exactlyf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Exactlyf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Exactly(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Exactly) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Exactly](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L259) {{% /tab %}} {{< /tabs >}} ### Nil{#nil} Nil asserts that the specified object is nil. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Nil(t, err) success: nil failure: "not nil" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNil(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T) success := assert.Nil(t, nil) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNil(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNil(t *testing.T) require.Nil(t, nil) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Nil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Nil) | package-level function | | [`assert.Nilf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Nilf) | formatted variant | | [`assert.(*Assertions).Nil(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Nil) | method variant | | [`assert.(*Assertions).Nilf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Nilf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Nil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Nil) | package-level function | | [`require.Nilf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Nilf) | formatted variant | | [`require.(*Assertions).Nil(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Nil) | method variant | | [`require.(*Assertions).Nilf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Nilf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L21) {{% /tab %}} {{< /tabs >}} ### NotEmpty{#notempty} NotEmpty asserts that the specified object is NOT [Empty](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Empty). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go if assert.NotEmpty(t, obj) { assertions.Equal(t, "two", obj[1](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#1)) } success: "not empty" failure: "" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEmpty(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T) success := assert.NotEmpty(t, "not empty") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEmpty(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEmpty(t *testing.T) require.NotEmpty(t, "not empty") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotEmpty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEmpty) | package-level function | | [`assert.NotEmptyf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEmptyf) | formatted variant | | [`assert.(*Assertions).NotEmpty(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotEmpty) | method variant | | [`assert.(*Assertions).NotEmptyf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotEmptyf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotEmpty(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEmpty) | package-level function | | [`require.NotEmptyf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEmptyf) | formatted variant | | [`require.(*Assertions).NotEmpty(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotEmpty) | method variant | | [`require.(*Assertions).NotEmptyf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotEmptyf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L100) {{% /tab %}} {{< /tabs >}} ### NotEqual{#notequal} NotEqual asserts that the specified values are NOT equal. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotEqual(t, obj1, obj2) Pointer variable equality is determined based on the equality of the referenced values (as opposed to the memory addresses). Function equality cannot be determined and will always fail. success: 123, 456 failure: 123, 123 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T) success := assert.NotEqual(t, 123, 456) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEqual(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEqual(t *testing.T) require.NotEqual(t, 123, 456) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotEqual(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqual) | package-level function | | [`assert.NotEqualf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualf) | formatted variant | | [`assert.(*Assertions).NotEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotEqual) | method variant | | [`assert.(*Assertions).NotEqualf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotEqualf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotEqual(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqual) | package-level function | | [`require.NotEqualf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualf) | formatted variant | | [`require.(*Assertions).NotEqual(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotEqual) | method variant | | [`require.(*Assertions).NotEqualf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotEqualf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotEqual(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqual) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqual](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L93) {{% /tab %}} {{< /tabs >}} ### NotEqualT[V comparable] {{% icon icon="star" color=orange %}}{#notequaltv-comparable} NotEqualT asserts that the specified values of the same comparable type are NOT equal. See [EqualT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotEqualT(t, obj1, obj2) success: 123, 456 failure: 123, 123 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T) success := assert.NotEqualT(t, 123, 456) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEqualT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEqualT(t *testing.T) require.NotEqualT(t, 123, 456) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualT) | package-level function | | [`assert.NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualT) | package-level function | | [`require.NotEqualTf[V comparable](t T, expected V, actual V, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotEqualT[V comparable](t T, expected V, actual V, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqualT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualT](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L122) {{% /tab %}} {{< /tabs >}} ### NotEqualValues{#notequalvalues} NotEqualValues asserts that two objects are not equal even when converted to the same type. Function equality cannot be determined and will always fail. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotEqualValues(t, obj1, obj2) success: uint32(123), int32(456) failure: uint32(123), int32(123) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEqualValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T) success := assert.NotEqualValues(t, uint32(123), int32(456)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotEqualValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotEqualValues(t *testing.T) require.NotEqualValues(t, uint32(123), int32(456)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotEqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualValues) | package-level function | | [`assert.NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotEqualValuesf) | formatted variant | | [`assert.(*Assertions).NotEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotEqualValues) | method variant | | [`assert.(*Assertions).NotEqualValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotEqualValuesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotEqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualValues) | package-level function | | [`require.NotEqualValuesf(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotEqualValuesf) | formatted variant | | [`require.(*Assertions).NotEqualValues(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotEqualValues) | method variant | | [`require.(*Assertions).NotEqualValuesf(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotEqualValuesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotEqualValues(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotEqualValues) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotEqualValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/equal.go#L179) {{% /tab %}} {{< /tabs >}} ### NotNil{#notnil} NotNil asserts that the specified object is not nil. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotNil(t, err) success: "not nil" failure: nil ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotNil(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T) success := assert.NotNil(t, "not nil") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotNil(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotNil(t *testing.T) require.NotNil(t, "not nil") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotNil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotNil) | package-level function | | [`assert.NotNilf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotNilf) | formatted variant | | [`assert.(*Assertions).NotNil(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotNil) | method variant | | [`assert.(*Assertions).NotNilf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotNilf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotNil(t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotNil) | package-level function | | [`require.NotNilf(t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotNilf) | formatted variant | | [`require.(*Assertions).NotNil(object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotNil) | method variant | | [`require.(*Assertions).NotNilf(object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotNilf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L43) {{% /tab %}} {{< /tabs >}} ### NotSame{#notsame} NotSame asserts that two pointers do not reference the same object. See [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotSame(t, ptr1, ptr2) success: &staticVar, ptr("static string") failure: &staticVar, staticVarPtr ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSame(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T) success := assert.NotSame(t, &staticVar, ptr("static string")) fmt.Printf("success: %t\n", success) } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var staticVar = "static string" func ptr[T any](value T) *T { p := value return &p } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSame(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSame(t *testing.T) require.NotSame(t, &staticVar, ptr("static string")) fmt.Println("passed") } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var staticVar = "static string" func ptr[T any](value T) *T { p := value return &p } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotSame(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSame) | package-level function | | [`assert.NotSamef(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSamef) | formatted variant | | [`assert.(*Assertions).NotSame(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotSame) | method variant | | [`assert.(*Assertions).NotSamef(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotSamef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotSame(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSame) | package-level function | | [`require.NotSamef(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSamef) | formatted variant | | [`require.(*Assertions).NotSame(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotSame) | method variant | | [`require.(*Assertions).NotSamef(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotSamef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L86) {{% /tab %}} {{< /tabs >}} ### NotSameT[P any] {{% icon icon="star" color=orange %}}{#notsametp-any} NotSameT asserts that two pointers do not reference the same object. See [SameT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotSameT(t, ptr1, ptr2) success: &staticVar, ptr("static string") failure: &staticVar, staticVarPtr ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSameT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T) success := assert.NotSameT(t, &staticVar, ptr("static string")) fmt.Printf("success: %t\n", success) } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var staticVar = "static string" func ptr[T any](value T) *T { p := value return &p } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSameT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSameT(t *testing.T) require.NotSameT(t, &staticVar, ptr("static string")) fmt.Println("passed") } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var staticVar = "static string" func ptr[T any](value T) *T { p := value return &p } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSameT) | package-level function | | [`assert.NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSameTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotSameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSameT) | package-level function | | [`require.NotSameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSameTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotSameT[P any](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#L118) {{% /tab %}} {{< /tabs >}} ### Same{#same} Same asserts that two pointers reference the same object. Both arguments must be pointer variables. Pointer variable sameness is determined based on the equality of both type and value. Unlike [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) pointers, [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same) pointers point to the same memory address. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Same(t, ptr1, ptr2) success: &staticVar, staticVarPtr failure: &staticVar, ptr("static string") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSame(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T) success := assert.Same(t, &staticVar, staticVarPtr) fmt.Printf("success: %t\n", success) } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar ) ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSame(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSame(t *testing.T) require.Same(t, &staticVar, staticVarPtr) fmt.Println("passed") } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar ) ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Same(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same) | package-level function | | [`assert.Samef(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Samef) | formatted variant | | [`assert.(*Assertions).Same(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Same) | method variant | | [`assert.(*Assertions).Samef(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Samef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Same(t T, expected any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Same) | package-level function | | [`require.Samef(t T, expected any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Samef) | formatted variant | | [`require.(*Assertions).Same(expected any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Same) | method variant | | [`require.(*Assertions).Samef(expected any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Samef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L24) {{% /tab %}} {{< /tabs >}} ### SameT[P any] {{% icon icon="star" color=orange %}}{#sametp-any} SameT asserts that two pointers of the same type reference the same object. See [Same](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Same). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SameT(t, ptr1, ptr2) success: &staticVar, staticVarPtr failure: &staticVar, ptr("static string") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSameT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T) success := assert.SameT(t, &staticVar, staticVarPtr) fmt.Printf("success: %t\n", success) } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar ) ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSameT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSameT(t *testing.T) require.SameT(t, &staticVar, staticVarPtr) fmt.Println("passed") } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" staticVarPtr = &staticVar ) ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameT) | package-level function | | [`assert.SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SameTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SameT[P any](t T, expected *P, actual *P, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SameT) | package-level function | | [`require.SameTf[P any](t T, expected *P, actual *P, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SameTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SameT[P any](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#L58) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/error.md000066400000000000000000000733711520301377500225370ustar00rootroot00000000000000--- title: "Error" description: "Asserting Errors" weight: 6 domains: - "error" keywords: - "EqualError" - "EqualErrorf" - "Error" - "Errorf" - "ErrorAs" - "ErrorAsf" - "ErrorContains" - "ErrorContainsf" - "ErrorIs" - "ErrorIsf" - "NoError" - "NoErrorf" - "NotErrorAs" - "NotErrorAsf" - "NotErrorIs" - "NotErrorIsf" --- Asserting Errors ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 8 functionalities. ```tree - [EqualError](#equalerror) | angles-right - [Error](#error) | angles-right - [ErrorAs](#erroras) | angles-right - [ErrorContains](#errorcontains) | angles-right - [ErrorIs](#erroris) | angles-right - [NoError](#noerror) | angles-right - [NotErrorAs](#noterroras) | angles-right - [NotErrorIs](#noterroris) | angles-right ``` ### EqualError{#equalerror} EqualError asserts that a function returned a non-nil error (i.e. an error) and that it is equal to the provided error. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go actualObj, err := SomeFunction() assertions.EqualError(t, err, expectedErrorString) success: ErrTest, "assert.ErrTest general error for testing" failure: ErrTest, "wrong error message" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T) success := assert.EqualError(t, assert.ErrTest, "assert.ErrTest general error for testing") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestEqualError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestEqualError(t *testing.T) require.EqualError(t, require.ErrTest, "assert.ErrTest general error for testing") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.EqualError(t T, err error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualError) | package-level function | | [`assert.EqualErrorf(t T, err error, errString string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualErrorf) | formatted variant | | [`assert.(*Assertions).EqualError(err error, errString string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualError) | method variant | | [`assert.(*Assertions).EqualErrorf(err error, errString string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.EqualErrorf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.EqualError(t T, err error, errString string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualError) | package-level function | | [`require.EqualErrorf(t T, err error, errString string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#EqualErrorf) | formatted variant | | [`require.(*Assertions).EqualError(err error, errString string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualError) | method variant | | [`require.(*Assertions).EqualErrorf(err error, errString string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.EqualErrorf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L90) {{% /tab %}} {{< /tabs >}} ### Error{#error} Error asserts that a function returned a non-nil error (i.e. an error). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go actualObj, err := SomeFunction() assertions.Error(t, err) success: ErrTest failure: nil ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T) success := assert.Error(t, assert.ErrTest) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestError(t *testing.T) require.Error(t, require.ErrTest) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Error(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Error) | package-level function | | [`assert.Errorf(t T, err error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Errorf) | formatted variant | | [`assert.(*Assertions).Error(err error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Error) | method variant | | [`assert.(*Assertions).Errorf(err error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Errorf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Error(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Error) | package-level function | | [`require.Errorf(t T, err error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Errorf) | formatted variant | | [`require.(*Assertions).Error(err error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Error) | method variant | | [`require.(*Assertions).Errorf(err error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Errorf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L65) {{% /tab %}} {{< /tabs >}} ### ErrorAs{#erroras} ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. This is a wrapper for [errors.As](https://pkg.go.dev/errors#As). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.ErrorAs(t, err, &target) success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) failure: ErrTest, new(*dummyError) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestErrorAs(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T) success := assert.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) fmt.Printf("success: %t\n", success) } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestErrorAs(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestErrorAs(t *testing.T) require.ErrorAs(t, fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError)) fmt.Println("passed") } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorAs) | package-level function | | [`assert.ErrorAsf(t T, err error, target any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorAsf) | formatted variant | | [`assert.(*Assertions).ErrorAs(err error, target any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorAs) | method variant | | [`assert.(*Assertions).ErrorAsf(err error, target any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorAsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorAs) | package-level function | | [`require.ErrorAsf(t T, err error, target any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorAsf) | formatted variant | | [`require.(*Assertions).ErrorAs(err error, target any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorAs) | method variant | | [`require.(*Assertions).ErrorAsf(err error, target any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorAsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L222) {{% /tab %}} {{< /tabs >}} ### ErrorContains{#errorcontains} ErrorContains asserts that a function returned a non-nil error (i.e. an error) and that the error contains the specified substring. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go actualObj, err := SomeFunction() assertions.ErrorContains(t, err, expectedErrorSubString) success: ErrTest, "general error" failure: ErrTest, "not in message" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestErrorContains(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) success := assert.ErrorContains(t, assert.ErrTest, "general error") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestErrorContains(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestErrorContains(t *testing.T) require.ErrorContains(t, require.ErrTest, "general error") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorContains) | package-level function | | [`assert.ErrorContainsf(t T, err error, contains string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorContainsf) | formatted variant | | [`assert.(*Assertions).ErrorContains(err error, contains string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorContains) | method variant | | [`assert.(*Assertions).ErrorContainsf(err error, contains string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorContainsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorContains) | package-level function | | [`require.ErrorContainsf(t T, err error, contains string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorContainsf) | formatted variant | | [`require.(*Assertions).ErrorContains(err error, contains string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorContains) | method variant | | [`require.(*Assertions).ErrorContainsf(err error, contains string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorContainsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L121) {{% /tab %}} {{< /tabs >}} ### ErrorIs{#erroris} ErrorIs asserts that at least one of the errors in err's chain matches target. This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.ErrorIs(t, err, io.EOF) success: fmt.Errorf("wrap: %w", io.EOF), io.EOF failure: ErrTest, io.EOF ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestErrorIs(t *testing.T) package main import ( "fmt" "io" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T) success := assert.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestErrorIs(t *testing.T) package main import ( "fmt" "io" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestErrorIs(t *testing.T) require.ErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.ErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorIs) | package-level function | | [`assert.ErrorIsf(t T, err error, target error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#ErrorIsf) | formatted variant | | [`assert.(*Assertions).ErrorIs(err error, target error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorIs) | method variant | | [`assert.(*Assertions).ErrorIsf(err error, target error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.ErrorIsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.ErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorIs) | package-level function | | [`require.ErrorIsf(t T, err error, target error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#ErrorIsf) | formatted variant | | [`require.(*Assertions).ErrorIs(err error, target error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorIs) | method variant | | [`require.(*Assertions).ErrorIsf(err error, target error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.ErrorIsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L150) {{% /tab %}} {{< /tabs >}} ### NoError{#noerror} NoError asserts that a function returned a nil error (i.e. no error). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go actualObj, err := SomeFunction() if assert.NoError(t, err) { assertions.Equal(t, expectedObj, actualObj) } success: nil failure: ErrTest ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T) success := assert.NoError(t, nil) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNoError(t *testing.T) require.NoError(t, nil) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NoError(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoError) | package-level function | | [`assert.NoErrorf(t T, err error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoErrorf) | formatted variant | | [`assert.(*Assertions).NoError(err error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoError) | method variant | | [`assert.(*Assertions).NoErrorf(err error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoErrorf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NoError(t T, err error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoError) | package-level function | | [`require.NoErrorf(t T, err error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoErrorf) | formatted variant | | [`require.(*Assertions).NoError(err error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoError) | method variant | | [`require.(*Assertions).NoErrorf(err error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoErrorf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L42) {{% /tab %}} {{< /tabs >}} ### NotErrorAs{#noterroras} NotErrorAs asserts that none of the errors in err's chain matches target, but if so, sets target to that error value. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotErrorAs(t, err, &target) success: ErrTest, new(*dummyError) failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotErrorAs(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T) success := assert.NotErrorAs(t, assert.ErrTest, new(*dummyError)) fmt.Printf("success: %t\n", success) } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotErrorAs(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorAs(t *testing.T) require.NotErrorAs(t, require.ErrTest, new(*dummyError)) fmt.Println("passed") } type dummyError struct { } func (d *dummyError) Error() string { return "dummy error" } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotErrorAs) | package-level function | | [`assert.NotErrorAsf(t T, err error, target any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotErrorAsf) | formatted variant | | [`assert.(*Assertions).NotErrorAs(err error, target any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotErrorAs) | method variant | | [`assert.(*Assertions).NotErrorAsf(err error, target any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotErrorAsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotErrorAs) | package-level function | | [`require.NotErrorAsf(t T, err error, target any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotErrorAsf) | formatted variant | | [`require.(*Assertions).NotErrorAs(err error, target any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotErrorAs) | method variant | | [`require.(*Assertions).NotErrorAsf(err error, target any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotErrorAsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L257) {{% /tab %}} {{< /tabs >}} ### NotErrorIs{#noterroris} NotErrorIs asserts that none of the errors in err's chain matches target. This is a wrapper for [errors.Is](https://pkg.go.dev/errors#Is). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotErrorIs(t, err, io.EOF) success: ErrTest, io.EOF failure: fmt.Errorf("wrap: %w", io.EOF), io.EOF ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotErrorIs(t *testing.T) package main import ( "fmt" "io" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) success := assert.NotErrorIs(t, assert.ErrTest, io.EOF) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotErrorIs(t *testing.T) package main import ( "fmt" "io" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotErrorIs(t *testing.T) require.NotErrorIs(t, require.ErrTest, io.EOF) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotErrorIs) | package-level function | | [`assert.NotErrorIsf(t T, err error, target error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotErrorIsf) | formatted variant | | [`assert.(*Assertions).NotErrorIs(err error, target error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotErrorIs) | method variant | | [`assert.(*Assertions).NotErrorIsf(err error, target error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotErrorIsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotErrorIs(t T, err error, target error, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotErrorIs) | package-level function | | [`require.NotErrorIsf(t T, err error, target error, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotErrorIsf) | formatted variant | | [`require.(*Assertions).NotErrorIs(err error, target error) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotErrorIs) | method variant | | [`require.(*Assertions).NotErrorIsf(err error, target error, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotErrorIsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`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#L188) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/file.md000066400000000000000000000577661520301377500223370ustar00rootroot00000000000000--- title: "File" description: "Asserting OS Files" weight: 7 domains: - "file" keywords: - "DirExists" - "DirExistsf" - "DirNotExists" - "DirNotExistsf" - "FileEmpty" - "FileEmptyf" - "FileExists" - "FileExistsf" - "FileNotEmpty" - "FileNotEmptyf" - "FileNotExists" - "FileNotExistsf" --- Asserting OS Files ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 6 functionalities. ```tree - [DirExists](#direxists) | angles-right - [DirNotExists](#dirnotexists) | angles-right - [FileEmpty](#fileempty) | angles-right - [FileExists](#fileexists) | angles-right - [FileNotEmpty](#filenotempty) | angles-right - [FileNotExists](#filenotexists) | angles-right ``` ### DirExists{#direxists} DirExists checks whether a directory exists in the given path. It also fails if the path is a file rather a directory or there is an error checking whether it exists. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.DirExists(t, "path/to/directory") success: filepath.Join(testDataPath(),"existing_dir") failure: filepath.Join(testDataPath(),"non_existing_dir") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestDirExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T) success := assert.DirExists(t, filepath.Join(testDataPath(), "existing_dir")) fmt.Printf("success: %t\n", success) } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestDirExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestDirExists(t *testing.T) require.DirExists(t, filepath.Join(testDataPath(), "existing_dir")) fmt.Println("passed") } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.DirExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#DirExists) | package-level function | | [`assert.DirExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#DirExistsf) | formatted variant | | [`assert.(*Assertions).DirExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.DirExists) | method variant | | [`assert.(*Assertions).DirExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.DirExistsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.DirExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#DirExists) | package-level function | | [`require.DirExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#DirExistsf) | formatted variant | | [`require.(*Assertions).DirExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.DirExists) | method variant | | [`require.(*Assertions).DirExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.DirExistsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.DirExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#DirExists) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#DirExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L82) {{% /tab %}} {{< /tabs >}} ### DirNotExists{#dirnotexists} DirNotExists checks whether a directory does not exist in the given path. It fails if the path points to an existing _directory_ only. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.DirNotExists(t, "path/to/directory") success: filepath.Join(testDataPath(),"non_existing_dir") failure: filepath.Join(testDataPath(),"existing_dir") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestDirNotExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T) success := assert.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) fmt.Printf("success: %t\n", success) } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestDirNotExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestDirNotExists(t *testing.T) require.DirNotExists(t, filepath.Join(testDataPath(), "non_existing_dir")) fmt.Println("passed") } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.DirNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#DirNotExists) | package-level function | | [`assert.DirNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#DirNotExistsf) | formatted variant | | [`assert.(*Assertions).DirNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.DirNotExists) | method variant | | [`assert.(*Assertions).DirNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.DirNotExistsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.DirNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#DirNotExists) | package-level function | | [`require.DirNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#DirNotExistsf) | formatted variant | | [`require.(*Assertions).DirNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.DirNotExists) | method variant | | [`require.(*Assertions).DirNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.DirNotExistsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.DirNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#DirNotExists) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#DirNotExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L112) {{% /tab %}} {{< /tabs >}} ### FileEmpty{#fileempty} FileEmpty checks whether a file exists in the given path and is empty. It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.FileEmpty(t, "path/to/file") success: filepath.Join(testDataPath(),"empty_file") failure: filepath.Join(testDataPath(),"existing_file") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileEmpty(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T) success := assert.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) fmt.Printf("success: %t\n", success) } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileEmpty(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileEmpty(t *testing.T) require.FileEmpty(t, filepath.Join(testDataPath(), "empty_file")) fmt.Println("passed") } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.FileEmpty(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileEmpty) | package-level function | | [`assert.FileEmptyf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileEmptyf) | formatted variant | | [`assert.(*Assertions).FileEmpty(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileEmpty) | method variant | | [`assert.(*Assertions).FileEmptyf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileEmptyf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.FileEmpty(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileEmpty) | package-level function | | [`require.FileEmptyf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileEmptyf) | formatted variant | | [`require.(*Assertions).FileEmpty(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileEmpty) | method variant | | [`require.(*Assertions).FileEmptyf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileEmptyf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.FileEmpty(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FileEmpty) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FileEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L141) {{% /tab %}} {{< /tabs >}} ### FileExists{#fileexists} FileExists checks whether a file exists in the given path. It also fails if the path points to a directory or there is an error when trying to check the file. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.FileExists(t, "path/to/file") success: filepath.Join(testDataPath(),"existing_file") failure: filepath.Join(testDataPath(),"non_existing_file") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T) success := assert.FileExists(t, filepath.Join(testDataPath(), "existing_file")) fmt.Printf("success: %t\n", success) } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileExists(t *testing.T) require.FileExists(t, filepath.Join(testDataPath(), "existing_file")) fmt.Println("passed") } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.FileExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileExists) | package-level function | | [`assert.FileExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileExistsf) | formatted variant | | [`assert.(*Assertions).FileExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileExists) | method variant | | [`assert.(*Assertions).FileExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileExistsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.FileExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileExists) | package-level function | | [`require.FileExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileExistsf) | formatted variant | | [`require.(*Assertions).FileExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileExists) | method variant | | [`require.(*Assertions).FileExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileExistsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.FileExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FileExists) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FileExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L23) {{% /tab %}} {{< /tabs >}} ### FileNotEmpty{#filenotempty} FileNotEmpty checks whether a file exists in the given path and is not empty. It fails if the file is empty, if the path points to a directory or there is an error when trying to check the file. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.FileNotEmpty(t, "path/to/file") success: filepath.Join(testDataPath(),"existing_file") failure: filepath.Join(testDataPath(),"empty_file") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileNotEmpty(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T) success := assert.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file")) fmt.Printf("success: %t\n", success) } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileNotEmpty(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileNotEmpty(t *testing.T) require.FileNotEmpty(t, filepath.Join(testDataPath(), "existing_file")) fmt.Println("passed") } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.FileNotEmpty(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileNotEmpty) | package-level function | | [`assert.FileNotEmptyf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileNotEmptyf) | formatted variant | | [`assert.(*Assertions).FileNotEmpty(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileNotEmpty) | method variant | | [`assert.(*Assertions).FileNotEmptyf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileNotEmptyf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.FileNotEmpty(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileNotEmpty) | package-level function | | [`require.FileNotEmptyf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileNotEmptyf) | formatted variant | | [`require.(*Assertions).FileNotEmpty(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileNotEmpty) | method variant | | [`require.(*Assertions).FileNotEmptyf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileNotEmptyf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.FileNotEmpty(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FileNotEmpty) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FileNotEmpty](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L183) {{% /tab %}} {{< /tabs >}} ### FileNotExists{#filenotexists} FileNotExists checks whether a file does not exist in a given path. It fails if the path points to an existing _file_ only. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.FileNotExists(t, "path/to/file") success: filepath.Join(testDataPath(),"non_existing_file") failure: filepath.Join(testDataPath(),"existing_file") ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileNotExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T) success := assert.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) fmt.Printf("success: %t\n", success) } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestFileNotExists(t *testing.T) package main import ( "fmt" "path/filepath" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestFileNotExists(t *testing.T) require.FileNotExists(t, filepath.Join(testDataPath(), "non_existing_file")) fmt.Println("passed") } func testDataPath() string { return filepath.Join("..", "internal", "assertions", "testdata") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.FileNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileNotExists) | package-level function | | [`assert.FileNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FileNotExistsf) | formatted variant | | [`assert.(*Assertions).FileNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileNotExists) | method variant | | [`assert.(*Assertions).FileNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FileNotExistsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.FileNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileNotExists) | package-level function | | [`require.FileNotExistsf(t T, path string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FileNotExistsf) | formatted variant | | [`require.(*Assertions).FileNotExists(path string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileNotExists) | method variant | | [`require.(*Assertions).FileNotExistsf(path string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FileNotExistsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.FileNotExists(t T, path string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FileNotExists) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FileNotExists](https://github.com/go-openapi/testify/blob/master/internal/assertions/file.go#L53) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/http.md000066400000000000000000000754111520301377500223620ustar00rootroot00000000000000--- title: "Http" description: "Asserting HTTP Response And Body" weight: 8 domains: - "http" keywords: - "HTTPBody" - "HTTPBodyf" - "HTTPBodyContains" - "HTTPBodyContainsf" - "HTTPBodyNotContains" - "HTTPBodyNotContainsf" - "HTTPError" - "HTTPErrorf" - "HTTPRedirect" - "HTTPRedirectf" - "HTTPStatusCode" - "HTTPStatusCodef" - "HTTPSuccess" - "HTTPSuccessf" --- Asserting HTTP Response And Body ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 7 functionalities. ```tree - [HTTPBodyContains](#httpbodycontains) | angles-right - [HTTPBodyNotContains](#httpbodynotcontains) | angles-right - [HTTPError](#httperror) | angles-right - [HTTPRedirect](#httpredirect) | angles-right - [HTTPStatusCode](#httpstatuscode) | angles-right - [HTTPSuccess](#httpsuccess) | angles-right ``` ### HTTPBodyContains{#httpbodycontains} HTTPBodyContains asserts that a specified handler returns a body that contains a string. Returns whether the assertion was successful (true) or not (false). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!" failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPBodyContains(t *testing.T) package main import ( "fmt" "net/http" "net/url" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T) success := assert.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") fmt.Printf("success: %t\n", success) } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPBodyContains(t *testing.T) package main import ( "fmt" "net/http" "net/url" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyContains(t *testing.T) require.HTTPBodyContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!") fmt.Println("passed") } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPBodyContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPBodyContains) | package-level function | | [`assert.HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPBodyContainsf) | formatted variant | | [`assert.(*Assertions).HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPBodyContains) | method variant | | [`assert.(*Assertions).HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPBodyContainsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPBodyContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPBodyContains) | package-level function | | [`require.HTTPBodyContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPBodyContainsf) | formatted variant | | [`require.(*Assertions).HTTPBodyContains(handler http.HandlerFunc, method string, url string, values url.Values, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPBodyContains) | method variant | | [`require.(*Assertions).HTTPBodyContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPBodyContainsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPBodyContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPBodyContains) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPBodyContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L147) {{% /tab %}} {{< /tabs >}} ### HTTPBodyNotContains{#httpbodynotcontains} HTTPBodyNotContains asserts that a specified handler returns a body that does not contain a string. Returns whether the assertion was successful (true) or not (false). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!" failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPBodyNotContains(t *testing.T) package main import ( "fmt" "net/http" "net/url" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T) success := assert.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") fmt.Printf("success: %t\n", success) } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPBodyNotContains(t *testing.T) package main import ( "fmt" "net/http" "net/url" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPBodyNotContains(t *testing.T) require.HTTPBodyNotContains(t, httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!") fmt.Println("passed") } func httpBody(w http.ResponseWriter, r *http.Request) { name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPBodyNotContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPBodyNotContains) | package-level function | | [`assert.HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPBodyNotContainsf) | formatted variant | | [`assert.(*Assertions).HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPBodyNotContains) | method variant | | [`assert.(*Assertions).HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPBodyNotContainsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPBodyNotContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPBodyNotContains) | package-level function | | [`require.HTTPBodyNotContainsf(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPBodyNotContainsf) | formatted variant | | [`require.(*Assertions).HTTPBodyNotContains(handler http.HandlerFunc, method string, url string, values url.Values, str any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPBodyNotContains) | method variant | | [`require.(*Assertions).HTTPBodyNotContainsf(handler http.HandlerFunc, method string, url string, values url.Values, str any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPBodyNotContainsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPBodyNotContains(t T, handler http.HandlerFunc, method string, url string, values url.Values, str any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPBodyNotContains) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPBodyNotContains](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L176) {{% /tab %}} {{< /tabs >}} ### HTTPError{#httperror} HTTPError asserts that a specified handler returns an error status code. Returns whether the assertion was successful (true) or not (false). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} success: httpError, "GET", "/", nil failure: httpOK, "GET", "/", nil ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPError(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T) success := assert.HTTPError(t, httpError, "GET", "/", nil) fmt.Printf("success: %t\n", success) } func httpError(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPError(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPError(t *testing.T) require.HTTPError(t, httpError, "GET", "/", nil) fmt.Println("passed") } func httpError(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPError(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPError) | package-level function | | [`assert.HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPErrorf) | formatted variant | | [`assert.(*Assertions).HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPError) | method variant | | [`assert.(*Assertions).HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPErrorf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPError(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPError) | package-level function | | [`require.HTTPErrorf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPErrorf) | formatted variant | | [`require.(*Assertions).HTTPError(handler http.HandlerFunc, method string, url string, values url.Values) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPError) | method variant | | [`require.(*Assertions).HTTPErrorf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPErrorf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPError(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPError) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPError](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L79) {{% /tab %}} {{< /tabs >}} ### HTTPRedirect{#httpredirect} HTTPRedirect asserts that a specified handler returns a redirect status code. Returns whether the assertion was successful (true) or not (false). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} success: httpRedirect, "GET", "/", nil failure: httpError, "GET", "/", nil ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPRedirect(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T) success := assert.HTTPRedirect(t, httpRedirect, "GET", "/", nil) fmt.Printf("success: %t\n", success) } func httpRedirect(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusTemporaryRedirect) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPRedirect(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPRedirect(t *testing.T) require.HTTPRedirect(t, httpRedirect, "GET", "/", nil) fmt.Println("passed") } func httpRedirect(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusTemporaryRedirect) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPRedirect(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPRedirect) | package-level function | | [`assert.HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPRedirectf) | formatted variant | | [`assert.(*Assertions).HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPRedirect) | method variant | | [`assert.(*Assertions).HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPRedirectf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPRedirect(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPRedirect) | package-level function | | [`require.HTTPRedirectf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPRedirectf) | formatted variant | | [`require.(*Assertions).HTTPRedirect(handler http.HandlerFunc, method string, url string, values url.Values) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPRedirect) | method variant | | [`require.(*Assertions).HTTPRedirectf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPRedirectf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPRedirect(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPRedirect) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPRedirect](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L53) {{% /tab %}} {{< /tabs >}} ### HTTPStatusCode{#httpstatuscode} HTTPStatusCode asserts that a specified handler returns a specified status code. Returns whether the assertion was successful (true) or not (false). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) success: httpOK, "GET", "/", nil, http.StatusOK failure: httpError, "GET", "/", nil, http.StatusOK ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPStatusCode(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T) success := assert.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK) fmt.Printf("success: %t\n", success) } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPStatusCode(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPStatusCode(t *testing.T) require.HTTPStatusCode(t, httpOK, "GET", "/", nil, http.StatusOK) fmt.Println("passed") } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPStatusCode(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPStatusCode) | package-level function | | [`assert.HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPStatusCodef) | formatted variant | | [`assert.(*Assertions).HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPStatusCode) | method variant | | [`assert.(*Assertions).HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPStatusCodef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPStatusCode(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPStatusCode) | package-level function | | [`require.HTTPStatusCodef(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPStatusCodef) | formatted variant | | [`require.(*Assertions).HTTPStatusCode(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPStatusCode) | method variant | | [`require.(*Assertions).HTTPStatusCodef(handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPStatusCodef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPStatusCode(t T, handler http.HandlerFunc, method string, url string, values url.Values, statuscode int, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPStatusCode) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPStatusCode](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L105) {{% /tab %}} {{< /tabs >}} ### HTTPSuccess{#httpsuccess} HTTPSuccess asserts that a specified handler returns a success status code. Returns whether the assertion was successful (true) or not (false). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) success: httpOK, "GET", "/", nil failure: httpError, "GET", "/", nil ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPSuccess(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T) success := assert.HTTPSuccess(t, httpOK, "GET", "/", nil) fmt.Printf("success: %t\n", success) } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestHTTPSuccess(t *testing.T) package main import ( "fmt" "net/http" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestHTTPSuccess(t *testing.T) require.HTTPSuccess(t, httpOK, "GET", "/", nil) fmt.Println("passed") } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPSuccess(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPSuccess) | package-level function | | [`assert.HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPSuccessf) | formatted variant | | [`assert.(*Assertions).HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPSuccess) | method variant | | [`assert.(*Assertions).HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPSuccessf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPSuccess(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPSuccess) | package-level function | | [`require.HTTPSuccessf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPSuccessf) | formatted variant | | [`require.(*Assertions).HTTPSuccess(handler http.HandlerFunc, method string, url string, values url.Values) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPSuccess) | method variant | | [`require.(*Assertions).HTTPSuccessf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPSuccessf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPSuccess(t T, handler http.HandlerFunc, method string, url string, values url.Values, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPSuccess) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPSuccess](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L27) {{% /tab %}} {{< /tabs >}} --- ## Other helpers ### HTTPBody{#httpbody} HTTPBody is a helper that returns the HTTP body of the response. It returns the empty string if building a new request fails. {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.HTTPBody(handler http.HandlerFunc, method string, url string, values url.Values) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPBody) | package-level function | | [`assert.HTTPBodyf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#HTTPBodyf) | formatted variant | | [`assert.(*Assertions).HTTPBody(handler http.HandlerFunc, method string, url string, values url.Values) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPBody) | method variant | | [`assert.(*Assertions).HTTPBodyf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.HTTPBodyf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.HTTPBody(handler http.HandlerFunc, method string, url string, values url.Values) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPBody) | package-level function | | [`require.HTTPBodyf(t T, handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ...any) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#HTTPBodyf) | formatted variant | | [`require.(*Assertions).HTTPBody(handler http.HandlerFunc, method string, url string, values url.Values) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPBody) | method variant | | [`require.(*Assertions).HTTPBodyf(handler http.HandlerFunc, method string, url string, values url.Values, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.HTTPBodyf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.HTTPBody(handler http.HandlerFunc, method string, url string, values url.Values) string`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#HTTPBody) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#HTTPBody](https://github.com/go-openapi/testify/blob/master/internal/assertions/http.go#L121) {{% /tab %}} {{< /tabs >}} --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/json.md000066400000000000000000000477631520301377500223650ustar00rootroot00000000000000--- title: "Json" description: "Asserting JSON Documents" weight: 9 domains: - "json" keywords: - "JSONEq" - "JSONEqf" - "JSONEqBytes" - "JSONEqBytesf" - "JSONEqT" - "JSONEqTf" - "JSONMarshalAsT" - "JSONMarshalAsTf" - "JSONUnmarshalAsT" - "JSONUnmarshalAsTf" --- Asserting JSON Documents ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 5 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [JSONEq](#jsoneq) | angles-right - [JSONEqBytes](#jsoneqbytes) | angles-right - [JSONEqT[EDoc, ADoc RText]](#jsoneqtedoc-adoc-rtext) | star | orange - [JSONMarshalAsT[EDoc RText]](#jsonmarshalastedoc-rtext) | star | orange - [JSONUnmarshalAsT[Object any, ADoc RText]](#jsonunmarshalastobject-any-adoc-rtext) | star | orange ``` ### JSONEq{#jsoneq} JSONEq asserts that two JSON strings are semantically equivalent. Expected and actual must be valid JSON. For dynamic redaction of the input text via a callback, use [JSONEqT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) success: `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}` failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONEq(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T) success := assert.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONEq(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONEq(t *testing.T) require.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.JSONEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEq) | package-level function | | [`assert.JSONEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqf) | formatted variant | | [`assert.(*Assertions).JSONEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.JSONEq) | method variant | | [`assert.(*Assertions).JSONEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.JSONEqf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.JSONEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEq) | package-level function | | [`require.JSONEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqf) | formatted variant | | [`require.(*Assertions).JSONEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.JSONEq) | method variant | | [`require.(*Assertions).JSONEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.JSONEqf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.JSONEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEq) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEq](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L65) {{% /tab %}} {{< /tabs >}} ### JSONEqBytes{#jsoneqbytes} JSONEqBytes asserts that two JSON slices of bytes are semantically equivalent. Expected and actual must be valid JSON. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) success: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`) failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONEqBytes(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T) success := assert.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONEqBytes(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONEqBytes(t *testing.T) require.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqBytes) | package-level function | | [`assert.JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqBytesf) | formatted variant | | [`assert.(*Assertions).JSONEqBytes(expected []byte, actual []byte) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.JSONEqBytes) | method variant | | [`assert.(*Assertions).JSONEqBytesf(expected []byte, actual []byte, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.JSONEqBytesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqBytes) | package-level function | | [`require.JSONEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqBytesf) | formatted variant | | [`require.(*Assertions).JSONEqBytes(expected []byte, actual []byte) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.JSONEqBytes) | method variant | | [`require.(*Assertions).JSONEqBytesf(expected []byte, actual []byte, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.JSONEqBytesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.JSONEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEqBytes) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEqBytes](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L25) > **Maintainer Note** > > Proposal for enhancement. > > We could use and indirection for users to inject their favorite JSON > library like we do for YAML. > {{% /tab %}} {{< /tabs >}} ### JSONEqT[EDoc, ADoc RText] {{% icon icon="star" color=orange %}}{#jsoneqtedoc-adoc-rtext} JSONEqT asserts that two JSON documents are semantically equivalent. The expected and actual arguments may be string or []byte. They do not need to be of the same type. Expected and actual must be valid JSON. NOTE: passed values (expected, actual) may be wrapped as functions to redact the input text dynamically. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONEqT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T) success := assert.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONEqT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONEqT(t *testing.T) require.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.JSONEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqT) | package-level function | | [`assert.JSONEqTf[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.JSONEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqT) | package-level function | | [`require.JSONEqTf[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONEqTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.JSONEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONEqT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONEqT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L90) {{% /tab %}} {{< /tabs >}} ### JSONMarshalAsT[EDoc RText] {{% icon icon="star" color=orange %}}{#jsonmarshalastedoc-rtext} JSONMarshalAsT wraps [JSONEqT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONEqT) after [json.Marshal](https://pkg.go.dev/json#Marshal). The input JSON may be a string or []byte. It fails if the marshaling returns an error or if the expected JSON bytes differ semantically from the expected ones. NOTE: passed expected value may be wrapped as a function to redact the input text dynamically. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go actual := struct { A int `json:"a"` }{ A: 10, } assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T) success := assert.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) fmt.Printf("success: %t\n", success) } type dummyStruct struct { A string b int } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONMarshalAsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONMarshalAsT(t *testing.T) require.JSONMarshalAsT(t, []byte(`{"A": "a"}`), dummyStruct{A: "a"}) fmt.Println("passed") } type dummyStruct struct { A string b int } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.JSONMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONMarshalAsT) | package-level function | | [`assert.JSONMarshalAsTf[EDoc RText](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONMarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.JSONMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONMarshalAsT) | package-level function | | [`require.JSONMarshalAsTf[EDoc RText](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONMarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.JSONMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONMarshalAsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONMarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L161) {{% /tab %}} {{< /tabs >}} ### JSONUnmarshalAsT[Object any, ADoc RText] {{% icon icon="star" color=orange %}}{#jsonunmarshalastobject-any-adoc-rtext} JSONUnmarshalAsT wraps [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) after [json.Unmarshal](https://pkg.go.dev/json#Unmarshal). The input JSON may be a string or []byte. It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. Be careful not to wrap the expected object into an "any" interface if this is not what you expected: the unmarshaling would take this type to unmarshal as a map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)any. NOTE: passed jazon value may be wrapped as a function to redact the input JSON dynamically. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go expected := struct { A int `json:"a"` }{ A: 10, } assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T) success := assert.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) fmt.Printf("success: %t\n", success) } type dummyStruct struct { A string b int } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestJSONUnmarshalAsT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestJSONUnmarshalAsT(t *testing.T) require.JSONUnmarshalAsT(t, dummyStruct{A: "a"}, []byte(`{"A": "a"}`)) fmt.Println("passed") } type dummyStruct struct { A string b int } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.JSONUnmarshalAsT[Object any, ADoc RText](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONUnmarshalAsT) | package-level function | | [`assert.JSONUnmarshalAsTf[Object any, ADoc RText](t T, expected Object, jazon ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#JSONUnmarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.JSONUnmarshalAsT[Object any, ADoc RText](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONUnmarshalAsT) | package-level function | | [`require.JSONUnmarshalAsTf[Object any, ADoc RText](t T, expected Object, jazon ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#JSONUnmarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.JSONUnmarshalAsT[Object any, ADoc RText](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#JSONUnmarshalAsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#JSONUnmarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/json.go#L124) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/metrics.md000066400000000000000000000245671520301377500230570ustar00rootroot00000000000000--- title: "Quick API index" description: | API Quick Index & Metrics. weight: -1 --- ## Domains All assertions are classified into **19** domains to help navigate the API, depending on your use case. ## API metrics Counts for core functionality, and generated variants (formatted, forward, forward-formatted). | Kind | Count | Note | | ------------------------- | ----------------- | ---- | | All core functions | 141 | Maintained core | | All core assertions | 137 | Usage with `*testing.T` | | Generic assertions | 53 | Type-safe assertions ("T" suffix) | | Helpers (not assertions) | 4 | General-purpose utilities, not assertions | | Others | 0 | | | assert/require variants | 442 | Generated variants | | Total assertions variants | 884 | Available assertions API | | Total API surface | 894 | | ## Quick index Table of core assertions, excluding variants. Each function is side by side with its logical opposite (when available). | Assertion | Opposite | Domain | Kind | | ------------------------ | ----------------- | ------ | ---- | | [Blocked](condition/#blocked) | [NotBlocked](condition/#notblocked) | condition | | | [BlockedT[E any, CHAN ~chan E]](condition/#blockedte-any-chan-chan-e) {{% icon icon="star" color=orange %}} | [NotBlockedT](condition/#notblockedte-any-chan-chan-e) | condition | | | [CallerInfo](common/#callerinfo) | | common | helper | | [Condition](condition/#condition) | | condition | | | [Consistently[C Conditioner]](condition/#consistentlyc-conditioner) {{% icon icon="star" color=orange %}} | | condition | | | [Contains](collection/#contains) | [NotContains](collection/#notcontains) | collection | | | [DirExists](file/#direxists) | [DirNotExists](file/#dirnotexists) | file | | | [ElementsMatch](collection/#elementsmatch) | [NotElementsMatch](collection/#notelementsmatch) | collection | | | [ElementsMatchT[E comparable]](collection/#elementsmatchte-comparable) {{% icon icon="star" color=orange %}} | [NotElementsMatchT](collection/#notelementsmatchte-comparable) | collection | | | [Empty](equality/#empty) | [NotEmpty](equality/#notempty) | equality | | | [Equal](equality/#equal) | [NotEqual](equality/#notequal) | equality | | | [EqualError](error/#equalerror) | | error | | | [EqualExportedValues](equality/#equalexportedvalues) | | equality | | | [EqualT[V comparable]](equality/#equaltv-comparable) {{% icon icon="star" color=orange %}} | [NotEqualT](equality/#notequaltv-comparable) | equality | | | [EqualValues](equality/#equalvalues) | [NotEqualValues](equality/#notequalvalues) | equality | | | [Error](error/#error) | [NoError](error/#noerror) | error | | | [ErrorAs](error/#erroras) | [NotErrorAs](error/#noterroras) | error | | | [ErrorContains](error/#errorcontains) | | error | | | [ErrorIs](error/#erroris) | [NotErrorIs](error/#noterroris) | error | | | [EventuallyWith[C CollectibleConditioner]](condition/#eventuallywithc-collectibleconditioner) {{% icon icon="star" color=orange %}} | | condition | | | [Eventually[C Conditioner]](condition/#eventuallyc-conditioner) {{% icon icon="star" color=orange %}} | [Never](condition/#neverc-neverconditioner) | condition | | | [Exactly](equality/#exactly) | | equality | | | [Fail](testing/#fail) | | testing | | | [FailNow](testing/#failnow) | | testing | | | [FileEmpty](file/#fileempty) | [FileNotEmpty](file/#filenotempty) | file | | | [FileExists](file/#fileexists) | [FileNotExists](file/#filenotexists) | file | | | [Greater](comparison/#greater) | [LessOrEqual](comparison/#lessorequal) | comparison | | | [GreaterOrEqual](comparison/#greaterorequal) | [Less](comparison/#less) | comparison | | | [GreaterOrEqualT[Orderable Ordered]](comparison/#greaterorequaltorderable-ordered) {{% icon icon="star" color=orange %}} | [LessT](comparison/#lesstorderable-ordered) | comparison | | | [GreaterT[Orderable Ordered]](comparison/#greatertorderable-ordered) {{% icon icon="star" color=orange %}} | [LessOrEqualT](comparison/#lessorequaltorderable-ordered) | comparison | | | [HTTPBody](http/#httpbody) | | http | helper | | [HTTPBodyContains](http/#httpbodycontains) | [HTTPBodyNotContains](http/#httpbodynotcontains) | http | | | [HTTPError](http/#httperror) | | http | | | [HTTPRedirect](http/#httpredirect) | | http | | | [HTTPStatusCode](http/#httpstatuscode) | | http | | | [HTTPSuccess](http/#httpsuccess) | | http | | | [Implements](type/#implements) | [NotImplements](type/#notimplements) | type | | | [InDelta](number/#indelta) | | number | | | [InDeltaMapValues](number/#indeltamapvalues) | | number | | | [InDeltaSlice](number/#indeltaslice) | | number | | | [InDeltaT[Number Measurable]](number/#indeltatnumber-measurable) {{% icon icon="star" color=orange %}} | | number | | | [InEpsilon](number/#inepsilon) | | number | | | [InEpsilonSlice](number/#inepsilonslice) | | number | | | [InEpsilonSymmetric](number/#inepsilonsymmetric) | | number | | | [InEpsilonSymmetricT[Number Measurable]](number/#inepsilonsymmetrictnumber-measurable) {{% icon icon="star" color=orange %}} | | number | | | [InEpsilonT[Number Measurable]](number/#inepsilontnumber-measurable) {{% icon icon="star" color=orange %}} | | number | | | [IsDecreasing](ordering/#isdecreasing) | [IsNonDecreasing](ordering/#isnondecreasing) | ordering | | | [IsDecreasingT[OrderedSlice ~[]E, E Ordered]](ordering/#isdecreasingtorderedslice-e-e-ordered) {{% icon icon="star" color=orange %}} | [IsNonDecreasingT](ordering/#isnondecreasingtorderedslice-e-e-ordered) | ordering | | | [IsIncreasing](ordering/#isincreasing) | [IsNonIncreasing](ordering/#isnonincreasing) | ordering | | | [IsIncreasingT[OrderedSlice ~[]E, E Ordered]](ordering/#isincreasingtorderedslice-e-e-ordered) {{% icon icon="star" color=orange %}} | [IsNonIncreasingT](ordering/#isnonincreasingtorderedslice-e-e-ordered) | ordering | | | [IsOfTypeT[EType any]](type/#isoftypetetype-any) {{% icon icon="star" color=orange %}} | [IsNotOfTypeT](type/#isnotoftypetetype-any) | type | | | [IsType](type/#istype) | [IsNotType](type/#isnottype) | type | | | [JSONEq](json/#jsoneq) | | json | | | [JSONEqBytes](json/#jsoneqbytes) | | json | | | [JSONEqT[EDoc, ADoc RText]](json/#jsoneqtedoc-adoc-rtext) {{% icon icon="star" color=orange %}} | | json | | | [JSONMarshalAsT[EDoc RText]](json/#jsonmarshalastedoc-rtext) {{% icon icon="star" color=orange %}} | | json | | | [JSONUnmarshalAsT[Object any, ADoc RText]](json/#jsonunmarshalastobject-any-adoc-rtext) {{% icon icon="star" color=orange %}} | | json | | | [Kind](type/#kind) | [NotKind](type/#notkind) | type | | | [Len](collection/#len) | | collection | | | [MapContainsT[Map ~map[K]V, K comparable, V any]](collection/#mapcontainstmap-mapkv-k-comparable-v-any) {{% icon icon="star" color=orange %}} | [MapNotContainsT](collection/#mapnotcontainstmap-mapkv-k-comparable-v-any) | collection | | | [MapEqualT[K, V comparable]](collection/#mapequaltk-v-comparable) {{% icon icon="star" color=orange %}} | [MapNotEqualT](collection/#mapnotequaltk-v-comparable) | collection | | | [Nil](equality/#nil) | [NotNil](equality/#notnil) | equality | | | [NoFileDescriptorLeak](safety/#nofiledescriptorleak) | | safety | | | [NoGoRoutineLeak](safety/#nogoroutineleak) | | safety | | | [ObjectsAreEqual](common/#objectsareequal) | | common | helper | | [ObjectsAreEqualValues](common/#objectsareequalvalues) | | common | helper | | [Panics](panic/#panics) | [NotPanics](panic/#notpanics) | panic | | | [PanicsWithError](panic/#panicswitherror) | | panic | | | [PanicsWithValue](panic/#panicswithvalue) | | panic | | | [Positive](comparison/#positive) | [Negative](comparison/#negative) | comparison | | | [PositiveT[SignedNumber SignedNumeric]](comparison/#positivetsignednumber-signednumeric) {{% icon icon="star" color=orange %}} | [NegativeT](comparison/#negativetsignednumber-signednumeric) | comparison | | | [Regexp](string/#regexp) | [NotRegexp](string/#notregexp) | string | | | [RegexpT[Rex RegExp, ADoc Text]](string/#regexptrex-regexp-adoc-text) {{% icon icon="star" color=orange %}} | [NotRegexpT](string/#notregexptrex-regexp-adoc-text) | string | | | [Same](equality/#same) | [NotSame](equality/#notsame) | equality | | | [SameT[P any]](equality/#sametp-any) {{% icon icon="star" color=orange %}} | [NotSameT](equality/#notsametp-any) | equality | | | [SeqContainsT[E comparable]](collection/#seqcontainste-comparable) {{% icon icon="star" color=orange %}} | [SeqNotContainsT](collection/#seqnotcontainste-comparable) | collection | | | [SliceContainsT[Slice ~[]E, E comparable]](collection/#slicecontainstslice-e-e-comparable) {{% icon icon="star" color=orange %}} | [SliceNotContainsT](collection/#slicenotcontainstslice-e-e-comparable) | collection | | | [SliceEqualT[E comparable]](collection/#sliceequalte-comparable) {{% icon icon="star" color=orange %}} | [SliceNotEqualT](collection/#slicenotequalte-comparable) | collection | | | [SliceSubsetT[Slice ~[]E, E comparable]](collection/#slicesubsettslice-e-e-comparable) {{% icon icon="star" color=orange %}} | [SliceNotSubsetT](collection/#slicenotsubsettslice-e-e-comparable) | collection | | | [SortedT[OrderedSlice ~[]E, E Ordered]](ordering/#sortedtorderedslice-e-e-ordered) {{% icon icon="star" color=orange %}} | [NotSortedT](ordering/#notsortedtorderedslice-e-e-ordered) | ordering | | | [StringContainsT[ADoc, EDoc Text]](collection/#stringcontainstadoc-edoc-text) {{% icon icon="star" color=orange %}} | [StringNotContainsT](collection/#stringnotcontainstadoc-edoc-text) | collection | | | [Subset](collection/#subset) | [NotSubset](collection/#notsubset) | collection | | | [True](boolean/#true) | [False](boolean/#false) | boolean | | | [TrueT[B Boolean]](boolean/#truetb-boolean) {{% icon icon="star" color=orange %}} | [FalseT](boolean/#falsetb-boolean) | boolean | | | [WithinDuration](time/#withinduration) | | time | | | [WithinRange](time/#withinrange) | | time | | | [YAMLEq](yaml/#yamleq) | | yaml | | | [YAMLEqBytes](yaml/#yamleqbytes) | | yaml | | | [YAMLEqT[EDoc, ADoc RText]](yaml/#yamleqtedoc-adoc-rtext) {{% icon icon="star" color=orange %}} | | yaml | | | [YAMLMarshalAsT[EDoc RText]](yaml/#yamlmarshalastedoc-rtext) {{% icon icon="star" color=orange %}} | | yaml | | | [YAMLUnmarshalAsT[Object any, ADoc RText]](yaml/#yamlunmarshalastobject-any-adoc-rtext) {{% icon icon="star" color=orange %}} | | yaml | | | [Zero](type/#zero) | [NotZero](type/#notzero) | type | | go-openapi-testify-c10ca71/docs/doc-site/api/number.md000066400000000000000000001150771520301377500226760ustar00rootroot00000000000000--- title: "Number" description: "Asserting Numbers" weight: 10 domains: - "number" keywords: - "InDelta" - "InDeltaf" - "InDeltaMapValues" - "InDeltaMapValuesf" - "InDeltaSlice" - "InDeltaSlicef" - "InDeltaT" - "InDeltaTf" - "InEpsilon" - "InEpsilonf" - "InEpsilonSlice" - "InEpsilonSlicef" - "InEpsilonSymmetric" - "InEpsilonSymmetricf" - "InEpsilonSymmetricT" - "InEpsilonSymmetricTf" - "InEpsilonT" - "InEpsilonTf" --- Asserting Numbers ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 9 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [InDelta](#indelta) | angles-right - [InDeltaMapValues](#indeltamapvalues) | angles-right - [InDeltaSlice](#indeltaslice) | angles-right - [InDeltaT[Number Measurable]](#indeltatnumber-measurable) | star | orange - [InEpsilon](#inepsilon) | angles-right - [InEpsilonSlice](#inepsilonslice) | angles-right - [InEpsilonSymmetric](#inepsilonsymmetric) | angles-right - [InEpsilonSymmetricT[Number Measurable]](#inepsilonsymmetrictnumber-measurable) | star | orange - [InEpsilonT[Number Measurable]](#inepsilontnumber-measurable) | star | orange ``` ### InDelta{#indelta} InDelta asserts that the two numerals are within delta of each other. Delta must be greater than or equal to zero. Expected and actual values should convert to float64. To compare large integers that can't be represented accurately as float64 (e.g. uint64), prefer [InDeltaT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT) to preserve the original type. #### Behavior with IEEE floating point arithmetic - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.Sqrt](-1), [math.Sqrt](-1), 0.0) - expected +Inf is matched only by a +Inf - expected -Inf is matched only by a -Inf {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InDelta(t, math.Pi, 22/7.0, 0.01) success: 1.0, 1.01, 0.02 failure: 1.0, 1.1, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDelta(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T) success := assert.InDelta(t, 1.0, 1.01, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDelta(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDelta(t *testing.T) require.InDelta(t, 1.0, 1.01, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta) | package-level function | | [`assert.InDeltaf(t T, expected any, actual any, delta float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaf) | formatted variant | | [`assert.(*Assertions).InDelta(expected any, actual any, delta float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InDelta) | method variant | | [`assert.(*Assertions).InDeltaf(expected any, actual any, delta float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InDeltaf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDelta) | package-level function | | [`require.InDeltaf(t T, expected any, actual any, delta float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaf) | formatted variant | | [`require.(*Assertions).InDelta(expected any, actual any, delta float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InDelta) | method variant | | [`require.(*Assertions).InDeltaf(expected any, actual any, delta float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InDeltaf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InDelta(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDelta) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDelta](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L35) {{% /tab %}} {{< /tabs >}} ### InDeltaMapValues{#indeltamapvalues} InDeltaMapValues is the same as [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta), but it compares all values between two maps. Both maps must have exactly the same keys. See [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InDeltaMapValues(t, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02) success: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.01}, 0.02 failure: map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.0}, map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)float64{"a": 1.1}, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T) success := assert.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDeltaMapValues(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaMapValues(t *testing.T) require.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaMapValues) | package-level function | | [`assert.InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaMapValuesf) | formatted variant | | [`assert.(*Assertions).InDeltaMapValues(expected any, actual any, delta float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InDeltaMapValues) | method variant | | [`assert.(*Assertions).InDeltaMapValuesf(expected any, actual any, delta float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InDeltaMapValuesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaMapValues) | package-level function | | [`require.InDeltaMapValuesf(t T, expected any, actual any, delta float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaMapValuesf) | formatted variant | | [`require.(*Assertions).InDeltaMapValues(expected any, actual any, delta float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InDeltaMapValues) | method variant | | [`require.(*Assertions).InDeltaMapValuesf(expected any, actual any, delta float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InDeltaMapValuesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InDeltaMapValues(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaMapValues](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L367) {{% /tab %}} {{< /tabs >}} ### InDeltaSlice{#indeltaslice} InDeltaSlice is the same as [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta), except it compares two slices. See [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) success: []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02 failure: []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T) success := assert.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDeltaSlice(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaSlice(t *testing.T) require.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaSlice) | package-level function | | [`assert.InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaSlicef) | formatted variant | | [`assert.(*Assertions).InDeltaSlice(expected any, actual any, delta float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InDeltaSlice) | method variant | | [`assert.(*Assertions).InDeltaSlicef(expected any, actual any, delta float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InDeltaSlicef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaSlice) | package-level function | | [`require.InDeltaSlicef(t T, expected any, actual any, delta float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaSlicef) | formatted variant | | [`require.(*Assertions).InDeltaSlice(expected any, actual any, delta float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InDeltaSlice) | method variant | | [`require.(*Assertions).InDeltaSlicef(expected any, actual any, delta float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InDeltaSlicef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InDeltaSlice(t T, expected any, actual any, delta float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L331) {{% /tab %}} {{< /tabs >}} ### InDeltaT[Number Measurable] {{% icon icon="star" color=orange %}}{#indeltatnumber-measurable} InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. [InDeltaT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT) accepts any go numeric type, including integer types. The main difference with [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta) is that the delta is expressed with the same type as the values, not necessarily a float64. Delta must be greater than or equal to zero. #### Behavior with IEEE floating point arithmetic - expected NaN is matched only by a NaN, e.g. this works: InDeltaT([math.NaN](), [math.Sqrt](-1), 0.0) - expected +Inf is matched only by a +Inf - expected -Inf is matched only by a -Inf {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) success: 1.0, 1.01, 0.02 failure: 1.0, 1.1, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDeltaT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T) success := assert.InDeltaT(t, 1.0, 1.01, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInDeltaT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInDeltaT(t *testing.T) require.InDeltaT(t, 1.0, 1.01, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT) | package-level function | | [`assert.InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaT) | package-level function | | [`require.InDeltaTf[Number Measurable](t T, expected Number, actual Number, delta Number, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InDeltaTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InDeltaT[Number Measurable](t T, expected Number, actual Number, delta Number, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InDeltaT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InDeltaT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L85) {{% /tab %}} {{< /tabs >}} ### InEpsilon{#inepsilon} InEpsilon asserts that expected and actual have a relative error less than epsilon. #### Behavior with IEEE floating point arithmetic - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.NaN](), [math.Sqrt](-1), 0.0) - expected +Inf is matched only by a +Inf - expected -Inf is matched only by a -Inf Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT). Formula: - If expected == 0: fail if |actual - expected| > epsilon - If expected != 0: fail if |actual - expected| > epsilon * |expected| This allows [InEpsilonT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonT) to work naturally across the full numeric range including zero. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InEpsilon(t, 100.0, 101.0, 0.02) success: 100.0, 101.0, 0.02 failure: 100.0, 110.0, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilon(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T) success := assert.InEpsilon(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilon(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilon(t *testing.T) require.InEpsilon(t, 100.0, 101.0, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilon) | package-level function | | [`assert.InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonf) | formatted variant | | [`assert.(*Assertions).InEpsilon(expected any, actual any, epsilon float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InEpsilon) | method variant | | [`assert.(*Assertions).InEpsilonf(expected any, actual any, epsilon float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InEpsilonf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilon) | package-level function | | [`require.InEpsilonf(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonf) | formatted variant | | [`require.(*Assertions).InEpsilon(expected any, actual any, epsilon float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InEpsilon) | method variant | | [`require.(*Assertions).InEpsilonf(expected any, actual any, epsilon float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InEpsilonf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InEpsilon(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilon) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilon](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L143) {{% /tab %}} {{< /tabs >}} ### InEpsilonSlice{#inepsilonslice} InEpsilonSlice is the same as [InEpsilon](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilon), except it compares each value from two slices. See [InEpsilon](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilon). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) success: []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02 failure: []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T) success := assert.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonSlice(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSlice(t *testing.T) require.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSlice) | package-level function | | [`assert.InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSlicef) | formatted variant | | [`assert.(*Assertions).InEpsilonSlice(expected any, actual any, epsilon float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InEpsilonSlice) | method variant | | [`assert.(*Assertions).InEpsilonSlicef(expected any, actual any, epsilon float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InEpsilonSlicef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonSlice) | package-level function | | [`require.InEpsilonSlicef(t T, expected any, actual any, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonSlicef) | formatted variant | | [`require.(*Assertions).InEpsilonSlice(expected any, actual any, epsilon float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InEpsilonSlice) | method variant | | [`require.(*Assertions).InEpsilonSlicef(expected any, actual any, epsilon float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InEpsilonSlicef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InEpsilonSlice(t T, expected any, actual any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSlice](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L422) {{% /tab %}} {{< /tabs >}} ### InEpsilonSymmetric{#inepsilonsymmetric} InEpsilonSymmetric asserts that 2 numbers are close, with a symmetric relative error. Unlike with [InEpsilon](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilon), both numbers play a symmetric role and the relative error is computed relative to the number with greatest amplitude. This mirrors the behavior of Python's [math.isclose](https://docs.python.org/3/library/math.html#math.isclose) (with the relative-tolerance term only). See also [InEpsilon](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilon). #### Behavior with IEEE floating point arithmetic - NaN is matched only by a NaN, e.g. this works: [InEpsilonSymmetric]([math.NaN](), [math.Sqrt](-1), 0.0) - +Inf is matched only by a +Inf - -Inf is matched only by a -Inf Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDelta](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDelta). Formula: - If x == 0 and y == 0: success - Otherwise fail if |x - y| > epsilon * max(|x|,|y|) {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InEpsilonSymmetric(t, 100.0, 101.0, 0.02) success: 100.0, 101.0, 0.02 failure: 100.0, 110.0, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonSymmetric(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSymmetric(t *testing.T) success := assert.InEpsilonSymmetric(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonSymmetric(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSymmetric(t *testing.T) require.InEpsilonSymmetric(t, 100.0, 101.0, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InEpsilonSymmetric(t T, x any, y any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSymmetric) | package-level function | | [`assert.InEpsilonSymmetricf(t T, x any, y any, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSymmetricf) | formatted variant | | [`assert.(*Assertions).InEpsilonSymmetric(x any, y any, epsilon float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InEpsilonSymmetric) | method variant | | [`assert.(*Assertions).InEpsilonSymmetricf(x any, y any, epsilon float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.InEpsilonSymmetricf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InEpsilonSymmetric(t T, x any, y any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonSymmetric) | package-level function | | [`require.InEpsilonSymmetricf(t T, x any, y any, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonSymmetricf) | formatted variant | | [`require.(*Assertions).InEpsilonSymmetric(x any, y any, epsilon float64) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InEpsilonSymmetric) | method variant | | [`require.(*Assertions).InEpsilonSymmetricf(x any, y any, epsilon float64, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.InEpsilonSymmetricf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InEpsilonSymmetric(t T, x any, y any, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSymmetric) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSymmetric](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L256) {{% /tab %}} {{< /tabs >}} ### InEpsilonSymmetricT[Number Measurable] {{% icon icon="star" color=orange %}}{#inepsilonsymmetrictnumber-measurable} InEpsilonSymmetricT is the type-safe version of [InEpsilonSymmetric](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSymmetric), comparing numbers of the same numerical type. See [InEpsilonSymmetric](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSymmetric). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InEpsilonSymmetricT(t, 100.0, 101.0, 0.02) success: 100.0, 101.0, 0.02 failure: 100.0, 110.0, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonSymmetricT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSymmetricT(t *testing.T) success := assert.InEpsilonSymmetricT(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonSymmetricT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonSymmetricT(t *testing.T) require.InEpsilonSymmetricT(t, 100.0, 101.0, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InEpsilonSymmetricT[Number Measurable](t T, x Number, y Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSymmetricT) | package-level function | | [`assert.InEpsilonSymmetricTf[Number Measurable](t T, x Number, y Number, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonSymmetricTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InEpsilonSymmetricT[Number Measurable](t T, x Number, y Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonSymmetricT) | package-level function | | [`require.InEpsilonSymmetricTf[Number Measurable](t T, x Number, y Number, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonSymmetricTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InEpsilonSymmetricT[Number Measurable](t T, x Number, y Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSymmetricT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonSymmetricT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L295) {{% /tab %}} {{< /tabs >}} ### InEpsilonT[Number Measurable] {{% icon icon="star" color=orange %}}{#inepsilontnumber-measurable} InEpsilonT asserts that expected and actual have a relative error less than epsilon. When expected is zero, epsilon is interpreted as an absolute error threshold, since relative error is mathematically undefined for zero values. Unlike [InDeltaT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT), which preserves the original type, [InEpsilonT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonT) converts the expected and actual numbers to float64, since the relative error doesn't make sense as an integer. #### Behavior with IEEE floating point arithmetic - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.NaN](), [math.Sqrt](-1), 0.0) - expected +Inf is matched only by a +Inf - expected -Inf is matched only by a -Inf Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InDeltaT). Formula: - If expected == 0: fail if |actual - expected| > epsilon - If expected != 0: fail if |actual - expected| > epsilon * |expected| This allows [InEpsilonT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonT) to work naturally across the full numeric range including zero. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.InEpsilon(t, 100.0, 101.0, 0.02) success: 100.0, 101.0, 0.02 failure: 100.0, 110.0, 0.05 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T) success := assert.InEpsilonT(t, 100.0, 101.0, 0.02) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestInEpsilonT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestInEpsilonT(t *testing.T) require.InEpsilonT(t, 100.0, 101.0, 0.02) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonT) | package-level function | | [`assert.InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#InEpsilonTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonT) | package-level function | | [`require.InEpsilonTf[Number Measurable](t T, expected Number, actual Number, epsilon float64, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#InEpsilonTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.InEpsilonT[Number Measurable](t T, expected Number, actual Number, epsilon float64, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#InEpsilonT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#InEpsilonT](https://github.com/go-openapi/testify/blob/master/internal/assertions/number.go#L201) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/ordering.md000066400000000000000000001125541520301377500232140ustar00rootroot00000000000000--- title: "Ordering" description: "Asserting How Collections Are Ordered" weight: 11 domains: - "ordering" keywords: - "IsDecreasing" - "IsDecreasingf" - "IsDecreasingT" - "IsDecreasingTf" - "IsIncreasing" - "IsIncreasingf" - "IsIncreasingT" - "IsIncreasingTf" - "IsNonDecreasing" - "IsNonDecreasingf" - "IsNonDecreasingT" - "IsNonDecreasingTf" - "IsNonIncreasing" - "IsNonIncreasingf" - "IsNonIncreasingT" - "IsNonIncreasingTf" - "NotSortedT" - "NotSortedTf" - "SortedT" - "SortedTf" --- Asserting How Collections Are Ordered ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 10 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [IsDecreasing](#isdecreasing) | angles-right - [IsDecreasingT[OrderedSlice ~[]E, E Ordered]](#isdecreasingtorderedslice-e-e-ordered) | star | orange - [IsIncreasing](#isincreasing) | angles-right - [IsIncreasingT[OrderedSlice ~[]E, E Ordered]](#isincreasingtorderedslice-e-e-ordered) | star | orange - [IsNonDecreasing](#isnondecreasing) | angles-right - [IsNonDecreasingT[OrderedSlice ~[]E, E Ordered]](#isnondecreasingtorderedslice-e-e-ordered) | star | orange - [IsNonIncreasing](#isnonincreasing) | angles-right - [IsNonIncreasingT[OrderedSlice ~[]E, E Ordered]](#isnonincreasingtorderedslice-e-e-ordered) | star | orange - [NotSortedT[OrderedSlice ~[]E, E Ordered]](#notsortedtorderedslice-e-e-ordered) | star | orange - [SortedT[OrderedSlice ~[]E, E Ordered]](#sortedtorderedslice-e-e-ordered) | star | orange ``` ### IsDecreasing{#isdecreasing} IsDecreasing asserts that the collection is strictly decreasing. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsDecreasing(t, []int{2, 1, 0}) assertions.IsDecreasing(t, []float{2, 1}) assertions.IsDecreasing(t, []string{"b", "a"}) success: []int{3, 2, 1} failure: []int{1, 2, 3} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsDecreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T) success := assert.IsDecreasing(t, []int{3, 2, 1}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsDecreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsDecreasing(t *testing.T) require.IsDecreasing(t, []int{3, 2, 1}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsDecreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasing) | package-level function | | [`assert.IsDecreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingf) | formatted variant | | [`assert.(*Assertions).IsDecreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsDecreasing) | method variant | | [`assert.(*Assertions).IsDecreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsDecreasingf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsDecreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsDecreasing) | package-level function | | [`require.IsDecreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsDecreasingf) | formatted variant | | [`require.(*Assertions).IsDecreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsDecreasing) | method variant | | [`require.(*Assertions).IsDecreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsDecreasingf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsDecreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsDecreasing) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsDecreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L205) {{% /tab %}} {{< /tabs >}} ### IsDecreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isdecreasingtorderedslice-e-e-ordered} IsDecreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) is strictly decreasing. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsDecreasingT(t, []int{2, 1, 0}) assertions.IsDecreasingT(t, []float{2, 1}) assertions.IsDecreasingT(t, []string{"b", "a"}) success: []int{3, 2, 1} failure: []int{1, 2, 3} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T) success := assert.IsDecreasingT(t, []int{3, 2, 1}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsDecreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsDecreasingT(t *testing.T) require.IsDecreasingT(t, []int{3, 2, 1}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingT) | package-level function | | [`assert.IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsDecreasingT) | package-level function | | [`require.IsDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsDecreasingTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsDecreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L236) {{% /tab %}} {{< /tabs >}} ### IsIncreasing{#isincreasing} IsIncreasing asserts that the collection is strictly increasing. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsIncreasing(t, []int{1, 2, 3}) assertions.IsIncreasing(t, []float{1, 2}) assertions.IsIncreasing(t, []string{"a", "b"}) success: []int{1, 2, 3} failure: []int{1, 1, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsIncreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T) success := assert.IsIncreasing(t, []int{1, 2, 3}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsIncreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsIncreasing(t *testing.T) require.IsIncreasing(t, []int{1, 2, 3}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsIncreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasing) | package-level function | | [`assert.IsIncreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingf) | formatted variant | | [`assert.(*Assertions).IsIncreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsIncreasing) | method variant | | [`assert.(*Assertions).IsIncreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsIncreasingf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsIncreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsIncreasing) | package-level function | | [`require.IsIncreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsIncreasingf) | formatted variant | | [`require.(*Assertions).IsIncreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsIncreasing) | method variant | | [`require.(*Assertions).IsIncreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsIncreasingf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsIncreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsIncreasing) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsIncreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L24) {{% /tab %}} {{< /tabs >}} ### IsIncreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isincreasingtorderedslice-e-e-ordered} IsIncreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) is strictly increasing. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsIncreasingT(t, []int{1, 2, 3}) assertions.IsIncreasingT(t, []float{1, 2}) assertions.IsIncreasingT(t, []string{"a", "b"}) success: []int{1, 2, 3} failure: []int{1, 1, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T) success := assert.IsIncreasingT(t, []int{1, 2, 3}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsIncreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsIncreasingT(t *testing.T) require.IsIncreasingT(t, []int{1, 2, 3}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingT) | package-level function | | [`assert.IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsIncreasingT) | package-level function | | [`require.IsIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsIncreasingTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsIncreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L54) {{% /tab %}} {{< /tabs >}} ### IsNonDecreasing{#isnondecreasing} IsNonDecreasing asserts that the collection is not strictly decreasing. This is the logical negation of [IsDecreasing](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasing): it succeeds whenever [IsDecreasing](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasing) would fail, including for sequences that are neither increasing nor decreasing (e.g. [3, 1, 2]). It is NOT the same as "weakly increasing" (a[i](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#i) <= a[i+1] for all i). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsNonDecreasing(t, []int{1, 1, 2}) assertions.IsNonDecreasing(t, []float{1, 2}) assertions.IsNonDecreasing(t, []string{"a", "b"}) success: []int{1, 1, 2} failure: []int{2, 1, 0} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T) success := assert.IsNonDecreasing(t, []int{1, 1, 2}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonDecreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasing(t *testing.T) require.IsNonDecreasing(t, []int{1, 1, 2}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsNonDecreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonDecreasing) | package-level function | | [`assert.IsNonDecreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonDecreasingf) | formatted variant | | [`assert.(*Assertions).IsNonDecreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsNonDecreasing) | method variant | | [`assert.(*Assertions).IsNonDecreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsNonDecreasingf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsNonDecreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonDecreasing) | package-level function | | [`require.IsNonDecreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonDecreasingf) | formatted variant | | [`require.(*Assertions).IsNonDecreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsNonDecreasing) | method variant | | [`require.(*Assertions).IsNonDecreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsNonDecreasingf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsNonDecreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasing) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L269) {{% /tab %}} {{< /tabs >}} ### IsNonDecreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isnondecreasingtorderedslice-e-e-ordered} IsNonDecreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) is NOT strictly decreasing. This is the logical negation of [IsDecreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingT): it succeeds whenever [IsDecreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingT) would fail, including for sequences that are neither increasing nor decreasing (e.g. [3, 1, 2]). It is NOT the same as "weakly increasing" (a[i](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#i) <= a[i+1] for all i). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsNonDecreasingT(t, []int{1, 1, 2}) assertions.IsNonDecreasingT(t, []float{1, 2}) assertions.IsNonDecreasingT(t, []string{"a", "b"}) success: []int{1, 1, 2} failure: []int{2, 1, 0} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T) success := assert.IsNonDecreasingT(t, []int{1, 1, 2}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonDecreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonDecreasingT(t *testing.T) require.IsNonDecreasingT(t, []int{1, 1, 2}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonDecreasingT) | package-level function | | [`assert.IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonDecreasingTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonDecreasingT) | package-level function | | [`require.IsNonDecreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonDecreasingTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonDecreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L304) {{% /tab %}} {{< /tabs >}} ### IsNonIncreasing{#isnonincreasing} IsNonIncreasing asserts that the collection is not strictly increasing. This is the logical negation of [IsIncreasing](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasing): it succeeds whenever [IsIncreasing](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasing) would fail, including for sequences that are neither increasing nor decreasing (e.g. [1, 3, 2]). It is NOT the same as "weakly decreasing" (a[i](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#i) >= a[i+1] for all i). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsNonIncreasing(t, []int{2, 1, 1}) assertions.IsNonIncreasing(t, []float{2, 1}) assertions.IsNonIncreasing(t, []string{"b", "a"}) success: []int{2, 1, 1} failure: []int{1, 2, 3} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T) success := assert.IsNonIncreasing(t, []int{2, 1, 1}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonIncreasing(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasing(t *testing.T) require.IsNonIncreasing(t, []int{2, 1, 1}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsNonIncreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonIncreasing) | package-level function | | [`assert.IsNonIncreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonIncreasingf) | formatted variant | | [`assert.(*Assertions).IsNonIncreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsNonIncreasing) | method variant | | [`assert.(*Assertions).IsNonIncreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsNonIncreasingf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsNonIncreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonIncreasing) | package-level function | | [`require.IsNonIncreasingf(t T, collection any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonIncreasingf) | formatted variant | | [`require.(*Assertions).IsNonIncreasing(collection any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsNonIncreasing) | method variant | | [`require.(*Assertions).IsNonIncreasingf(collection any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsNonIncreasingf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsNonIncreasing(t T, collection any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasing) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasing](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L144) {{% /tab %}} {{< /tabs >}} ### IsNonIncreasingT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#isnonincreasingtorderedslice-e-e-ordered} IsNonIncreasingT asserts that a slice of [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) is NOT strictly increasing. This is the logical negation of [IsIncreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingT): it succeeds whenever [IsIncreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingT) would fail, including for sequences that are neither increasing nor decreasing (e.g. [1, 3, 2]). It is NOT the same as "weakly decreasing" (a[i](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#i) >= a[i+1] for all i). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsNonIncreasingT(t, []int{2, 1, 1}) assertions.IsNonIncreasingT(t, []float{2, 1}) assertions.IsNonIncreasingT(t, []string{"b", "a"}) success: []int{2, 1, 1} failure: []int{1, 2, 3} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T) success := assert.IsNonIncreasingT(t, []int{2, 1, 1}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNonIncreasingT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNonIncreasingT(t *testing.T) require.IsNonIncreasingT(t, []int{2, 1, 1}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonIncreasingT) | package-level function | | [`assert.IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNonIncreasingTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonIncreasingT) | package-level function | | [`require.IsNonIncreasingTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNonIncreasingTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNonIncreasingT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L179) {{% /tab %}} {{< /tabs >}} ### NotSortedT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#notsortedtorderedslice-e-e-ordered} NotSortedT asserts that the slice of [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) is NOT sorted (i.e. non-strictly increasing). Unlike [IsDecreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsDecreasingT), it accepts slices that are neither increasing nor decreasing. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotSortedT(t, []int{3, 2, 3}) assertions.NotSortedT(t, []float{2, 1}) assertions.NotSortedT(t, []string{"b", "a"}) success: []int{3, 1, 3} failure: []int{1, 4, 8} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSortedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T) success := assert.NotSortedT(t, []int{3, 1, 3}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotSortedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotSortedT(t *testing.T) require.NotSortedT(t, []int{3, 1, 3}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSortedT) | package-level function | | [`assert.NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotSortedTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSortedT) | package-level function | | [`require.NotSortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotSortedTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotSortedT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotSortedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L112) {{% /tab %}} {{< /tabs >}} ### SortedT[OrderedSlice ~[]E, E Ordered] {{% icon icon="star" color=orange %}}{#sortedtorderedslice-e-e-ordered} SortedT asserts that the slice of [Ordered](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Ordered) is sorted (i.e. non-strictly increasing). Unlike [IsIncreasingT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsIncreasingT), it accepts elements to be equal. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.SortedT(t, []int{1, 2, 3}) assertions.SortedT(t, []float{1, 2}) assertions.SortedT(t, []string{"a", "b"}) success: []int{1, 1, 3} failure: []int{1, 4, 2} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSortedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T) success := assert.SortedT(t, []int{1, 1, 3}) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestSortedT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestSortedT(t *testing.T) require.SortedT(t, []int{1, 1, 3}) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SortedT) | package-level function | | [`assert.SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#SortedTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SortedT) | package-level function | | [`require.SortedTf[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#SortedTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#SortedT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#SortedT](https://github.com/go-openapi/testify/blob/master/internal/assertions/order.go#L83) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/panic.md000066400000000000000000000365101520301377500224720ustar00rootroot00000000000000--- title: "Panic" description: "Asserting A Panic Behavior" weight: 12 domains: - "panic" keywords: - "NotPanics" - "NotPanicsf" - "Panics" - "Panicsf" - "PanicsWithError" - "PanicsWithErrorf" - "PanicsWithValue" - "PanicsWithValuef" --- Asserting A Panic Behavior ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 4 functionalities. ```tree - [NotPanics](#notpanics) | angles-right - [Panics](#panics) | angles-right - [PanicsWithError](#panicswitherror) | angles-right - [PanicsWithValue](#panicswithvalue) | angles-right ``` ### NotPanics{#notpanics} NotPanics asserts that the code inside the specified function does NOT panic. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotPanics(t, func(){ RemainCalm() }) success: func() { } failure: func() { panic("panicking") } ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotPanics(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T) success := assert.NotPanics(t, func() { }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotPanics(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotPanics(t *testing.T) require.NotPanics(t, func() { }) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotPanics(t T, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotPanics) | package-level function | | [`assert.NotPanicsf(t T, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotPanicsf) | formatted variant | | [`assert.(*Assertions).NotPanics(f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotPanics) | method variant | | [`assert.(*Assertions).NotPanicsf(f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotPanicsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotPanics(t T, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotPanics) | package-level function | | [`require.NotPanicsf(t T, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotPanicsf) | formatted variant | | [`require.(*Assertions).NotPanics(f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotPanics) | method variant | | [`require.(*Assertions).NotPanicsf(f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotPanicsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotPanics(t T, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotPanics) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotPanics](https://github.com/go-openapi/testify/blob/master/internal/assertions/panic.go#L112) {{% /tab %}} {{< /tabs >}} ### Panics{#panics} Panics asserts that the code inside the specified function panics. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Panics(t, func(){ GoCrazy() }) success: func() { panic("panicking") } failure: func() { } ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPanics(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T) success := assert.Panics(t, func() { panic("panicking") }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPanics(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPanics(t *testing.T) require.Panics(t, func() { panic("panicking") }) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Panics(t T, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Panics) | package-level function | | [`assert.Panicsf(t T, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Panicsf) | formatted variant | | [`assert.(*Assertions).Panics(f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Panics) | method variant | | [`assert.(*Assertions).Panicsf(f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Panicsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Panics(t T, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Panics) | package-level function | | [`require.Panicsf(t T, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Panicsf) | formatted variant | | [`require.(*Assertions).Panics(f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Panics) | method variant | | [`require.(*Assertions).Panicsf(f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Panicsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Panics(t T, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Panics) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Panics](https://github.com/go-openapi/testify/blob/master/internal/assertions/panic.go#L25) {{% /tab %}} {{< /tabs >}} ### PanicsWithError{#panicswitherror} PanicsWithError asserts that the code inside the specified function panics, and that the recovered panic value is an error that satisfies the [EqualError](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#EqualError) comparison. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) success: ErrTest.Error(), func() { panic(ErrTest) } failure: ErrTest.Error(), func() { } ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPanicsWithError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T) success := assert.PanicsWithError(t, assert.ErrTest.Error(), func() { panic(assert.ErrTest) }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPanicsWithError(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPanicsWithError(t *testing.T) require.PanicsWithError(t, assert.ErrTest.Error(), func() { panic(assert.ErrTest) }) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.PanicsWithError(t T, errString string, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PanicsWithError) | package-level function | | [`assert.PanicsWithErrorf(t T, errString string, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PanicsWithErrorf) | formatted variant | | [`assert.(*Assertions).PanicsWithError(errString string, f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.PanicsWithError) | method variant | | [`assert.(*Assertions).PanicsWithErrorf(errString string, f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.PanicsWithErrorf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.PanicsWithError(t T, errString string, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PanicsWithError) | package-level function | | [`require.PanicsWithErrorf(t T, errString string, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PanicsWithErrorf) | formatted variant | | [`require.(*Assertions).PanicsWithError(errString string, f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.PanicsWithError) | method variant | | [`require.(*Assertions).PanicsWithErrorf(errString string, f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.PanicsWithErrorf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.PanicsWithError(t T, errString string, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#PanicsWithError) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#PanicsWithError](https://github.com/go-openapi/testify/blob/master/internal/assertions/panic.go#L78) {{% /tab %}} {{< /tabs >}} ### PanicsWithValue{#panicswithvalue} PanicsWithValue asserts that the code inside the specified function panics, and that the recovered panic value equals the expected panic value. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) success: "panicking", func() { panic("panicking") } failure: "panicking", func() { } ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T) success := assert.PanicsWithValue(t, "panicking", func() { panic("panicking") }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestPanicsWithValue(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestPanicsWithValue(t *testing.T) require.PanicsWithValue(t, "panicking", func() { panic("panicking") }) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.PanicsWithValue(t T, expected any, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PanicsWithValue) | package-level function | | [`assert.PanicsWithValuef(t T, expected any, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#PanicsWithValuef) | formatted variant | | [`assert.(*Assertions).PanicsWithValue(expected any, f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.PanicsWithValue) | method variant | | [`assert.(*Assertions).PanicsWithValuef(expected any, f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.PanicsWithValuef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.PanicsWithValue(t T, expected any, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PanicsWithValue) | package-level function | | [`require.PanicsWithValuef(t T, expected any, f func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#PanicsWithValuef) | formatted variant | | [`require.(*Assertions).PanicsWithValue(expected any, f func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.PanicsWithValue) | method variant | | [`require.(*Assertions).PanicsWithValuef(expected any, f func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.PanicsWithValuef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.PanicsWithValue(t T, expected any, f func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#PanicsWithValue) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#PanicsWithValue](https://github.com/go-openapi/testify/blob/master/internal/assertions/panic.go#L50) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/safety.md000066400000000000000000000317601520301377500226750ustar00rootroot00000000000000--- title: "Safety" description: "Checks Against Leaked Resources (Goroutines, File Descriptors)" weight: 13 domains: - "safety" keywords: - "NoFileDescriptorLeak" - "NoFileDescriptorLeakf" - "NoGoRoutineLeak" - "NoGoRoutineLeakf" --- Checks Against Leaked Resources (Goroutines, File Descriptors) ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 2 functionalities. ```tree - [NoFileDescriptorLeak](#nofiledescriptorleak) | angles-right - [NoGoRoutineLeak](#nogoroutineleak) | angles-right ``` ### NoFileDescriptorLeak{#nofiledescriptorleak} NoFileDescriptorLeak ensures that no file descriptor leaks from inside the tested function. This assertion works on Linux (via /proc/self/fd) and macOS (via fstat probing). On other platforms, the test is skipped. NOTE: this assertion is not compatible with parallel tests. File descriptors are a process-wide resource; concurrent tests opening files would cause false positives. Sockets, pipes, and other kernel-internal descriptors (Linux anon_inode, darwin kqueue) are filtered out by default, as these are typically managed by the Go runtime. #### Concurrency [NoFileDescriptorLeak](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoFileDescriptorLeak) is not compatible with parallel tests. File descriptors are a process-wide resource; any concurrent I/O from other goroutines may cause false positives. Calls to [NoFileDescriptorLeak](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoFileDescriptorLeak) are serialized with a mutex to prevent multiple leak checks from interfering with each other. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go NoFileDescriptorLeak(t, func() { // code that should not leak file descriptors }) success: func() {} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T) package main import ( "fmt" "runtime" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { if runtime.GOOS != "linux" { // This example is only runnable on linux. On other platforms, the assertion skips the test. // We force the expected output below, so that tests don't fail on other platforms. fmt.Println("success: true") return } t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T) success := assert.NoFileDescriptorLeak(t, func() { }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoFileDescriptorLeak(t *testing.T) package main import ( "fmt" "runtime" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { if runtime.GOOS != "linux" { // This example is only runnable on linux. On other platforms, the assertion skips the test. // We force the expected output below, so that tests don't fail on other platforms. fmt.Println("passed") return } t := new(testing.T) // should come from testing, e.g. func TestNoFileDescriptorLeak(t *testing.T) require.NoFileDescriptorLeak(t, func() { }) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NoFileDescriptorLeak(t T, tested func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoFileDescriptorLeak) | package-level function | | [`assert.NoFileDescriptorLeakf(t T, tested func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoFileDescriptorLeakf) | formatted variant | | [`assert.(*Assertions).NoFileDescriptorLeak(tested func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoFileDescriptorLeak) | method variant | | [`assert.(*Assertions).NoFileDescriptorLeakf(tested func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoFileDescriptorLeakf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NoFileDescriptorLeak(t T, tested func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoFileDescriptorLeak) | package-level function | | [`require.NoFileDescriptorLeakf(t T, tested func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoFileDescriptorLeakf) | formatted variant | | [`require.(*Assertions).NoFileDescriptorLeak(tested func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoFileDescriptorLeak) | method variant | | [`require.(*Assertions).NoFileDescriptorLeakf(tested func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoFileDescriptorLeakf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NoFileDescriptorLeak(t T, tested func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoFileDescriptorLeak) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoFileDescriptorLeak](https://github.com/go-openapi/testify/blob/master/internal/assertions/safety.go#L110) {{% /tab %}} {{< /tabs >}} ### NoGoRoutineLeak{#nogoroutineleak} NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. NOTE: only the go routines spawned from inside the tested function are checked for leaks. No filter or configuration is needed to exclude "known go routines". Resource cleanup should be done inside the tested function, and not using [testing.T.Cleanup](https://pkg.go.dev/testing#T.Cleanup), as t.Cleanup is called after the leak check. #### Edge cases - if the tested function panics leaving behind leaked goroutines, these are detected. - if the tested function calls [runtime.Goexit](https://pkg.go.dev/runtime#Goexit) (e.g. from [testing.T.FailNow](https://pkg.go.dev/testing#T.FailNow)) leaving behind leaked goroutines, these are detected. - if a panic occurs in one of the leaked go routines, it cannot be recovered with certainty and the calling program will usually panic. #### Concurrency [NoGoRoutineLeak](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoGoRoutineLeak) may be used safely in parallel tests. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go NoGoRoutineLeak(t, func() { ... }, "should not leak any go routine", ) success: func() {} ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T) success := assert.NoGoRoutineLeak(t, func() { }) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T) package main import ( "fmt" "sync" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // normally provided by test blocker := make(chan struct{}) var wg sync.WaitGroup defer func() { // clean resources _after_ the test close(blocker) wg.Wait() }() wg.Add(1) // This examplifies how a function that leaks a goroutine is detected. result := assert.NoGoRoutineLeak(t, func() { // true when there is no leak go func() { defer wg.Done() <-blocker // leaked: blocks until cleanup }() }) // Error message from test would typically return the leaked goroutine, e.g.: // # 0x69c8e8 github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48 .../assert_adhoc_example_7_test.go:30 fmt.Printf("has leak: %t", !result) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNoGoRoutineLeak(t *testing.T) require.NoGoRoutineLeak(t, func() { }) fmt.Println("passed") } ``` {{% /card %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNoGoRoutineLeak(t *testing.T) // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "fmt" "sync" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(mockFailNowT) // normally provided by test // Since this test is failing and calls [runtime.Goexit], we need a mock to // avoid the example trigger a panick. blocker := make(chan struct{}) var wg sync.WaitGroup defer func() { // clean resources _after_ the test close(blocker) wg.Wait() }() wg.Add(1) // This examplifies how a function that leaks a goroutine is detected. require.NoGoRoutineLeak(t, func() { // true when there is no leak go func() { defer wg.Done() <-blocker // leaked: blocks until cleanup }() }) // Error message from test would typically return the leaked goroutine, e.g.: // # 0x69c8e8 github.com/go-openapi/testify/v2/assert_test.ExampleNoGoRoutineLeak.func2.1+0x48 .../assert_adhoc_example_7_test.go:30 fmt.Printf("passed: %t", !t.Failed()) } type mockFailNowT struct { failed bool } // Helper is like [testing.T.Helper] but does nothing. func (mockFailNowT) Helper() {} func (m *mockFailNowT) Errorf(format string, args ...any) { _ = format _ = args } func (m *mockFailNowT) FailNow() { m.failed = true } func (m *mockFailNowT) Failed() bool { return m.failed } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NoGoRoutineLeak(t T, tested func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoGoRoutineLeak) | package-level function | | [`assert.NoGoRoutineLeakf(t T, tested func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NoGoRoutineLeakf) | formatted variant | | [`assert.(*Assertions).NoGoRoutineLeak(tested func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoGoRoutineLeak) | method variant | | [`assert.(*Assertions).NoGoRoutineLeakf(tested func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NoGoRoutineLeakf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NoGoRoutineLeak(t T, tested func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoGoRoutineLeak) | package-level function | | [`require.NoGoRoutineLeakf(t T, tested func(), msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NoGoRoutineLeakf) | formatted variant | | [`require.(*Assertions).NoGoRoutineLeak(tested func()) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoGoRoutineLeak) | method variant | | [`require.(*Assertions).NoGoRoutineLeakf(tested func(), msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NoGoRoutineLeakf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NoGoRoutineLeak(t T, tested func(), msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NoGoRoutineLeak) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NoGoRoutineLeak](https://github.com/go-openapi/testify/blob/master/internal/assertions/safety.go#L56) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/string.md000066400000000000000000000346521520301377500227130ustar00rootroot00000000000000--- title: "String" description: "Asserting Strings" weight: 14 domains: - "string" keywords: - "NotRegexp" - "NotRegexpf" - "NotRegexpT" - "NotRegexpTf" - "Regexp" - "Regexpf" - "RegexpT" - "RegexpTf" --- Asserting Strings ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 4 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [NotRegexp](#notregexp) | angles-right - [NotRegexpT[Rex RegExp, ADoc Text]](#notregexptrex-regexp-adoc-text) | star | orange - [Regexp](#regexp) | angles-right - [RegexpT[Rex RegExp, ADoc Text]](#regexptrex-regexp-adoc-text) | star | orange ``` ### NotRegexp{#notregexp} NotRegexp asserts that a specified regular expression does not match a string. See [Regexp](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") assertions.NotRegexp(t, "^start", "it's not starting") success: "^start", "not starting" failure: "^start", "starting" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotRegexp(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T) success := assert.NotRegexp(t, "^start", "not starting") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotRegexp(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotRegexp(t *testing.T) require.NotRegexp(t, "^start", "not starting") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexp) | package-level function | | [`assert.NotRegexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpf) | formatted variant | | [`assert.(*Assertions).NotRegexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotRegexp) | method variant | | [`assert.(*Assertions).NotRegexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotRegexpf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexp) | package-level function | | [`require.NotRegexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpf) | formatted variant | | [`require.(*Assertions).NotRegexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotRegexp) | method variant | | [`require.(*Assertions).NotRegexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotRegexpf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotRegexp) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotRegexp](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L92) {{% /tab %}} {{< /tabs >}} ### NotRegexpT[Rex RegExp, ADoc Text] {{% icon icon="star" color=orange %}}{#notregexptrex-regexp-adoc-text} NotRegexpT asserts that a specified regular expression does not match a string. See [RegexpT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") assertions.NotRegexp(t, "^start", "it's not starting") success: "^start", "not starting" failure: "^start", "starting" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotRegexpT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T) success := assert.NotRegexpT(t, "^start", "not starting") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotRegexpT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotRegexpT(t *testing.T) require.NotRegexpT(t, "^start", "not starting") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpT) | package-level function | | [`assert.NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotRegexpTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpT) | package-level function | | [`require.NotRegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotRegexpTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotRegexpT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotRegexpT](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L133) {{% /tab %}} {{< /tabs >}} ### Regexp{#regexp} Regexp asserts that a specified regular expression matches a string. The regular expression may be passed as a [regexp.Regexp](https://pkg.go.dev/regexp#Regexp), a string or a []byte and will be compiled. The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint](https://pkg.go.dev/fmt#Sprint). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Regexp(t, regexp.MustCompile("start"), "it's starting") assertions.Regexp(t, "start...$", "it's not starting") success: "^start", "starting" failure: "^start", "not starting" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestRegexp(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T) success := assert.Regexp(t, "^start", "starting") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestRegexp(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestRegexp(t *testing.T) require.Regexp(t, "^start", "starting") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Regexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp) | package-level function | | [`assert.Regexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexpf) | formatted variant | | [`assert.(*Assertions).Regexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Regexp) | method variant | | [`assert.(*Assertions).Regexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Regexpf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Regexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Regexp) | package-level function | | [`require.Regexpf(t T, rx any, actual any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Regexpf) | formatted variant | | [`require.(*Assertions).Regexp(rx any, actual any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Regexp) | method variant | | [`require.(*Assertions).Regexpf(rx any, actual any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Regexpf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Regexp(t T, rx any, actual any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Regexp) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Regexp](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L27) {{% /tab %}} {{< /tabs >}} ### RegexpT[Rex RegExp, ADoc Text] {{% icon icon="star" color=orange %}}{#regexptrex-regexp-adoc-text} RegexpT asserts that a specified regular expression matches a string. The actual argument to be matched may be a string or []byte. See [Regexp](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Regexp). {{% expand title="Examples" %}} {{< tabs >}} success: "^start", "starting" failure: "^start", "not starting" {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestRegexpT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T) success := assert.RegexpT(t, "^start", "starting") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestRegexpT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestRegexpT(t *testing.T) require.RegexpT(t, "^start", "starting") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpT) | package-level function | | [`assert.RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#RegexpTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#RegexpT) | package-level function | | [`require.RegexpTf[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#RegexpTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#RegexpT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#RegexpT](https://github.com/go-openapi/testify/blob/master/internal/assertions/string.go#L64) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/testing.md000066400000000000000000000125601520301377500230540ustar00rootroot00000000000000--- title: "Testing" description: "Mimics Methods From The Testing Standard Library" weight: 15 domains: - "testing" keywords: - "Fail" - "Failf" - "FailNow" - "FailNowf" --- Mimics Methods From The Testing Standard Library ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 2 functionalities. ```tree - [Fail](#fail) | angles-right - [FailNow](#failnow) | angles-right ``` ### Fail{#fail} Fail reports a failure through. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Fail(t, "failed") ``` {{< /tab >}} {{% tab title="Examples" %}} ```go failure: "failed" ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Fail(t T, failureMessage string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Fail) | package-level function | | [`assert.Failf(t T, failureMessage string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Failf) | formatted variant | | [`assert.(*Assertions).Fail(failureMessage string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Fail) | method variant | | [`assert.(*Assertions).Failf(failureMessage string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Failf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Fail(t T, failureMessage string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Fail) | package-level function | | [`require.Failf(t T, failureMessage string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Failf) | formatted variant | | [`require.(*Assertions).Fail(failureMessage string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Fail) | method variant | | [`require.(*Assertions).Failf(failureMessage string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Failf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Fail(t T, failureMessage string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Fail) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Fail](https://github.com/go-openapi/testify/blob/master/internal/assertions/testing.go#L23) {{% /tab %}} {{< /tabs >}} ### FailNow{#failnow} FailNow fails test. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.FailNow(t, "failed") ``` {{< /tab >}} {{% tab title="Examples" %}} ```go failure: "failed" ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.FailNow(t T, failureMessage string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FailNow) | package-level function | | [`assert.FailNowf(t T, failureMessage string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#FailNowf) | formatted variant | | [`assert.(*Assertions).FailNow(failureMessage string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FailNow) | method variant | | [`assert.(*Assertions).FailNowf(failureMessage string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.FailNowf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.FailNow(t T, failureMessage string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FailNow) | package-level function | | [`require.FailNowf(t T, failureMessage string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#FailNowf) | formatted variant | | [`require.(*Assertions).FailNow(failureMessage string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FailNow) | method variant | | [`require.(*Assertions).FailNowf(failureMessage string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.FailNowf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.FailNow(t T, failureMessage string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#FailNow) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#FailNow](https://github.com/go-openapi/testify/blob/master/internal/assertions/testing.go#L45) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/time.md000066400000000000000000000225761520301377500223450ustar00rootroot00000000000000--- title: "Time" description: "Asserting Times And Durations" weight: 16 domains: - "time" keywords: - "WithinDuration" - "WithinDurationf" - "WithinRange" - "WithinRangef" --- Asserting Times And Durations ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 2 functionalities. ```tree - [WithinDuration](#withinduration) | angles-right - [WithinRange](#withinrange) | angles-right ``` ### WithinDuration{#withinduration} WithinDuration asserts that the two times are within duration delta of each other. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.WithinDuration(t, time.Now(), 10*time.Second) success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second failure: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestWithinDuration(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T) success := assert.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestWithinDuration(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestWithinDuration(t *testing.T) require.WithinDuration(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.WithinDuration(t T, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithinDuration) | package-level function | | [`assert.WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithinDurationf) | formatted variant | | [`assert.(*Assertions).WithinDuration(expected time.Time, actual time.Time, delta time.Duration) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.WithinDuration) | method variant | | [`assert.(*Assertions).WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.WithinDurationf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.WithinDuration(t T, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#WithinDuration) | package-level function | | [`require.WithinDurationf(t T, expected time.Time, actual time.Time, delta time.Duration, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#WithinDurationf) | formatted variant | | [`require.(*Assertions).WithinDuration(expected time.Time, actual time.Time, delta time.Duration) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.WithinDuration) | method variant | | [`require.(*Assertions).WithinDurationf(expected time.Time, actual time.Time, delta time.Duration, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.WithinDurationf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.WithinDuration(t T, expected time.Time, actual time.Time, delta time.Duration, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#WithinDuration) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#WithinDuration](https://github.com/go-openapi/testify/blob/master/internal/assertions/time.go#L21) {{% /tab %}} {{< /tabs >}} ### WithinRange{#withinrange} WithinRange asserts that a time is within a time range (inclusive). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) failure: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestWithinRange(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T) success := assert.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestWithinRange(t *testing.T) package main import ( "fmt" "testing" "time" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestWithinRange(t *testing.T) require.WithinRange(t, time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithinRange) | package-level function | | [`assert.WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithinRangef) | formatted variant | | [`assert.(*Assertions).WithinRange(actual time.Time, start time.Time, end time.Time) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.WithinRange) | method variant | | [`assert.(*Assertions).WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.WithinRangef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#WithinRange) | package-level function | | [`require.WithinRangef(t T, actual time.Time, start time.Time, end time.Time, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#WithinRangef) | formatted variant | | [`require.(*Assertions).WithinRange(actual time.Time, start time.Time, end time.Time) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.WithinRange) | method variant | | [`require.(*Assertions).WithinRangef(actual time.Time, start time.Time, end time.Time, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.WithinRangef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.WithinRange(t T, actual time.Time, start time.Time, end time.Time, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#WithinRange) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#WithinRange](https://github.com/go-openapi/testify/blob/master/internal/assertions/time.go#L45) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/type.md000066400000000000000000001070221520301377500223560ustar00rootroot00000000000000--- title: "Type" description: "Asserting Types Rather Than Values" weight: 17 domains: - "type" keywords: - "Implements" - "Implementsf" - "IsNotOfTypeT" - "IsNotOfTypeTf" - "IsNotType" - "IsNotTypef" - "IsOfTypeT" - "IsOfTypeTf" - "IsType" - "IsTypef" - "Kind" - "Kindf" - "NotImplements" - "NotImplementsf" - "NotKind" - "NotKindf" - "NotZero" - "NotZerof" - "Zero" - "Zerof" --- Asserting Types Rather Than Values ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 10 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [Implements](#implements) | angles-right - [IsNotOfTypeT[EType any]](#isnotoftypetetype-any) | star | orange - [IsNotType](#isnottype) | angles-right - [IsOfTypeT[EType any]](#isoftypetetype-any) | star | orange - [IsType](#istype) | angles-right - [Kind](#kind) | angles-right - [NotImplements](#notimplements) | angles-right - [NotKind](#notkind) | angles-right - [NotZero](#notzero) | angles-right - [Zero](#zero) | angles-right ``` ### Implements{#implements} Implements asserts that an object is implemented by the specified interface. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Implements(t, (*MyInterface)(nil), new(MyObject)) success: ptr(dummyInterface), new(testing.T) failure: (*error)(nil), new(testing.T) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestImplements(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T) success := assert.Implements(t, ptr(dummyInterface), new(testing.T)) fmt.Printf("success: %t\n", success) } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" dummyInterface assert.T ) func ptr[T any](value T) *T { p := value return &p } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestImplements(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestImplements(t *testing.T) require.Implements(t, ptr(dummyInterface), new(testing.T)) fmt.Println("passed") } //nolint:gochecknoglobals // this is on purpose to share a common pointer when testing var ( staticVar = "static string" dummyInterface require.T ) func ptr[T any](value T) *T { p := value return &p } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Implements) | package-level function | | [`assert.Implementsf(t T, interfaceObject any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Implementsf) | formatted variant | | [`assert.(*Assertions).Implements(interfaceObject any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Implements) | method variant | | [`assert.(*Assertions).Implementsf(interfaceObject any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Implementsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Implements) | package-level function | | [`require.Implementsf(t T, interfaceObject any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Implementsf) | formatted variant | | [`require.(*Assertions).Implements(interfaceObject any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Implements) | method variant | | [`require.(*Assertions).Implementsf(interfaceObject any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Implementsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Implements) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Implements](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L21) {{% /tab %}} {{< /tabs >}} ### IsNotOfTypeT[EType any] {{% icon icon="star" color=orange %}}{#isnotoftypetetype-any} IsNotOfTypeT asserts that an object is not of a given type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsOfType[MyType](t,myVar) success: 123.123 failure: myType(123.123) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T) success := assert.IsNotOfTypeT[myType](t, 123.123) fmt.Printf("success: %t\n", success) } type myType float64 ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNotOfTypeT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNotOfTypeT(t *testing.T) require.IsNotOfTypeT[myType](t, 123.123) fmt.Println("passed") } type myType float64 ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNotOfTypeT) | package-level function | | [`assert.IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNotOfTypeTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNotOfTypeT) | package-level function | | [`require.IsNotOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNotOfTypeTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNotOfTypeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L144) {{% /tab %}} {{< /tabs >}} ### IsNotType{#isnottype} IsNotType asserts that the specified objects are not of the same type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsNotType(t, &NotMyStruct{}, &MyStruct{}) success: int32(123), int64(456) failure: 123, 456 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNotType(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T) success := assert.IsNotType(t, int32(123), int64(456)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsNotType(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsNotType(t *testing.T) require.IsNotType(t, int32(123), int64(456)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsNotType(t T, theType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNotType) | package-level function | | [`assert.IsNotTypef(t T, theType any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsNotTypef) | formatted variant | | [`assert.(*Assertions).IsNotType(theType any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsNotType) | method variant | | [`assert.(*Assertions).IsNotTypef(theType any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsNotTypef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsNotType(t T, theType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNotType) | package-level function | | [`require.IsNotTypef(t T, theType any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsNotTypef) | formatted variant | | [`require.(*Assertions).IsNotType(theType any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsNotType) | method variant | | [`require.(*Assertions).IsNotTypef(theType any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsNotTypef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsNotType(t T, theType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsNotType) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsNotType](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L123) {{% /tab %}} {{< /tabs >}} ### IsOfTypeT[EType any] {{% icon icon="star" color=orange %}}{#isoftypetetype-any} IsOfTypeT asserts that an object is of a given type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsOfTypeT[MyType](t,myVar) success: myType(123.123) failure: 123.123 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T) success := assert.IsOfTypeT[myType](t, myType(123.123)) fmt.Printf("success: %t\n", success) } type myType float64 ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsOfTypeT(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsOfTypeT(t *testing.T) require.IsOfTypeT[myType](t, myType(123.123)) fmt.Println("passed") } type myType float64 ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsOfTypeT) | package-level function | | [`assert.IsOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsOfTypeTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsOfTypeT) | package-level function | | [`require.IsOfTypeTf[EType any](t T, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsOfTypeTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsOfTypeT](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L98) {{% /tab %}} {{< /tabs >}} ### IsType{#istype} IsType asserts that the specified objects are of the same type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.IsType(t, &MyStruct{}, &MyStruct{}) success: 123, 456 failure: int32(123), int64(456) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsType(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T) success := assert.IsType(t, 123, 456) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestIsType(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestIsType(t *testing.T) require.IsType(t, 123, 456) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.IsType(t T, expectedType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsType) | package-level function | | [`assert.IsTypef(t T, expectedType any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#IsTypef) | formatted variant | | [`assert.(*Assertions).IsType(expectedType any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsType) | method variant | | [`assert.(*Assertions).IsTypef(expectedType any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.IsTypef) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.IsType(t T, expectedType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsType) | package-level function | | [`require.IsTypef(t T, expectedType any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#IsTypef) | formatted variant | | [`require.(*Assertions).IsType(expectedType any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsType) | method variant | | [`require.(*Assertions).IsTypef(expectedType any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.IsTypef) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.IsType(t T, expectedType any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#IsType) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#IsType](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L76) {{% /tab %}} {{< /tabs >}} ### Kind{#kind} Kind asserts that the [reflect.Kind](https://pkg.go.dev/reflect#Kind) of a given object matches the expected [reflect.Kind](https://pkg.go.dev/reflect#Kind). Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See also [reflect.Value.Kind](https://pkg.go.dev/reflect#Value.Kind). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Kind(t, reflect.String, "Hello World") success: reflect.String, "hello" failure: reflect.String, 0 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestKind(t *testing.T) package main import ( "fmt" "reflect" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T) success := assert.Kind(t, reflect.String, "hello") fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestKind(t *testing.T) package main import ( "fmt" "reflect" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestKind(t *testing.T) require.Kind(t, reflect.String, "hello") fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Kind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Kind) | package-level function | | [`assert.Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Kindf) | formatted variant | | [`assert.(*Assertions).Kind(expectedKind reflect.Kind, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Kind) | method variant | | [`assert.(*Assertions).Kindf(expectedKind reflect.Kind, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Kindf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Kind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Kind) | package-level function | | [`require.Kindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Kindf) | formatted variant | | [`require.(*Assertions).Kind(expectedKind reflect.Kind, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Kind) | method variant | | [`require.(*Assertions).Kindf(expectedKind reflect.Kind, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Kindf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Kind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Kind) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Kind](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L214) {{% /tab %}} {{< /tabs >}} ### NotImplements{#notimplements} NotImplements asserts that an object does not implement the specified interface. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotImplements(t, (*MyInterface)(nil), new(MyObject)) success: (*error)(nil), new(testing.T) failure: ptr(dummyInterface), new(testing.T) ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotImplements(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T) success := assert.NotImplements(t, (*error)(nil), new(testing.T)) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotImplements(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotImplements(t *testing.T) require.NotImplements(t, (*error)(nil), new(testing.T)) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotImplements(t T, interfaceObject any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotImplements) | package-level function | | [`assert.NotImplementsf(t T, interfaceObject any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotImplementsf) | formatted variant | | [`assert.(*Assertions).NotImplements(interfaceObject any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotImplements) | method variant | | [`assert.(*Assertions).NotImplementsf(interfaceObject any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotImplementsf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotImplements(t T, interfaceObject any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotImplements) | package-level function | | [`require.NotImplementsf(t T, interfaceObject any, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotImplementsf) | formatted variant | | [`require.(*Assertions).NotImplements(interfaceObject any, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotImplements) | method variant | | [`require.(*Assertions).NotImplementsf(interfaceObject any, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotImplementsf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotImplements(t T, interfaceObject any, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotImplements) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotImplements](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L49) {{% /tab %}} {{< /tabs >}} ### NotKind{#notkind} NotKind asserts that the [reflect.Kind](https://pkg.go.dev/reflect#Kind) of a given object does not match the expected [reflect.Kind](https://pkg.go.dev/reflect#Kind). Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) are comparable to [reflect.Invalid](https://pkg.go.dev/reflect#Invalid). See also [reflect.Value.Kind](https://pkg.go.dev/reflect#Value.Kind). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotKind(t, reflect.Int, "Hello World") success: reflect.String, 0 failure: reflect.String, "hello" ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotKind(t *testing.T) package main import ( "fmt" "reflect" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T) success := assert.NotKind(t, reflect.String, 0) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotKind(t *testing.T) package main import ( "fmt" "reflect" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotKind(t *testing.T) require.NotKind(t, reflect.String, 0) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotKind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotKind) | package-level function | | [`assert.NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotKindf) | formatted variant | | [`assert.(*Assertions).NotKind(expectedKind reflect.Kind, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotKind) | method variant | | [`assert.(*Assertions).NotKindf(expectedKind reflect.Kind, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotKindf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotKind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotKind) | package-level function | | [`require.NotKindf(t T, expectedKind reflect.Kind, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotKindf) | formatted variant | | [`require.(*Assertions).NotKind(expectedKind reflect.Kind, object any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotKind) | method variant | | [`require.(*Assertions).NotKindf(expectedKind reflect.Kind, object any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotKindf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotKind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotKind) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotKind](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L248) {{% /tab %}} {{< /tabs >}} ### NotZero{#notzero} NotZero asserts that i is not the zero value for its type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.NotZero(t, obj) success: 1 failure: 0 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotZero(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T) success := assert.NotZero(t, 1) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestNotZero(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestNotZero(t *testing.T) require.NotZero(t, 1) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.NotZero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotZero) | package-level function | | [`assert.NotZerof(t T, i any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#NotZerof) | formatted variant | | [`assert.(*Assertions).NotZero(i any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotZero) | method variant | | [`assert.(*Assertions).NotZerof(i any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.NotZerof) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.NotZero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotZero) | package-level function | | [`require.NotZerof(t T, i any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#NotZerof) | formatted variant | | [`require.(*Assertions).NotZero(i any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotZero) | method variant | | [`require.(*Assertions).NotZerof(i any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.NotZerof) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.NotZero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#NotZero) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#NotZero](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L190) {{% /tab %}} {{< /tabs >}} ### Zero{#zero} Zero asserts that i is the zero value for its type. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go assertions.Zero(t, obj) success: 0 failure: 1 ``` {{< /tab >}} {{% tab title="Testable Examples (assert)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestZero(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/assert" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T) success := assert.Zero(t, 0) fmt.Printf("success: %t\n", success) } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{% tab title="Testable Examples (require)" %}} {{% cards %}} {{% card %}} *[Copy and click to open Go Playground](https://go.dev/play/)* ```go // real-world test would inject *testing.T from TestZero(t *testing.T) package main import ( "fmt" "testing" "github.com/go-openapi/testify/v2/require" ) func main() { t := new(testing.T) // should come from testing, e.g. func TestZero(t *testing.T) require.Zero(t, 0) fmt.Println("passed") } ``` {{% /card %}} {{% /cards %}} {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.Zero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Zero) | package-level function | | [`assert.Zerof(t T, i any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Zerof) | formatted variant | | [`assert.(*Assertions).Zero(i any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Zero) | method variant | | [`assert.(*Assertions).Zerof(i any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.Zerof) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.Zero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Zero) | package-level function | | [`require.Zerof(t T, i any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Zerof) | formatted variant | | [`require.(*Assertions).Zero(i any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Zero) | method variant | | [`require.(*Assertions).Zerof(i any, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.Zerof) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.Zero(t T, i any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#Zero) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#Zero](https://github.com/go-openapi/testify/blob/master/internal/assertions/type.go#L168) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/api/yaml.md000066400000000000000000000335471520301377500223510ustar00rootroot00000000000000--- title: "Yaml" description: "Asserting Yaml Documents" weight: 18 domains: - "yaml" keywords: - "YAMLEq" - "YAMLEqf" - "YAMLEqBytes" - "YAMLEqBytesf" - "YAMLEqT" - "YAMLEqTf" - "YAMLMarshalAsT" - "YAMLMarshalAsTf" - "YAMLUnmarshalAsT" - "YAMLUnmarshalAsTf" --- Asserting Yaml Documents ## Assertions [![GoDoc][godoc-badge]][godoc-url] {class="inline-badge"} _All links point to _ This domain exposes 5 functionalities. Generic assertions are marked with a {{% icon icon="star" color=orange %}}. ```tree - [YAMLEq](#yamleq) | angles-right - [YAMLEqBytes](#yamleqbytes) | angles-right - [YAMLEqT[EDoc, ADoc RText]](#yamleqtedoc-adoc-rtext) | star | orange - [YAMLMarshalAsT[EDoc RText]](#yamlmarshalastedoc-rtext) | star | orange - [YAMLUnmarshalAsT[Object any, ADoc RText]](#yamlunmarshalastobject-any-adoc-rtext) | star | orange ``` ### YAMLEq{#yamleq} YAMLEq asserts that two YAML strings are equivalent. See [YAMLEqBytes](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqBytes). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Examples" %}} ```go panic: "key: value", "key: value" should panic without the yaml feature enabled. ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq) | package-level function | | [`assert.YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqf) | formatted variant | | [`assert.(*Assertions).YAMLEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEq) | method variant | | [`assert.(*Assertions).YAMLEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEq) | package-level function | | [`require.YAMLEqf(t T, expected string, actual string, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqf) | formatted variant | | [`require.(*Assertions).YAMLEq(expected string, actual string) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEq) | method variant | | [`require.(*Assertions).YAMLEqf(expected string, actual string, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.YAMLEq(t T, expected string, actual string, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEq) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEq](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L78) {{% /tab %}} {{< /tabs >}} ### YAMLEqBytes{#yamleqbytes} YAMLEqBytes asserts that two YAML slices of bytes are equivalent. Expected and actual must be valid YAML. #### Important By default, this function is disabled and will panic. To enable it, you should add a blank import like so: import( "github.com/go-openapi/testify/enable/yaml/v2" ) For dynamic redaction of the input text via a callback, use [YAMLEqT](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqT). {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go expected := `--- key: value --- key: this is a second document, it is not evaluated ` actual := `--- key: value --- key: this is a subsequent document, it is not evaluated ` assertions.YAMLEq(t, expected, actual) ``` {{< /tab >}} {{% tab title="Examples" %}} ```go panic: []byte("key: value"), []byte("key: value") should panic without the yaml feature enabled. ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqBytes) | package-level function | | [`assert.YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqBytesf) | formatted variant | | [`assert.(*Assertions).YAMLEqBytes(expected []byte, actual []byte) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqBytes) | method variant | | [`assert.(*Assertions).YAMLEqBytesf(expected []byte, actual []byte, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Assertions.YAMLEqBytesf) | method formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqBytes) | package-level function | | [`require.YAMLEqBytesf(t T, expected []byte, actual []byte, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqBytesf) | formatted variant | | [`require.(*Assertions).YAMLEqBytes(expected []byte, actual []byte) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqBytes) | method variant | | [`require.(*Assertions).YAMLEqBytesf(expected []byte, actual []byte, msg string, args ..any)`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#Assertions.YAMLEqBytesf) | method formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.YAMLEqBytes(t T, expected []byte, actual []byte, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEqBytes) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEqBytes](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L47) {{% /tab %}} {{< /tabs >}} ### YAMLEqT[EDoc, ADoc RText] {{% icon icon="star" color=orange %}}{#yamleqtedoc-adoc-rtext} YAMLEqT asserts that two YAML documents are equivalent. The expected and actual arguments may be string or []byte. They do not need to be of the same type. See [YAMLEqBytes](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqBytes). NOTE: passed values (expected, actual) may be wrapped as functions to redact the input text dynamically. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Examples" %}} ```go panic: "key: value", "key: value" should panic without the yaml feature enabled. ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.YAMLEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqT) | package-level function | | [`assert.YAMLEqTf[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEqTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.YAMLEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqT) | package-level function | | [`require.YAMLEqTf[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLEqTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.YAMLEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLEqT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLEqT](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L99) {{% /tab %}} {{< /tabs >}} ### YAMLMarshalAsT[EDoc RText] {{% icon icon="star" color=orange %}}{#yamlmarshalastedoc-rtext} YAMLMarshalAsT wraps [YAMLEq](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq) after [yaml.Marshal](https://pkg.go.dev/yaml#Marshal). The input YAML may be a string or []byte. It fails if the marshaling returns an error or if the expected YAML bytes differ semantically from the expected ones. NOTE: passed expected value may be wrapped as a function to redact the input text dynamically. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go actual := struct { A int `yaml:"a"` }{ A: 10, } assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) ``` {{< /tab >}} {{% tab title="Examples" %}} ```go panic: "key: value", "key: value" should panic without the yaml feature enabled. ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.YAMLMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLMarshalAsT) | package-level function | | [`assert.YAMLMarshalAsTf[EDoc RText](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLMarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.YAMLMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLMarshalAsT) | package-level function | | [`require.YAMLMarshalAsTf[EDoc RText](t T, expected EDoc, object any, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLMarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.YAMLMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLMarshalAsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLMarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L170) {{% /tab %}} {{< /tabs >}} ### YAMLUnmarshalAsT[Object any, ADoc RText] {{% icon icon="star" color=orange %}}{#yamlunmarshalastobject-any-adoc-rtext} YAMLUnmarshalAsT wraps [Equal](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#Equal) after [yaml.Unmarshal](https://pkg.go.dev/yaml#Unmarshal). The input YAML may be a string or []byte. It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. Be careful not to wrap the expected object into an "any" interface if this is not what you expected: the unmarshaling would take this type to unmarshal as a map[string](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#string)any. NOTE: passed yamlDoc value may be wrapped as a function to redact the input text dynamically. {{% expand title="Examples" %}} {{< tabs >}} {{% tab title="Usage" %}} ```go expected := struct { A int `yaml:"a"` }{ A: 10, } assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) ``` {{< /tab >}} {{% tab title="Examples" %}} ```go panic: "key: value", "key: value" should panic without the yaml feature enabled. ``` {{< /tab >}} {{< /tabs >}} {{% /expand %}} {{< tabs >}} {{% tab title="assert" style="secondary" %}} | Signature | Usage | |--|--| | [`assert.YAMLUnmarshalAsT[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLUnmarshalAsT) | package-level function | | [`assert.YAMLUnmarshalAsTf[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLUnmarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="require" style="secondary" %}} | Signature | Usage | |--|--| | [`require.YAMLUnmarshalAsT[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLUnmarshalAsT) | package-level function | | [`require.YAMLUnmarshalAsTf[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msg string, args ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/require#YAMLUnmarshalAsTf) | formatted variant | {{% /tab %}} {{% tab title="internal" style="accent" icon="wrench" %}} | Signature | Usage | |--|--| | [`assertions.YAMLUnmarshalAsT[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msgAndArgs ...any) bool`](https://pkg.go.dev/github.com/go-openapi/testify/v2/internal/assertions#YAMLUnmarshalAsT) | internal implementation | **Source:** [github.com/go-openapi/testify/v2/internal/assertions#YAMLUnmarshalAsT](https://github.com/go-openapi/testify/blob/master/internal/assertions/yaml.go#L133) {{% /tab %}} {{< /tabs >}} --- --- Generated with github.com/go-openapi/testify/codegen/v2 [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify/v2 [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 go-openapi-testify-c10ca71/docs/doc-site/project/000077500000000000000000000000001520301377500217465ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/doc-site/project/APPROACH.md000066400000000000000000000305601520301377500234710ustar00rootroot00000000000000--- title: "An approach to testing" description: "How testify builds on top Go testing without replacing it" weight: 20 --- {{% notice primary "TL;DR" "meteor" %}} Software testing comes in two major styles: assertion-style (xUnit tradition: JUnit, pytest, NUnit, **testify**) and BDD-style (RSpec, Cucumber, **Ginkgo**). Both are valid approaches. However, we think that **testify's assertion-style naturally aligns with Go's core values**: simplicity, explicitness, standard library first, minimal abstraction. Testify brings powerful testing to Go developers who embrace these values: zero-dependencies, reflection-based or generic assertions, and no framework (just works with `go test`). **If you chose Go for its philosophy, assertion-style testing is the natural extension of those values to your test suite.** {{% /notice %}} --- ## Make testing better. Keep it Go **go-openapi/testify** follows a simple philosophy: **make Go testing better without reinventing it**. Testify follows the assertion style: it is not a BDD framework. So you won't find chaining methods that produce English-like sentences. Unlike frameworks that introduce new paradigms and require specialized tooling, testify builds directly on top of Go's great standard `testing` package. It provides powerful assertions and utilities while preserving the familiar patterns that Go developers already know. Testing patterns and constructs remain standard. ### Core Principles **1. Zero Dependencies** Testify has no external dependencies. Everything you need is self-contained, with internalized implementations of required functionality. This means: - No dependency conflicts in your project - No supply chain security concerns - No version compatibility issues - Chrome is opt-in (all extra features that need additional dependencies are opt-in) **2. Standard Go Compatibility** Works seamlessly with `go test` and the standard library: - No special CLI tools required - No framework-specific test runners - Standard Go subtests with `t.Run()` - Native IDE support out of the box - Works with any Go test runner **3. Type Safety with Generics** Testify embraces Go's type system: - Most assertions come with a **generic variant** for compile-time type safety - Catch type mismatches before tests even run - On average **10x faster** than reflection-based assertions - Full type inference: no manual type parameters needed - Complex cases that require dynamic typing use go reflection **4. Simplicity and Clarity** Keep testing straightforward: - Function-based assertions with clear semantics - No new DSL to learn - Minimal cognitive overhead - Immediate productivity for any Go developer --- ## Testing Styles: Assertion vs. BDD Software testing has evolved into two primary styles, each with passionate advocates across programming communities. ### Assertion-Style Testing (xUnit tradition) **Core idea**: Write tests as regular code with explicit assertions. Originating with Kent Beck's SUnit (Smalltalk) and popularized by JUnit (Java), this style emphasizes: - Tests are functions/methods in the language - Direct assertion calls verify behavior - Standard language constructs for organization - Minimal framework abstraction **Examples across languages:** {{< tabs >}} {{% tab title="JUnit (Java)" %}} ```java // JUnit (Java) @Test public void testUserCreation() { User user = createUser("alice@example.com"); assertNotNull(user); assertEquals("alice@example.com", user.getEmail()); } ``` {{% /tab %}} {{% tab title="pytest (python)" %}} ```python # pytest (Python) def test_user_creation(): user = create_user("alice@example.com") assert user is not None assert user.email == "alice@example.com" ``` {{% /tab %}} {{% tab title="NUnit (C#)" %}} ```csharp // NUnit (C#) [Test] public void TestUserCreation() { var user = CreateUser("alice@example.com"); Assert.IsNotNull(user); Assert.AreEqual("alice@example.com", user.Email); } ``` {{% /tab %}} {{% tab title="Testify (go)" %}} ```go // Testify (go) import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestUserCreation(t *testing.T) { user := CreateUser("alice@example.com") require.NotNil(t, user) assert.Equal(t, "alice@example.com", user.Email) } ``` {{% /tab %}} {{% /tabs %}} **Frameworks**: JUnit, NUnit, xUnit.net, pytest, PHPUnit, Go's `testing` package... and `testify`. ### BDD-Style Testing (Behavior-Driven Development) **Core idea**: Write tests as executable specifications in narrative form. Originating with RSpec (Ruby) and influenced by Dan North's BDD methodology, this style emphasizes: - Tests describe behavior in natural language structure - Hierarchical organization (describe/context/it) - Focus on readability and documentation value - Framework-specific DSL **Examples across languages:** {{< tabs >}} {{% tab title="RSpec (Ruby)" %}} ```ruby # RSpec (Ruby) describe "User creation" do it "creates a valid user" do user = create_user("alice@example.com") expect(user).not_to be_nil expect(user.email).to eq("alice@example.com") end end ``` {{% /tab %}} {{% tab title="Jasmine (JS)" %}} ```javascript // Jasmine/Mocha (JavaScript) describe("User creation", function() { it("creates a valid user", function() { const user = createUser("alice@example.com"); expect(user).not.toBe(null); expect(user.email).toEqual("alice@example.com"); }); }); ``` {{% /tab %}} {{% tab title="behave (python)" %}} ```python # behave (Python) Scenario: User creation Given a valid email address When I create a user with "alice@example.com" Then the user should exist And the email should be "alice@example.com" ``` {{% /tab %}} {{% tab title="Ginkgo (go)" %}} ```go // Ginkgo import ( "github.com/onsi/ginkgo/v2" "github.com/onsi/gomega" ) var _ = ginkgo.Describe("User creation", func() { ginkgo.It("creates a valid user", func() { user := CreateUser("alice@example.com") gomega.Expect(user).NotTo(gomega.BeNil()) gomega.Expect(user.Email).To(gomega.Equal("alice@example.com")) }) }) ``` {{% /tab %}} {{% /tabs %}} **Frameworks**: RSpec, Jasmine, Mocha, Cucumber, behave, **Ginkgo/Gomega** ### Both Are Valid **Assertion-style strengths:** - Low cognitive overhead (just code) - Minimal framework abstraction - IDE tooling works naturally - Easy to learn and adopt **BDD-style strengths:** - Readable test specifications - Natural hierarchical organization - Self-documenting intent - Stakeholder-friendly output **The debate continues** across all programming communities. Neither style is objectively superior; they optimize for different values and team preferences. --- ## Assertion-Style and Go Values While both styles have merit in general, **assertion-style testing aligns naturally with Go's core philosophy**. ### Go's Design Values Go emphasizes: - **Simplicity**: maximize clarity - **Explicitness**: No magic, no hidden behavior - **Standard library first**: Build on solid foundations - **Readability**: Code is read more than written - **Minimal abstraction**: Minimize concepts ### How Assertion-Style Matches Go **1. Simplicity** Assertion-style keeps tests simple: they're just Go functions. ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestAdd(t *testing.T) { result := Add(2, 3) assert.Equal(t, 5, result) // Straightforward, even though we haven't written "When().Two().Plus().Three().IsNot(5).Fail()"... } ``` No new mental model. No framework semantics to learn. If you know Go, you know how to test with our lib. **2. Explicitness** Every assertion is an explicit function call with clear semantics: ```go assert.NotNil(t, user) // Explicit: check for nil assert.ErrorIs(t, err, ErrNotFound) // Explicit: check error identity assert.ElementsMatch(t, expected, actual) // Explicit: check collection equality ``` Compare to matcher-based approaches where behavior is composed through framework objects. Assertion-style makes test intent immediately clear _to a programmer_. **3. Standard Library First** Testify builds on `testing.T`: no replacement, just enhancement. ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserAPI(t *testing.T) { t.Run("creation", func(t *testing.T) { // Standard Go subtest t.Parallel() // Standard Go test parallelism user := CreateUser("alice@example.com") assert.NotNil(t, user) // Enhanced with assertions }) } ``` Works with `go test`. Works with standard tooling. Works with the Go ecosystem. **4. Readability Through Directness** Go prioritizes code that's easy to read and understand: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) // Clear, direct, readable func TestEmailValidation(t *testing.T) { tests := []struct { email string valid bool }{ {"alice@example.com", true}, {"invalid-email", false}, } for _, tt := range tests { t.Run(tt.email, func(t *testing.T) { err := ValidateEmail(tt.email) if tt.valid { assert.NoError(t, err) return } assert.Error(t, err) }) } } ``` Standard control flow. Standard Go idioms. No DSL to decode. Better for most developers, perhaps less so for stakeholders not familiar with Go. **5. Minimal Abstraction** Go avoids abstraction for abstraction's sake. Testify provides assertions: nothing more. - No test lifecycle framework - No dependency injection system - No specialized runners - No mandatory patterns Just functions that verify behavior and produce clear errors. Solve the testing problem, don't create a testing ecosystem. ### The Natural Fit If you appreciate Go's philosophy and if you choose Go because you value simplicity, explicitness, and building on standards, then **assertion-style testing is the natural extension of those values to your test suite**. BDD frameworks serve teams with different priorities (narrative specifications, framework-managed workflows, stakeholder communication). Those are valid priorities. But they optimize for values orthogonal to Go's design philosophy. For such teams, **Ginkgo/Gomega** provides a great BDD testing framework. **For Go developers who embrace Go values, assertion-style testing is the idiomatic approach.**. And **testify** is the tool. --- ## Assertion-Style in Go: Testify vs. BDD Frameworks Go's testing ecosystem reflects the broader assertion-vs-BDD divide: ### Different Philosophies | Aspect | Testify (Assertion-Style) | Ginkgo/Gomega (BDD-Style) | |--------|---------------------------|---------------------------| | **Testing style** | xUnit tradition | BDD tradition | | **Approach** | Enhance standard testing | Replace with BDD framework | | **Integration** | Works with `go test` directly | Requires `ginkgo` CLI tool | | **Learning curve** | Immediate (standard Go) | Moderate (new DSL) | | **Dependencies** | Zero external packages | Multiple framework packages | | **Type safety** | Generic assertions | Reflection-based matchers | | **Organization** | Standard Go subtests | Narrative hierarchy (Describe/Context/It) | | **Go philosophy** | Aligns with Go values | Different priorities | ### Example Comparison **Assertion-style (Testify):** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserCreation(t *testing.T) { t.Parallel() user := CreateUser("alice@example.com") // Clear, type-safe assertions require.NotNil(t, user) assert.EqualT(t, "alice@example.com", user.Email) // Compile-time type check assert.True(t, user.Active) } ``` **BDD-style (Ginkgo/Gomega):** ```go var _ = Describe("User Creation", func() { var user *User BeforeEach(func() { user = CreateUser("alice@example.com") }) It("creates a valid user", func() { Expect(user).ToNot(BeNil()) Expect(user.Email).To(Equal("alice@example.com")) Expect(user.Active).To(BeTrue()) }) }) ``` Both approaches are valid. They reflect different testing philosophies that span the entire software industry. The question for Go developers is: **which style aligns with the values that drew you to Go in the first place?** --- ## See also - [API Reference](../api/) - Browse all {{% siteparam "metrics.assertions" %}} assertions by domain - [Generics Guide](../usage/GENERICS.md) - Leverage type-safe assertions - [Migration Guide](../usage/MIGRATION.md) - Switch from stretchr/testify - [Examples](../usage/EXAMPLES.md) - See testify in action --- go-openapi-testify-c10ca71/docs/doc-site/project/LICENSE.md000066400000000000000000000262471520301377500233650ustar00rootroot00000000000000--- title: LICENSE description: Apache-2.0 License weight: 10 --- ``` Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ``` go-openapi-testify-c10ca71/docs/doc-site/project/MOTIVATION.md000066400000000000000000000056421520301377500237700ustar00rootroot00000000000000--- title: Motivation description: Motivations for this fork weight: 5 --- From the maintainers of `testify`, it looks like a v2 will eventually be released, but they'll do it at their own pace. We like all the principles they exposed to build this v2. [See discussion about v2](https://github.com/stretchr/testify/discussions/1560). However, at `go-openapi` we would like to address the well-known issues in `testify` with different priorities. With this fork, we want to: 1. [x] remove all external dependencies. 2. [x] make it easy to maintain and extend. 3. [x] pare down some of the chrome that has been added over the years. --- {{% notice style="primary" title="Extended hand" icon="hand" %}} We hope that this endeavor will help the original project with a live-drill of what a v2 could look like. Hopefully, some of our ideas will eventually percolate back into the original project and help the wider community of go developers write better, clearer test code. Feedback is welcome and we are always happy to discuss with people who face the same problems as we do: avoid breaking changes, APIs that became bloated over a decade or so, uncontrolled dependencies, difficult choices when it comes to introduce breaking changes, conflicting demands from users etc. {{% /notice %}} You might also be curious about our [ROADMAP](./maintainers/ROADMAP.md). --- 1. We wanted first to remove all external dependencies. > For all our libraries and generated test code we don't want test dependencies > to drill farther than `import github.com/go-openapi/testify/v2`, but on some specific (and controlled) > occasions. > > In this fork, all external stuff is either internalized (`go-spew`, `difflib`), > removed (`mocks`, `suite`, `http`) or specifically enabled by importing this module > (`github.com/go-openapi/testify/enable/yaml/v2`). 2. Make it easy to maintain and extend. > For go-openapi, testify should just be yet another part of our toolkit. > We need it to work, be easily adaptable to our needs and not divert our development effort away from our other repos. > This big refactor is an investment. 3. We want to pare down some of the chrome that has been added over the years > The `go-openapi` libraries and the `go-swagger` project make a rather limited use of the vast API provided by `testify`. > > With this first version of the fork, we have removed `mocks` and `suite`, which we don't use. > They might be added later on, with better controlled dependencies. > > In the forthcoming maintenance of this fork, much of the "chrome" or "ambiguous" API will be pared down. > There is no commitment yet on the stability of the API. > > Chrome would be added later: we have the "enable" packages just for that for when external dependencies are needed. 4. We want to add new features like generics, more useful assertions for JSON and safety checks. 5. We want to get rid of the API quirks and gotchas that panic or return unexpected results. go-openapi-testify-c10ca71/docs/doc-site/project/NOTICE.md000066400000000000000000000106701520301377500232550ustar00rootroot00000000000000--- title: NOTICE description: Code attribution and other LICENSE terms weight: 11 --- ``` // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 This software library, github.com/go-openapi/testify, includes software developed by the go-swagger and go-openapi maintainers ("go-swagger maintainers"). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. You may obtain a copy of the License at This software is copied from, derived from, and inspired mainly by github.com/stretchr/testify. It ships with copies of other software which license terms are recalled below. github.com/stretchr/testify =========================== // SPDX-FileCopyrightText: Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. // SPDX-License-Identifier: MIT MIT License Copyright (c) 2012-2020 Mat Ryer, Tyler Bunnell and contributors. Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. github.com/davecgh/go-spew =========================== // SPDX-FileCopyrightText: Copyright (c) 2012-2016 Dave Collins // SPDX-License-Identifier: ISC ISC License Copyright (c) 2012-2016 Dave Collins Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. github.com/pmezard/go-difflib =========================== // SPDX-FileCopyrightText: Copyright (c) 2013, Patrick Mezard // SPDX-License-Identifier: MIT-like Copyright (c) 2013, Patrick Mezard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. ``` go-openapi-testify-c10ca71/docs/doc-site/project/README.md000066400000000000000000000144631520301377500232350ustar00rootroot00000000000000--- title: README description: | Introducing go-openapi/testify/v2. - Why choose testify/v2 - Approach - Main features - Differences with v1 weight: 2 --- **The v2 our tests wanted** ## Why choose `go-openapi/testify/v2` * 95% compatible with `stretchr/testify` — if you already use it, our migration tool automates the switch * Actively maintained: regular fixes and evolutions, many PRs proposed upstream are already in * Zero external dependencies — you import what you need, with opt-in modules for extras (e.g. YAML, colorized output) * Modernized codebase targeting go1.25+ * Go routine leak detection built in: zero-setup, no false positives, works with parallel tests (unlike `go.uber.org/goleak`) * File descriptor leak detection (linux-only) * Type-safe assertions with generics (see [a basic example](../usage/EXAMPLES.md)) — migration to generics can be automated too. [Read the full story](../usage/GENERICS.md) * Safe async assertions, extended JSON & YAML assertions * Coming in `v2.5.0`: non-flaky async assertions using `synctest`, and internal tools exposed as standalone modules (spew, unified diff, goleak) * We take documentation seriously: [searchable doc site](../../api/) with testable examples and a complete tutorial, plus detailed [godoc][godoc-url] for every assertion ### This fork isn't for everyone * You need the `mock` package — we removed it and won't bring it back. For suites, we're [open to discussion](https://github.com/go-openapi/testify/discussions/75) about a redesigned approach * Your project must support Go versions older than 1.25 * You rely on `testifylint` or other tooling that expects the `stretchr/testify` import path * You need 100% API compatibility — we're at 95%, and the remaining 5% are intentional removals ## Motivation See [why we wanted a v2](./MOTIVATION.md). ### Approach with this fork This fork targets **go1.25**. * [x] **zero** external dependencies by default * [x] extra features (and dependencies) are opt-in * [x] **modernized** code base * [x] **simplified** maintenance * [x] can add or remove assertions with ease * [x] **mitigated API bloat** with comprehensive domain-indexed documentation * [x] can leverage generics without backward compatibility concerns The approach will be **selective and pragmatic** rather than comprehensive: * **Targeted improvements** where generics provide clear value without compromising existing functionality * **Focus on eliminating anti-patterns** like dummy value instantiation in `IsType` (see #1805) * **Preserve reflection-based flexibility** for comparing complex types rather than forcing everything through generic constraints * **Careful constraint design** to ensure type safety without being overly restrictive or permissive The goal is to enhance type safety and developer experience where it matters most, while maintaining the flexibility that makes testify useful for real-world testing scenarios. ## Breaking changes from v1 * `YAMLEq` panics by default: must enable the feature with an additional blank import * deprecated types and methods have been removed * removed the `suite`, `mocks` and `http` packages * replaced internal utility package `_codegen` by `codegen` See [all changes from v1](../usage/CHANGES.md) and check out our [ROADMAP](./maintainers/ROADMAP.md). ## API Stability Guarantee The assertions currently used by go-openapi projects constitute our **stable API**. These entry points will remain backward compatible. Other assertions may evolve as we refine the v2 API. --- ## See Also **Getting Started:** - [Examples](../usage/EXAMPLES.md) - Practical code examples for using testify v2 - [Usage Guide](../usage/USAGE.md) - API conventions and navigation guide - [Migration Guide](../usage/MIGRATION.md) - Migrating from stretchr/testify v1 **Project Documentation:** - [Changes from v1](../usage/CHANGES.md) - Complete list of changes and new features - [Roadmap](./maintainers/ROADMAP.md) - Future development plans - [Architecture](./maintainers/ARCHITECTURE.md) - Technical architecture and design decisions - [Contributing](./contributing/CONTRIBUTING.md) - How to contribute to this project - [The original README](./maintainers/ORIGINAL.md) --- [test-badge]: https://github.com/go-openapi/testify/actions/workflows/go-test.yml/badge.svg [test-url]: https://github.com/go-openapi/testify/actions/workflows/go-test.yml [cov-badge]: https://codecov.io/gh/go-openapi/testify/branch/master/graph/badge.svg [cov-url]: https://codecov.io/gh/go-openapi/testify [vuln-scan-badge]: https://github.com/go-openapi/testify/actions/workflows/scanner.yml/badge.svg [vuln-scan-url]: https://github.com/go-openapi/testify/actions/workflows/scanner.yml [codeql-badge]: https://github.com/go-openapi/testify/actions/workflows/codeql.yml/badge.svg [codeql-url]: https://github.com/go-openapi/testify/actions/workflows/codeql.yml [release-badge]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Ftestify.svg [release-url]: https://badge.fury.io/go/github.com%2Fgo-openapi%2Ftestify [gocard-badge]: https://goreportcard.com/badge/github.com/go-openapi/testify [gocard-url]: https://goreportcard.com/report/github.com/go-openapi/testify [codefactor-badge]: https://img.shields.io/codefactor/grade/github/go-openapi/testify [codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/testify [doc-badge]: https://img.shields.io/badge/doc-site-blue?link=https%3A%2F%2Fgoswagger.io%2Fgo-openapi%2F [doc-url]: https://goswagger.io/go-openapi [godoc-badge]: https://pkg.go.dev/badge/github.com/go-openapi/testify [godoc-url]: http://pkg.go.dev/github.com/go-openapi/testify [slack-badge]: https://slackin.goswagger.io/badge.svg [slack-url]: https://slackin.goswagger.io [license-badge]: http://img.shields.io/badge/license-Apache%20v2-orange.svg [license-url]: https://github.com/go-openapi/testify/?tab=Apache-2.0-1-ov-file#readme [goversion-badge]: https://img.shields.io/github/go-mod/go-version/go-openapi/testify [goversion-url]: https://github.com/go-openapi/testify/blob/master/go.mod [top-badge]: https://img.shields.io/github/languages/top/go-openapi/testify [commits-badge]: https://img.shields.io/github/commits-since/go-openapi/testify/latest go-openapi-testify-c10ca71/docs/doc-site/project/SECURITY.md000066400000000000000000000030761520301377500235450ustar00rootroot00000000000000--- title: Security Policy description: | This document outlines the commitment and practices of the go-openapi maintainers regarding security weight: 3 --- This policy outlines the commitment and practices of the go-openapi maintainers regarding security. ## Supported Versions | Version | Supported | | ------- | ------------------ | | 2.2.x | :white_check_mark: | ## Vulnerability checks in place This repository uses automated vulnerability scans, at every merged commit and at least once a week. We use: * [`GitHub CodeQL`][codeql-url] * [`trivy`][trivy-url] * [`govulncheck`][govulncheck-url] Reports are centralized in github security reports and visible only to the maintainers. ## Reporting a vulnerability If you become aware of a security vulnerability that affects the current repository, **please report it privately to the maintainers** rather than opening a publicly visible GitHub issue. Please follow the instructions provided by github to [Privately report a security vulnerability][github-guidance-url]. {{% notice primary "TL;DR" "meteor" %}} > On Github, navigate to the project's "Security" tab then click on "Report a vulnerability". {{% /notice %}} [codeql-url]: https://github.com/github/codeql [trivy-url]: https://trivy.dev/docs/latest/getting-started [govulncheck-url]: https://go.dev/blog/govulncheck [github-guidance-url]: https://docs.github.com/en/code-security/security-advisories/guidance-on-reporting-and-writing-information-about-vulnerabilities/privately-reporting-a-security-vulnerability#privately-reporting-a-security-vulnerability go-openapi-testify-c10ca71/docs/doc-site/project/_index.md000066400000000000000000000003521520301377500235360ustar00rootroot00000000000000--- title: "Project Documentation" weight: 3 description: | Project roadmap, contributing guidelines and maintainer's guide. --- Useful guides for contributors and maintainers. --- {{< children type="card" description="true" >}} go-openapi-testify-c10ca71/docs/doc-site/project/contributing/000077500000000000000000000000001520301377500244555ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/doc-site/project/contributing/CODE_OF_CONDUCT.md000066400000000000000000000063311520301377500272570ustar00rootroot00000000000000--- title: Code Of Conduct description: Contributor Covenant Code of Conduct weight: 11 --- ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to making participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, gender identity and expression, level of experience, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: * Using welcoming and inclusive language * Being respectful of differing viewpoints and experiences * Gracefully accepting constructive criticism * Focusing on what is best for the community * Showing empathy towards other community members Examples of unacceptable behavior by participants include: * The use of sexualized language or imagery and unwelcome sexual attention or advances * Trolling, insulting/derogatory comments, and personal or political attacks * Public or private harassment * Publishing others' private information, such as a physical or electronic address, without explicit permission * Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies both within project spaces and in public spaces when an individual is representing the project or its community. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at ivan+abuse@flanders.co.nz. All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [https://contributor-covenant.org/version/1/4][version] [homepage]: https://contributor-covenant.org [version]: https://contributor-covenant.org/version/1/4/ go-openapi-testify-c10ca71/docs/doc-site/project/contributing/CONTRIBUTING.md000066400000000000000000000227331520301377500267150ustar00rootroot00000000000000--- title: Contributing Guidelines description: | You'll find here general guidelines to contribute to this project. There are many ways to help this project. Contributing code is only one of those... weight: 1 --- You'll find here general guidelines to contribute to this project. They mostly correspond to standard practices for open source repositories. We have tried to keep things as simple as possible. {{% notice primary "TL;DR" "meteor" %}} > If you're an experienced go developer on github, then you should just feel at home with us > and you may well skip the rest of this document. > > You'll essentially apply the usual guidelines for a go library project on github. {{% /notice %}} These guidelines are common to all libraries published on github by the `go-openapi` organization, so you'll feel at home with any of our projects. You'll find more detailed (or repo-specific) instructions in the [maintainer's docs][maintainers-doc]. ## How can I contribute? {{% tab title="There are many ways to contribute" %}} There are many ways in which you can contribute, not just code. Here are a few ideas: * Reporting issues or bugs * Suggesting improvements * Documentation * Art work that makes the project look great * Code * proposing bug fixes and new features that are within the main project scope * improving test coverage * addressing code quality issues {{< /tab >}} ## Questions & issues {{< tabs >}} {{% tab title="Asking questions" %}} ### Asking a question You may inquire anything about this library by reporting a "Question" issue on github. You may also join our discord server where you may discuss issues or requests. [![Discord Server][discord-badge]][discord-url] [discord-badge]: https://img.shields.io/discord/1446918742398341256?logo=discord&label=discord&color=blue [discord-url]: https://discord.gg/FfnFYaC3k5 {{% /tab %}} {{% tab title="Reporting issues" %}} ### Reporting issues Reporting a problem with our libraries _is_ a valuable contribution. You can do this on the github issues page of this repository. Please be as specific as possible when describing your issue. Whenever relevant, please provide information about your environment (go version, OS). Adding a code snippet to reproduce the issue is great, and a big time saver for maintainers. {{% /tab %}} {{% tab title="Triaging issues" %}} ### Triaging issues You can help triage issues which may include: * reproducing bug reports * asking for important information, such as version numbers or reproduction instructions * answering questions and sharing your insight in issue comments {{% /tab %}} {{< /tabs >}} ## Code contributions {{< tabs >}} {{% tab title="All pull requests are welcome" %}} ### Pull requests are always welcome We are always thrilled to receive pull requests, and we do our best to process them as fast as possible. Not sure if that typo is worth a pull request? Do it! We will appreciate it. If your pull request is not accepted on the first try, don't be discouraged! If there's a problem with the implementation, hopefully you've received feedback on what to improve. If you have a lot of ideas or a lot of issues to solve, try to refrain a bit and post focused pull requests. Think that they must be reviewed by a maintainer and it is easy to lose track of things on big PRs. We're trying very hard to keep the go-openapi packages lean and focused. Together, these packages constitute a toolkit for go developers: it won't do everything for everybody out of the box, but everybody can use it to do just about everything related to OpenAPI. This means that we might decide against incorporating a new feature. However, there might be a way to implement that feature *on top of* our libraries. {{% /tab %}} {{% tab title="Environment" %}} ### Environment You just need a `go` compiler to be installed. No special tools are needed to work with our libraries. The minimal go compiler version required is always the old stable (latest minor go version - 1). Our libraries are designed and tested to work on `Linux`, `MacOS` and `Windows`. If you're used to work with `go` you should already have everything in place. Although not required, you'll be certainly more productive with a local installation of `golangci-lint`, the meta-linter our CI uses. If you don't have it, you may install it like so: ```sh go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest ``` {{% /tab %}} {{< /tabs >}} ### Conventions {{< tabs >}} {{% tab title="Git Flow" %}} #### Git flow Fork the repo and make changes to your fork in a feature branch. To submit a pull request, push your branch to your fork (e.g. `upstream` remote): github will propose to open a pull request on the original repository. Typically you'd follow some common naming conventions: - if it's a bug fixing branch, name it `fix/XXX-something` where XXX is the number of the issue on github - if it's a feature branch, create an enhancement issue to announce your intentions, and name it `feature/XXX-something` where XXX is the number of the issue. > NOTE: we don't enforce naming conventions on branches: it's your fork after all. {{% /tab %}} {{% tab title="Tests" %}} #### Tests Submit unit tests for your changes. Go has a great built-in test framework ; use it! Take a look at existing tests for inspiration, and run the full test suite on your branch before submitting a pull request. Our CI measures test coverage and the test coverage of every patch. Although not a blocking step - because there are so many special cases - this is an indicator that maintainers consider when approving a PR. Please try your best to cover at least 80% of your patch. {{% /tab %}} {{% tab title="Code Style" %}} #### Code style You may read our stance on code style [there](./STYLE.md). {{% /tab %}} {{% tab title="Documentation" %}} #### Documentation Don't forget to update the documentation when creating or modifying a feature. Most documentation for this library is directly found in code as comments for godoc. The documentation for this go-openapi package is published on [the public go docs site][go-doc]. --- Check your documentation changes for clarity, concision, and correctness. If you want to assess the rendering of your changes when published to `pkg.go.dev`, you may want to install the `pkgsite` tool proposed by `golang.org`. ```sh go install golang.org/x/pkgsite/cmd/pkgsite@latest ``` Then run on the repository folder: ```sh pkgsite . ``` This will run a godoc server locally where you may see the documentation generated from your local repository. [go-doc]: https://pkg.go.dev/github.com/go-openapi/testify {{% /tab %}} {{% tab title="Commit Messages" %}} #### Commit messages Pull requests descriptions should be as clear as possible and include a reference to all the issues that they address. Pull requests must not contain commits from other users or branches. Commit messages are not required to follow the "conventional commit" rule, but it's certainly a good thing to follow that convention (e.g. "fix: fixed panic in XYZ", "ci: did this", "feat: did that" ...). The title in your commit message is used directly to produce our release notes: try to keep them neat. The commit message body should detail your changes. If an issue should be closed by a commit, please add this reference in the commit body: ``` * fixes #{issue number} ``` {{% /tab %}} {{% tab title="Code Review" %}} #### Code review Code review comments may be added to your pull request. Discuss, then make the suggested modifications and push additional commits to your feature branch. Be sure to post a comment after pushing. The new commits will show up in the pull request automatically, but the reviewers will not be notified unless you comment. Before the pull request is merged, **make sure that you've squashed your commits into logical units of work** using `git rebase -i` and `git push -f`. After every commit the test suite should be passing. Include documentation changes in the same commit so that a revert would remove all traces of the feature or fix. {{% /tab %}} {{% tab title="Sign Your Work" %}} #### Sign your work Software is developed by real people. The sign-off is a simple line at the end of your commit message, which certifies that you wrote it or otherwise have the right to pass it on as an open-source patch. We require the simple DCO below with an email signing your commit. PGP-signed commit are greatly appreciated but not required. The rules are pretty simple: * read our [DCO][dco-doc] (from [developercertificate.org][dco-source]) * if you agree with these terms, then you just add a line to every git commit message Signed-off-by: Joe Smith using your real name (sorry, no pseudonyms or anonymous contributions.) You can add the sign-off when creating the git commit via `git commit -s`. [dco-doc]: ./DCO.md [dco-source]: https://developercertificate.org {{% /tab %}} {{< /tabs >}} ## Code contributions by AI agents Our agentic friends are welcome to contribute! We only have a few demands to keep-up with human maintainers. 1. Issues and PRs written or posted by agents should always mention the original (human) poster for reference 2. We don't accept PRs attributed to agents. We don't want commits signed like "author: @claude.code". Agents or bots may coauthor commits, though. 3. Security vulnerability reports by agents should always be reported privately and mention the original (human) poster (see also [Security Policy][security-doc]). [maintainers-doc]: ../maintainers/MAINTAINERS.md [security-doc]: ../SECURITY.md go-openapi-testify-c10ca71/docs/doc-site/project/contributing/CONTRIBUTORS.md000066400000000000000000000542141520301377500267420ustar00rootroot00000000000000--- title: Contributors description: Acknowledgements to all-time contributors weight: 10 --- {{% notice primary "TL;DR" "meteor" %}} > Recognition and thanks to all 256 contributors who have helped build testify from its origins to this fork. > Complete list of contributors with commit counts and links to their contributions. {{% /notice %}} ## Emeritus We thank these members for their service to this community. - Repository: ['go-openapi/testify'] | Total Contributors | Total Contributions | | --- | --- | | 257 | 1242 | | Username | All Time Contribution Count | All Commits | | --- | --- | --- | | @fredbi | 180 | | | @ernesto-jimenez | 129 | | | @brackendawson | 110 | | | @dolmen | 103 | | | @tylerb | 64 | | | @boyan-soubachov | 35 | | | @leighmcculloch | 26 | | | @pmezard | 19 | | | @alexandear | 18 | | | @nelsam | 17 | | | @alexpantyukhin | 16 | | | @DAddYE | 14 | | | @MovieStoreGuy | 11 | | | @mikeauclair | 11 | | | @architagr | 11 | | | @arjunmahishi | 10 | | | @arjun-1 | 9 | | | @esdrasbeleza | 9 | | | @matryer | 9 | | | @ccoVeille | 8 | | | @AdRiley | 8 | | | @mvdkleijn | 8 | | | @linusbarth | 8 | | | @craig65535 | 8 | | | @muesli | 7 | | | @emou | 7 | | | @HaraldNordgren | 7 | | | @cszczepaniak | 7 | | | @Ogoyukin | 6 | | | @Neokil | 6 | | | @torkelrogstad | 6 | | | @gohargasparyan | 6 | | | @mutaiib | 5 | | | @vkryukov | 5 | | | @posener | 5 | | | @kevinburkesegment | 5 | | | @Saluev | 5 | | | @hendrywiranto | 5 | | | @vyas-git | 4 | | | @tsioftas | 4 | | | @viblo | 4 | | | @paulbellamy | 4 | | | @palsivertsen | 4 | | | @uberbo | 4 | | | @bsoubachov | 4 | | | @anpez | 4 | | | @Copilot | 4 | | | @olivergondza | 4 | | | @mvrahden | 4 | | | @jveski | 4 | | | @npxcomplete | 4 | | | @taichi | 3 | | | @spirin | 3 | | | @zjx20 | 3 | | | @redachl | 3 | | | @senyasdt | 3 | | | @reynieroz | 3 | | | @obeattie | 3 | | | @nmiyake | 3 | | | @josephholsten | 3 | | | @jonhoo | 3 | | | @hanzei | 3 | | | @AngelKitty | 3 | | | @andreas | 3 | | | @yiminc-zz | 2 | | | @hawkingrei | 2 | | | @viswajithiii | 2 | | | @SuperQ | 2 | | | @xsleonard | 2 | | | @srenatus | 2 | | | @snirye | 2 | | | @ryu-ton10 | 2 | | | @izaurio | 2 | | | @gz-c | 2 | | | @techfg | 2 | | | @visualfc | 2 | | | @everdance | 2 | | | @greg0ire | 2 | | | @sikehish | 2 | | | @LandonTClipp | 2 | | | @anupcshan | 2 | | | @AchoArnold | 2 | | | @2pi | 2 | | | @bboreham | 2 | | | @bmatsuo | 2 | | | @dcormier | 2 | | | @dencold | 2 | | | @siliconbrain | 2 | | | @czeslavo | 2 | | | @ikravets | 2 | | | @jeffwidman | 2 | | | @joshrendek | 2 | | | @jcelliott | 2 | | | @mhamrle | 2 | | | @rinchsan | 2 | | | @mrbroll | 2 | | | @rubensayshi | 2 | | | @renatoprime | 2 | | | @oalders | 2 | | | @rdwilliamson | 1 | | | @RmbRT | 1 | | | @RichardKnop | 1 | | | @rgarcia | 1 | | | @pietern | 1 | | | @peterebden | 1 | | | @pdbrito | 1 | | | @pquerna | 1 | | | @pdufour | 1 | | | @moolmanruan | 1 | | | @rleungx | 1 | | | @sam-nelson-mcn | 1 | | | @g7r | 1 | | | @s-urbaniak | 1 | | | @shaneHowearth | 1 | | | @simonmulser | 1 | | | @brahmaroutu | 1 | | | @stdedos | 1 | | | @aldas | 1 | | | @comogo | 1 | | | @TechnotronicOz | 1 | | | @n9netales | 1 | | | @anacrolix | 1 | | | @mmorel-35 | 1 | | | @wallclockbuilder | 1 | | | @MAnyKey | 1 | | | @mchlp | 1 | | | @lesichkovm | 1 | | | @mlsteele | 1 | | | @neilconway | 1 | | | @nbaztec | 1 | | | @bobuhiro11 | 1 | | | @doloopwhile | 1 | | | @OladapoAjala | 1 | | | @omarkohl | 1 | | | @parkr | 1 | | | @pwfcurry | 1 | | | @phemmer | 1 | | | @stevenh | 1 | | | @cuishuang | 1 | | | @dave | 1 | | | @GlenDC | 1 | | | @egawata | 1 | | | @hectorj | 1 | | | @hidu | 1 | | | @lisitsky | 1 | | | @lummie | 1 | | | @myxo | 1 | | | @nicoche | 1 | | | @perrydunn | 1 | | | @philtay | 1 | | | @sarathsp06 | 1 | | | @sunpe | 1 | | | @timfeirg | 1 | | | @tscales | 1 | | | @api-card | 1 | | | @wwade | 1 | | | @agonzalezro | 1 | | | @hikyaru-suzuki | 1 | | | @groz | 1 | | | @tedeh | 1 | | | @terinjokes | 1 | | | @guettli | 1 | | | @tschaub | 1 | | | @tobikris | 1 | | | @TomWright | 1 | | | @prochac | 1 | | | @tabboud | 1 | | | @xtonyjiang | 1 | | | @tcsc | 1 | | | @qerdcv | 1 | | | @vincentcr | 1 | | | @vitalyisaev2 | 1 | | | @marshall-lee | 1 | | | @boekkooi-impossiblecloud | 1 | | | @willogden | 1 | | | @ybrustin | 1 | | | @yrk-lab | 1 | | | @ac7 | 1 | | | @bogdandrutu | 1 | | | @docbobo | 1 | | | @moorereason | 1 | | | @chakrit | 1 | | | @lazywei | 1 | | | @hugelgupf | 1 | | | @csmarchbanks | 1 | | | @cam72cam | 1 | | | @connor4312 | 1 | | | @shousper | 1 | | | @coryb | 1 | | | @SchumacherFM | 1 | | | @danielheller | 1 | | | @danielchatfield | 1 | | | @danielwhite | 1 | | | @hairyhenderson | 1 | | | @davidjb | 1 | | | @dpw | 1 | | | @dennwc | 1 | | | @lowjoel | 1 | | | @3scalation | 1 | | | @AutomateAaron | 1 | | | @adamluzsi | 1 | | | @adamwg | 1 | | | @packrat386 | 1 | | | @atombender | 1 | | | @AlekSi | 1 | | | @kejadlen | 1 | | | @alxn | 1 | | | @aud10slave | 1 | | | @ErebusBat | 1 | | | @shazow | 1 | | | @tamccall | 1 | | | @Ararsa-Derese | 1 | | | @bozaro | 1 | | | @bartventer | 1 | | | @bwplotka | 1 | | | @bayesianmind | 1 | | | @benbjohnson | 1 | | | @blakewilson-wf | 1 | | | @jonasfj | 1 | | | @jcamenisch | 1 | | | @JonCrowther | 1 | | | @ernsheong | 1 | | | @jinnovation | 1 | | | @xordspar0 | 1 | | | @jayd3e | 1 | | | @capoferro | 1 | | | @nyarly | 1 | | | @justbuchanan | 1 | | | @jedevc | 1 | | | @ingwarsw | 1 | | | @navytux | 1 | | | @itzamna314 | 1 | | | @leepa | 1 | | | @luan | 1 | | | @Lucaber | 1 | | | @miparnisari | 1 | | | @martin-sucha | 1 | | | @dominicbarnes | 1 | | | @dlclark | 1 | | | @bits01 | 1 | | | @dmacvicar | 1 | | | @echarrod | 1 | | | @evanphx | 1 | | | @fahimbagar | 1 | | | @gavincabbage | 1 | | | @glesica | 1 | | | @guncha | 1 | | | @cryptix | 1 | | | @henrahmagix | 1 | | | @hslatman | 1 | | | @chirino | 1 | | | @ianrose14 | 1 | | | @ifraixedes | 1 | | | @jszwec | 1 | | | @programmer04 | 1 | | | @jaguilar | 1 | | | @jbowes | 1 | | _this file was generated by the [Contributors GitHub Action](https://github.com/github-community-projects/contributors)_ go-openapi-testify-c10ca71/docs/doc-site/project/contributing/DCO.md000066400000000000000000000043401520301377500254050ustar00rootroot00000000000000--- title: Developer's Certificate of Origin description: Signing your work weight: 2 --- {{% notice style="primary" title="TL;DR" icon="pen-nib" %}} We welcome this (simple email signature): ``` Author: Frederic BIDON Date: Mon Dec 15 11:24:43 2025 +0100 feat: brainstrom RAG-equipped linter Signed-off-by: Frederic BIDON ``` or this (PGP-signed commit): ``` gpg: Signature made Tue 30 Dec 2025 09:53:48 PM CET gpg: using RSA key XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX gpg: Good signature from "Frédéric BIDON " Author: Frederic BIDON Date: Tue Dec 30 21:53:48 2025 +0100 tentative project doc site Signed-off-by: Frederic BIDON ``` {{% /notice %}} From : ``` Developer Certificate of Origin Version 1.1 Copyright (C) 2004, 2006 The Linux Foundation and its contributors. 660 York Street, Suite 102, San Francisco, CA 94110 USA Everyone is permitted to copy and distribute verbatim copies of this license document, but changing it is not allowed. Developer's Certificate of Origin 1.1 By making a contribution to this project, I certify that: (a) The contribution was created in whole or in part by me and I have the right to submit it under the open source license indicated in the file; or (b) The contribution is based upon previous work that, to the best of my knowledge, is covered under an appropriate open source license and I have the right under that license to submit that work with modifications, whether created in whole or in part by me, under the same open source license (unless I am permitted to submit under a different license), as indicated in the file; or (c) The contribution was provided directly to me by some other person who certified (a), (b) or (c) and I have not modified it. (d) I understand and agree that this project and the contribution are public and that a record of the contribution (including all personal information I submit with it, including my sign-off) is maintained indefinitely and may be redistributed consistent with this project or the open source license(s) involved. ``` go-openapi-testify-c10ca71/docs/doc-site/project/contributing/STYLE.md000066400000000000000000000157021520301377500257040ustar00rootroot00000000000000--- title: Coding Style description: Coding style at `go-openapi` weight: 3 --- ## Coding style at `go-openapi` {{% notice primary "TL;DR" "meteor" %}} > Let's be honest: at `go-openapi` and `go-swagger` we've never been super-strict on code style and linting. > > But perhaps now (2025) is the time to adopt a different stance. {{% /notice %}} Even though our repos have been early adopters of `golangci-lint` years ago (we used some other metalinter before), our decade-old codebase is only realigned to new rules from time to time. Now go-openapi and go-swagger together make up a really large codebase, which is taxing to maintain and keep afloat. Code quality and the harmonization of rules have thus become things that we need now. ## Meta-linter Universally formatted go code promotes ease of writing, reading, and maintenance. You should run `golangci-lint run` before committing your changes. Many editors have plugins that do that automatically. > We use the `golangci-lint` meta-linter. The configuration lies in > [`.golangci.yml`][golangci-yml]. > You may read [the linter's configuration reference][golangci-doc] for additional reference. This configuration is essentially the same across all `go-openapi` projects. Some projects may require slightly different settings. ## Linting rules posture Thanks to go's original design, we developers don't have to waste much time arguing about code figures of style. However, the number of available linters has been growing to the point that we need to pick a choice. ### Our approach: evaluate, don't consume blindly As early adopters of `golangci-lint` (and its predecessors), we've watched linting orthodoxy shift back and forth over the years. Patterns that were idiomatic one year get flagged the next; rules that seemed reasonable in isolation produce noise at scale. Conversations with maintainers of other large Go projects confirmed what our own experience taught us: **the default linter set is a starting point, not a prescription**. Our stance is deliberate: - **Start from `default: all`**, then consciously disable what doesn't earn its keep. This forces us to evaluate every linter and articulate why we reject it — the disabled list is a design rationale, not technical debt. - **Tune thresholds rather than disable** when a linter's principle is sound but its defaults are too aggressive for a mature codebase. - **Require justification for every `//nolint`** directive. Each one must carry an inline comment explaining why it's there. We maintain a full audit in [`docs/LINTING.md`][linting-audit]. - **Prefer disabling a linter over scattering `//nolint`** across the codebase. If a linter produces systematic false positives on patterns we use intentionally, the linter goes — not our code. - **Keep the configuration consistent** across all `go-openapi` repositories. Per-repo divergence is a maintenance tax we don't want to pay. The result is a three-layer defense: the `.golangci.yml` config as a baseline, `//nolint` with mandatory justification for the rare exceptions, and `docs/LINTING.md` as an audit trail. Contributors should read the disabled list as a set of conscious choices, not gaps to fill. We enable all linters published by `golangci-lint` by default, then disable a few ones. {{% tab title="Disabled linters" %}} Here are the reasons why they are disabled (update: Feb. 2026, `golangci-lint v2.8.0`). ```yaml disable: - depguard # we don't want to configure rules to constrain imports. That's the reviewer's job - exhaustruct # we don't want to configure regexp's to check type name. That's the reviewer's job - funlen # we accept cognitive complexity as a meaningful metric, but function length is not relevant - godox # we don't see any value in forbidding TODO's etc in code - nlreturn # we usually apply this "blank line" rule to make code less compact. We just don't want to enforce it - nonamedreturns # we don't see any valid reason why we couldn't use named returns - noinlineerr # there is no value added forbidding inlined err - paralleltest # we like parallel tests. We just don't want them to be enforced everywhere - recvcheck # we like the idea of having pointer and non-pointer receivers - testpackage # we like test packages. We just don't want them to be enforced everywhere - thelper # too many false positives on test case factories returning func(*testing.T). See note below - tparallel # see paralleltest - varnamelen # sometimes, we like short variables. The linter doesn't catch cases when a short name is good - whitespace # no added value - wrapcheck # although there is some sense with this linter's general idea, it produces too much noise - wsl # no added value. Noise - wsl_v5 # no added value. Noise ``` As you may see, we agree with the objective of most linters, at least the principle they are supposed to enforce. But all linters do not support fine-grained tuning to tolerate some cases and not some others. > **Note on `thelper`**: the only value we needed from this linter was checking for `t.Helper()` calls > inside genuine test helpers. Unfortunately, it produces persistent false positives on test case factories > (functions returning `func(*testing.T)`), which is a pattern we use extensively with our `iter.Seq`-based > table-driven tests. It also enforces naming conventions we don't subscribe to. The issue has been > reported upstream. We prefer disabling it entirely over maintaining `//nolint:thelper` directives > across every test file. {{< /tab >}} {{% tab title="Relaxed linter settings" %}} When this is possible, we enable linters with relaxed constraints. ```yaml settings: dupl: threshold: 200 # in a older code base such as ours, we have to be tolerant with a little redundancy # Hopefully, we'll be able to gradually get rid of those. goconst: min-len: 2 min-occurrences: 3 cyclop: max-complexity: 20 # the default is too low for most of our functions. 20 is a nicer trade-off gocyclo: min-complexity: 20 exhaustive: # when using default in switch, this should be good enough default-signifies-exhaustive: true default-case-required: true lll: line-length: 180 # we just want to avoid extremely long lines. # It is no big deal if a line or two don't fit on your terminal. ``` {{< /tab >}} Final note: since we have switched to a forked version of `stretchr/testify`, we no longer benefit from the great `testifylint` linter for tests. [golangci-yml]: https://github.com/go-openapi/testify/blob/master/.golangci.yml [golangci-doc]: https://golangci-lint.run/docs/linters/configuration/ [linting-audit]: https://github.com/go-openapi/testify/blob/master/docs/LINTING.md go-openapi-testify-c10ca71/docs/doc-site/project/contributing/_index.md000066400000000000000000000004301520301377500262420ustar00rootroot00000000000000--- title: "Contributing to Testify v2" description: "How can I contribute to this project?" weight: 3 --- How to contribute to [https://github.com/go-openapi/testify][repo-url]? {{< children type="card" description="true" >}} [repo-url]: https://github.com/go-openapi/testify go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/000077500000000000000000000000001520301377500242605ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/ARCHITECTURE.md000066400000000000000000000115431520301377500264700ustar00rootroot00000000000000--- title: "Architecture" description: Project structure weight: 2 --- ## Primer ### Goals We want the maintenance of dozens of test assertions, times many variants, to remain reasonably low. The maintenance flow is intended to require different activities and levels of understanding, depending on the complexity of a planned evolution. {{< mermaid align="center" zoom="true" >}} journey section Fixes & minor enhancements internal/assertions:5: Knowledge of the functionality section New dependencies internal/assertions/enable/...:5: Understanding of the repo architecture enable/...:5: Understanding of the repo architecture section API changes regenerate code:5: No specific knowledge section New constructs to support code & doc generator:5: Knowledge of internals {{< /mermaid >}} Most common maintenance tasks should not require much more than fixing/enhancing the code in `internal/assertions`. API changes need an extra code generation. Dependency changes (adding new features that need extra dependencies) is a bit more involved, but still manageable. The code & doc generator should rapidly become a very stable component. The maintenance of the generator itself remains an operation that requires an extended understanding of the internals of the project. Fixes and enhancements propagate naturally to the variants without the need to regenerate code. ### The maths with assertion variants Each test assertion produces 2 base variants (assert, require). Each of these variants produces another formatted variant. Except for generic assertions, we produce one "forward" variant and one "forward formatted" variant (as methods). **For every non-generic assertion: 8 variants.** **For every generic assertion: 4 variants.** **For every "helper" function (not an assertion): 2 variants.** All these variants make up several hundreds functions, which poses a challenge for maintenance and documentation. We have adopted code and documentation generation as a means to mitigate this issue. #### Current 1. Generic assertions (with type parameters): {{% siteparam "metrics.generics" %}} functions 2. Non-generic assertions (with t T parameter, no type parameters): {{% siteparam "metrics.nongeneric_assertions" %}} functions 3. Helper functions (no t T parameter): {{% siteparam "metrics.helpers" %}} functions Total: {{% siteparam "metrics.functions" %}} functions to _maintain_ **Generated Functions** 1. Generated variants in each package (assert/require): {{% siteparam "metrics.package_variants" %}} 2. Helpers: {{% siteparam "metrics.helpers" %}} 3. Constructors: 2 (1 in assert, 1 in require) Overall: {{% siteparam "metrics.total_functions" %}} generated functions ## Architecture Overview {{< mermaid align="center" zoom="true" >}} graph LR; classDef event font-size:small,font-family:Monospace; trigger@{ shape: rounded, label: "go generate" } codegen[["🛠️ codegen"]] docs@{ shape: documents, label: "📚 API docs"} source[["📦 internal/assertions (source of truth)"]] trigger:::event -.-> codegen source --> codegen --> assert source --> codegen --> require codegen --> docs {{< /mermaid >}} **Single Source of Truth: `internal/assertions/`** All assertion implementations live in `internal/assertions/`, organized by domain: - Functions are implemented once with comprehensive tests - Doc comments include "Examples:" sections that drive test generation (including testable examples) - Both `assert/` and `require/` packages are 100% generated from this source **Code Generator: `codegen/`** The generator scans `internal/assertions/` and produces: - Package-level functions (`assert.Equal`, `require.Equal`) - Format variants (`assert.Equalf`, `require.Equalf`) - Forward methods (`a.Equal()`, `r.Equal()`) - Tests for all variants - Testable examples for godoc - Documentation for documentation site, organized by domain **Generated Packages: `assert/` and `require/`** The generated functions directly call the internal implementation: no code duplication or change in semantics. **Generated Documentation: `docs/doc-site/api/`** Everything in these packages is generated. Never edit generated files directly. Exceptions: * `doc.go` is not generated * ad hoc testable examples are 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. ## See Also - [Code generation](./CODEGEN.md) - Detailed view of our code and doc generator go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/BENCHMARKS.md000066400000000000000000000165231520301377500262260ustar00rootroot00000000000000--- title: 'Benchmarks' description: 'Performance Benchmarks: Generic vs Reflection' weight: 10 --- **Last Updated**: 2026-01-20 ## Overview While the primary motivation for adding **generic assertion functions** to testify v2 was **compile-time type safety** (see [Generics Guide](../../usage/GENERICS.md) for details), our benchmarking revealed an unexpected bonus: **dramatic performance improvements** ranging from 1.2x to 81x faster, with up to 99% reduction in memory allocations for collection operations. This document focuses on the performance measurements and explains why these improvements occur. ## Type Safety First, Performance Second Generic assertions catch type errors when writing tests, not when running them. For example: ```go // Reflection: Compiles, fails at runtime assert.ElementsMatch(t, []int{1, 2}, []string{"a", "b"}) // Generic: Compiler catches the error immediately assert.ElementsMatchT(t, []int{1, 2}, []string{"a", "b"}) // ❌ Compile error! ``` See the [Generics Guide](../../usage/GENERICS.md) for comprehensive coverage of type safety benefits, refactoring safety, and when to use generic vs reflection variants. ## Performance Results by Category ### 🏆 Collection Operations: Exceptional Gains Collection operations see the most dramatic improvements due to elimination of per-element reflection overhead: | Function | Speedup | Memory Impact | Why It Matters | |----------|---------|---------------|----------------| | **ElementsMatch (10 items)** | **21x faster** | 568 B → 320 B (44% reduction) | Common test operation | | **ElementsMatch (100 items)** | **39x faster** | 41 KB → 3.6 KB (91% reduction) | Scales superlinearly | | **ElementsMatch (1000 items)** | **81x faster** | 4 MB → 33 KB (99% reduction) | Large collection testing | | **SliceContainsT** | **16x faster** | 4 allocs → 0 | Membership testing | | **SeqContainsT (iter.Seq)** | **25x faster** | 55 allocs → 9 | Go 1.23+ iterators | | **SliceSubset** | **43x faster** | 17 allocs → 0 | Subset verification | **Key insight**: ElementsMatch's O(n²) complexity amplifies the benefits—the speedup **increases** with collection size (21x → 39x → 81x). ### ⚡ Comparison Operations: Zero-Allocation Wins Direct operator usage (`>`, `<`, `==`) eliminates reflection overhead and boxing entirely: | Function | Speedup | Allocations | Benchmark Data | |----------|---------|-------------|----------------| | **Greater/Less** | **10-15x faster** | 1 → 0 allocs | 139.1ns → 17.9ns | | **Positive/Negative** | **16-22x faster** | 1 → 0 allocs | 121.5ns → 7.6ns | | **GreaterOrEqual/LessOrEqual** | **10-11x faster** | 1 → 0 allocs | Similar pattern | | **Equal** | **10-13x faster** | 0 allocs (both) | 44.8ns → 3.5ns | | **NotEqual** | **11x faster** | 0 allocs (both) | Comparable to Equal | **Key insight**: Comparison operations are frequently used in tests. 10-15x speedup on common assertions accumulates quickly across large test suites. ### 📊 Ordering Operations: Eliminating Per-Element Overhead Ordering checks iterate over collections, so eliminating per-element reflection creates significant gains: | Function | Speedup | Allocation Impact | |----------|---------|-------------------| | **IsIncreasing** | **7.4x faster** | 11 allocs → 0 | | **IsDecreasing** | **9.5x faster** | 11 allocs → 0 | | **IsNonDecreasing** | **8x faster** | 4 allocs → 0 | | **IsNonIncreasing** | **6.5x faster** | 4 allocs → 0 | ### 🔍 Type Checks: Cleaner API, Better Performance Generic type checks eliminate reflection and provide a cleaner API: | Function | Speedup | Notes | |----------|---------|-------| | **IsOfTypeT** | **9-11x faster** | No dummy value needed with generics | | **IsNotOfTypeT** | **Similar gains** | Type parameter makes intent explicit | ### ⚖️ Modest Gains: Where Processing Dominates Some operations see smaller improvements because expensive processing dominates: | Category | Speedup | Why Gains Are Limited | |----------|---------|----------------------| | **Same/NotSame** | **1.5-2x** | Pointer comparison already fast | | **Boolean checks** | **~2x** | Simple bool comparison | | **JSONEq** | **Marginal** | JSON parsing/unmarshaling dominates | | **Regexp** | **Marginal** | Regex compilation dominates | **Key insight**: Even modest performance gains come with the benefit of compile-time type safety. ## Understanding the Performance Gains ### Allocation Elimination The most dramatic speedups come from eliminating allocations entirely: - **ElementsMatch (1000 elements)**: 501,503 → 3 allocations (99.999% reduction) - **All comparison operations**: 1 → 0 allocations - **Ordering checks**: 4-11 → 0 allocations Less allocation pressure means faster execution and reduced GC overhead, especially impactful in large test suites. ### Superlinear Scaling For operations with O(n²) or O(n) complexity, eliminating per-element reflection overhead creates superlinear gains: - **ElementsMatch**: 21x (10 items) → 39x (100 items) → 81x (1000 items) - The speedup **increases** with collection size ### Cumulative Impact Test suites typically run thousands of assertions: - **Small test suite** (1,000 assertions): 10x average speedup = significantly faster CI runs - **Large test suite** (10,000+ assertions): Cumulative savings become substantial - **Particularly valuable** in CI/CD pipelines where test execution time directly affects deployment velocity ## Sample Benchmark Data Representative results from `go test -bench=. ./internal/assertions`: ``` # Collection operations BenchmarkElementsMatch/reflect/1000-16 25.5 ms/op 4.0 MB/op 501503 allocs/op BenchmarkElementsMatch/generic/1000-16 316 µs/op 33 KB/op 3 allocs/op ↑ 81x faster ↑ 99% less memory # Comparison operations BenchmarkGreater/reflect/int-16 139.1 ns/op 34 B/op 1 allocs/op BenchmarkGreater/generic/int-16 17.9 ns/op 0 B/op 0 allocs/op ↑ 7.8x faster # Equality checks BenchmarkEqual/reflect/int-16 44.8 ns/op 0 B/op 0 allocs/op BenchmarkEqual/generic/int-16 3.5 ns/op 0 B/op 0 allocs/op ↑ 13x faster ``` ## Adopting Generic Assertions See the [Migration Guide](../../usage/MIGRATION.md) for step-by-step instructions on migrating to generic assertions, and the [Generics Guide](../../usage/GENERICS.md) for comprehensive coverage of type safety benefits and usage patterns. --- ## Running Benchmarks To run the benchmarks yourself: ```bash go test -run=^$ -bench=. -benchmem ./internal/assertions ``` ## Benchmark Coverage **38/{{% siteparam "metrics.generics" %}} generic functions benchmarked across 10 domains:** - Boolean (2): TrueT, FalseT - Collection (16): StringContainsT, SliceContainsT, MapContainsT, SeqContainsT, ElementsMatchT, SliceSubsetT, SliceEqualT, MapEqualT, and negative variants - Comparison (6): GreaterT, LessT, GreaterOrEqualT, LessOrEqualT, PositiveT, NegativeT - Equality (4): EqualT, NotEqualT, SameT, NotSameT - JSON (1): JSONEqT - Number (2): InDeltaT, InEpsilonT - Ordering (6): IsIncreasingT, IsDecreasingT, IsNonIncreasingT, IsNonDecreasingT, SortedT, NotSortedT - String (2): RegexpT, NotRegexpT - Type (2): IsOfTypeT, IsNotOfTypeT - YAML (1): YAMLEqT (benchmarked separately in enable/yaml module) --- go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/CODEGEN.md000066400000000000000000000223731520301377500256550ustar00rootroot00000000000000--- title: Code Generation description: Code generation workflow and maintenance. weight: 3 --- {{% notice primary "TL;DR" "meteor" %}} > The entire assert/require API (600+ functions) is generated from 76 source assertions in `internal/assertions/`. > Run `go generate ./...` to regenerate everything. Add new assertions by editing source files and adding examples. {{% /notice %}} ## Maintaining Generated Code This repository uses code generation extensively to maintain consistency across assertion packages. ### Code Generation Pipeline {{< mermaid align="center" zoom="true" >}} graph TD source["📦 internal/assertions/*.go"] scanner["🔍 Scanner go/packages + go/types"] model["fa:fa-database Model data structures"] templates["📝 Templates Go text/template"] outputs["📤 Generated Code"] source --> scanner scanner --> extract_metadata extract_metadata --> model model --> templates templates --> outputs subgraph extract_metadata["Extract Metadata"] direction BT extract["Extractor"] comments["godoc comments"] --o extract examples["examples: values comments"] --o extract domains["domain tags"] --o extract sigs["Function signatures"] --o extract sigs["Other internal annotations comments"] --o extract end outputs -.-> assert_package outputs -.-> require_package outputs -.-> docs@{shape: documents, label: "docs/doc-site/**/*.md"} subgraph assert_package direction BT assert@{shape: documents, label: "assert/*.go"} tests_assert["*_test.go files"] --o assert example_tests_assert["*_examples_test.go files"] --o assert subgraph not_generated_assert["*not generated*"] direction LR docgo_assert@{ shape: document, label: "doc.go" } adhoc_assert@{ shape: document, label: "*_adhoc*_test.go" } end end subgraph require_package direction BT require@{shape: documents, label: "require/*.go"} tests_require["*_test.go files"] --o require example_tests_require["*_examples_test.go files"] --o require subgraph not_generated_require["*not generated*"] direction LR docgo_require@{ shape: document, label: "doc.go" } adhoc_require@{ shape: document, label: "*_adhoc*_test.go" } end end style not_generated_assert fill:#4a9eff,color:#fff style not_generated_require fill:#4a9eff,color:#fff {{< /mermaid >}} > The generator scans source code, extracts metadata, builds a model, and applies templates to generate ~800+ functions, tests, and documentation from ~100+ source functions. --- ### Adding a New Assertion **Complete workflow:** 1. **Add function to `internal/assertions/.go`:** The following example would like go to `string.go`, next to the `Regexp` assertion. ```go import ( "fmt" "strings" ) // StartsWith asserts that the string starts with the given prefix. // // Examples: // // success: "hello world", "hello" // failure: "hello world", "bye" func StartsWith(t T, str, prefix string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } if !strings.HasPrefix(str, prefix) { return Fail(t, fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...) } return true } ``` 2. **Add tests to `internal/assertions/_test.go`:** Write comprehensive table-driven tests covering edge cases. 3. **Run code generation:** ```bash go generate ./... ``` 4. **Done!** All 8 variants are generated with tests and examples: - `assert.StartsWith(t, str, prefix)` - `assert.StartsWithf(t, str, prefix, "msg")` - `a.StartsWith(str, prefix)` (forward method) - `a.StartsWithf(str, prefix, "msg")` - `require.StartsWith(t, str, prefix)` - `require.StartsWithf(t, str, prefix, "msg")` - `r.StartsWith(str, prefix)` (forward method) - `r.StartsWithf(str, prefix, "msg")` #### How One Function Becomes Eight {{< mermaid align="center" zoom="true" >}} graph TD source["1 Source Function internal/assertions/Equal()"] source --> assert_group["assert Package"] source --> require_group["require Package"] assert_group --> assert_pkg["assert.Equal(t, a, b) package-level"] assert_group --> assert_fmt["assert.Equalf(t, a, b, msg) formatted variant"] assert_group --> assert_fwd["a.Equal(a, b) forward method"] assert_group --> assert_fwdfmt["a.Equalf(a, b, msg) forward + formatted"] require_group --> require_pkg["require.Equal(t, a, b) package-level (fatal)"] require_group --> require_fmt["require.Equalf(t, a, b, msg) formatted variant (fatal)"] require_group --> require_fwd["r.Equal(a, b) forward method (fatal)"] require_group --> require_fwdfmt["r.Equalf(a, b, msg) forward + formatted (fatal)"] style source fill:#4a9eff,color:#fff style assert_group fill:#90ee90,color:#000 style require_group fill:#ffb6c1,color:#000 {{< /mermaid >}} > **reflection-based assertions become 8, generic assertions become 4** > > (plus tests and documentation for each). --- ### Example Annotations Format The "Examples:" section in doc comments drives test and example generation: ```go // Examples: // // success: // failure: // panic: // ``` **Rules:** - Use valid Go expressions that can be directly inserted into test code - `success:` and `failure:` are required for most assertions - `panic:` is optional (used for assertions like Panics, YAMLEq) - Multiple examples of the same type are allowed (e.g., multiple `success:` lines) - Examples are extracted by the scanner and used to generate: - Unit tests (success + failure cases) - Testable examples (success cases only for simplicity) **Example with multiple success cases:** ```go // Examples: // // success: []string{"a", "b"}, 2 // slice // success: map[string]int{"a": 1}, 1 // map // success: "hello", 5 // string // failure: []string{"a"}, 5 ``` #### Example-Driven Test Generation {{< mermaid align="center" zoom="true" >}} graph LR doccomment["Doc Comment with Examples:"] parser["📖 Example Parser"] cases["Test Cases success/failure/panic"] multiplier["Multiply × 8"] tests["Generated Tests"] doccomment --> parser parser --> cases cases --> multiplier multiplier --> pkg_assert["assert package test"] multiplier --> fmt_assert["assert format test"] multiplier --> fwd_assert["assert forward test"] multiplier --> fwdfmt_assert["assert fwd+fmt test"] multiplier --> pkg_require["require package test"] multiplier --> fmt_require["require format test"] multiplier --> fwd_require["require forward test"] multiplier --> fwdfmt_require["require fwd+fmt test"] pkg_assert & fmt_assert & fwd_assert & fwdfmt_assert & pkg_require & fmt_require & fwd_require & fwdfmt_require --> tests style cases fill:orange,color:black; style multiplier fill:yellow,color:black; style tests fill:lightgreen,color:black; {{< /mermaid >}} > Each example in doc comments generates 8 test functions (one per variant), ensuring 100% test coverage of generated code. > In addition, the generator produces testable examples (somewhat redundant with "passed" tests) so every function gets > a testable example displayed on pkg.go.dev. --- ### Special Cases in Generated Tests For complex assertions requiring special setup, the test templates support conditional logic. See `codegen/internal/generator/templates/assertion_assertions_test.gotmpl` for examples of: - Custom mock selection based on function behavior (mockT vs mockFailNowT) - Package-specific test helpers (testDataPath, httpOK, etc.) - Handling functions without test examples (generates `t.Skip()`) Some go expressions won't fit nicely for examples (examples use an external package, e.g. `assert_test`). To cover these edge cases, a `relocate` function map currently rewrites the example values to be used from an external package. The relocation uses go parsing capabilities. The only hard-coded exception if for `PanicFunc`. (see `codegen/internal/generator/funcmap.go`). ### Generator Flags ```bash go run ./codegen/main.go \ -work-dir=.. \ -input-package=github.com/go-openapi/testify/v2/internal/assertions \ -output-packages=assert,require \ -target-root=.. \ -include-format-funcs=true \ -include-forward-funcs=true \ -include-tests=true \ -include-examples=true \ -runnable-examples=true \ -include-helpers=true \ -include-generics=false ``` Current usage with `go generate` (see `doc.go`): ```go //go:generate go run ./codegen/main.go -target-root . -work-dir . ``` **Note:** Generic functions are planned but not yet implemented. ### Verification After generation, verify: ```bash # All tests pass go test ./... # Coverage remains high go test -cover ./internal/assertions # Should be ~94% go test -cover ./assert # Should be ~99.5% go test -cover ./require # Should be ~99.5% # Examples are valid go test -run Example ./assert go test -run Example ./require ``` The 0.5% coverage gap comes from helper functions (non-assertion functions) that don't have "Examples:" annotations. go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/DEPENDENCIES.md000066400000000000000000000272321520301377500264360ustar00rootroot00000000000000--- title: Optional Dependencies description: Zero-dependency architecture with opt-in external features. weight: 4 --- {{% notice primary "TL;DR" "meteor" %}} > The main module has zero external dependencies. Optional features (YAML, colorized output) are activated > by importing separate `enable/` modules. Internal stubs panic with helpful messages when a feature is used > without being enabled. {{% /notice %}} ## The Problem Testing libraries sit at the bottom of the dependency tree: every package in a project imports them. Any dependency pulled in by the testing library propagates to all consumers. The original `stretchr/testify` pulls in `gopkg.in/yaml.v3`, `github.com/davecgh/go-spew`, and `github.com/pmw/go-difflib` for *all* users, even those who never call `YAMLEq`. Our goal: **zero external dependencies in the main module**, with opt-in features for users who need them. ## Architecture Overview Three layers collaborate to deliver optional features without coupling: {{< mermaid align="center" zoom="true" >}} graph TD user["User Code"] enablemod["enable/yaml or enable/colors
separate Go modules"] stubs["enable/stubs/yaml or enable/stubs/colors
public delegation API"] internal["internal/assertions/enable/yaml or .../colors
internal stubs with function pointers"] assertions["internal/assertions/yaml.go or equal.go, diff.go
assertion implementations"] user -- "blank import" --> enablemod enablemod -- "init(): wires real impl" --> stubs stubs -- "delegates to" --> internal assertions -- "calls" --> internal style enablemod fill:#4a9eff,color:#fff style stubs fill:#90ee90,color:#000 style internal fill:#ffb6c1,color:#000 style assertions fill:#f0f0f0,color:#000 {{< /mermaid >}} | Layer | Location | Has external deps? | Purpose | |-------|----------|-------------------|---------| | **Feature module** | `enable/yaml/`, `enable/colors/` | Yes (own `go.mod`) | Imports the real library, wires it in via `init()` | | **Public stubs** | `enable/stubs/yaml/`, `enable/stubs/colors/` | No | Stable public API that delegates to internal package | | **Internal stubs** | `internal/assertions/enable/yaml/`, `.../colors/` | No | Holds function pointers, panics when unset | | **Assertions** | `internal/assertions/*.go` | No | Calls internal stubs; unaware of external libraries | --- ## How It Works: YAML ### The Wiring Chain ``` User imports: _ "github.com/go-openapi/testify/enable/yaml/v2" │ ▼ enable/yaml/enable_yaml.go init() ├─ calls yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) └─ calls yamlstub.EnableYAMLWithMarshal(yaml.Marshal) │ ▼ enable/stubs/yaml/enable_yaml.go └─ delegates to internal/assertions/enable/yaml │ ▼ internal/assertions/enable/yaml/enable_yaml.go └─ stores function pointers in package-level vars │ ▼ internal/assertions/yaml.go └─ calls yaml.Unmarshal() / yaml.Marshal() via the stored pointers ``` ### What Happens Without Enablement If a user calls `assert.YAMLEq(t, a, b)` without the blank import, the internal stub panics: ``` panic: YAML is not enabled. To enable YAML support, add a blank import: import _ "github.com/go-openapi/testify/enable/yaml/v2" ``` This is intentional: fail fast with a clear fix, rather than silently returning wrong results. ### Internal Stub Pattern (YAML) The internal stub stores function pointers that start as `nil`: ```go // internal/assertions/enable/yaml/enable_yaml.go var ( enableYAMLUnmarshal func([]byte, any) error enableYAMLMarshal func(any) ([]byte, error) ) func Unmarshal(in []byte, out any) error { if enableYAMLUnmarshal == nil { panic("YAML is not enabled...") } return enableYAMLUnmarshal(in, out) } ``` ### Feature Module (YAML) The feature module is a separate Go module with its own `go.mod`: ``` // enable/yaml/go.mod module github.com/go-openapi/testify/enable/yaml/v2 require go.yaml.in/yaml/v3 v3.0.4 ``` Its `init()` wires in the real implementation: ```go // enable/yaml/enable_yaml.go func init() { yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) yamlstub.EnableYAMLWithMarshal(yaml.Marshal) } ``` ### Custom YAML Library Users can bypass the default `enable/yaml` module and inject their own YAML library: ```go import ( yaml "github.com/goccy/go-yaml" yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" ) func init() { yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) } ``` This works because the public stubs API accepts any function matching the expected signature. --- ## How It Works: Colorized Output ### The Wiring Chain ``` User imports: _ "github.com/go-openapi/testify/enable/colors/v2" │ ▼ enable/colors/enable.go init() ├─ registers CLI flags: -testify.colorized, -testify.theme, -testify.colorized.notty ├─ reads env vars: TESTIFY_COLORIZED, TESTIFY_THEME, TESTIFY_COLORIZED_NOTTY ├─ detects TTY via golang.org/x/term └─ calls colorstub.Enable(func() []Option { ... }) │ ▼ enable/stubs/colors/enable_colors.go └─ delegates Enable() to internal/assertions/enable/colors │ ▼ internal/assertions/enable/colors/enable_colors.go └─ stores enabler function, resolves lazily via sync.Once │ ▼ internal/assertions/equal.go, diff.go └─ calls colors.ExpectedColorizer(), colors.ActualColorizer(), colors.Options() ``` ### Lazy Initialization Colors use a different pattern than YAML: lazy initialization with `sync.Once`. ```go // internal/assertions/enable/colors/enable_colors.go var ( resolveOptionsOnce sync.Once optionsEnabler func() []Option ) func Enable(enabler func() []Option) { optionsEnabler = enabler } func resolveOptions() { resolveOptionsOnce.Do(func() { if optionsEnabler == nil { // Not enabled: use no-op colorizers return } // Enabled: build ANSI colorizers from options o := optionsWithDefaults(optionsEnabler()) colorOptions = makeDiffOptions(o) stringColorizers = setColorizers(o) }) } ``` **Why lazy?** The `init()` function in `enable/colors` registers CLI flags, but flag values are only available after `flag.Parse()` runs (which happens when the test binary starts). The `sync.Once` defers resolution until the first assertion call, when flags are ready. **Why not panic?** Colors are purely cosmetic. If not enabled, assertions work identically with plain text output. No-op colorizers are used by default. ### Feature Module (Colors) ``` // enable/colors/go.mod module github.com/go-openapi/testify/enable/colors/v2 require golang.org/x/term v0.39.0 ``` The `golang.org/x/term` dependency is used solely for TTY detection (`term.IsTerminal`). --- ## Design Decisions ### Why Three Layers? A simpler two-layer design (feature module calls internal directly) would work, but three layers provide: 1. **Stable public API** -- `enable/stubs/` is the contract users depend on for custom wiring. Internal package paths can change without breaking user code. 2. **Testability** -- stubs can be tested independently of feature modules. 3. **Substitutability** -- users can wire alternative implementations through the stubs API without importing the default feature module. ### Panic vs. Silent Degradation | Feature | Strategy | Reason | |---------|----------|--------| | YAML | Panic | Incorrect results are worse than a crash. If `YAMLEq` silently fails, tests pass when they shouldn't. | | Colors | No-op | Missing colors don't affect correctness. Assertions work fine without ANSI codes. | ### Why Separate Go Modules? Each `enable/` feature is its own Go module (own `go.mod`). This means: - `go mod tidy` in the main module never pulls in `go.yaml.in/yaml/v3` or `golang.org/x/term` - Users who `go get github.com/go-openapi/testify/v2` get zero transitive dependencies - Feature dependencies are resolved only when the feature module is imported --- ## Adding a New Optional Feature Follow these steps to add a new opt-in feature (e.g., a hypothetical JSON schema validator): ### 1. Create internal stubs ```go // internal/assertions/enable/jsonschema/enable.go package jsonschema var validateFunc func(schema, document []byte) error func Validate(schema, document []byte) error { if validateFunc == nil { panic(`JSON Schema validation is not enabled. Import: _ "github.com/go-openapi/testify/enable/jsonschema/v2"`) } return validateFunc(schema, document) } func EnableWithValidate(fn func([]byte, []byte) error) { validateFunc = fn } ``` ### 2. Create public stubs ```go // enable/stubs/jsonschema/enable.go package jsonschema import internal "github.com/go-openapi/testify/v2/internal/assertions/enable/jsonschema" func EnableWithValidate(fn func([]byte, []byte) error) { internal.EnableWithValidate(fn) } ``` ### 3. Create feature module ``` // enable/jsonschema/go.mod module github.com/go-openapi/testify/enable/jsonschema/v2 require github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 ``` ```go // enable/jsonschema/enable.go package jsonschema import ( "github.com/santhosh-tekuri/jsonschema/v6" stub "github.com/go-openapi/testify/v2/enable/stubs/jsonschema" ) func init() { stub.EnableWithValidate(func(schema, doc []byte) error { // wire in the real validator }) } ``` ### 4. Use in assertions ```go // internal/assertions/jsonschema.go package assertions import jsonschema "github.com/go-openapi/testify/v2/internal/assertions/enable/jsonschema" func JSONSchemaValid(t T, schema, document []byte, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } if err := jsonschema.Validate(schema, document); err != nil { return Fail(t, fmt.Sprintf("JSON Schema validation failed: %s", err), msgAndArgs...) } return true } ``` ### 5. Add integration tests Create `internal/testintegration/jsonschema/` with tests that import the feature module and exercise the assertion end-to-end. Update `internal/testintegration/go.mod` to add the new dependency. --- ## Integration Testing Optional features require a separate Go module for end-to-end testing, since the main module cannot import external dependencies. The `internal/testintegration/` module serves this purpose: ``` internal/testintegration/ ├── go.mod # Imports: yaml, colors, rapid ├── yaml/ # Tests YAMLEq with real YAML parser ├── colors/ # Tests colorized output with ANSI detection └── spew/ # Property-based testing (unrelated to opt-in features) ``` See `internal/testintegration/README.md` for details on running these tests. --- ## Module Map {{< mermaid align="center" zoom="true" >}} graph LR main["github.com/go-openapi/testify/v2
main module
zero dependencies"] yaml_mod["enable/yaml/v2
go.yaml.in/yaml/v3"] colors_mod["enable/colors/v2
golang.org/x/term"] testint["internal/testintegration/v2
yaml + colors + rapid"] yaml_mod -- "replace =>" --> main colors_mod -- "replace =>" --> main testint -- "replace =>" --> main style main fill:#4a9eff,color:#fff style yaml_mod fill:#90ee90,color:#000 style colors_mod fill:#90ee90,color:#000 style testint fill:#ffb6c1,color:#000 {{< /mermaid >}} All feature modules use `replace` directives to point to the local main module during development. In production, Go module resolution handles versioning automatically. go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/MAINTAINERS.md000066400000000000000000000163541520301377500263650ustar00rootroot00000000000000--- title: Maintainer's Guide description: General guidelines for maintainers weight: 1 --- {{% notice primary "TL;DR" "meteor" %}} > Comprehensive guide for maintainers covering repository structure, CI/CD workflows, release procedures, and development practices. > Essential reading for anyone contributing to or maintaining this project. {{% /notice %}} ## Repo structure This project is organized as a monorepo with multiple go modules. ## Repo configuration * Default branch: master * Protected branches: master * Branch protection rules: * require pull requests and approval * required status checks: - DCO (simple email sign-off) - Lint - All tests completed * Auto-merge enabled (used for dependabot updates and other auto-merged PR's, e.g. contributors update) ## Continuous Integration ### Code Quality checks * meta-linter: [golangci-lint][golangci-url] * linter config: [`.golangci.yml`][linter-config] (see our [posture][style-doc] on linters) * Code quality assessment: [CodeFactor][codefactor-url] * Code quality badges * [go report card][gocard-url] * [CodeFactor][codefactor-url] > **NOTES** > > codefactor inherits roles from github. There is no need to create a dedicated account. > > The codefactor app is installed at the organization level (`github.com/go-openapi`). > > There is no special token to setup in github for CI usage. ### Testing * Test reports * Uploaded to codecov: * Test coverage reports * Uploaded to codecov: * Fuzz testing * Fuzz tests are handled separately by CI and may reuse a cached version of the fuzzing corpus. At this moment, cache may not be shared between feature branches or feature branch and master. The minimized corpus produced on failure is uploaded as an artifact and should be added manually to `testdata/fuzz/...`. Coverage threshold status is informative and not blocking. This is because the thresholds are difficult to tune and codecov oftentimes reports false negatives or may fail to upload coverage. All tests across `go-openapi` use our fork of `stretchr/testify` (this repo): `github.com/go-openapi/testify`. This allows for minimal test dependencies. > **NOTES** > > codecov inherits roles from github. There is no need to create a dedicated account. > However, there is only 1 maintainer allowed to be the admin of the organization on codecov > with their free plan. > > The codecov app is installed at the organization level (`github.com/go-openapi`). > > There is no special token to setup in github for CI usage. > A organization-level token used to upload coverage and test reports is managed at codecov: > no setup is required on github. ### Automated updates * dependabot * configuration: [`dependabot.yaml`][dependabot-config] Principle: * codecov applies updates and security patches to the github-actions and golang ecosystems. * all updates from "trusted" dependencies (github actions, golang.org packages, go-openapi packages are auto-merged if they successfully pass CI. * go version updates Principle: * we support the 2 latest minor versions of the go compiler (`stable`, `oldstable`) * `go.mod` should be updated (manually) whenever there is a new go minor release (e.g. every 6 months). > This means that our projects always have a 6 months lag to enforce new features from the go compiler. > > However, new features of go may be used with a "go:build" tag: this allows users of the newer > version to benefit the new feature while users still running with `oldstable` use another version > that still builds. * contributors * a [`CONTRIBUTORS.md`][contributors-doc] file is updated weekly, with all-time contributors to the repository * the `github-actions[bot]` posts a pull request to do that automatically * at this moment, this pull request is not auto-approved/auto-merged (bot cannot approve its own PRs) ### Vulnerability scanners There are 3 complementary scanners - obviously, there is some overlap, but each has a different focus. * GitHub `CodeQL` * `trivy` * `govulnscan` None of these tools require an additional account or token. Github CodeQL configuration is set to "Advanced", so we may collect a CI status for this check (e.g. for badges). Scanners run on every commit to master and at least once a week. Reports are centralized in github security reports for code scanning tools. ## Releases **For single module repos:** A bump release workflow can be triggered from the github actions UI to cut a release with a few clicks. The release process is minimalist: * push a semver tag (i.e v{major}.{minor}.{patch}) to the master branch. * the CI handles this to generate a github release with release notes * release notes generator: git-cliff * configuration: the `.cliff.toml` is defined as a share configuration on remote repo [ci-worflows/.cliff.toml][remote-cliff-config] Commits from maintainers are preferably PGP-signed. Tags are preferably PGP-signed. > We want our released to show as "verified" on github. The tag message introduces the release notes (e.g. a summary of this release). The release notes generator does not assume that commits are necessarily "conventional commits". **For mono-repos with multiple modules:** The release process is slightly different because we need to update cross-module dependencies before pushing a tag. A bump release workflow (mono-repo) can be triggered from the github actions UI to cut a release with a few clicks. It works with the same input as the one for single module repos, and first creates a PR (auto-merged) that updates the different go.mod files _before_ pushing the desired git tag. Commits and tags pushed by the workflow bot are PGP-signed ("go-openapi[bot]"). ## Other files Standard documentation: * [CONTRIBUTING.md][contributing-doc] guidelines * [DCO.md][dco-doc] terms for first-time contributors to read * [CODE_OF_CONDUCT.md][coc-doc] * [SECURITY.md][security-doc] policy: how to report vulnerabilities privately * [LICENSE][license-doc] terms * [NOTICE][notice-doc] on supplementary license terms (original authors, copied code etc) Reference documentation (released): * [pkg.go.dev (fka godoc)][godoc-url] [linter-config]: https://github.com/go-openapi/testify/blob/master/.golangci.yml [local-cliff-config]: https://github.com/go-openapi/testify/blob/master/.cliff.toml [remote-cliff-config]: https://github.com/go-openapi/ci-workflows/blob/master/.cliff.toml [dependabot-config]: https://github.com/go-openapi/testify/blob/master/.github/dependabot.yaml [gocard-url]: https://goreportcard.com/report/github.com/go-openapi/testify [codefactor-url]: https://www.codefactor.io/repository/github/go-openapi/testify [golangci-url]: https://golangci-lint.run/ [godoc-url]: https://pkg.go.dev/github.com/go-openapi/testify/v2 [contributors-doc]: ../contributing/CONTRIBUTORS.md [contributing-doc]: ../contributing/CONTRIBUTING.md [dco-doc]: ../contributing/DCO.md [style-doc]: ../contributing/STYLE.md [coc-doc]: ../contributing/CODE_OF_CONDUCT.md [security-doc]: ../SECURITY.md [license-doc]: ../LICENSE.md [notice-doc]: ../NOTICE.md go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/ORIGINAL.md000066400000000000000000000277431520301377500260230ustar00rootroot00000000000000--- title: Original README description: | Original README from github.com/stretchr/testify. Always important to remember where we come from. weight: 10 --- Testify - Thou Shalt Write Tests ================================ > [!NOTE] > Testify is being maintained at v1, no breaking changes will be accepted in this repo. > [See discussion about v2](https://github.com/stretchr/testify/discussions/1560). [![Build Status](https://github.com/stretchr/testify/actions/workflows/main.yml/badge.svg?branch=master)](https://github.com/stretchr/testify/actions/workflows/main.yml) [![Go Report Card](https://goreportcard.com/badge/github.com/stretchr/testify)](https://goreportcard.com/report/github.com/stretchr/testify) [![PkgGoDev](https://pkg.go.dev/badge/github.com/stretchr/testify)](https://pkg.go.dev/github.com/stretchr/testify) Go code (golang) set of packages that provide many tools for testifying that your code will behave as you intend. Features include: * [Easy assertions](#assert-package) * [Mocking](#mock-package) * [Testing suite interfaces and functions](#suite-package) Get started: * Install testify with [one line of code](#installation), or [update it with another](#staying-up-to-date) * For an introduction to writing test code in Go, see https://go.dev/doc/code#Testing * Check out the API Documentation https://pkg.go.dev/github.com/stretchr/testify * Use [testifylint](https://github.com/Antonboom/testifylint) (via [golangci-lint](https://golangci-lint.run/)) to avoid common mistakes * A little about [Test-Driven Development (TDD)](https://en.wikipedia.org/wiki/Test-driven_development) [`assert`](https://pkg.go.dev/github.com/stretchr/testify/assert "API documentation") package ------------------------------------------------------------------------------------------- The `assert` package provides some helpful methods that allow you to write better test code in Go. * Prints friendly, easy to read failure descriptions * Allows for very readable code * Optionally annotate each assertion with a message See it in action: ```go package yours import ( "testing" "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { // assert equality assert.Equal(t, 123, 123, "they should be equal") // assert inequality assert.NotEqual(t, 123, 456, "they should not be equal") // assert for nil (good for errors) assert.Nil(t, object) // assert for not nil (good when you expect something) if assert.NotNil(t, object) { // now we know that object isn't nil, we are safe to make // further assertions without causing any errors assert.Equal(t, "Something", object.Value) } } ``` * Every assert func takes the `testing.T` object as the first argument. This is how it writes the errors out through the normal `go test` capabilities. * Every assert func returns a bool indicating whether the assertion was successful or not, this is useful for if you want to go on making further assertions under certain conditions. if you assert many times, use the below: ```go package yours import ( "testing" "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { assert := assert.New(t) // assert equality assert.Equal(123, 123, "they should be equal") // assert inequality assert.NotEqual(123, 456, "they should not be equal") // assert for nil (good for errors) assert.Nil(object) // assert for not nil (good when you expect something) if assert.NotNil(object) { // now we know that object isn't nil, we are safe to make // further assertions without causing any errors assert.Equal("Something", object.Value) } } ``` [`require`](https://pkg.go.dev/github.com/stretchr/testify/require "API documentation") package --------------------------------------------------------------------------------------------- The `require` package provides same global functions as the `assert` package, but instead of returning a boolean result they terminate current test. These functions must be called from the goroutine running the test or benchmark function, not from other goroutines created during the test. Otherwise race conditions may occur. See [t.FailNow](https://pkg.go.dev/testing#T.FailNow) for details. [`mock`](https://pkg.go.dev/github.com/stretchr/testify/mock "API documentation") package ---------------------------------------------------------------------------------------- The `mock` package provides a mechanism for easily writing mock objects that can be used in place of real objects when writing test code. An example test function that tests a piece of code that relies on an external object `testObj`, can set up expectations (testify) and assert that they indeed happened: ```go package yours import ( "testing" "github.com/stretchr/testify/mock" ) /* Test objects */ // MyMockedObject is a mocked object that implements an interface // that describes an object that the code I am testing relies on. type MyMockedObject struct { mock.Mock } // DoSomething is a method on MyMockedObject that implements some interface // and just records the activity, and returns what the Mock object tells it to. // // In the real object, this method would do something useful, but since this // is a mocked object - we're just going to stub it out. // // NOTE: This method is not being tested here, code that uses this object is. func (m *MyMockedObject) DoSomething(number int) (bool, error) { args := m.Called(number) return args.Bool(0), args.Error(1) } /* Actual test functions */ // TestSomething is an example of how to use our test object to // make assertions about some target code we are testing. func TestSomething(t *testing.T) { // create an instance of our test object testObj := new(MyMockedObject) // set up expectations testObj.On("DoSomething", 123).Return(true, nil) // call the code we are testing targetFuncThatDoesSomethingWithObj(testObj) // assert that the expectations were met testObj.AssertExpectations(t) } // TestSomethingWithPlaceholder is a second example of how to use our test object to // make assertions about some target code we are testing. // This time using a placeholder. Placeholders might be used when the // data being passed in is normally dynamically generated and cannot be // predicted beforehand (eg. containing hashes that are time sensitive) func TestSomethingWithPlaceholder(t *testing.T) { // create an instance of our test object testObj := new(MyMockedObject) // set up expectations with a placeholder in the argument list testObj.On("DoSomething", mock.Anything).Return(true, nil) // call the code we are testing targetFuncThatDoesSomethingWithObj(testObj) // assert that the expectations were met testObj.AssertExpectations(t) } // TestSomethingElse2 is a third example that shows how you can use // the Unset method to cleanup handlers and then add new ones. func TestSomethingElse2(t *testing.T) { // create an instance of our test object testObj := new(MyMockedObject) // set up expectations with a placeholder in the argument list mockCall := testObj.On("DoSomething", mock.Anything).Return(true, nil) // call the code we are testing targetFuncThatDoesSomethingWithObj(testObj) // assert that the expectations were met testObj.AssertExpectations(t) // remove the handler now so we can add another one that takes precedence mockCall.Unset() // return false now instead of true testObj.On("DoSomething", mock.Anything).Return(false, nil) testObj.AssertExpectations(t) } ``` For more information on how to write mock code, check out the [API documentation for the `mock` package](https://pkg.go.dev/github.com/stretchr/testify/mock). You can use the [mockery tool](https://vektra.github.io/mockery/latest/) to autogenerate the mock code against an interface as well, making using mocks much quicker. [`suite`](https://pkg.go.dev/github.com/stretchr/testify/suite "API documentation") package ----------------------------------------------------------------------------------------- > [!WARNING] > The suite package does not support parallel tests. See [#934](https://github.com/stretchr/testify/issues/934). The `suite` package provides functionality that you might be used to from more common object-oriented languages. With it, you can build a testing suite as a struct, build setup/teardown methods and testing methods on your struct, and run them with 'go test' as per normal. An example suite is shown below: ```go // Basic imports import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/suite" ) // Define the suite, and absorb the built-in basic suite // functionality from testify - including a T() method which // returns the current testing context type ExampleTestSuite struct { suite.Suite VariableThatShouldStartAtFive int } // Make sure that VariableThatShouldStartAtFive is set to five // before each test func (suite *ExampleTestSuite) SetupTest() { suite.VariableThatShouldStartAtFive = 5 } // All methods that begin with "Test" are run as tests within a // suite. func (suite *ExampleTestSuite) TestExample() { assert.Equal(suite.T(), 5, suite.VariableThatShouldStartAtFive) } // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run func TestExampleTestSuite(t *testing.T) { suite.Run(t, new(ExampleTestSuite)) } ``` For a more complete example, using all of the functionality provided by the suite package, look at our [example testing suite](https://github.com/stretchr/testify/blob/master/suite/suite_test.go) For more information on writing suites, check out the [API documentation for the `suite` package](https://pkg.go.dev/github.com/stretchr/testify/suite). `Suite` object has assertion methods: ```go // Basic imports import ( "testing" "github.com/stretchr/testify/suite" ) // Define the suite, and absorb the built-in basic suite // functionality from testify - including assertion methods. type ExampleTestSuite struct { suite.Suite VariableThatShouldStartAtFive int } // Make sure that VariableThatShouldStartAtFive is set to five // before each test func (suite *ExampleTestSuite) SetupTest() { suite.VariableThatShouldStartAtFive = 5 } // All methods that begin with "Test" are run as tests within a // suite. func (suite *ExampleTestSuite) TestExample() { suite.Equal(suite.VariableThatShouldStartAtFive, 5) } // In order for 'go test' to run this suite, we need to create // a normal test function and pass our suite to suite.Run func TestExampleTestSuite(t *testing.T) { suite.Run(t, new(ExampleTestSuite)) } ``` ------ Installation ============ To install Testify, use `go get`: go get github.com/stretchr/testify This will then make the following packages available to you: github.com/stretchr/testify/assert github.com/stretchr/testify/require github.com/stretchr/testify/mock github.com/stretchr/testify/suite github.com/stretchr/testify/http (deprecated) Import the `testify/assert` package into your code using this template: ```go package yours import ( "testing" "github.com/stretchr/testify/assert" ) func TestSomething(t *testing.T) { assert.True(t, true, "True is true!") } ``` ------ Staying up to date ================== To update Testify to the latest version, use `go get -u github.com/stretchr/testify`. ------ Supported go versions ================== We currently support the most recent major Go versions from 1.19 onward. ------ Contributing ============ Please feel free to submit issues, fork the repository and send pull requests! When submitting an issue, we ask that you please include a complete test function that demonstrates the issue. Extra credit for those using Testify to write the test code that demonstrates it. Code generation is used. [Look for `Code generated with`](https://github.com/search?q=repo%3Astretchr%2Ftestify%20%22Code%20generated%20with%22&type=code) at the top of some files. Run `go generate ./...` to update generated files. We also chat on the [Gophers Slack](https://gophers.slack.com) group in the `#testify` and `#testify-dev` channels. ------ License ======= This project is licensed under the terms of the MIT license. go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/ROADMAP.md000066400000000000000000000107161520301377500256720ustar00rootroot00000000000000--- title: "Roadmap" description: "Let's share our plans." weight: 5 --- ## What's next with this project? {{< mermaid align="center" zoom="true" >}} timeline title Planned releases section Q4 2025 ✅ v2.0 (Nov 2025) : Zero dependencies : optional dependencies (YAML) : modernized code (relint) : JSONEqBytes section Q1 2026 ✅ v2.1 (Jan 2026) : Generated assertions : complete refactoring : documentation site : panic handling fixes : removed deprecated : go1.24+ ✅ v2.2 (Fev 2026) : Generics : Kind/NotKind : SortedT, NotSortedT : complete test refactoring : more benchmarks. Perf improvements : optional dependencies (colorized) ✅ v2.3 (Fev 2026) : Other extensions : Extensible Assertion type : JSON & YAML assertions: JSONMarshalsAs... : NoGoRoutineLeak : more documentation and examples ✅ v2.4 (Mar 2026) : Stabilize API (no more removals) : NoFileDescriptorLeak (Linux) : Eventually, Eventually (with context), Consistently : Migration tool section Q2 2026 ✅ v2.5 (May 2026) : synctest opt-in for Eventually, Never, Consistently, EventuallyWith : NoFileDescriptorLeak (macOS) : InEpsilonSymmetric, Blocked/NotBlocked : redactor functons for JSON & YAML assertions : export internal tools (spew, difflib) : go1.25+ 🔍 v2.6 (June 2026) : (tentative) : go build guards (codegen) : ErrorAsType (go1.26+) {{< /mermaid >}} ## Dropped enveavors For the moment, and after some research, we punt on the following features. We might reconsider these choices in the future, but for now, we are unsure about whether they are worth the added complexity. * Enrich `CollectT` (either as an interface or an extended type that wraps `testing.TB`) - for `EventuallyWith` (see also [#1862](https://github.com/stretchr/testify/issues/1862)). * Expose the internal go routine leak detection package as a drop-in replacement for `go.uber.org/go-leak` * Port `NoFileDescriptorLeak` to Windows OS * Consider hijacking [msgAndArgs ...any] to pass options into assertions ## Notes 1. [x] The first release comes with zero dependencies and an unstable API (see below [our use case](#usage-at-go-openapi)) 2. [x] This project is going to be injected as the main and sole test dependency of the `go-openapi` libraries 3. [x] Since we have leveled the go requirements to the rest of the go-openapi (currently go1.24) there is quite a bit of relinting lying ahead. 4. [x] Valuable pending pull requests from the original project could be merged (e.g. `JSONEqBytes`) or transformed as "enable" modules (e.g. colorized output) 5. [x] More testing and bug fixes (from upstream or detected during our testing) 6. [x] Introduces colorization (opt-in) 7. [x] Introduces generics 8. [x] Realign behavior re quirks, bugs, unexpected logics ... (e.g. `IsNonDecreasing`, `EventuallyWithT`...) 10. [x] Unclear assertions might be provided an alternative verb (e.g. `EventuallyWithT`) ### Adoption timeline at go-openapi 1. [x] Jan 2026: all go-openapi projects adopts the forked testify 2. [x] Feb 2026: all go-openapi projects transition to generics 3. [x] Mar 2026: go-swagger transitions to the forked testify ### What won't come anytime soon * mocks: we use [mockery](https://github.com/vektra/mockery) and prefer the simpler `matryer` mocking-style. testify-style mocks are thus not going to be supported anytime soon. * extra convoluted stuff in the like of `InDeltaSlice` (more likely to be removed) ## Upstream Tracking We actively monitor [github.com/stretchr/testify](https://github.com/stretchr/testify) for updates, new issues, and proposals. **Review frequency**: Quarterly (next review: April 2026) **Processed items**: 31 upstream PRs and issues have been reviewed, with 23 implemented/merged, 4 superseded by our implementation, 2 informational, and 2 currently under consideration. For a complete catalog of all upstream PRs and issues we've processed (implemented, adapted, superseded, or monitoring), see the [Upstream Tracking](../../usage/TRACKING.md). go-openapi-testify-c10ca71/docs/doc-site/project/maintainers/_index.md000066400000000000000000000002171520301377500260500ustar00rootroot00000000000000--- title: "Maintainer's Guidelines" description: "How to maintain this beast?" weight: 4 --- {{< children type="card" description="true" >}} go-openapi-testify-c10ca71/docs/doc-site/usage/000077500000000000000000000000001520301377500214045ustar00rootroot00000000000000go-openapi-testify-c10ca71/docs/doc-site/usage/CHANGES.md000066400000000000000000000635011520301377500230030ustar00rootroot00000000000000--- title: "Changes from v1" description: "All changes from testify/v1" weight: 15 --- ## Summary **Key Changes:** - ✅ **Zero Dependencies**: Completely self-contained - ✅ **New functions**: 60 additional assertions (44 generic + 16 reflection-based) - ✅ **Performance**: ~10x for generic variants (from 1.2x to 81x, your mileage may vary) - ✅ **Breaking changes**: Requires go1.25, removed suites, mocks, http tooling, and deprecated functions. YAMLEq becomes optional (panics by default). --- **Testify v2 represents a comprehensive modernization** - ✅ **Type Safety**: generic assertions catch errors at compile time - ✅ **Documentation**: compelling documentation site to search the API by use-case domain - ✅ **Maintainability**: 100% code generation from single source - ✅ **Quality**: 96% test coverage, use unified test scenarios, extensive fuzzing & benchmarking This fork maintains compatibility where possible while making bold improvements in architecture, safety, and performance. The original philosophy of `testify` is preserved, and the new API is 90% compatible. **Fork Information:** - **Upstream repository**: [github.com/stretchr/testify](https://github.com/stretchr/testify) - **Fork date**: 2025-01-09 - **Fork commit**: `feb1324bc3d000fed7b21dfe20bec72ecca27502` See also a quick [migration guide](./MIGRATION.md). ## Cross-Domain Changes {{% tabs %}} {{% tab title="Additions" color=green %}} ### Major Additions #### Usage | Change | Origin | Description | |--------|--------|-------------| | **Generic assertions** | Multiple upstream proposals | Added {{% siteparam "metrics.generics" %}} type-safe assertion functions with `T` suffix across {{% siteparam "metrics.domains" %}} domains | | **Zero dependencies** | Design goal | Internalized go-spew and difflib; removed all external dependencies | | **Optional YAML support** | Design goal | YAML assertions are now enabled via opt-in `enable/yaml` module | | **Colorized output** | [#1467], [#1480], [#1232], [#994] | Optional colorization via `enable/color` module with themes | | **Enhanced diff output** | [#1829] | Improved time.Time rendering, deterministic map ordering | [#1829]: https://github.com/stretchr/testify/issues/1829 #### Maintainability | Change | Origin | Description | |--------|--------|-------------| | **Code generation** | Design goal | 100% generated assert/require packages from {{% siteparam "metrics.functions" %}} core functions (see [API metrics](../api/metrics.md)) | | **Code modernization** | Design goal | Relinted, refactored and modernized the code base, including internalized difflib and go-spew| | **Refactored tests** | Design goal | Full refactoring of tests on assertion functions, with unified test scenarios for reflection-based/generic assertions | [#1232]: https://github.com/stretchr/testify/pull/1232 [#1467]: https://github.com/stretchr/testify/pull/1467 [#1480]: https://github.com/stretchr/testify/pull/1480 [#1829]: https://github.com/stretchr/testify/issues/1829 [#994]: https://github.com/stretchr/testify/pull/994 {{% /tab %}} {{% tab title="Removals" style=warning %}} ### Major Removals (Breaking Changes) | Removed | Reason | |---------|--------| | **Suite package** | Complex interactions with dependencies; might re-introduce this feature later | | **Mock package** | Use specialized [mockery](https://github.com/vektra/mockery) tool instead | | **HTTP package** | Simplified focus; may be reintroduced later | | **Deprecated functions** | Clean slate for v2 | | **Renaming** | `NoDirExists` renamed into `DirNotExists`. `NoFileExists` renamed into `FileNotExists`| ### Infrastructure Improvements | Change | Description | |--------|-------------| | **Internalized dependencies** | go-spew and difflib internalized with modernized code | | **Module structure** | Clean separation: core (zero deps), enable modules (optional) | | **Documentation site** | Hugo-based site with domain-organized API reference | | **Fuzz testing** | Fuzz test on spew.Sdump based on random data structures generation | | **Comprehensive benchmarks** | 37 benchmarks comparing generic vs reflection performance | | **Advanced CI** | Reuse go-openapi workflows with tests and coverage reporting, fuzz testing, release automation | {{% /tab %}} {{% /tabs %}} ## Bug Fixes and Safety Improvements {{% tabs %}} {{% tab title="Bug fixes" color=green %}} ### Critical Fixes reported upstream | Issue/PR | Domain | Description | |----------|--------|-------------| | [#1223] | Display | Display uint values in decimal instead of hex | | [#1611] | Condition | Fixed goroutine leak in Eventually/Never | | [#1813] | Internal (spew) | Fixed panic with unexported fields (via #1828) | | [#1818] | String | Fixed panic on invalid regex in Regexp/NotRegexp | | [#1822] | Internal (spew) | Deterministic map ordering in diffs | | [#1825] | Equality | Fixed panic when using EqualValues with incomparable types | | [#1828] | Internal (spew) | Fixed panic with unexported fields in maps | [#1223]: https://github.com/stretchr/testify/pull/1223 [#1611]: https://github.com/stretchr/testify/issues/1611 [#1813]: https://github.com/stretchr/testify/issues/1813 [#1818]: https://github.com/stretchr/testify/pull/1818 [#1822]: https://github.com/stretchr/testify/issues/1822 [#1825]: https://github.com/stretchr/testify/pull/1825 [#1828]: https://github.com/stretchr/testify/pull/1828 {{% /tab %}} {{% tab title="Safety Improvements" color=blue %}} ### Comprehensive Spew Testing - Added property-based fuzzing for go-spew with random type generator - Fixed circular reference edge cases (pointer wrapped in interface, circular map reference) - Supersedes upstream [#1824] [#1824]: https://github.com/stretchr/testify/pull/1824 ### Reflection Safety - More defensive guards re-reflect panic risk in `EqualExportedValues` - Fixed 50 unchecked type assertions across test codebase - Zero linting issues with `forcetypeassert` linter {{% /tab %}} {{% /tabs %}} ## Changes by Domain ### Boolean {{% expand title="Generics" %}} #### New Generic Functions (2) | Function | Type | Origin | Description | |----------|------|--------|-------------| | `TrueT[B ~bool]` | Generic | Generics initiative | Type-safe boolean true assertion | | `FalseT[B ~bool]` | Generic | Generics initiative | Type-safe boolean false assertion | {{% /expand %}} **Behavior changes**: None ### Collection {{% expand title="Generics" %}} #### New Generic Functions (12) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `StringContainsT[S Text]` | String or []byte | Type-safe string/bytes contains check | | `StringNotContainsT[S Text]` | String or []byte | Type-safe string/bytes not-contains check | | `SliceContainsT[E comparable]` | Comparable element | Type-safe slice membership check | | `SliceNotContainsT[E comparable]` | Comparable element | Type-safe slice non-membership check | | `MapContainsT[K comparable, V any]` | Key type | Type-safe map key check | | `MapNotContainsT[K comparable, V any]` | Key type | Type-safe map key absence check | | `SeqContainsT[E comparable]` | Iterator element | Type-safe iterator membership check (Go 1.23+) | | `SeqNotContainsT[E comparable]` | Iterator element | Type-safe iterator non-membership check (Go 1.23+) | | `ElementsMatchT[E comparable]` | Slice element | Type-safe slice equality (any order) | | `NotElementsMatchT[E comparable]` | Slice element | Type-safe slice inequality | | `SliceSubsetT[E comparable]` | Slice element | Type-safe subset relationship check | | `SliceNotSubsetT[E comparable]` | Slice element | Type-safe non-subset check | **Origin**: Generic initiative + [#1685] (partial - SeqContains variants only) **Performance**: 16-81x faster than reflection-based variants (see [benchmarks](../project/maintainers/BENCHMARKS.md)) [#1685]: https://github.com/stretchr/testify/pull/1685 {{% /expand %}} **Behavior changes**: None ### Comparison {{% expand title="Generics" %}} #### New Generic Functions (6) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `GreaterT[V Ordered]` | extended Ordered type[^1] | Type-safe greater-than comparison | | `GreaterOrEqualT[V Ordered]` | Ordered type | Type-safe >= comparison | | `LessT[V Ordered]` | Ordered type | Type-safe less-than comparison | | `LessOrEqualT[V Ordered]` | Ordered type | Type-safe <= comparison | | `PositiveT[V Ordered]` | Ordered type | Type-safe positive value check (> 0) | | `NegativeT[V Ordered]` | Ordered type | Type-safe negative value check (< 0) | [^1]: Ordered is defined as the union of standard go ordered types, plus `[]byte` and `time.Time`. **Origin**: Generics initiative **Performance**: 10-22x faster than reflection-based variants. See [Benchmarks](../project/maintainers/BENCHMARKS.md) {{% /expand %}} **Behavior changes**: None ### Condition #### New Functions (2) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `Consistently[C Conditioner]` | `func() bool` or `func(context.Context) error` | async assertion to express "always true" (adapted proposal [#1606], [#1087]) | | `(*CollectT).Cancel` | — | explicit escape hatch to abort an `EventuallyWith` assertion immediately (adapted proposal [#1830]) | #### New Types — synctest opt-in (4) | Type | Underlying | Purpose | |------|------------|---------| | `WithSynctest` | `func() bool` | Opt-in wrapper for `Eventually`, `Never`, `Consistently` — runs the polling loop inside a [`testing/synctest`](https://pkg.go.dev/testing/synctest) bubble with a fake clock | | `WithSynctestContext` | `func(context.Context) error` | Same as above, for the context/error condition form (`Eventually`, `Consistently`) | | `WithSynctestCollect` | `func(*CollectT)` | Same as above, for `EventuallyWith` | | `WithSynctestCollectContext` | `func(context.Context, *CollectT)` | Same as above, for the `EventuallyWith` context variant | **Benefits**: deterministic tick counts, zero real wall-clock time for long timeouts, elimination of timing-induced flakiness in test suites. **Constraints**: requires Go 1.25+; caller must pass a concrete `*testing.T`; condition must not perform real I/O and must not be started from an external goroutine driving state change via real time. See the [`WithSynctest` godoc](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#WithSynctest) and the [Async Testing section of EXAMPLES.md](./EXAMPLES.md#deterministic-polling-with-synctest-opt-in). [#1087]: https://github.com/stretchr/testify/issues/1087 [#1606]: https://github.com/stretchr/testify/pulls/1606 #### ⚠️ Behavior Changes | Change | Origin | Description | |--------|--------|-------------| | Fixed goroutine leak | [#1611] | Consolidated `Eventually`, `Never`, and `EventuallyWith` into single `pollCondition` function | | Context-based polling | Internal refactoring | Reimplemented with context-based approach for better resource management | | Unified implementation | Internal refactoring | Single implementation eliminates code duplication and prevents resource leaks | | `func(context.Context) error` conditions | extensions to the async domain | control over context allows for more complex cases to be supported | | Type parameter | Internal refactoring | `Eventually` now accepts several signatures for its condition and uses a type parameter (non-breaking) | | `Never[C NeverConditioner]` now generic | synctest opt-in | `Never` gained a narrow type constraint (`func() bool | WithSynctest`) to accept the synctest wrapper. Existing call sites are unaffected; only code that stores `Never` in a function value of type `func(T, func() bool, ...)` needs to specify `Never[func() bool]` (non-breaking for typical use) | | `CollectT.FailNow` now per-tick | aligns with [stretchr/testify] semantics and [#1819] | `FailNow` aborts the current tick only; the poller retries on the next tick. This makes `require`-style assertions inside `EventuallyWith` behave naturally (was: cancel the whole assertion immediately) | | New `CollectT.Cancel` | implements [#1830] | Explicit escape hatch to abort the whole `EventuallyWith` assertion immediately, cancelling the polling context before exiting via `runtime.Goexit` | | Per-tick goroutine wrap | implements [#1819] | The condition function is evaluated in its own goroutine so that `runtime.Goexit` (including transitively via `require`) only aborts the current tick and not the surrounding poll loop | | Channels now bubble-owned | synctest opt-in | `conditionChan` and `doneChan` moved from `newConditionPoller` into `pollCondition` so that they are created inside a synctest bubble when the caller opts in. This is a correctness fix for synctest activation AND a general lifecycle cleanup (per-call, not per-struct). | **Impact**: This fix eliminates goroutine leaks that could occur when using `Eventually` or `Never` assertions. The new implementation uses a context-based approach that properly manages resources and provides a cleaner shutdown mechanism. Callers should **NOT** assume that the call to `Eventually` or `Never` exits before the condition is evaluated. **⚠️ Breaking behavior change for `EventuallyWith`**: Existing code that relied on `collect.FailNow()` aborting the whole assertion must switch to `collect.Cancel()`. Code that used `require.*(collect, …)` inside `EventuallyWith` expecting retry semantics now works as intended (previously it short-circuited on the first failure). See [MIGRATION](./MIGRATION.md#collectt-failnow-vs-cancel). **Supersedes**: This implementation honors upstream proposals [#1819] (handle unexpected exits, via the per-tick goroutine wrap) and [#1830] (`CollectT.Halt`, exposed as `CollectT.Cancel`). [stretchr/testify]: https://github.com/stretchr/testify [#1611]: https://github.com/stretchr/testify/issues/1611 [#1819]: https://github.com/stretchr/testify/pull/1819 [#1830]: https://github.com/stretchr/testify/pull/1830 ### Breaking Changes | Change | Origin | Description | |--------|--------|-------------| | **Renaming** | Internal refactoring | `EventuallyWithT` renamed into `EventuallyWith` (conflicted with the convention adopted for generics) | | **Removal** | API simplification | `Comparison` type is removed as a mere alias to `func() bool` | ### Equality {{% expand title="Generics" %}} #### New Generic Functions (4) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `EqualT[V comparable]` | Comparable type | Type-safe equality check | | `NotEqualT[V comparable]` | Comparable type | Type-safe inequality check | | `SameT[V comparable]` | Comparable type | Type-safe pointer identity check | | `NotSameT[V comparable]` | Comparable type | Type-safe pointer difference check | **Origin**: Generics initiative **Performance**: 10-13x faster for Equal/NotEqual, 1.5-2x for Same/NotSame {{% /expand %}} #### ⚠️ Behavior Changes | Function | Change | Reason | |----------|--------|--------| | `EqualValues` | Now fails with function types (like `Equal`) | [#1825] - Consistency and safety | | `Same`/`NotSame` | Two nil pointers of same type now correctly considered "same" | Edge case fix | [#1825]: https://github.com/stretchr/testify/pull/1825 ### Error **New functions**: None **Behavior changes**: None ### File | Function | Type | Origin | Description | |----------|------|--------|-------------| | `FileEmpty` | Reflection | New addition | Assert file exists and is empty (0 bytes) | | `FileNotEmpty` | Reflection | New addition | Assert file exists and is not empty | **Note**: `DirExists` was already present in upstream, `NoDirExists` renamed into `DirNotExists`. `NoFileExists` renamed into `FileNotExists`.² **Behavior changes**: None ### HTTP **New functions**: None **Behavior changes**: None ### JSON {{% expand title="Generics" %}} #### New Generic Functions (3) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `JSONEqT[S Text]` | String or []byte | Type-safe JSON semantic equality | | `JSONMarshalAsT[EDoc Text]` | String or []byte | Type-safe JSON marshal and equality check | | `JSONUnmarshalAsT[ADoc Text, Object any]` | String or []byte | Type-safe JSON unmarshal and equality check | **Performance**: Comparable (JSON parsing dominates) {{% /expand %}} {{% expand title="Reflection-based" %}} #### New Reflection Function (1) | Function | Origin | Description | |----------|--------|-------------| | `JSONEqBytes` | [#1513] | JSON equality for byte slices | [#1513]: https://github.com/stretchr/testify/pull/1513 {{% /expand %}} **Behavior changes**: None ### Number {{% expand title="Generics" %}} #### New Generic Functions (2) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `InDeltaT[V Float\|Integer]` | Numeric type | Type-safe float comparison with absolute delta | | `InEpsilonT[V Float]` | Float type | Type-safe float comparison with relative epsilon | **Origin**: Generics initiative **Performance**: 1.2-1.5x faster {{% /expand %}} #### ⚠️ Behavior Changes - Fixed IEEE 754 edge case handling (NaN, Inf) - Added support for zero expected value in `InEpsilon` (falls back to absolute error) - Fixed invalid type conversion for `uintptr` in reflect-based compare ### Ordering {{% expand title="Generics" %}} #### New Generic Functions (6) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `IsIncreasingT[E Ordered]` | Ordered[^1] slice element | Type-safe strictly increasing check | | `IsDecreasingT[E Ordered]` | Ordered slice element | Type-safe strictly decreasing check | | `IsNonIncreasingT[E Ordered]` | Ordered slice element | Type-safe non-increasing check (allows equal) | | `IsNonDecreasingT[E Ordered]` | Ordered slice element | Type-safe non-decreasing check (allows equal) | | `SortedT[E cmp.Ordered]` | Ordered slice element | Type-safe sorted check (generic-only function) | | `NotSortedT[E cmp.Ordered]` | Ordered slice element | Type-safe unsorted check (generic-only function) | **Origin**: Generics initiative **Performance**: 6.5-9.5x faster **Note**: `SortedT` and `NotSortedT` are generic-only (no reflection equivalents) {{% /expand %}} #### ⚠️ Behavior Changes | Function | Change | Reason | |----------|--------|--------| | `IsNonDecreasing` | Logic corrected to match documentation | Inverted logic fixed | | `IsNonIncreasing` | Logic corrected to match documentation | Inverted logic fixed | ### Panic **New functions**: None #### ⚠️ Behavior Changes Removed extraneous type declaration `PanicTestFunc` (`func()`). ### Safety **New domain** for resource leak detection. | Function | Type | Description | |----------|------|-------------| | `NoGoRoutineLeak` | Reflection | Assert that no goroutines leak from a tested function | | `NoFileDescriptorLeak` | Reflection | Assert that no file descriptors leak from a tested function (Linux, macOS) | #### Implementation `NoGoRoutineLeak` uses **pprof labels** instead of stack-trace heuristics (like `go.uber.org/goleak`): - Only goroutines spawned by the tested function are checked - Pre-existing goroutines (runtime, pools, parallel tests) are ignored automatically - No configuration or filter lists needed - Works safely with `t.Parallel()` `NoFileDescriptorLeak` compares open file descriptors before and after the tested function. Linux uses `/proc/self/fd`; macOS probes the process FD table with `fstat` and resolves vnode paths via `fcntl(F_GETPATH)`. On other platforms the assertion skips cleanly. See [Examples](./EXAMPLES.md#goroutine-leak-detection) for usage patterns. ### String {{% expand title="Generics" %}} #### New Generic Functions (2) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `RegexpT[S Text]` | String or []byte | Type-safe regex match check | | `NotRegexpT[S Text]` | String or []byte | Type-safe regex non-match check | **Origin**: Generics initiative **Performance**: 1.2x faster (regex compilation dominates) {{% /expand %}} #### ⚠️ Behavior Changes | Change | Origin | Description | |--------|--------|-------------| | Fix panic on invalid regex | [#1818] | Handle invalid regex patterns gracefully | | Refactored regex handling | Internal | Fixed quirks with unexpected behavior on some input types | [#1818]: https://github.com/stretchr/testify/pull/1818 ### Testing **New functions**: None **Behavior changes**: None ### Time **New functions**: None #### ⚠️ Behavior Changes | Change | Origin | Description | |--------|--------|-------------| | Fix time.Time rendering in diffs | [#1829] | Improved time display in failure messages | [1829]: https://github.com/stretchr/testify/issues/1829 ### Type {{% expand title="Generics" %}} #### New Generic Functions (2) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `IsOfTypeT[EType any]` | Expected type | Type assertion without dummy value | | `IsNotOfTypeT[EType any]` | Expected type | Negative type assertion without dummy value | **Origin**: [#1805] **Performance**: 9-11x faster [#1805]: https://github.com/stretchr/testify/issues/1805 {{% /expand %}} {{% expand title="Reflection-based" %}} #### New Reflection Functions (2) | Function | Origin | Description | |----------|--------|-------------| | `Kind` | [#1803] | Assert value is of specific reflect.Kind | | `NotKind` | [#1803] | Assert value is not of specific reflect.Kind | [#1803]: https://github.com/stretchr/testify/pull/1803 {{% /expand %}} **Behavior changes**: None ### YAML {{% expand title="Generics" %}} #### New Generic Functions (3) | Function | Type Parameters | Description | |----------|-----------------|-------------| | `YAMLEqT[S Text]` | String or []byte | Type-safe YAML semantic equality | | `YAMLMarshalAsT[EDoc Text]` | String or []byte | Type-safe YAML marshal and equality check | | `YAMLUnmarshalAsT[ADoc Text, Object any]` | String or []byte | Type-safe YAML unmarshal and equality check | **Performance**: Comparable (YAML parsing dominates) {{% /expand %}} {{% expand title="Reflection-based" %}} #### New Reflection Function (1) | Function | Origin | Description | |----------|--------|-------------| | `YAMLEqBytes` | Consistency | YAML equality for byte slices (matches JSONEqBytes) | {{% /expand %}} #### ⚠️ Behavior Changes **Architecture change**: YAML support is now opt-in via `import _ "github.com/go-openapi/testify/v2/enable/yaml"` **Behavior changes**: None ## Other changes ### Performance Improvements See [Performance Benchmarks](../project/maintainers/BENCHMARKS.md) for a detailed presentation. #### Generic vs Reflection Performance | Domain | Function | Speedup | Key Benefit | |--------|----------|---------|-------------| | Collection | ElementsMatchT | **21-81x** | Scales with collection size | | Equality | EqualT | **10-13x** | Zero allocations | | Comparison | GreaterT/LessT | **10-22x** | Zero allocations | | Collection | SliceContainsT | **16x** | Zero allocations | | Collection | SeqContainsT | **25x** | Iterator optimization | | Ordering | IsIncreasingT | **7-9x** | Zero allocations | | Type | IsOfTypeT | **9-11x** | No reflection overhead | **Memory savings**: Up to 99% reduction in allocations for large collections ### Architecture Changes These affect the way the project is maintained, but not how it is used. #### Code Generation All assert and require packages are 100% generated from a single source: - **Source**: `internal/assertions/` (~6,000 LOC) - **Generated**: ~800+ functions across assert/require packages - **Variants**: 8 variants per assertion (assert/require x standard/format/forward/forward+format), 4 variants for generic assertions (assert/require x standard/format) > NOTE: generic assertions obviously can't be propagated as a "forward variant", i.e > as a method of the `Assertion` object. #### Module Structure The project adopts a mono-repo structure (with the appropriate changes made in CI). This means that the github repo exposes several independent go modules. ``` github.com/go-openapi/testify/v2 # Core (zero deps) [go.mod] ├── assert/ # Generated package ├── require/ # Generated package ├── internal/ # Internalized dependencies │ ├── spew/ # Internalized go-spew │ ├── difflib/ # Internalized go-difflib │ └── assertions/ # Single source of truth ├── enable/ # Modules for optional features │ ├── yaml/ # Optional YAML support [go.mod] │ └── color/ # Optional colorization [go.mod] │ └── codegen/ # Code and documentation generator [go.mod] ``` ### Documentation - Hugo-based documentation site - Domain-organized API reference ({{% siteparam "metrics.domains" %}} domains) - Comprehensive examples and tutorials - Performance benchmarks ## Project Metrics | Metric | Value | |--------|-------| | **Total functions** | {{% siteparam "metrics.functions" %}} (see [API metrics](../api/metrics.md)) | | **Total assertions** | {{% siteparam "metrics.assertions" %}} base assertions | | **Generic assertions** | {{% siteparam "metrics.generics" %}} | | **Performance improvement** | 1.2x to 81x faster | | **Dependencies** | 0 external (was 2 required) | | **Test coverage** | 96% overall, 99% on public APIs | | **Documentation domains** | {{% siteparam "metrics.domains" %}} logical categories | --- ## See Also - [Migration Guide](./MIGRATION.md) - Step-by-step guide to migrating from testify v1 - [Generics Guide](./GENERICS.md) - Detailed documentation of all {{% siteparam "metrics.generics" %}} generic assertions - [Performance Benchmarks](../project/maintainers/BENCHMARKS.md) - Comprehensive performance analysis - [Examples](./EXAMPLES.md) - Practical usage examples showing new features - [Tutorial](./TUTORIAL.md) - Best practices for writing tests with testify v2 - [API Reference](../api/_index.md) - Complete assertion catalog organized by domain go-openapi-testify-c10ca71/docs/doc-site/usage/EXAMPLES.md000066400000000000000000000666011520301377500231550ustar00rootroot00000000000000--- title: "Examples" description: "Practical examples for using testify v2" weight: 2 --- {{% notice primary "TL;DR" "meteor" %}} > If you've already used `github.com/stretchr/testify`, adopting v2 will be straightforward. {{% /notice %}} More examples to showcase generic assertions specifically may be found [here](./GENERICS.md). ## Quick Start The simplest way to get started with testify is using the `assert` package: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestCalculator(t *testing.T) { result := Add(2, 3) assert.Equal(t, 5, result) } ``` ## assert vs require **Use `assert`** when you want tests to continue after a failure: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { user := GetUser(123) // All three checks run, even if the first fails assert.NotNil(t, user) assert.Equal(t, "Alice", user.Name) assert.Equal(t, 25, user.Age) } ``` **Use `require`** when subsequent checks depend on earlier ones: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestUser(t *testing.T) { user := GetUser(123) // Stop immediately if user is nil (prevents panic on next line) require.NotNil(t, user) // Only runs if user is not nil assert.Equal(t, "Alice", user.Name) } ``` **Rule of thumb:** Use `require` for preconditions, `assert` for actual checks. --- ## Common Assertions ### Equality ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestEquality(t *testing.T) { // Basic equality assert.Equal(t, 42, actualValue) // Deep equality for slices, maps, structs assert.Equal(t, []int{1, 2, 3}, result) // Check inequality assert.NotEqual(t, 0, result) // Type-converting equality (123 == int64(123)) assert.EqualValues(t, 123, int64(123)) } ``` ### Collections ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestCollections(t *testing.T) { list := []string{"apple", "banana", "cherry"} // Check if collection contains an element assert.Contains(t, list, "banana") assert.NotContains(t, list, "orange") // Check length assert.Len(t, list, 3) // Check if empty assert.NotEmpty(t, list) assert.Empty(t, []string{}) // Check if all elements match (order doesn't matter) assert.ElementsMatch(t, []int{3, 1, 2}, []int{1, 2, 3}) } ``` ### Errors ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestErrors(t *testing.T) { // Check if function returns an error err := DoSomething() assert.Error(t, err) // Check if function succeeds err = DoSomethingElse() assert.NoError(t, err) // Check specific error message err = Divide(10, 0) assert.EqualError(t, err, "division by zero") // Check if error contains text assert.ErrorContains(t, err, "division") // Check error type with errors.Is assert.ErrorIs(t, err, ErrDivisionByZero) } ``` ### Nil Checks ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNil(t *testing.T) { var ptr *User assert.Nil(t, ptr) user := &User{Name: "Alice"} assert.NotNil(t, user) } ``` ### Boolean and Comparisons ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestBooleans(t *testing.T) { assert.True(t, isValid) assert.False(t, hasErrors) // Numeric comparisons assert.Greater(t, 10, 5) assert.GreaterOrEqual(t, 10, 10) assert.Less(t, 5, 10) assert.LessOrEqual(t, 5, 5) } ``` --- ## Assertion Variants Testify provides multiple ways to call assertions: ### 1. Package-Level Functions ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestPackageLevel(t *testing.T) { assert.Equal(t, 42, result) require.NotNil(t, user) } ``` ### 2. Formatted Variants (Custom Messages) ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestFormatted(t *testing.T) { // Add custom failure message with formatting assert.Equalf(t, 42, result, "expected answer to be %d", 42) require.NotNilf(t, user, "user %d should exist", userID) } ``` ### 3. Forward Methods (Cleaner Syntax) ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestForward(t *testing.T) { a := assert.New(t) r := require.New(t) // No need to pass 't' each time a.Equal(42, result) a.NotEmpty(list) r.NotNil(user) r.NoError(err) } ``` ### 4. Forward Methods with Formatting ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestForwardFormatted(t *testing.T) { a := assert.New(t) a.Equalf(42, result, "expected answer to be %d", 42) a.Lenf(list, 3, "expected %d items", 3) } ``` **Recommendation:** Use package-level functions for simple tests, forward methods for tests with many assertions. --- ## Table-Driven Tests The idiomatic Go pattern for testing multiple cases should be: ```go import ( "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func TestAdd(t *testing.T) { tests := slices.Values([]struct { name string a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"negative numbers", -2, -3, -5}, {"mixed signs", -2, 3, 1}, {"with zero", 0, 5, 5}, }) for tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Add(tt.a, tt.b) assert.Equal(t, tt.expected, result) }) } } ``` With forward methods for cleaner syntax: ```go func TestAdd(t *testing.T) { tests := slices.Values([]struct { name string a, b int expected int }{ {"positive numbers", 2, 3, 5}, {"negative numbers", -2, -3, -5}, }) for tt := range tests { t.Run(tt.name, func(t *testing.T) { a := assert.New(t) result := Add(tt.a, tt.b) a.Equal(tt.expected, result) a.Greater(result, tt.a) }) } } ``` --- ## Real-World Examples ### Testing HTTP Handlers ```go import ( "net/http" "net/http/httptest" "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestUserHandler(t *testing.T) { req := httptest.NewRequest("GET", "/users/123", nil) w := httptest.NewRecorder() handler := NewUserHandler() handler.ServeHTTP(w, req) require.Equal(t, http.StatusOK, w.Code) // Check response body contains expected data body := w.Body.String() assert.Contains(t, body, `"name":"Alice"`) assert.Contains(t, body, `"id":123`) } ``` ### Testing JSON ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestJSONResponse(t *testing.T) { expected := `{"name":"Alice","age":25}` actual := `{"age":25,"name":"Alice"}` // JSONEq compares JSON semantically (ignores key order, whitespace) assert.JSONEq(t, expected, actual) } ``` ### Testing with Subtests ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserOperations(t *testing.T) { user := &User{ID: 123, Name: "Alice"} t.Run("creation", func(t *testing.T) { assert.NotNil(t, user) assert.Equal(t, 123, user.ID) }) t.Run("update", func(t *testing.T) { user.Name = "Bob" assert.Equal(t, "Bob", user.Name) }) t.Run("deletion", func(t *testing.T) { err := DeleteUser(user.ID) assert.NoError(t, err) }) } ``` ### Testing Panics ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestPanics(t *testing.T) { // Function should panic assert.Panics(t, func() { Divide(10, 0) }) // Function should NOT panic assert.NotPanics(t, func() { Divide(10, 2) }) // Function should panic with specific value assert.PanicsWithValue(t, "division by zero", func() { Divide(10, 0) }) } ``` --- ## Advanced Patterns ### Setup and Teardown ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestWithSetup(t *testing.T) { // Setup db := setupTestDatabase(t) t.Cleanup(func() { db.Close() // Teardown }) // Test user := &User{Name: "Alice"} err := db.Save(user) require.NoError(t, err) // Verify loaded, err := db.Find(user.ID) require.NoError(t, err) assert.Equal(t, "Alice", loaded.Name) } ``` ### Helper Functions ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUsers(t *testing.T) { user := GetUser(123) assertUserValid(t, user) // Failures point to this line, not inside helper } func assertUserValid(t *testing.T, user *User) { t.Helper() // Mark as helper for better error messages assert.NotNil(t, user) assert.NotEmpty(t, user.Name) assert.Greater(t, user.Age, 0) } ``` ### Combining Multiple Assertions ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserCompleteness(t *testing.T) { a := assert.New(t) user := GetUser(123) // Chain multiple checks cleanly a.NotNil(user) a.NotEmpty(user.Name) a.NotEmpty(user.Email) a.Greater(user.Age, 0) a.True(user.Active) } ``` ### Asynchronous Testing Testify provides three assertions for testing asynchronous code: `Eventually`, `Never`, and `EventuallyWith`. {{% notice warning %}} > Asynchronous testing may sometimes be unavoidable. It should be avoided whenever possible. > > Async tests (with timeouts, ticks etc) may easily become flaky under heavy concurrence on small CI runners. > > When you've control over the code you test, always prefer sync tests, possibly with well-designed mocks. {{% /notice %}} #### Eventually: Wait for a Condition to Become True Use `Eventually` when testing code that updates state asynchronously (background goroutines, event loops, caches). ```go import ( "sync" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func TestBackgroundProcessor(t *testing.T) { // Simulate a background processor that updates state var processed bool var mu sync.Mutex go func() { time.Sleep(50 * time.Millisecond) mu.Lock() processed = true mu.Unlock() }() // Wait up to 200ms for the background task to complete, // checking every 10ms assert.Eventually(t, func() bool { mu.Lock() defer mu.Unlock() return processed }, 200*time.Millisecond, 10*time.Millisecond, "background processor should have completed") } // Real-world example: Testing cache warming func TestCacheWarming(t *testing.T) { cache := NewCache() cache.StartWarmup() // Populates cache in background // Verify cache becomes ready within 5 seconds assert.Eventually(t, func() bool { return cache.IsReady() && cache.Size() > 0 }, 5*time.Second, 100*time.Millisecond, "cache should warm up and contain entries") } ``` #### Never: Ensure a Condition Remains False Use `Never` to verify that something undesirable never happens during a time window (no data corruption, no invalid state). ```go import ( "sync/atomic" "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func TestNoDataCorruption(t *testing.T) { var counter atomic.Int32 stopChan := make(chan struct{}) defer close(stopChan) // Start multiple goroutines incrementing safely for i := 0; i < 10; i++ { go func() { ticker := time.NewTicker(5 * time.Millisecond) defer ticker.Stop() for { select { case <-stopChan: return case <-ticker.C: counter.Add(1) } } }() } // Verify counter never goes negative (indicating corruption) assert.Never(t, func() bool { return counter.Load() < 0 }, 500*time.Millisecond, 20*time.Millisecond, "counter should never go negative") } // Real-world example: Testing rate limiter doesn't exceed threshold func TestRateLimiter(t *testing.T) { limiter := NewRateLimiter(100) // 100 requests per second max stopChan := make(chan struct{}) defer close(stopChan) // Hammer the limiter with requests for i := 0; i < 50; i++ { go func() { ticker := time.NewTicker(1 * time.Millisecond) defer ticker.Stop() for { select { case <-stopChan: return case <-ticker.C: limiter.Allow() } } }() } // Verify we never exceed the rate limit over 2 seconds assert.Never(t, func() bool { return limiter.CurrentRate() > 120 // 20% tolerance }, 2*time.Second, 50*time.Millisecond, "rate limiter should never exceed threshold") } ``` #### EventuallyWith: Complex Async Assertions Use `EventuallyWith` when you need multiple assertions to pass together. The `CollectT` parameter lets you make regular assertions. ```go import ( "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func TestAPIEventualConsistency(t *testing.T) { // Simulate an eventually-consistent API api := NewEventuallyConsistentAPI() api.CreateUser("alice", "alice@example.com") // Wait for the user to be fully replicated across all shards // All conditions must pass in the same tick assert.EventuallyWith(t, func(c *assert.CollectT) { user, err := api.GetUser("alice") // All these assertions must pass together assert.NoError(c, err, "user should be retrievable") assert.NotNil(c, user, "user should exist") assert.EqualT(c, "alice@example.com", user.Email, "email should match") assert.True(c, user.Replicated, "user should be replicated") assert.GreaterOrEqual(c, user.ReplicaCount, 3, "should be on at least 3 replicas") }, 10*time.Second, 500*time.Millisecond, "user should be eventually consistent across all replicas") } // Real-world example: Testing distributed cache sync func TestDistributedCacheSync(t *testing.T) { primary := NewCacheNode("primary") replica1 := NewCacheNode("replica1") replica2 := NewCacheNode("replica2") // Connect nodes for replication primary.AddReplica(replica1) primary.AddReplica(replica2) // Write to primary primary.Set("key", "value", 5*time.Minute) // Verify value propagates to all replicas with correct TTL assert.EventuallyWith(t, func(c *assert.CollectT) { val1, ttl1, ok1 := replica1.Get("key") val2, ttl2, ok2 := replica2.Get("key") // All replicas must have the value assert.True(c, ok1, "replica1 should have the key") assert.True(c, ok2, "replica2 should have the key") // Values must match assert.EqualT(c, "value", val1, "replica1 value should match") assert.EqualT(c, "value", val2, "replica2 value should match") // TTL should be approximately the same (within 1 second) assert.InDelta(c, 5*time.Minute, ttl1, float64(time.Second), "replica1 TTL should be close to original") assert.InDelta(c, 5*time.Minute, ttl2, float64(time.Second), "replica2 TTL should be close to original") }, 5*time.Second, 100*time.Millisecond, "cache value should replicate to all nodes with correct TTL") } // Advanced: Using require to abort the current tick cleanly // // require.X(collect, …) fails the current tick via runtime.Goexit; // the poller retries on the next tick. This keeps the "eventually // converges" semantics while letting you short-circuit nil-pointer // and other cascading checks safely. func TestEventuallyWithRequire(t *testing.T) { api := NewAPI() assert.EventuallyWith(t, func(c *assert.CollectT) { resp, err := api.HealthCheck() // If err != nil, require aborts THIS tick — the poller retries next tick. // No manual `return` needed; no risk of nil-pointer on resp below. require.NoError(c, err, "health check should not error") assert.EqualT(c, "healthy", resp.Status) assert.Greater(c, resp.Uptime, 0) assert.NotEmpty(c, resp.Version) }, 30*time.Second, 1*time.Second, "API should become healthy") } // Advanced: Using collect.Cancel() to abort the whole assertion immediately // // Cancel() is the escape hatch for situations where retrying is pointless — // e.g. the upstream resource has been observed in an unrecoverable state and // waiting for the timeout only delays the failure report. func TestEventuallyWithCancel(t *testing.T) { api := NewAPI() assert.EventuallyWith(t, func(c *assert.CollectT) { resp, err := api.HealthCheck() require.NoError(c, err) // retry on transient errors if resp.Status == "dead" { // Permanent failure: no retry will help. Abort the whole assertion. assert.Fail(c, "API reported unrecoverable state: dead") c.Cancel() } assert.EqualT(c, "healthy", resp.Status) }, 30*time.Second, 1*time.Second, "API should become healthy") } ``` **Key differences:** - **Eventually**: Simple boolean condition, use for single checks - **Never**: Opposite of Eventually, verifies condition stays false - **EventuallyWith**: Complex checks with multiple assertions, use when you need detailed failure messages **Best practices:** 1. Choose appropriate timeouts: long enough for async operations, short enough for fast test feedback 2. Choose appropriate tick intervals: frequent enough to catch state changes, infrequent enough to avoid overhead 3. Use `EventuallyWith` when you need to understand *which* assertion failed 4. Use `Eventually` for simple boolean conditions (faster, simpler) 5. Use `Never` to verify invariants over time (no race conditions, no invalid state) #### Deterministic polling with synctest (opt-in) All four async assertions (`Eventually`, `Never`, `Consistently`, `EventuallyWith`) accept an **opt-in wrapper** that runs the polling loop inside a [testing/synctest] bubble. Inside the bubble, `time.Ticker`, `time.After`, and `context.WithTimeout` use a **fake clock** that advances only when all goroutines are durably blocked — so the tick count is deterministic and long timeouts cost zero real wall-clock time. Wrappers: | Wrapper | Underlying condition form | Used by | |---------|---------------------------|---------| | `WithSynctest` | `func() bool` | `Eventually`, `Never`, `Consistently` | | `WithSynctestContext` | `func(context.Context) error` | `Eventually`, `Consistently` | | `WithSynctestCollect` | `func(*CollectT)` | `EventuallyWith` | | `WithSynctestCollectContext` | `func(context.Context, *CollectT)` | `EventuallyWith` | Minimal example: ```go import ( "testing" "time" "github.com/go-openapi/testify/v2/assert" ) func TestDeterministicPolling(t *testing.T) { attempts := 0 cond := func() bool { attempts++ return attempts == 5 // converges on the 5th tick } // 1-hour timeout with 1-minute tick completes in microseconds of real // wall-clock time under the fake clock. Exactly 5 calls to the condition. assert.Eventually(t, assert.WithSynctest(cond), 1*time.Hour, 1*time.Minute) } ``` {{% notice info %}} **When to use synctest wrappers:** the condition is pure compute, or uses `time.Sleep`/timers/tickers/channels created inside the condition. These are ideal for deterministic tests of retry logic and polling loops. {{% /notice %}} {{% notice warning %}} **When NOT to use them:** - The condition performs real I/O (network, filesystem, syscalls): those block goroutines non-durably, so the fake clock stalls and the timeout may not fire. - External goroutines drive state change via real time (e.g. a `go func() { time.Sleep(...); flag = true }()` started *before* the assertion call): the external goroutine runs on real time, while the bubble advances fake time independently. - The test body is already running inside a `synctest.Test` bubble: nested bubbles are forbidden and will panic. In that case, just use the plain condition form — the outer bubble already gives you fake time. - The caller is not a `*testing.T` (e.g. a mock): activation requires a concrete `*testing.T`; with other `T` implementations, the wrapper silently falls back to real-time polling. {{% /notice %}} Shared state between the condition and enclosing scope (counters, atomics, flags) works as expected. Polling is already serialized (see **Concurrency** in the `Eventually` godoc), so no additional synchronization is required inside the condition. See also the testable examples: - [`ExampleWithSynctest_asyncReady`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#example-package-WithSynctest-asyncReady) - [`ExampleWithSynctestContext_healthCheck`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#example-package-WithSynctestContext-healthCheck) - [`ExampleWithSynctestCollect_convergence`](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#example-package-WithSynctestCollect-convergence) [testing/synctest]: https://pkg.go.dev/testing/synctest ### Goroutine Leak Detection Use `NoGoRoutineLeak` to verify that your code doesn't leak goroutines. This is critical for long-running applications, connection pools, and worker patterns. ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestWorkerPool(t *testing.T) { assert.NoGoRoutineLeak(t, func() { pool := NewWorkerPool(10) pool.Start() // Submit work pool.Submit(func() { /* do something */ }) // Cleanup MUST happen inside the tested function pool.Shutdown() }) } ``` #### Why Use It? Traditional goroutine leak detection (like `go.uber.org/goleak`) requires maintaining filter lists to exclude known system goroutines. This approach is brittle and prone to false positives when: - Running parallel tests - Using connection pools (database, HTTP, gRPC) - Background runtime goroutines change between Go versions **`NoGoRoutineLeak` uses pprof labels** instead of stack-trace heuristics: - Only goroutines spawned by your tested function are checked - Pre-existing goroutines (runtime, connection pools, other tests) are ignored automatically - No configuration or filter lists needed - Works safely with `t.Parallel()` #### Real-World Example: Testing a Server ```go import ( "net/http" "testing" "github.com/go-openapi/testify/v2/assert" ) func TestHTTPServer(t *testing.T) { assert.NoGoRoutineLeak(t, func() { // Start server server := &http.Server{Addr: ":0", Handler: myHandler} go server.ListenAndServe() // Do some requests... resp, _ := http.Get("http://" + server.Addr + "/health") resp.Body.Close() // Shutdown MUST happen inside the tested function server.Shutdown(context.Background()) }) } ``` #### Important: Cleanup Inside the Tested Function Resource cleanup must happen inside the tested function, not via `t.Cleanup()`: ```go // ❌ WRONG: t.Cleanup runs AFTER the leak check func TestWrong(t *testing.T) { var server *Server t.Cleanup(func() { server.Stop() }) // Too late! assert.NoGoRoutineLeak(t, func() { server = StartServer() // Leak detected because server is still running }) } // ✅ CORRECT: cleanup inside tested function func TestCorrect(t *testing.T) { assert.NoGoRoutineLeak(t, func() { server := StartServer() defer server.Stop() // Cleanup happens before leak check // ... test code ... }) } ``` #### Edge Cases - **Panics**: If the tested function panics while goroutines are still running, the leak is detected and reported along with the panic - **`t.FailNow()`/`runtime.Goexit()`**: Leaks are still detected even if the tested function exits early - **Transitive goroutines**: Goroutines spawned by child goroutines inherit the label and are tracked ### Extensible assertions The `Assertions` type may be extended to fit your needs like so. ```go import ( "fmt" "strings" "github.com/go-openapi/testify/v2/assert" ) // Assertions is a customized version of [assert.Assertions]. type Assertions struct { *assert.Assertions } func New(t assert.T) *Assertions { return &Assertions{ Assertions: assert.New(t), } } // StartsWith asserts that the string starts with the given prefix. // // Examples: // // success: "hello world", "hello" // failure: "hello world", "bye" func (a *Assertions) StartsWith(str, prefix string, msgAndArgs ...any) bool { if h, ok := a.T.(assert.H); ok { h.Helper() // preserve the original failing location } if !strings.HasPrefix(str, prefix) { return a.Fail(fmt.Sprintf("Expected %q to start with %q", str, prefix), msgAndArgs...) } return true } ``` --- ## YAML Support (Optional) YAML assertions require explicit opt-in: ```go import ( "testing" _ "github.com/go-openapi/testify/enable/yaml/v2" // Enable YAML support "github.com/go-openapi/testify/v2/assert" ) func TestYAML(t *testing.T) { expected := ` name: Alice age: 25 ` actual := ` age: 25 name: Alice ` // YAMLEq compares YAML semantically assert.YAMLEq(t, expected, actual) } ``` **Note:** Without the `enable/yaml` import, YAML assertions will panic with a helpful message. --- ## Colorized Output (Optional) Testify can colorize test failure output for better readability. This is an opt-in feature. ### Enabling Colors ```go import ( "testing" _ "github.com/go-openapi/testify/enable/colors/v2" // Enable colorized output "github.com/go-openapi/testify/v2/assert" ) func TestExample(t *testing.T) { assert.Equal(t, "expected", "actual") // Failure will be colorized } ``` ### Activation Colors are activated via command line flag or environment variable: ```bash # Via flag go test -v -testify.colorized ./... # Via environment variable TESTIFY_COLORIZED=true go test -v ./... ``` ### Themes Two themes are available for different terminal backgrounds: ```bash # Dark theme (default) - bright colors for dark terminals go test -v -testify.colorized ./... # Light theme - normal colors for light terminals go test -v -testify.colorized -testify.theme=light ./... # Or via environment TESTIFY_COLORIZED=true TESTIFY_THEME=light go test -v ./... ``` ### CI Environments By default, colorization is disabled when output is not a terminal. To force colors in CI environments that support ANSI codes: ```bash TESTIFY_COLORIZED=true TESTIFY_COLORIZED_NOTTY=true go test -v ./... ``` ### What Gets Colorized - **Expected values** in assertion failures (green) - **Actual values** in assertion failures (red) - **Diff output**: - Deleted lines (red) - Inserted lines (yellow) - Context lines (green) **Note:** Without the `enable/colors` import, output remains uncolored (no panic, just no colors). See [screenshot](./MIGRATION.md#optional-enable-colorized-output). --- ## Best Practices 1. **Use `require` for preconditions** - Stop test immediately if setup fails 2. **Use `assert` for actual checks** - See all failures in one test run 3. **Add custom messages for complex checks** - Use formatted variants when assertion failure needs context 4. **Prefer table-driven tests** - Test multiple cases systematically 5. **Use forward methods for many assertions** - Reduces repetition in long tests 6. **Keep tests focused** - One logical concept per test function 7. **Use subtests for related scenarios** - Group related checks with `t.Run()` 8. **Mark helpers with `t.Helper()`** - Get better error locations --- ## Migration from stdlib testing **Before (stdlib):** ```go import "testing" func TestOld(t *testing.T) { result := Calculate(5) if result != 10 { t.Errorf("Expected 10, got %d", result) } if len(items) == 0 { t.Error("Expected non-empty items") } } ``` **After (testify):** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNew(t *testing.T) { a := assert.New(t) result := Calculate(5) a.Equal(10, result) a.NotEmpty(items) } ``` **Benefits:** - More readable - assertions read like English - Better error messages - shows expected vs actual automatically - Less boilerplate - no manual formatting - More assertions - Contains, ElementsMatch, JSONEq, etc. --- ## See Also - [Tutorial](./TUTORIAL.md) - Comprehensive guide to writing great tests with testify patterns - [Usage Guide](./USAGE.md) - API conventions, naming patterns, and how to navigate the documentation - [Generics Guide](./GENERICS.md) - Type-safe assertions for better compile-time checking - [Migration Guide](./MIGRATION.md) - Migrating from stretchr/testify v1 to this fork - [API Reference](../api/_index.md) - Complete assertion catalog organized by domain go-openapi-testify-c10ca71/docs/doc-site/usage/GENERICS.md000066400000000000000000000445121520301377500231330ustar00rootroot00000000000000--- title: Generics description: Using generic assertions. weight: 10 --- Testify v2 provides **{{% siteparam "metrics.generics" %}} generic assertion functions** that offer compile-time type safety alongside the traditional reflection-based assertions. Generic variants are identified by the `T` suffix (e.g., `EqualT`, `GreaterT`, `ElementsMatchT`). {{% notice style="success" title="Type Safety First" icon="check" %}} Generic assertions catch type mismatches **when writing tests**, not when running them. The performance improvements (1.2x-81x faster) are a bonus on top of this primary benefit. {{% /notice %}} ## Quick Start Generic assertions work exactly like their reflection-based counterparts, but with compile-time type checking: {{< cards >}} {{% card title="Reflection-based" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { expected := 42 actual := getUserAge() // Compiles, but type errors appear at runtime assert.Equal(t, expected, actual) } ``` {{% /card %}} {{% card title="Generic (Type-safe)" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { expected := 42 actual := getUserAge() // Compiler checks types immediately assert.EqualT(t, expected, actual) } ``` {{% /card %}} {{< /cards >}} ## When to Use Generic Variants ### ✅ Use Generic Variants (`*T` functions) When: 1. **Testing with known concrete types** - The most common case ```go assert.EqualT(t, 42, result) // int comparison assert.GreaterT(t, count, 0) // numeric comparison assert.ElementsMatchT(t, expected, actual) // slice comparison ``` 2. **You want refactoring safety** - Compiler catches broken tests immediately ```go // If getUserIDs() changes from []int to []string, // the compiler flags this line immediately assert.ElementsMatchT(t, expectedIDs, getUserIDs()) ``` 3. **IDE assistance matters** - Autocomplete suggests only correctly-typed variables ```go // Typing: assert.EqualT(t, expectedUser, actual // ^ // IDE suggests: actualUser ✓ (correct type) // actualOrder ✗ (wrong type - grayed out) ``` 4. **Performance-critical tests** - See [benchmarks](../project/maintainers/BENCHMARKS.md) for 1.2-81x speedups ### 🔄 Use Reflection Variants (no suffix) When: 1. **Intentionally comparing different types** - Especially with `EqualValues` ```go // Comparing int and int64 for semantic equality assert.EqualValues(t, int64(42), int32(42)) // ✓ Reflection handles this assert.EqualT(t, int64(42), int32(42)) // ❌ Compiler error ``` 2. **Working with heterogeneous collections** - `[]any` or `interface{}` slices ```go mixed := []any{1, "string", true} assert.Contains(t, mixed, "string") // ✓ Reflection works ``` 3. **Dynamic type scenarios** - Where compile-time type is unknown ```go var result interface{} = getResult() assert.Equal(t, expected, result) // ✓ Reflection handles dynamic types ``` 4. **Backward compatibility** - Existing test code using reflection-based assertions ## Type Safety Benefits ### Catching Refactoring Errors Generic assertions act as a safety net during refactoring: {{< cards >}} {{% card title="Without Generics ❌" %}} ```go // Original code type UserID int var userIDs []UserID assert.ElementsMatch(t, userIDs, getActiveUsers()) // Later: UserID changes to string type UserID string var userIDs []UserID // Test still compiles! // Fails mysteriously at runtime or passes with wrong comparison assert.ElementsMatch(t, userIDs, getActiveUsers()) ``` {{% /card %}} {{% card title="With Generics ✅" %}} ```go // Original code type UserID int var userIDs []UserID assert.ElementsMatchT(t, userIDs, getActiveUsers()) // Later: UserID changes to string type UserID string var userIDs []UserID // Compiler immediately flags the error assert.ElementsMatchT(t, userIDs, getActiveUsers()) // ❌ Compile error: type mismatch! ``` {{% /card %}} {{< /cards >}} ### Preventing Wrong Comparisons Generic assertions force you to think about what you're comparing: {{< cards >}} {{% card title="Pointer vs Value Comparison" %}} ```go expected := &User{ID: 1, Name: "Alice"} actual := &User{ID: 1, Name: "Alice"} // Reflection: Compares pointer addresses (probably wrong) assert.Equal(t, expected, actual) // ✗ Fails (different addresses) // Generic: Makes the intent explicit assert.EqualT(t, expected, actual) // Compares pointers assert.EqualT(t, *expected, *actual) // Compares values ✓ ``` {{% /card %}} {{% card title="Type Confusion Prevention" %}} ```go userID := 42 orderID := "ORD-123" // Reflection: Compiles, wrong comparison assert.Equal(t, userID, orderID) // Runtime failure // Generic: Compiler catches the mistake assert.EqualT(t, userID, orderID) // ❌ Compile error! ``` {{% /card %}} {{< /cards >}} ## Available Generic Functions Testify v2 provides generic variants across all major domains: ### Equality (4 functions) - `EqualT[V comparable]` - Type-safe equality for comparable types - `NotEqualT[V comparable]` - Type-safe inequality - `SameT[V comparable]` - Pointer identity check - `NotSameT[V comparable]` - Different pointer check ### Comparison (6 functions) - `GreaterT[V Ordered]` - Type-safe greater-than comparison - `GreaterOrEqualT[V Ordered]` - Type-safe >= - `LessT[V Ordered]` - Type-safe less-than comparison - `LessOrEqualT[V Ordered]` - Type-safe <= - `PositiveT[V SignedNumeric]` - Assert value > 0 - `NegativeT[V SignedNumeric]` - Assert value < 0 ### Collection (16 functions) - `StringContainsT[S Text]` - String/byte slice contains substring - `SliceContainsT[E comparable]` - Slice contains element - `MapContainsT[K comparable, V any]` - Map contains key - `SeqContainsT[E comparable]` - Iterator contains element (Go 1.23+) - `ElementsMatchT[E comparable]` - Slices have same elements (any order) - `SliceSubsetT[E comparable]` - Slice is subset of another - `SliceEqualT[E comparable]` - Slices are equal (same order) - `MapEqualT[K, V comparable]` - Maps are equal (same keys and values) - Plus negative variants: `*NotContainsT`, `NotElementsMatchT`, `SliceNotSubsetT`, `SliceNotEqualT`, `MapNotEqualT` ### Ordering (6 functions) - `IsIncreasingT[E Ordered]` - Slice elements strictly increasing - `IsDecreasingT[E Ordered]` - Slice elements strictly decreasing - `IsNonIncreasingT[E Ordered]` - Slice elements non-increasing (allows equal) - `IsNonDecreasingT[E Ordered]` - Slice elements non-decreasing (allows equal) - `SortedT[E Ordered]` - Slice is sorted (generic-only function) - `NotSortedT[E Ordered]` - Slice is not sorted (generic-only function) ### Numeric (2 functions) - `InDeltaT[V Measurable]` - Numeric comparison with absolute delta (supports integers and floats) - `InEpsilonT[V Measurable]` - Numeric comparison with relative epsilon (supports integers and floats) ### Boolean (2 functions) - `TrueT[B Boolean]` - Assert boolean is true - `FalseT[B Boolean]` - Assert boolean is false ### String (2 functions) - `RegexpT[S Text]` - String matches regex (string or []byte) - `NotRegexpT[S Text]` - String doesn't match regex ### Type (2 functions) - `IsOfTypeT[EType any]` - Assert value is of type EType (no dummy value needed!) - `IsNotOfTypeT[EType any]` - Assert value is not of type EType ### JSON & YAML (2 functions) - `JSONEqT[S Text]` - JSON strings are semantically equal - `YAMLEqT[S Text]` - YAML strings are semantically equal {{% notice style="info" title="See Complete API" icon="book" %}} For a detailed documentation of all generic functions, see the [API Reference](../api/_index.md) organized by domain. {{% /notice %}} ## Practical Examples ### Example 1: Collection Testing {{< cards >}} {{% card title="Type-Safe Collection Assertions" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserPermissions(t *testing.T) { user := getUser(123) expectedPerms := []string{"read", "write"} actualPerms := user.Permissions // Compiler ensures both slices are []string assert.ElementsMatchT(t, expectedPerms, actualPerms) // Check subset relationship assert.SliceSubsetT(t, []string{"read"}, actualPerms) } ``` {{% /card %}} {{% card title="Iterator Support (Go 1.23+)" %}} ```go import ( "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSequenceContains(t *testing.T) { // iter.Seq[int] from Go 1.23 numbers := slices.Values([]int{1, 2, 3, 4, 5}) // Type-safe iterator checking assert.SeqContainsT(t, numbers, 3) assert.SeqNotContainsT(t, numbers, 99) } ``` {{% /card %}} {{< /cards >}} ### Example 2: Numeric Comparisons {{< cards >}} {{% card title="Ordered Types" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestPricing(t *testing.T) { price := calculatePrice(item) discount := calculateDiscount(item) // Type-safe numeric comparisons assert.PositiveT(t, price) assert.GreaterT(t, price, discount) assert.LessOrEqualT(t, discount, price) } ``` {{% /card %}} {{% card title="Float Comparisons" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestPhysicsCalculation(t *testing.T) { result := calculateVelocity(mass, force) expected := 42.0 // Type-safe float comparison with delta assert.InDeltaT(t, expected, result, 1e-6) // Or with epsilon (relative error) assert.InEpsilonT(t, expected, result, 0.001) } ``` {{% /card %}} {{< /cards >}} ### Example 3: Type Checking Without Dummy Values The `IsOfTypeT` function eliminates the need for dummy values: {{< cards >}} {{% card title="Old Way (Reflection)" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestGetUser(t *testing.T) { result := getUser(123) // Need to create a dummy User instance assert.IsType(t, User{}, result) // Or use a pointer dummy assert.IsType(t, (*User)(nil), result) } ``` {{% /card %}} {{% card title="New Way (Generic)" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestGetUser(t *testing.T) { result := getUser(123) // No dummy value needed! assert.IsOfTypeT[User](t, result) // For pointer types assert.IsOfTypeT[*User](t, result) } ``` {{% /card %}} {{< /cards >}} ### Example 4: Sorting and Ordering {{< cards >}} {{% card title="Ordering Checks" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSortedData(t *testing.T) { timestamps := []int64{ 1640000000, 1640000100, 1640000200, } // Type-safe ordering assertions assert.IsIncreasingT(t, timestamps) assert.SortedT(t, timestamps) // Generic-only function } ``` {{% /card %}} {{% card title="Custom Ordered Types" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) type Priority int const ( Low Priority = iota Medium High ) func TestPriorities(t *testing.T) { tasks := []Priority{Low, Medium, High} // Works with Ordered types (custom types supported) assert.IsNonDecreasingT(t, tasks) } ``` {{% /card %}} {{< /cards >}} ## Migration Guide ### Step 1: Identify High-Value Targets Start with the most common assertions that benefit most from type safety: ```go // High value: Collection operations (also get big performance wins) assert.Equal → assert.EqualT assert.ElementsMatch → assert.ElementsMatchT assert.Contains → assert.ContainsT (SliceContainsT/MapContainsT/StringContainsT) // High value: Comparisons (eliminate allocations) assert.Greater → assert.GreaterT assert.Less → assert.LessT assert.Positive → assert.PositiveT // High value: Type checks (cleaner API) assert.IsType(t, User{}, v) → assert.IsOfTypeT[User](t, v) ``` ### Step 2: Automated Search & Replace Use your IDE or tools to find and replace systematically: ```bash # Find all Equal assertions grep -r "assert\.Equal(" . --include="*_test.go" # Find all require.Greater assertions grep -r "require\.Greater(" . --include="*_test.go" ``` ### Step 3: Fix Compiler Errors The compiler will catch type mismatches. This is a feature, not a bug: {{< cards >}} {{% card title="Compiler Error" %}} ```go // Original code assert.EqualT(t, int64(result), count) // ❌ Error: mismatched types int64 and int ``` {{% /card %}} {{% card title="Fix Option 1: Same Type" %}} ```go // Convert to same type assert.EqualT(t, int64(result), int64(count)) ``` {{% /card %}} {{% card title="Fix Option 2: Use Reflection" %}} ```go // If cross-type comparison is intentional assert.Equal(t, int64(result), count) ``` {{% /card %}} {{< /cards >}} ### Step 4: Incremental Adoption You don't need to migrate everything at once: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestMixedAssertions(t *testing.T) { // Use generic where types are known assert.EqualT(t, 42, getAge()) assert.GreaterT(t, count, 0) // Keep reflection for dynamic types var result interface{} = getResult() assert.Equal(t, expected, result) // Both styles coexist peacefully } ``` ## Performance Benefits Generic assertions provide significant performance improvements, especially for collection operations: | Operation | Speedup | When It Matters | |-----------|---------|-----------------| | **ElementsMatchT** | **21-81x faster** | Large collections, hot test paths | | **EqualT** | **10-13x faster** | Most common assertion | | **GreaterT/LessT** | **10-22x faster** | Numeric comparisons | | **SliceContainsT** | **16x faster** | Collection membership tests | {{% notice style="success" title="Learn More" icon="chart-line" %}} See the complete [Performance Benchmarks](../project/maintainers/BENCHMARKS.md) for detailed analysis and real benchmark results. {{% /notice %}} ## Best Practices ### ✅ Do 1. **Prefer generic variants by default** - Type safety is always valuable ```go assert.EqualT(t, expected, actual) // ✓ Type safe ``` 2. **Let the compiler guide you** - Type errors reveal design issues ```go // Compiler error reveals you're comparing wrong types assert.EqualT(t, userID, orderID) // ❌ Good - catches mistake! ``` 3. **Use explicit types for clarity** ```go assert.IsOfTypeT[*User](t, result) // ✓ Clear intent ``` 4. **Leverage performance wins in hot paths** - Generic assertions are faster ```go // Table-driven tests with many iterations for _, tc := range testCases { assert.EqualT(t, tc.expected, tc.actual) // ✓ Fast } ``` ### ❌ Don't 1. **Don't force generics for dynamic types** ```go var result interface{} = getResult() assert.Equal(t, expected, result) // ✓ Reflection is fine here ``` 2. **Don't use reflection to avoid fixing types** ```go // Bad: Using reflection to bypass type safety assert.Equal(t, expected, actual) // ✗ Defeats the purpose // Good: Fix the types or use EqualValues if intentional assert.EqualT(t, expected, actual) // ✓ Type safe ``` 3. **Don't create unnecessary type conversions** ```go // Bad: Unnecessary conversion assert.EqualT(t, int64(42), int64(result)) // Good: Work with natural types assert.EqualT(t, 42, result) ``` ## Type Constraints Reference Generic assertions use custom type constraints defined in `internal/assertions/generics.go`: | Constraint | Definition | Description | Example Types | |------------|------------|-------------|---------------| | `comparable` | Go built-in | Types that support `==` and `!=` | `int`, `string`, `bool`, pointers, structs (if all fields are comparable) | | `Boolean` | `~bool` | Boolean and named bool types | `bool`, `type MyBool bool` | | `Text` | `~string \| ~[]byte` | String or byte slice types | `string`, `[]byte`, custom string/byte types | | `Ordered` | `cmp.Ordered \| []byte \| time.Time` | **Extends** `cmp.Ordered` with byte slices and time | Standard ordered types plus `[]byte` and `time.Time` | | `SignedNumeric` | `~int... \| ~float32 \| ~float64` | Signed integers and floats | `int`, `int8`-`int64`, `float32`, `float64` | | `UnsignedNumeric` | `~uint...` | Unsigned integers | `uint`, `uint8`-`uint64` | | `Measurable` | `SignedNumeric \| UnsignedNumeric` | All numeric types (for delta comparisons) | Used by `InDeltaT`/`InEpsilonT` - supports **integers AND floats** | | `RegExp` | `Text \| *regexp.Regexp` | Regex pattern or compiled regexp | `string`, `[]byte`, `*regexp.Regexp` | {{% notice style="primary" title="Key Differences from Standard Go Constraints" icon="info" %}} - **`Ordered` is extended**: Adds `[]byte` and `time.Time` to `cmp.Ordered` for seamless `bytes.Compare()` and `time.Time.Compare()` support - **`Measurable` supports integers**: `InDeltaT` and `InEpsilonT` work with both integers and floats, not just floating-point types - **Custom type support**: All constraints use the `~` operator to support custom types (e.g., `type UserID int`) {{% /notice %}} ## Summary **Generic assertions in testify v2 provide:** ✅ **Type Safety**: Catch errors when writing tests, not when running them ✅ **Performance**: 1.2x to 81x faster than reflection-based assertions ✅ **Better IDE Support**: Autocomplete suggests correctly-typed values ✅ **Refactoring Safety**: Compiler catches broken tests immediately ✅ **Zero Downside**: Always as fast or faster than reflection variants **Start using generic assertions today** - add the `T` suffix to your existing assertions and let the compiler guide you to better, safer tests. --- {{% notice style="tip" title="Quick Reference" icon="lightbulb" %}} - **Generic functions**: Add `T` suffix (e.g., `EqualT`, `GreaterT`, `ElementsMatchT`) - **Format variants**: Add `Tf` suffix (e.g., `EqualTf`, `GreaterTf`) - **When to use**: Prefer generics for known concrete types - **When not to**: Keep reflection for dynamic types and cross-type comparisons - **Performance**: See [benchmarks](../project/maintainers/BENCHMARKS.md) for dramatic speedups {{% /notice %}} --- ## See Also - [Examples](./EXAMPLES.md) - Practical examples including generic assertion usage - [Tutorial](./TUTORIAL.md) - Best practices for writing tests with testify - [Usage Guide](./USAGE.md) - API conventions and naming patterns - [Performance Benchmarks](../project/maintainers/BENCHMARKS.md) - Detailed performance analysis of generic vs reflection - [Changes from v1](./CHANGES.md) - Complete list of new generic functions and improvements - [API Reference](../api/_index.md) - Complete assertion catalog with all generic variants go-openapi-testify-c10ca71/docs/doc-site/usage/MIGRATION.md000066400000000000000000000270711520301377500232660ustar00rootroot00000000000000--- title: "Migration Guide" description: "Migrating from testify/v1" weight: 20 --- ## Migration Guide from stretchr/testify v1 This guide covers migrating from `stretchr/testify` to `go-openapi/testify/v2`. You can use the [automated migration tool](#automated-migration-tool) or migrate [manually](#manual-migration). ### Automated Migration Tool `migrate-testify` automates both the import migration (pass 1) and the generic upgrade (pass 2). It uses `go/packages` and `go/types` for type-checked, semantics-preserving transformations. #### Installation ```bash go install github.com/go-openapi/testify/hack/migrate-testify/v2@latest ``` This installs the `migrate-testify` binary into your `$GOBIN`. #### Quick Start ```bash # Run both passes on the current directory (preview first, then apply) migrate-testify --all --dry-run . migrate-testify --all . # Or run each pass separately migrate-testify --migrate . migrate-testify --upgrade-generics . ``` #### Pass 1: Import Migration (`--migrate`) Rewrites `stretchr/testify` imports to `go-openapi/testify/v2`: ```bash # Dry-run to preview changes migrate-testify --migrate --dry-run . # Apply changes migrate-testify --migrate . ``` This pass handles: - Import path rewriting (`assert`, `require`, root package) - Function renames (`EventuallyWithT` to `EventuallyWith`, `NoDirExists` to `DirNotExists`, etc.) - Type replacement (`PanicTestFunc` to `func()`) - YAML enable import injection (adds `_ "github.com/go-openapi/testify/v2/enable/yaml"` when `YAMLEq` is used) - Incompatible import detection (`mock`, `suite`, `http` packages emit warnings with guidance) - `go.mod` update (drops `stretchr/testify`, adds `go-openapi/testify/v2`) #### Pass 2: Generic Upgrade (`--upgrade-generics`) Upgrades reflection-based assertions to generic variants where types are statically resolvable and the semantics are preserved: ```bash # Dry-run to preview changes migrate-testify --upgrade-generics --dry-run . # Apply changes migrate-testify --upgrade-generics . ``` The tool is conservative: it only upgrades when: - Argument types are statically known (no `any`, no `interface{}`) - Types satisfy the required constraint (`comparable`, `Ordered`, `Text`, etc.) - For `Equal`/`NotEqual`: types are "deeply comparable" (no pointers or structs with pointer fields) - For `Contains`: the container type disambiguates to `StringContainsT`, `SliceContainsT`, or `MapContainsT` - `IsType` is flagged for manual review (argument count changes) Assertions that cannot be safely upgraded are tracked and reported in the summary with a specific reason (e.g., "pointer type", "interface{}/any", "type mismatch"). Use `--verbose` to see the file and line of each skipped assertion. #### Reference ``` Usage: migrate-testify [flags] [directory] Migrate stretchr/testify to go-openapi/testify/v2 and upgrade to generic assertions. Flags: -all Run both passes sequentially -dry-run Show diffs without modifying files -migrate Run pass 1: stretchr/testify -> go-openapi/testify/v2 -upgrade-generics Run pass 2: reflection -> generic assertions -verbose Print detailed transformation info -skip-gomod Skip go.mod changes -skip-vendor Skip vendor/ directory (default true) -version string Target testify version (default "v2.3.0") At least one of --migrate, --upgrade-generics, or --all is required. Mono-repo support: Pass 1 walks the filesystem and works across module boundaries. Pass 2 requires type information and uses go/packages to load code. For multi-module repos, a go.work file must be present so that pass 2 can load all workspace modules. Create one with: go work init . ./sub/module1 ./sub/module2 ... Post-migration checklist: - Run your linter: the migration may surface pre-existing unchecked linting issues. - Run your test suite to verify all tests still pass. ``` --- ### Manual Migration #### 1. Update Import Paths ```go // Old import "github.com/stretchr/testify/assert" import "github.com/stretchr/testify/require" // New import "github.com/go-openapi/testify/v2/assert" import "github.com/go-openapi/testify/v2/require" ``` #### 2. Optional: Enable YAML Support If you use `YAMLEq` assertions: this feature is now opt-in. ```go import _ "github.com/go-openapi/testify/enable/yaml/v2" ``` Without this import, YAML assertions will panic with a helpful error message. #### 3. Optional: Enable Colorized Output ```go import _ "github.com/go-openapi/testify/enable/colors/v2" ``` Use go additional test flags or environment variables: `TESTIFY_COLORIZED=true`, `TESTIFY_THEME=dark|light` Example: ``` go test -v -testify.colorized -testify.theme=light . ``` ![Colorized Test](colorized.png) #### 4. Optional: Adopt Generic Assertions For better type safety and performance, consider migrating to generic assertion variants. This is entirely optional: reflection-based assertions continue to work as before. ##### Identify Generic-Capable Assertions Look for these common assertions in your tests: ```go // Equality assert.Equal → assert.EqualT assert.NotEqual → assert.NotEqualT // Comparisons assert.Greater → assert.GreaterT assert.Less → assert.LessT assert.Positive → assert.PositiveT assert.Negative → assert.NegativeT // Collections assert.Contains → assert.ContainsT (or SliceContainsT/MapContainsT/StringContainsT) assert.ElementsMatch → assert.ElementsMatchT assert.Subset → assert.SubsetT // Ordering assert.IsIncreasing → assert.IsIncreasingT assert.IsDecreasing → assert.IsDecreasingT // Type checks assert.IsType(t, User{}, v) → assert.IsOfTypeT[User](t, v) // No dummy value! ``` Simply add `T` to the function name. The compiler will check types automatically: ```go // Before assert.Equal(t, expected, actual) assert.ElementsMatch(t, slice1, slice2) // After assert.EqualT(t, expected, actual) assert.ElementsMatchT(t, slice1, slice2) ``` ##### Fix Type Mismatches The compiler will now catch type errors. This is a feature—it reveals bugs: ```go // Compiler catches this assert.EqualT(t, int64(42), int32(42)) // Error: mismatched types int64 and int32 // Fix: Use same type assert.EqualT(t, int64(42), int64(actual)) // Or: Use reflection if cross-type comparison is intentional assert.Equal(t, int64(42), int32(42)) // Still works ``` ##### Pointer Semantics: When NOT to Upgrade Generic assertions use Go's `==` operator, while reflection-based assertions use `reflect.DeepEqual`. For most types these are equivalent, but **they differ for pointers and structs containing pointers**: ```go a := &MyStruct{Name: "alice"} b := &MyStruct{Name: "alice"} assert.Equal(t, a, b) // PASSES (reflect.DeepEqual compares pointed-to values) assert.EqualT(t, a, b) // FAILS (== compares pointer addresses) ``` **Do not upgrade to generic variants when:** - Arguments are pointer types (`*T`) — `EqualT` compares addresses, not values - Arguments are structs with pointer fields — `==` compares field addresses, `DeepEqual` compares field values - You intentionally rely on cross-type comparison (`int64` vs `int32`) The automated migration tool handles this automatically by only upgrading assertions where the argument types are "deeply comparable" — types where `==` and `reflect.DeepEqual` produce the same result. ##### Benefits of Generic Assertions - **Compile-time type safety**: Catch errors when writing tests - **Performance**: 1.2x to 81x faster (see [Benchmarks](../project/maintainers/BENCHMARKS.md)) - **IDE support**: Better autocomplete with type constraints - **Refactoring safety**: Type changes break tests at compile time, not runtime See the [Generics Guide](./GENERICS.md) for detailed usage patterns and best practices. #### 5. Remove Suite/Mock Usage Replace testify mocks with: - [mockery](https://github.com/vektra/mockery) for mocking Replace testify suites with: - Standard Go subtests for test organization - or wait until we reintroduce this feature (possible, but not certain) #### 6. Replace `go.uber.org/goleak` with `NoGoRoutineLeak` If you use `go.uber.org/goleak` to detect goroutine leaks in tests, consider replacing it with `assert.NoGoRoutineLeak` (or `require.NoGoRoutineLeak`), which is built into testify v2. ```go // Before (with goleak) import "go.uber.org/goleak" func TestNoLeak(t *testing.T) { defer goleak.VerifyNone(t) // ... test code ... } // After (with testify v2) import "github.com/go-openapi/testify/v2/assert" func TestNoLeak(t *testing.T) { assert.NoGoRoutineLeak(t, func() { // ... test code ... }) } ``` This removes the `go.uber.org/goleak` dependency. This step is not automated by the migration tool. #### 7. Remove use of the `testify/http` package If you were still using the deprecated package `github.com/stretchr/testitfy/http`, you'll need to replace it by the standard `net/http/httptest` package. We won't reintroduce this package ever. --- ## Breaking Changes Summary ### Removed Packages - `suite` - Use standard Go subtests - `mock` - Use [mockery](https://github.com/vektra/mockery) - `http` - May be reintroduced later ### Removed Functions and Types - All deprecated functions from v1 removed - Removed extraneous "helper" types: `PanicTestFunc` (`func()`) ### Behavior Changes Make sure to check the [behavior changes](./CHANGES.md) as we have fixed a few quirks in the existing API (mostly edge cases handling). #### `CollectT.FailNow` vs `Cancel` {#collectt-failnow-vs-cancel} In `stretchr/testify`, `CollectT.FailNow()` aborts the current tick and lets `EventuallyWithT` retry on the next tick. Early versions of this fork changed that to "cancel the whole assertion immediately", which effectively broke the most common pattern (`require.X(collect, …)` inside `EventuallyWith`). As of v2.4, `CollectT` exposes two distinct methods, matching what users actually want: | Method | Effect | |-------------------------|---------------------------------------------------------------------------------------| | `collect.FailNow()` | Fails the **current tick** only. Poller retries on the next tick. Matches stretchr. | | `collect.Cancel()` | Cancels the polling context and aborts the **whole assertion** immediately. New API. | **Migration from stretchr/testify**: no code change is required — `FailNow` behaves the same as upstream. **Migration from earlier versions of this fork**: if you wrote `collect.FailNow()` relying on the whole-assertion-abort behavior, switch the call to `collect.Cancel()`. The migration tool emits an advisory warning for every `collect.FailNow()` call it finds; review each one and switch to `Cancel()` where you want the old behavior. The safe default (keeping `FailNow()`) matches the stretchr semantics that most users expect. **Why the split**: `require`-style assertions inside a polling loop are only useful if they abort the current evaluation and let the loop converge. Cancelling the whole loop on the first failing tick defeats the purpose of `EventuallyWith`. See [TRACKING](./TRACKING.md) entries for [#1819] and [#1830] for the upstream discussions. [#1819]: https://github.com/stretchr/testify/pull/1819 [#1830]: https://github.com/stretchr/testify/pull/1830 --- ## See Also - [Changes from v1](./CHANGES.md) - Complete list of all changes, fixes, and new features - [Examples](./EXAMPLES.md) - Practical examples showing v2 usage patterns - [Generics Guide](./GENERICS.md) - Learn about the 43 new type-safe generic assertions - [Usage Guide](./USAGE.md) - API conventions and how to navigate the documentation - [Tutorial](./TUTORIAL.md) - Best practices for writing tests with testify v2 go-openapi-testify-c10ca71/docs/doc-site/usage/TRACKING.md000066400000000000000000000241351520301377500231350ustar00rootroot00000000000000--- title: "Upstream Tracking" description: "All issues and PRs reviewed for this fork" weight: 16 --- ## Upstream Tracking We continue to monitor and selectively adopt changes from the upstream repository. ### Implemented from Upstream - ✅ [#1513] - JSONEqBytes - ✅ [#1803] - Kind/NotKind assertions - ✅ [#1805] - IsOfTypeT[T] generic assertions - ✅ [#1685] - Partial iterator support (SeqContainsT variants) - ✅ [#1828] - Spew panic fixes - ✅ [#1611] - Goroutine leak in Eventually/Never (drove the context-based pollCondition refactor) - ✅ [#1825], [#1818], [#1223], [#1813], [#1822], [#1829] - Various bug fixes - ✅ [#1606], [#1087] - Consistently assertion - ✅ [#1848] - Subset error message - ✅ [#1839] - `InEpsilonSymmetric` (number equality with symmetric role) - ✅ [#1840] - JSON/YAML `Redactor` pattern (dynamic input redaction, inspired by Insta) - ✅ [#1859] - Channel assertions (`Blocked` / `NotBlocked`) ### Superseded by Our Implementation - ✅ [#1801] - Error message on large collections for `Len` - ⛔ [#1845] - Fix Eventually/Never regression (superseded by context-based pollCondition) - ✅ [#1830] - CollectT.Halt() (implemented as `CollectT.Cancel()` in v2.4 — see [CHANGES](./CHANGES.md)) - ✅ [#1819] - Handle unexpected exits (handled by per-tick goroutine wrap in v2.4) - ⛔ [#1824] - Spew testing (superseded by property-based fuzzing) [#1830]: https://github.com/stretchr/testify/pull/1830 [#1824]: https://github.com/stretchr/testify/pull/1824 **Review frequency**: Quarterly (next review: May 2026) --- [#1223]: https://github.com/stretchr/testify/pull/1223 [#1513]: https://github.com/stretchr/testify/pull/1513 [#1611]: https://github.com/stretchr/testify/issues/1611 [#1685]: https://github.com/stretchr/testify/pull/1685 [#1803]: https://github.com/stretchr/testify/pull/1803 [#1805]: https://github.com/stretchr/testify/issues/1805 [#1813]: https://github.com/stretchr/testify/issues/1813 [#1818]: https://github.com/stretchr/testify/pull/1818 [#1822]: https://github.com/stretchr/testify/issues/1822 [#1825]: https://github.com/stretchr/testify/pull/1825 [#1828]: https://github.com/stretchr/testify/pull/1828 [#1829]: https://github.com/stretchr/testify/issues/1829 ## Appendix: Upstream References This table catalogs all upstream PRs and issues from [github.com/stretchr/testify](https://github.com/stretchr/testify) that we have processed. ### Implemented (Adapted or Merged) | Reference | Type | Summary | Outcome in Fork | |-----------|------|---------|-----------------| | [#994] | PR | Colorize expected vs actual values | ✅ Adapted into `enable/color` module with themes and configuration | | [#1087] | Issue | Consistently assertion | ✅ Adapted | | [#1223] | PR | Display uint values in decimal instead of hex | ✅ Merged - Applied to diff output | | [#1232] | PR | Colorized output for expected/actual/errors | ✅ Adapted into `enable/color` module | | [#1356] | PR | panic(nil) handling for Go 1.21+ | ✅ Merged - Updated panic assertions | | [#1467] | PR | Colorized output with terminal detection | ✅ Adapted into `enable/color` module (most mature implementation) | | [#1480] | PR | Colorized diffs via TESTIFY_COLORED_DIFF env var | ✅ Adapted with env var support in `enable/color` | | [#1513] | PR | JSONEqBytes for byte slice JSON comparison | ✅ Merged - Added to JSON domain | | [#1606] | PR | Consistently assertion | ✅ Adapted | | [#1611] | Issue | Goroutine leak in Eventually/Never | ✅ Fixed by using context.Context (consolidation into single pollCondition function) | | [#1685] | PR | Iterator support (`iter.Seq`) for Contains/ElementsMatch | ✅ Partial - Implemented SeqContainsT and SeqNotContainsT only | | [#1772] | PR | YAML library migration to maintained fork | ✅ Adapted - Used gopkg.in/yaml.v3 in optional `enable/yaml` module | | [#1797] | PR | Codegen package consolidation and licensing | ✅ Adapted - Complete rewrite of code generation system | | [#1803] | PR | Kind/NotKind assertions | ✅ Merged - Added to Type domain | | [#1805] | Issue | Generic `IsOfType[T]()` without dummy value | ✅ Implemented - IsOfTypeT and IsNotOfTypeT in Type domain | | [#1813] | Issue | Panic with unexported fields | ✅ Fixed via #1828 in internalized spew | | [#1816] | Issue | Fix panic on unexported struct key in map | ✅ Fixed in internalized go-spew | | [#1818] | PR | Fix panic on invalid regex in Regexp/NotRegexp | ✅ Merged - Added graceful error handling | | [#1822] | Issue | Deterministic map ordering in diffs | ✅ Fixed in internalized go-spew | | [#1825] | PR | Fix panic using EqualValues with incomparable types | ✅ Merged - Enhanced type safety in EqualValues | | [#1826] | Issue | Type safety with spew (meta-issue) | ✅ Addressed through comprehensive fuzzing and fixes | | [#1828] | PR | Fixed panic with unexported fields in maps | ✅ Merged into internalized go-spew | | [#1829] | Issue | Fix time.Time rendering in diffs | ✅ Fixed in internalized go-spew | | [#1848] | PR | Subset (garbled error message) | ✅ Adapted | | [#1839] | PR | Number equality with symmetric role | ✅ Adapted | | [#1840] | Issue | JSON presence check without exact values | ✅ Adapted | | [#1859] | Issue | Channel assertions | ✅ Adapted | [#994]: https://github.com/stretchr/testify/pull/994 [#1232]: https://github.com/stretchr/testify/pull/1232 [#1356]: https://github.com/stretchr/testify/pull/1356 [#1467]: https://github.com/stretchr/testify/pull/1467 [#1480]: https://github.com/stretchr/testify/pull/1480 [#1576]: https://github.com/stretchr/testify/pull/1576 [#1601]: https://github.com/stretchr/testify/issues/1601 [#1772]: https://github.com/stretchr/testify/pull/1772 [#1797]: https://github.com/stretchr/testify/pull/1797 [#1816]: https://github.com/stretchr/testify/issues/1816 [#1826]: https://github.com/stretchr/testify/issues/1826 [#1829]: https://github.com/stretchr/testify/issues/1829 [#1087]: https://github.com/stretchr/testify/issues/1087 [#1606]: https://github.com/stretchr/testify/pull/1606 [#1839]: https://github.com/stretchr/testify/pull/1839 [#1840]: https://github.com/stretchr/testify/issues/1840 [#1848]: https://github.com/stretchr/testify/pull/1848 [#1859]: https://github.com/stretchr/testify/pull/1859 ### Superseded by Our Implementation | Reference | Type | Summary | Why Superseded | |-----------|------|---------|----------------| | [#1801] | Issue | `Len` error printing large collections | Duplicate issue upstream. This is fixed by generalized truncation | | [#1845] | PR | Fix Eventually/Never regression | Superseded by context-based pollCondition implementation (we don't have this bug) | | [#1819] | PR | Handle unexpected exits in Eventually | Implemented in v2.4 via per-tick goroutine wrap — a `runtime.Goexit` in the condition only aborts the current tick | | [#1824] | PR | Spew testing improvements | Superseded by property-based fuzzing with random type generator | | [#1830] | PR | `CollectT.Halt()` for stopping tests | Implemented in v2.4 as `CollectT.Cancel()` — see [CHANGES](./CHANGES.md) | ### Under Consideration (Monitoring) | Reference | Type | Summary | Status | |-----------|------|---------|--------| | [#1576] | Issue/PR | `EqualValues` assertion | 🔍 Monitoring [#1863]- Wrong equality when comparing float32 and float64| | [#1860] | Issue+PR | `ErrorAsType[E]` for Go 1.26+ - PR: [#1861] | 🔍 Monitoring - Interesting UX syntax | ### Informational (Not Implemented) | Reference | Type | Summary | Outcome | |-----------|------|---------|---------| | [#1147] | Issue | General discussion about generics adoption | ℹ️ Marked "Not Planned" upstream - We implemented our own generics approach ({{% siteparam "metrics.generics" %}} functions) | | [#1308] | PR | Comprehensive refactor with generic type parameters | ℹ️ Draft for v2.0.0 upstream - We took a different approach with the same objective | | [#1591], [#1601] | PR + Issue | `NoFieldIsZero` recursive zero-value assertion | ⛔ **Won't do** - Considered and prototyped (2026-04-26). Same conclusion as upstream maintainers: semantics is too ambiguous (map keys, []byte, pointer targets, unexported fields, cycles, time.Time-style smart-zero types) and overlaps too heavily with [Equal](...) for legitimate use cases. Each pitfall fix adds a knob; full version is a struct validator, not an assertion. | | [#1862] | Issue | `CollectT` redesign / `testing.TB` interop | ⛔ **Won't do (for now)** - Studied in depth (2026-04-17). All four design options (interface widening, embedding `*testing.T`, opt-in `CollectTB` wrapper, `CollectT`-as-interface) carry visible costs; Go's `testing.TB.private()` blocks any clean proxy. Workaround for affected users is a 3-line per-helper adapter. Revisit if traction warrants the breaking churn. | [#1147]: https://github.com/stretchr/testify/issues/1147 [#1308]: https://github.com/stretchr/testify/pull/1308 [#1591]: https://github.com/stretchr/testify/pull/1591 [#1576]: https://github.com/stretchr/testify/pull/1576 [#1801]: https://github.com/stretchr/testify/pull/1801 [#1819]: https://github.com/stretchr/testify/pull/1819 [#1845]: https://github.com/stretchr/testify/pull/1845 [#1859]: https://github.com/stretchr/testify/pull/1859 [#1860]: https://github.com/stretchr/testify/pull/1860 [#1861]: https://github.com/stretchr/testify/pull/1861 [#1862]: https://github.com/stretchr/testify/pull/1862 [#1863]: https://github.com/stretchr/testify/pull/1863 ### Summary Statistics | Category | Count | |----------|-------| | **Implemented/Merged** | 27 | | **Superseded** | 5 | | **Monitoring** | 2 | | **Informational** | 4 | | **Total Processed** | 38 | **Note**: This fork maintains an active relationship with upstream, regularly reviewing new PRs and issues. The quarterly review process ensures we stay informed about upstream developments while maintaining our architectural independence. --- ## See Also - [Changes from v1](./CHANGES.md) - Complete list of all implemented changes and new features - [Roadmap](../project/maintainers/ROADMAP.md) - Future plans and development priorities - [Architecture](../project/maintainers/ARCHITECTURE.md) - Technical architecture and design decisions - [Original Project](../project/maintainers/ORIGINAL.md) - History and acknowledgments --- go-openapi-testify-c10ca71/docs/doc-site/usage/TUTORIAL.md000066400000000000000000000345161520301377500232020ustar00rootroot00000000000000--- title: "Tutorial" description: "How to write great tests with go and testify" weight: 3 --- {{% notice primary "TL;DR" "meteor" %}} > Master the iterator pattern for table-driven tests, understand when to use `require` vs `assert`, and learn testify best practices. > Essential patterns and anti-patterns for writing maintainable, effective Go tests. {{% /notice %}} ## What makes a good test? A good test is: 1. **Focused** - Tests one logical concept 2. **Independent** - Can run in any order, in parallel 3. **Repeatable** - Same input always produces same result 4. **Fast** - Runs quickly to encourage frequent execution 5. **Have clear expectations** - Failure messages immediately show what broke **With testify, you write tests that read like documentation:** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestUserCreation(t *testing.T) { user := CreateUser("alice@example.com") require.NotNil(t, user) assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will fail and stop before assert.True(t, user.Active) } ``` {{% notice style="tip" title="tip" icon="meteor" %}} Adopt a test layout similar to your functionality. ```sh # ❌ Don't do this - confusing boolean.go file.go all_test.go ``` ```go // ✅ Better - clear mapping between features and tests boolean.go boolean_test.go file.go file_test.go ``` {{% /notice %}} The assertions are self-documenting - you can read the test and immediately understand what behavior is being verified. --- ## Patterns ### Simple test logic Oftentimes, much of the test logic can be replaced by a proper use of `require`. ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) // ❌ Don't do this - repetitive and hard to maintain func TestUserCreation(t *testing.T) { user := CreateUser("alice@example.com") if assert.NotNil(t, user) { assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will skip this test assert.True(t, user.Active) } } ``` ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) // ✅ Better - linear flow, no indented subcases func TestUserCreation(t *testing.T) { user := CreateUser("alice@example.com") require.NotNil(t, user) assert.Equal(t, "alice@example.com", user.Email) // if user is nil, will fail and stop before assert.True(t, user.Active) } ``` ### Table-Driven Tests with Iterator Pattern The **iterator pattern** is the idiomatic way to write table-driven tests in Go 1.23+. This repository uses it extensively, and you should too. #### Why Table-Driven Tests? Instead of writing separate test functions for each case: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) // ❌ Don't do this - repetitive and hard to maintain func TestAdd_PositiveNumbers(t *testing.T) { result := Add(2, 3) assert.Equal(t, 5, result) } func TestAdd_NegativeNumbers(t *testing.T) { result := Add(-2, -3) assert.Equal(t, -5, result) } func TestAdd_MixedSigns(t *testing.T) { result := Add(-2, 3) assert.Equal(t, 1, result) } ``` Write one test function with multiple cases: ```go // ✅ Better - all cases in one place func TestAdd(t *testing.T) { // All test cases defined once // Test logic written once // Easy to add new cases for c := range addTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := Add(c.a, c.b) assert.Equal(t, c.expected, result) }) } } func addTestCases() iter.Seq[addTestCase] { ... } ``` #### The Iterator Pattern **Structure:** ```go import ( "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) // 1. Define a test case struct type addTestCase struct { name string a, b int expected int } // 2. Create an iterator function returning iter.Seq[T] func addTestCases() iter.Seq[addTestCase] { return slices.Values([]addTestCase{ { name: "positive numbers", a: 2, b: 3, expected: 5, }, { name: "negative numbers", a: -2, b: -3, expected: -5, }, { name: "mixed signs", a: -2, b: 3, expected: 1, }, { name: "with zero", a: 0, b: 5, expected: 5, }, }) } // 3. Test function iterates over cases using range func TestAdd(t *testing.T) { t.Parallel() for c := range addTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result := Add(c.a, c.b) assert.Equal(t, c.expected, result) }) } } ``` #### Why This Pattern Is Better **Clean separation of concerns:** - Test data (in iterator function) separate from test logic (in test function) - Easy to see all test cases at a glance - Easy to add new cases without touching test logic **Type safety:** - Compiler enforces struct fields - No risk of wrong number of arguments - IDE autocomplete works perfectly **Excellent for parallel execution:** - Both the outer test and subtests can run in parallel - `t.Parallel()` catches race conditions early **Reusable:** - Iterator functions can be reused across multiple test functions - Share test cases between related tests **Maintainable:** - Adding a case: just append to the slice - Changing test logic: edit one place - Renaming fields: IDE refactoring works #### Comparison with Traditional Pattern **Traditional inline pattern:** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestAdd(t *testing.T) { tests := []struct { name string a, b int expected int }{ {"positive", 2, 3, 5}, {"negative", -2, -3, -5}, // Test data mixed with test function // Hard to reuse // No named fields - order matters } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { result := Add(tt.a, tt.b) assert.Equal(t, tt.expected, result) }) } } ``` **Iterator pattern:** ```go import ( "iter" "slices" "testing" ) // Test logic separate and clean func TestAdd(t *testing.T) { t.Parallel() for c := range addTestCases() { // Clean iteration // ... } } type addTestCase struct { name string a, b int expected int } // Test data in separate function - clean, reusable func addTestCases() iter.Seq[addTestCase] { return slices.Values([]addTestCase{ { name: "positive numbers", // Named fields a: 2, // Self-documenting b: 3, expected: 5, }, // More cases... }) } ``` #### When to Use Iterator Pattern **Always use it for:** - Any test with 2+ test cases - Tests requiring complex setup per case - Tests that benefit from parallel execution - Any table-driven test scenario **Example - complex setup:** ```go import ( "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserValidation(t *testing.T) { t.Parallel() for c := range userValidationCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() err := ValidateUser(c.user) if c.shouldErr { assert.Error(t, err) assert.ErrorContains(t, err, c.errMsg) } else { assert.NoError(t, err) } }) } } type userValidationCase struct { name string user User shouldErr bool errMsg string } func userValidationCases() iter.Seq[userValidationCase] { return slices.Values([]userValidationCase{ { name: "valid user", user: User{ Name: "Alice", Email: "alice@example.com", Age: 25, }, shouldErr: false, }, { name: "missing email", user: User{ Name: "Bob", Age: 30, }, shouldErr: true, errMsg: "email is required", }, { name: "invalid age", user: User{ Name: "Charlie", Email: "charlie@example.com", Age: -5, }, shouldErr: true, errMsg: "age must be positive", }, }) } ``` --- ### Using testify with Iterator Pattern The iterator pattern works beautifully with testify's forward methods: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserOperations(t *testing.T) { t.Parallel() for c := range userOperationCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() a := assert.New(t) // Forward assertion object user := PerformOperation(c.input) // Clean assertions without repeating 't' a.NotNil(user) a.Equal(c.expectedName, user.Name) a.Greater(user.ID, 0) }) } } ``` --- ### Helper Functions with t.Helper() When extracting common assertions into helper functions, use `t.Helper()` to get better error messages: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func assertUserValid(t *testing.T, user *User) { t.Helper() // Makes test failures point to the caller assert.NotNil(t, user) assert.NotEmpty(t, user.Name) assert.NotEmpty(t, user.Email) assert.Greater(t, user.Age, 0) } func TestUserCreation(t *testing.T) { user := CreateUser("alice@example.com") // If this fails, error points HERE, not inside assertUserValid assertUserValid(t, user) } ``` Without `t.Helper()`, failures would show the line number inside `assertUserValid`, making it harder to find the actual failing test. --- ### Parallel Test Execution Always use `t.Parallel()` unless you have a specific reason not to: ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestAdd(t *testing.T) { t.Parallel() // Outer test runs in parallel for c := range addTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() // Each subtest runs in parallel result := Add(c.a, c.b) assert.Equal(t, c.expected, result) }) } } ``` **Benefits:** - Tests run faster - Catches race conditions and shared state bugs - Encourages writing independent tests **When NOT to use parallel:** - Tests that modify global state - Tests that use the same external resource (file, database, etc.) - Integration tests with shared setup --- ### Setup and Teardown Use `defer` for cleanup: ```go func TestDatabaseOperations(t *testing.T) { db := setupTestDatabase(t) t.Cleanup(func() { _ = db.Close() // Always runs, even if test fails } user := &User{Name: "Alice"} err := db.Save(user) require.NoError(t, err) // Stop if save fails loaded, err := db.Find(user.ID) require.NoError(t, err) assert.Equal(t, "Alice", loaded.Name) } ``` **Pattern for resources:** 1. Create resource 2. Immediately defer cleanup 3. Use the resource 4. Cleanup happens automatically --- ### Edge Cases to Test Always include these test categories: #### 1. Empty/Zero Values ```go { name: "empty string", input: "", expected: defaultValue, }, { name: "nil slice", input: nil, expected: emptyResult, }, ``` #### 2. Single Element ```go { name: "single item", input: []string{"only"}, expected: "only", }, ``` #### 3. Multiple Elements ```go { name: "multiple items", input: []string{"first", "second", "third"}, expected: "first,second,third", }, ``` #### 4. Boundary Conditions ```go { name: "maximum value", input: math.MaxInt64, expected: overflow, }, { name: "special characters", input: "hello@#$%world", expected: sanitized, }, ``` --- ### Testing Errors **Bad practice - checking error string:** ```go // ❌ Fragile - breaks if error message changes if err == nil || err.Error() != "division by zero" { t.Error("wrong error") } ``` **Good practice - checking error chain:** ```go // ✅ Semantic error checking assert.Error(t, err) assert.ErrorContains(t, err, "division") // ✅ Check error type (possibly wrapped) assert.ErrorIs(t, err, ErrDivisionByZero) // ✅ Check for specific error message assert.EqualError(t, err, "division by zero") ``` --- ### Complete Example Here's a complete example showing all patterns together: ```go package calculator_test import ( "iter" "slices" "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) type divideTestCase struct { name string a, b float64 expected float64 shouldErr bool } func divideTestCases() iter.Seq[divideTestCase] { return slices.Values([]divideTestCase{ { name: "positive numbers", a: 10, b: 2, expected: 5, shouldErr: false, }, { name: "negative dividend", a: -10, b: 2, expected: -5, shouldErr: false, }, { name: "division by zero", a: 10, b: 0, shouldErr: true, }, { name: "zero dividend", a: 0, b: 5, expected: 0, shouldErr: false, }, }) } func TestDivide(t *testing.T) { t.Parallel() for c := range divideTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() result, err := Divide(c.a, c.b) if c.shouldErr { assert.Error(t, err) assert.ErrorIs(t, err, ErrDivisionByZero) } else { require.NoError(t, err) assert.Equal(t, c.expected, result) } }) } } ``` --- ## Best Practices Summary 1. **Use the iterator pattern** - `iter.Seq[T]` for all table-driven tests 2. **Separate test data from test logic** - Iterator functions are your test data 3. **Use `t.Parallel()`** - Both outer tests and subtests 4. **Use `t.Helper()`** - In assertion helper functions 5. **Use `require` for preconditions** - Stop test if setup fails 6. **Use `assert` for checks** - See all failures 7. **Test edge cases** - Empty, single, multiple, boundary conditions 8. **Use forward methods** - `assert.New(t)` for tests with many assertions 9. **Use semantic error checking** - `ErrorIs`, `ErrorContains`, not string comparison 10. **Keep tests focused** - One logical concept per test --- ## Examples in This Repository See real-world usage of these patterns: - **Iterator Pattern**: `codegen/internal/generator/funcmaps/funcmaps_test.go` - **Domain Tests**: `codegen/internal/generator/domains/domains_test.go` - **Assertion Tests**: `internal/assertions/*_test.go` - **Comprehensive Coverage**: `codegen/internal/scanner/comments-parser/` (all test files) **Study these to see the patterns in action!** --- ## See Also - [Examples](./EXAMPLES.md) - Practical code examples covering common testing scenarios - [Usage Guide](./USAGE.md) - API conventions and how to navigate the documentation - [Generics Guide](./GENERICS.md) - Type-safe assertions with compile-time checking - [Migration Guide](./MIGRATION.md) - Migrating from stretchr/testify v1 - [API Reference](../api/_index.md) - Complete assertion catalog organized by domain go-openapi-testify-c10ca71/docs/doc-site/usage/USAGE.md000066400000000000000000000504051520301377500225760ustar00rootroot00000000000000--- title: Usage description: "Introduction Guide" weight: 1 --- {{% notice primary "TL;DR" "meteor" %}} > Learn testify's naming conventions (assert vs require, format variants, generic `T` suffix), argument order patterns, and how to navigate > {{% siteparam "metrics.assertions" %}} assertions organized into {{% siteparam "metrics.domains" %}} domains. Start here to understand the API structure. {{% /notice %}} Testify v2 provides **{{% siteparam "metrics.functions" %}} functions** ({{% siteparam "metrics.assertions" %}} assertions including {{% siteparam "metrics.generics" %}} generic variants, plus {{% siteparam "metrics.helpers" %}} helper functions) organized into {{% siteparam "metrics.domains" %}} domains. This guide explains how to navigate the API and use the naming conventions effectively. ## How the API is Organized Assertions are grouped by domain for easier discovery: | Domain | Examples | Count | |--------|----------|-------| | **Boolean** | `True`, `False` | {{% siteparam "metrics.by_domain.boolean.count" %}} | | **Collection** | `Contains`, `Len`, `Empty`, `ElementsMatch` | {{% siteparam "metrics.by_domain.collection.count" %}} | | **Comparison** | `Greater`, `Less`, `Positive` | {{% siteparam "metrics.by_domain.comparison.count" %}} | | **Condition** | `Eventually`, `Never`, `Consistently` | {{% siteparam "metrics.by_domain.condition.count" %}} | | **Equality** | `Equal`, `NotEqual`, `EqualValues`, `Same`, `Exactly` | {{% siteparam "metrics.by_domain.equality.count" %}} | | **Error** | `Error`, `NoError`, `ErrorIs`, `ErrorAs`, `ErrorContains` | {{% siteparam "metrics.by_domain.error.count" %}} | | **File** | `FileExists`, `DirExists`, `FileEmpty` | {{% siteparam "metrics.by_domain.file.count" %}} | | **HTTP** | `HTTPSuccess`, `HTTPRedirect`, `HTTPError` | {{% siteparam "metrics.by_domain.http.count" %}} | | **JSON** | `JSONEq`, `JSONEqBytes`, `JSONMarshalAs` | {{% siteparam "metrics.by_domain.json.count" %}} | | **Number** | `InDelta`, `InEpsilon` | {{% siteparam "metrics.by_domain.number.count" %}} | | **Ordering** | `IsIncreasing`, `Sorted` | {{% siteparam "metrics.by_domain.ordering.count" %}} | | **Panic** | `Panics`, `NotPanics` | {{% siteparam "metrics.by_domain.panic.count" %}} | | **Safety** | `NoGoRoutineLeak`, `NoFileDescriptorLeak` | {{% siteparam "metrics.by_domain.safety.count" %}} | | **String** | `Regexp`, `NotRegexp` | {{% siteparam "metrics.by_domain.string.count" %}} | | **Testing** | `CallerInfo`, `FailNow` | {{% siteparam "metrics.by_domain.testing.count" %}} | | **Time** | `WithinDuration`, `WithinRange` | {{% siteparam "metrics.by_domain.time.count" %}} | | **Type** | `IsType`, `Implements`, `Zero` | {{% siteparam "metrics.by_domain.type.count" %}} | | **YAML** | `YAMLEq`, `YAMLEqBytes`, `YAMLMarshalAs` | {{% siteparam "metrics.by_domain.yaml.count" %}} | {{% notice style="info" title="Browse by Domain" icon="book" %}} See the complete [API Reference](../api/_index.md) organized by domain for a detailed documentation of all assertions. {{% /notice %}} ## Navigating the Documentation ### Quick Reference - **[Examples](./EXAMPLES.md)** - Practical code examples for common testing scenarios - **[API Reference](../api/_index.md)** - Complete assertion catalog organized by domain - **[Generics Guide](../GENERICS.md)** - Using type-safe assertions with the `T` suffix - **[Changes](../CHANGES.md)** - All changes since fork from stretchr/testify - **[pkg.go.dev](https://pkg.go.dev/github.com/go-openapi/testify/v2)** - godoc API reference with full signatures ### Finding the Right Assertion 1. Browse the [API Reference](../api/_index.md) by domain (e.g., "Collection" for slice operations) 2. Search in the [API Reference](../api/_index.md) (use search box) 3. Check (or search) the provided [Examples](./EXAMPLES.md) for practical usage patterns 4. Check [pkg.go.dev](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert) for alphabetical listing 5. Use your editor's Go to Definition on any assertion 6. Use your IDE's autocomplete - type `assert.` and explore ## API Conventions Understanding the naming patterns helps you find and use the right assertions quickly. ### Package Choice: `assert` vs `require` {{< cards >}} {{% card title="assert - Non-Fatal" %}} **Use when**: Tests should continue after failures to gather more context ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { user := getUser() assert.NotNil(t, user) // ✓ Returns false assert.Equal(t, "Alice", user.Name) // Still runs assert.True(t, user.Active) // Still runs } ``` **Returns**: `bool` indicating success/failure {{% /card %}} {{% card title="require - Fatal" %}} **Use when**: Test cannot continue meaningfully after failure ```go import ( "testing" "github.com/go-openapi/testify/v2/require" ) func TestUser(t *testing.T) { user := getUser() require.NotNil(t, user) // ✓ Calls t.FailNow() if fails require.Equal(t, "Alice", user.Name) // Safe to proceed require.True(t, user.Active) // user is guaranteed non-nil } ``` **Returns**: Nothing (void) - stops test on failure {{% /card %}} {{< /cards >}} ### Function Variants Each assertion comes in multiple variants following consistent patterns: | Pattern | Example | Description | |---------|---------|-------------| | **Base** | `Equal(t, expected, actual)` | Standard assertion | | **Format** (`f` suffix) | `Equalf(t, expected, actual, "checking %s", name)` | With custom message | | **Generic** (`T` suffix) | `EqualT(t, expected, actual)` | Type-safe variant | | **Generic + Format** (`Tf` suffix) | `EqualTf(t, expected, actual, "checking %s", name)` | Type-safe with message | The `f` suffix follows Go's standard library convention (like `Printf`, `Errorf`): it accepts a format string followed by arguments for custom failure messages. {{% notice style="tip" title="When to Use Each Variant" icon="lightbulb" %}} - **Base/Generic**: Use by default - testify provides detailed failure messages - **Format variants**: Add context when testing similar values in loops or complex scenarios - **Generic (`T` suffix)**: Prefer for compile-time type safety and better performance {{% /notice %}} ### Inverse Assertions Most assertions come with their opposite variant, typically formed by adding a `Not` prefix: | Assertion | Inverse | Pattern | |-----------|---------|---------| | `Equal` | `NotEqual` | `Not` prefix | | `Nil` | `NotNil` | `Not` prefix | | `Empty` | `NotEmpty` | `Not` prefix | | `Contains` | `NotContains` | `Not` prefix | | `Zero` | `NotZero` | `Not` prefix | | `Same` | `NotSame` | `Not` prefix | | `Panics` | `NotPanics` | `Not` prefix | | `Regexp` | `NotRegexp` | `Not` prefix | **Exceptions:** Some assertions use semantic opposites instead of the `Not` prefix: | Assertion | Inverse | Reason | |-----------|---------|--------| | `True` | `False` | Semantic opposites (`NotTrue` doesn't sound natural) | | `Positive` | `Negative` | Semantic opposites, except for 0 which is neither | | `Greater` | `LessOrEqual` | Comparative opposites (and not `NotGreater`) | | `GreaterOrEqual` | `Less` | Comparative opposites | {{% notice style="info" title="Why Semantic Opposites?" icon="question" %}} These exceptions follow natural English usage: - Testing for `False` is clearer than testing for "not true" - (strictly) `Negative` numbers are semantically opposite to (strictly) `Positive`, unless when `Zero`, and not "not positive" - `Less` is the natural opposite of `Greater` in comparisons {{% /notice %}} **More semantic opposites:** | Assertion | Inverse | Reason | |-----------|---------|--------| | `Eventually` | `Never` | Semantic opposites for polling conditions | **Not inverses:** Some assertions are independent checks, not inverses of each other: | Assertions | Relationship | |------------|--------------| | `IsIncreasing` / `IsDecreasing` | Independent checks (a sequence can be neither) | | `IsNonIncreasing` / `IsNonDecreasing` | Independent checks (a sequence can be neither) | | `Sorted` / `NotSorted` | True inverse pair using `Not` prefix | **Generic variants:** All inverse assertions have corresponding generic variants (suffix `T` or `Tf`): - `NotEqualT`, `FalseT`, `NegativeT`, `IsDecreasingT`, etc. ### Argument Order Patterns Most assertions follow the **"expected, actual"** pattern, but several categories use different conventions: #### Standard Pattern: Expected, Actual The majority of assertions check an actual value against an expected value: ```go assert.Equal(t, expected, actual) assert.NotEqual(t, expected, actual) assert.InDelta(t, expected, actual, delta) assert.JSONEq(t, expected, actual) assert.YAMLEq(t, expected, actual) assert.WithinDuration(t, expected, actual, delta) assert.Implements(t, (*interface)(nil), object) // Expected interface, actual object ``` #### Comparison Operators: e1, e2 Comparison assertions express the relationship directly (reads as "assert e1 > e2"): ```go assert.Greater(t, e1, e2) // Asserts: e1 > e2 assert.GreaterOrEqual(t, e1, e2) // Asserts: e1 >= e2 assert.Less(t, e1, e2) // Asserts: e1 < e2 assert.LessOrEqual(t, e1, e2) // Asserts: e1 <= e2 ``` #### Exceptions using Different Argument Orders {{% tabs %}} {{% tab title="Unary checks" color=green %}} **Unary checks** (test a single value): ```go assert.True(t, value) assert.False(t, value) assert.Nil(t, value) assert.Empty(t, object) assert.Zero(t, value) assert.Positive(t, value) assert.Negative(t, value) assert.Error(t, err) assert.NoError(t, err) assert.Panics(t, panicFunc) ``` {{% /tab %}} {{% tab title="Object-first" color=green %}} **Object-first pattern** (object under test, then expected property): ```go assert.Len(t, object, expectedLength) // Object first, expected length second assert.IsType(t, expectedType, object) // Expected type first, object second ``` {{% /tab %}} {{% tab title="Container-first" color=green %}} **Container-first pattern** (container, then element/subset): ```go assert.Contains(t, container, element) // Container first, element second assert.StringContains(t, str, substring) // String first, substring second assert.SliceContains(t, slice, element) // Slice first, element second assert.Subset(t, list, subset) // Superset first, subset second assert.ElementsMatch(t, listA, listB) // Either order works (symmetric) ``` {{% /tab %}} {{% tab title="Error assertions" color=green %}} **Error assertions** (error first, then expected property): ```go assert.ErrorContains(t, err, substring) // Error first, expected substring second assert.ErrorIs(t, err, target) // Error first, target error second assert.ErrorAs(t, err, &target) // Error first, target pointer second assert.EqualError(t, err, expectedString) // Error first, expected message second ``` {{% /tab %}} {{% tab title="Special cases" color=green %}} **Special cases**: ```go assert.HTTPSuccess(t, handler, method, url, values) // Handler first, HTTP params follow assert.Eventually(t, condition, waitFor, tick) // Condition first, timing params follow ``` {{% /tab %}} {{% /tabs %}} {{% notice style="tip" title="Finding Argument Order" icon="lightbulb" %}} When unsure about argument order: - Check the [API Reference](../api/_index.md) for detailed signatures - Use IDE autocomplete to see parameter names - Consult [pkg.go.dev](https://pkg.go.dev/github.com/go-openapi/testify/v2/assert) for complete documentation {{% /notice %}} ### Forward Methods Create an `Assertions` object to reduce repetition in tests with many assertions. **Both styles are equivalent** - choose based on your preference and test structure. **⚠️ Generic assertions are not directly available as forward methods** (this is a limitation of go generics). However, the forward-style assertion may use generic assertions like shown below. {{< tabs >}} {{% tab title="Package-Level Functions" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { user := getUser() assert.NotNil(t, user) assert.Equal(t, "Alice", user.Name) assert.True(t, user.Active) assert.Greater(t, user.Age, 0) } ``` {{% /tab %}} {{% tab title="Forward Methods" %}} ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { a := assert.New(t) // Create once user := getUser() a.NotNil(user) // No 't' needed a.Equal("Alice", user.Name) a.True(user.Active) } ``` {{% /tab %}} {{% tab title="Forward Methods with generics" %}} This pattern overcomes the go limitation so you may use a "forward-style" and still benefit from generics. ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUser(t *testing.T) { a := assert.New(t) // Create once user := getUser() a.NotNil(user) // No 't' needed assert.EqualT(a.T, "Alice", user.Name) // use a.T as an escape hatch to call generics a.True(user.Active) } ``` {{% /tab %}} {{< /tabs >}} ## Common Usage Patterns {{% tabs %}} {{% tab title="Table-driven tests" color=green %}} **Pattern 1: Table-Driven Tests** ```go import ( "slices" "testing" "github.com/go-openapi/testify/v2/assert" ) func TestCalculation(t *testing.T) { tests := slices.Values([]struct { name string input int expected int }{ {"positive", 5, 25}, {"negative", -3, 9}, {"zero", 0, 0}, }) for tt := range tests { t.Run(tt.name, func(t *testing.T) { result := square(tt.input) assert.Equal(t, tt.expected, result) }) } } ``` {{% /tab %}} {{% tab title="Multiple assertions" color=green %}} **Pattern 2: Multiple Assertions (assert for context)** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestUserValidation(t *testing.T) { user := createUser() // Use assert to see all failures assert.NotEmpty(t, user.Name) // Check name assert.NotEmpty(t, user.Email) // Check email assert.Greater(t, user.Age, 0) // Check age // All assertions run - see complete picture } ``` {{% /tab %}} {{% tab title="Early exit" color=green %}} **Pattern 3: Early Exit (use require for prerequisites)** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestDatabaseQuery(t *testing.T) { db := connectDB() require.NotNil(t, db) // Stop if no connection result := db.Query("SELECT * FROM users") require.NoError(t, result.Error) // Stop if query fails // Safe to proceed - db and result are valid assert.NotEmpty(t, result.Rows) } ``` {{% /tab %}} {{% tab title="Type-safe" color=green %}} **Pattern 4: Type-Safe Generics** ```go import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestTypeSafety(t *testing.T) { expected := []int{1, 2, 3} actual := getNumbers() // Compiler checks types at compile time assert.ElementsMatchT(t, expected, actual) assert.GreaterT(t, len(actual), 0) // If getNumbers() changes return type, // compiler catches it immediately } ``` {{% /tab %}} {{% /tabs %}} ## Getting Started 1. **Import the package:** ```go import "github.com/go-openapi/testify/v2/assert" // or import "github.com/go-openapi/testify/v2/require" ``` 2. **Choose your style:** - Package-level: `assert.Equal(t, expected, actual)` - Forward methods: `a := assert.New(t); a.Equal(expected, actual)` 3. **Explore by domain:** - Browse [API Reference](../api/_index.md) to discover assertions - Check [Examples](./EXAMPLES.md) for practical patterns 4. **Use generics for type safety:** - See [Generics Guide](./GENERICS.md) for type-safe assertions - Add `T` suffix for compile-time type checking ## Best Practices ✅ **Do:** - Use `require` for prerequisites that make subsequent assertions meaningless (or will possibly panic) - Use `assert` when you want to see all failures in a test - Prefer generic variants (`*T` functions) for compile-time type safety - Use format variants (`*f`) to add context in complex scenarios - Browse by domain in the API reference to discover relevant assertions ❌ **Don't:** - Don't mix `assert` and `require` randomly - be intentional - Don't add unnecessary format messages - testify provides detailed output - Don't ignore compiler errors from generic variants - they reveal design issues - Don't forget that both packages provide the same assertions with different behavior --- {{% notice style="success" title="Ready to Test" icon="check" %}} **Next Steps:** - Explore [Examples](./EXAMPLES.md) for practical usage patterns - Browse the [API Reference](../api/_index.md) to discover assertions - Read the [Generics Guide](./GENERICS.md) for type-safe testing - Check [pkg.go.dev](https://pkg.go.dev/github.com/go-openapi/testify/v2) for complete reference {{% /notice %}} --- ## Customization ### Using a Custom YAML Unmarshaler By default, testify uses `gopkg.in/yaml.v3` for YAML assertions (e.g. `YAMLEq`) when you import the standard `enable/yaml/v2` package. However, you can register a custom YAML unmarshaler to use alternative libraries like [goccy/go-yaml](https://github.com/goccy/go-yaml), either because you need additional features such as colored error messages or better performance. #### How It Works The YAML support in testify works through a registration mechanism: 1. `internal/assertions/yaml.go` calls `yaml.Unmarshal()` - an abstraction layer 2. The abstraction layer panics if no unmarshaler is registered 3. The `enable/yaml/v2` package registers `gopkg.in/yaml.v3` via `init()` when imported (e.g. on blank import) 4. You can register a custom unmarshaler using `enable/stubs/yaml.EnableYAMLWithUnmarshal()` #### Example: Using goccy/go-yaml Create a custom enable package in your test code: ```go package testutil import ( goccyyaml "github.com/goccy/go-yaml" yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" ) func init() { // Register goccy/go-yaml as the YAML unmarshaler yamlstub.EnableYAMLWithUnmarshal(goccyyaml.Unmarshal) } ``` Then import your custom enable package in your tests: ```go // File: mypackage/user_test.go package mypackage import ( "testing" _ "yourmodule/internal/testutil" // Register goccy/go-yaml "github.com/go-openapi/testify/v2/assert" ) func TestUserYAML(t *testing.T) { expected := ` name: Alice email: alice@example.com age: 30 ` actual := serializeUser(getUser()) // Now uses goccy/go-yaml under the hood assert.YAMLEq(t, expected, actual) } ``` #### Why Use a Custom YAML Library? Different YAML libraries offer different trade-offs: **`gopkg.in/yaml.v3` (default):** - De facto standard library for Go YAML - Widely used and well-tested - Complete YAML 1.2 support **`github.com/goccy/go-yaml`:** - Better performance (up to 2-3x faster) - Colored error messages for debugging - Better error reporting with line/column numbers - JSON-like syntax support - Comment preservation (useful for config testing) #### Important Notes 1. **Register once:** Call `EnableYAMLWithUnmarshal()` only once, typically in an `init()` function 2. **Not concurrent-safe:** The registration is global and should happen during package or main program initialization 3. **Signature compatibility:** The custom unmarshaler must match the signature `func([]byte, any) error` 4. **No mixing:** Don't import both `github.com/go-openapi/testify/enable/yaml/v2` and your custom enable package - choose one #### Advanced: Wrapping Unmarshalers You can also wrap an unmarshaler to add custom behavior: ```go package testutil import ( "fmt" "log" goccyyaml "github.com/goccy/go-yaml" yamlstub "github.com/go-openapi/testify/v2/enable/stubs/yaml" ) func init() { // Wrap the unmarshaler to add logging or validation yamlstub.EnableYAMLWithUnmarshal(func(data []byte, v any) error { // Custom pre-processing if len(data) == 0 { return fmt.Errorf("empty YAML document") } // Call the actual unmarshaler err := goccyyaml.Unmarshal(data, v) // Custom post-processing if err != nil { log.Printf("YAML unmarshal error: %v", err) } return err }) } ``` This pattern allows you to add logging, validation, or transformation logic around any YAML library. --- ## See Also - [Examples](./EXAMPLES.md) - Practical code examples for common testing scenarios - [Tutorial](./TUTORIAL.md) - Comprehensive guide to writing great tests with testify patterns - [Generics Guide](./GENERICS.md) - Type-safe assertions with compile-time checking - [Migration Guide](./MIGRATION.md) - Migrating from stretchr/testify v1 - [API Reference](../api/_index.md) - Complete assertion catalog organized by domain go-openapi-testify-c10ca71/docs/doc-site/usage/_index.md000066400000000000000000000002611520301377500231730ustar00rootroot00000000000000--- title: Usage description: Guides, examples and tutorials. weight: 2 --- Guides, examples and tutorials about `testify/v2`. {{< children type="card" description="true" >}} go-openapi-testify-c10ca71/enable/000077500000000000000000000000001520301377500170675ustar00rootroot00000000000000go-openapi-testify-c10ca71/enable/colors/000077500000000000000000000000001520301377500203705ustar00rootroot00000000000000go-openapi-testify-c10ca71/enable/colors/assertions_test.go000066400000000000000000000065301520301377500241540ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import ( "fmt" "os" "strings" "testing" target "github.com/go-openapi/testify/v2/assert" colorstub "github.com/go-openapi/testify/v2/enable/stubs/colors" ) func TestMain(m *testing.M) { // we can't easily simulate arg flags in CI (uses gotestsum etc). // Similarly, env vars are evaluated too early. colorstub.Enable( func() []colorstub.Option { return []colorstub.Option{ colorstub.WithEnable(true), colorstub.WithSanitizedTheme(flags.theme), } }) os.Exit(m.Run()) } func TestAssertJSONEq(t *testing.T) { t.Parallel() mockT := new(mockT) res := target.JSONEq(mockT, `{"hello": "world", "foo": "bar"}`, `{"hello": "worldwide", "foo": "bar"}`) target.False(t, res) output := mockT.errorString() t.Log(output) // best to visualize the output target.Contains(t, neuterize(output), neuterize(expectedColorizedDiff)) } func TestAssertJSONEq_Array(t *testing.T) { t.Parallel() mockT := new(mockT) res := target.JSONEq(mockT, `["foo", {"hello": "world", "nested": "hash"}]`, `["bar", {"nested": "hash", "hello": "world"}]`) target.False(t, res) output := mockT.errorString() t.Log(output) // best to visualize the output target.Contains(t, neuterize(output), neuterize(expectedColorizedArrayDiff)) } func neuterize(str string) string { // remove blanks and replace escape sequences for readability blankRemover := strings.NewReplacer("\t", "", " ", "", "\x1b", "^[") return blankRemover.Replace(str) } type mockT struct { errorFmt string args []any } // Helper is like [testing.T.Helper] but does nothing. func (mockT) Helper() {} func (m *mockT) Errorf(format string, args ...any) { m.errorFmt = format m.args = args } func (m *mockT) Failed() bool { return m.errorFmt != "" } func (m *mockT) errorString() string { return fmt.Sprintf(m.errorFmt, m.args...) } // captured output (indentation is not checked) // //nolint:staticcheck // indeed we want to check the escape sequences in this test const ( expectedColorizedDiff = ` Not equal: expected: map[string]interface {}{"foo":"bar", "hello":"world"} actual : map[string]interface {}{"foo":"bar", "hello":"worldwide"} Diff: --- Expected +++ Actual @@ -2,3 +2,3 @@  (string) (len=3) "foo": (string) (len=3) "bar", - (string) (len=5) "hello": (string) (len=5) "world" + (string) (len=5) "hello": (string) (len=9) "worldwide"  }  ` expectedColorizedArrayDiff = `Not equal: expected: []interface {}{"foo", map[string]interface {}{"hello":"world", "nested":"hash"}} actual : []interface {}{"bar", map[string]interface {}{"hello":"world", "nested":"hash"}} Diff: --- Expected +++ Actual @@ -1,3 +1,3 @@  ([]interface {}) (len=2) { - (string) (len=3) "foo", + (string) (len=3) "bar",  (map[string]interface {}) (len=2) {  ` ) go-openapi-testify-c10ca71/enable/colors/disable_testintegration.go000066400000000000000000000006111520301377500256230ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 //go:build !testcolorized package colors // integrationTestHatch is specifically designed to force colorized settings in integration tests. // When no build tag "testcolorized" is defined (the default), it returns false and is a noop. func integrationTestHatch() bool { return false } go-openapi-testify-c10ca71/enable/colors/doc.go000066400000000000000000000022221520301377500214620ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package colors enables colorized tests with basic and portable ANSI terminal codes. // // Colorization is disabled by default when the standard output is not a terminal. // // Colors are somewhat limited. We want the package to work on unix and windows without any extra dependencies. // // # Usage // // To enable the test colorization feature, use a blank import like so: // // import ( // _ "github.com/go-openapi/testify/enable/colors/v2" // ) // // # Command line arguments // // - testify.colorized={true|false} // - testify.theme={dark|light} // - testify.colorized.notty={true|false} (enable colorization even when the output is not a terminal) // // The default theme used is dark. // // To run tests on a terminal with colorized output: // // - run: go test -v -testify.colorized ./... // // # Environment variables // // Colorization may be enabled from environment: // // - TESTIFY_COLORIZED=true // - TESTIFY_THEME=dark // - TESTIFY_COLORIZED_NOTTY=true // // Command line arguments take precedence over environment. package colors go-openapi-testify-c10ca71/enable/colors/enable.go000066400000000000000000000034011520301377500221430ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import ( "flag" "os" "strconv" "strings" "golang.org/x/term" colorstub "github.com/go-openapi/testify/v2/enable/stubs/colors" ) const ( envVarColorize = "TESTIFY_COLORIZED" envVarTheme = "TESTIFY_THEME" envVarNoTTY = "TESTIFY_COLORIZED_NOTTY" envVarTestHatch = "TESTIFY_INTEGRATION_TEST" // escape hatch to enable init from CI ) var flags cliFlags //nolint:gochecknoglobals // it's okay to store the state CLI flags in a package global type cliFlags struct { colorized bool theme string notty bool } func init() { //nolint:gochecknoinits // it's okay: we want to declare CLI flags when a blank import references this package isTerminal := term.IsTerminal(1) flag.BoolVar(&flags.colorized, "testify.colorized", colorizeFromEnv(), "testify: colorized output") flag.StringVar(&flags.theme, "testify.theme", themeFromEnv(), "testify: color theme (light,dark)") flag.BoolVar(&flags.notty, "testify.colorized.notty", nottyFromEnv(), "testify: force colorization, even if not a tty") colorstub.Enable( func() []colorstub.Option { return []colorstub.Option{ colorstub.WithEnable(flags.colorized && (isTerminal || flags.notty)), colorstub.WithSanitizedTheme(flags.theme), } }) } func colorizeFromEnv() bool { envColorize := os.Getenv(envVarColorize) isEnvConfigured, _ := strconv.ParseBool(envColorize) return isEnvConfigured || integrationTestHatch() } func themeFromEnv() string { envTheme := os.Getenv(envVarTheme) return strings.ToLower(envTheme) } func nottyFromEnv() bool { envNoTTY := os.Getenv(envVarNoTTY) isEnvNoTTY, _ := strconv.ParseBool(envNoTTY) return isEnvNoTTY || integrationTestHatch() } go-openapi-testify-c10ca71/enable/colors/enable_testintegration.go000066400000000000000000000006731520301377500254560ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 //go:build testcolorized package colors // integrationTestHatch is specifically designed to force colorized settings in integration tests. // When build tag "testcolorized" is defined, it returns true and colorization is enabled, regardless // of command line arguments or environment. func integrationTestHatch() bool { return true } go-openapi-testify-c10ca71/enable/colors/go.mod000066400000000000000000000003631520301377500215000ustar00rootroot00000000000000module github.com/go-openapi/testify/enable/colors/v2 require ( github.com/go-openapi/testify/v2 v2.5.1 golang.org/x/term v0.40.0 ) require golang.org/x/sys v0.41.0 // indirect replace github.com/go-openapi/testify/v2 => ../.. go 1.25.0 go-openapi-testify-c10ca71/enable/colors/go.sum000066400000000000000000000004641520301377500215270ustar00rootroot00000000000000golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k= golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.40.0 h1:36e4zGLqU4yhjlmxEaagx2KuYbJq3EwY8K943ZsHcvg= golang.org/x/term v0.40.0/go.mod h1:w2P8uVp06p2iyKKuvXIm7N/y0UCRt3UfJTfZ7oOpglM= go-openapi-testify-c10ca71/enable/doc.go000066400000000000000000000004421520301377500201630ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package enable contains go modules with extra-dependencies. // // They allow extended capabilities to be added to testify by importing // these packages specifically. package enable go-openapi-testify-c10ca71/enable/stubs/000077500000000000000000000000001520301377500202275ustar00rootroot00000000000000go-openapi-testify-c10ca71/enable/stubs/colors/000077500000000000000000000000001520301377500215305ustar00rootroot00000000000000go-openapi-testify-c10ca71/enable/stubs/colors/enable_colors.go000066400000000000000000000031051520301377500246650ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package colors is an indirection to handle colorized output. // // This package allows the builder to override the indirection with an alternative implementation // of colorized printers. package colors import ( colorstub "github.com/go-openapi/testify/v2/internal/assertions/enable/colors" ) // Enable registers colorized options for pretty-printing the output of assertions. // // The provided enabler defers the initialization, so we may retrieve flags after command line parsing // or other initialization tasks. // // This is not intended for concurrent use. func Enable(enabler func() []Option) { colorstub.Enable(enabler) } // re-exposed internal types. type ( // Option is a colorization option. Option = colorstub.Option // Theme is a colorization theme for testify output. Theme = colorstub.Theme ) const ( ThemeDark = colorstub.ThemeDark ThemeLight = colorstub.ThemeLight ) // WithEnable enables colorization. func WithEnable(enabled bool) Option { return colorstub.WithEnable(enabled) } // WithSanitizedTheme sets a colorization theme from a string. func WithSanitizedTheme(theme string) Option { return colorstub.WithSanitizedTheme(theme) } // WithTheme sets a colorization theme. func WithTheme(theme Theme) Option { return colorstub.WithTheme(theme) } // WithDark sets the [ThemeDark] color theme. func WithDark() Option { return colorstub.WithDark() } // WithLight sets the [ThemeLight] color theme. func WithLight() Option { return colorstub.WithLight() } go-openapi-testify-c10ca71/enable/stubs/colors/enable_colors_test.go000066400000000000000000000026361520301377500257340ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import ( "fmt" "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) { // Test execution is serialized on the sensitive package initialization section // // Proper test coverage of Enable() is assured by the testintegration package. for _, opt := range []Option{ WithSanitizedTheme("light"), WithDark(), WithLight(), WithTheme(ThemeDark), } { mock := new(mockT) target.NotPanics(mock, func() { testEnableWithLock(opt) // executed serially _ = target.JSONEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "worldwide", "foo": "bar"}`) }) // we may call several times with different options, but only the first setup is going to be used: // colorization is set lazily with a sync.Once global by the consuming package. t.Log(mock.errorString()) } } func testEnableWithLock(opt Option) { colorstub.Enable( func() []colorstub.Option { return []colorstub.Option{ colorstub.WithEnable(true), opt, } }) } type mockT struct { errorFmt string args []any } func (m *mockT) Errorf(format string, args ...any) { m.errorFmt = format m.args = args } func (m *mockT) errorString() string { return fmt.Sprintf(m.errorFmt, m.args...) } go-openapi-testify-c10ca71/enable/stubs/doc.go000066400000000000000000000011011520301377500213140ustar00rootroot00000000000000// 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 go-openapi-testify-c10ca71/enable/stubs/yaml/000077500000000000000000000000001520301377500211715ustar00rootroot00000000000000go-openapi-testify-c10ca71/enable/stubs/yaml/enable_yaml.go000066400000000000000000000022641520301377500237740ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package yaml is an indirection to the internal implementation that handles YAML deserialization. // // This package allows the builder to override the indirection with an alternative implementation // of YAML deserialization. package yaml import ( yamlstub "github.com/go-openapi/testify/v2/internal/assertions/enable/yaml" ) // EnableYAMLWithUnmarshal registers a YAML-capable unmarshaler. // // This is not intended for concurrent use. // // Most users would register using a init() function or enabling the // registered library provided when importing "github.com/go-openapi/testify/enable/yaml/v2" like so. // // The default registration uses [go.yaml.in/yaml/v3] to deserialize YAML. // // import( // _ "github.com/go-openapi/testify/enable/yaml/v2" // ) func EnableYAMLWithUnmarshal(unmarshaller func([]byte, any) error) { yamlstub.EnableYAMLWithUnmarshal(unmarshaller) } // EnableYAMLWithMarshal registers a YAML-capable marshaler. // // See [EnableYAMLWithUnmarshal]. func EnableYAMLWithMarshal(marshaller func(any) ([]byte, error)) { yamlstub.EnableYAMLWithMarshal(marshaller) } go-openapi-testify-c10ca71/enable/stubs/yaml/enable_yaml_test.go000066400000000000000000000020771520301377500250350ustar00rootroot00000000000000// 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" ) // TestEnableYAML is merely a smoke test to validate that the chain of calls is resolved properly. func TestEnableYAML(t *testing.T) { t.Parallel() EnableYAMLWithUnmarshal(func(_ []byte, _ any) error { return fmt.Errorf("called: %w", target.ErrTest) }) EnableYAMLWithMarshal(func(_ any) ([]byte, error) { return nil, fmt.Errorf("called: %w", target.ErrTest) }) type dummy struct { Hello string `yaml:"hello"` Foo string `yaml:"foo"` } value := dummy{Hello: "world", Foo: "bar"} mock := new(testing.T) target.False(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) // the struct is correct, but we inject a serializer that always fails target.False(t, target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "bar"}`)) target.False(t, target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "bar"}`, value)) } go-openapi-testify-c10ca71/enable/yaml/000077500000000000000000000000001520301377500200315ustar00rootroot00000000000000go-openapi-testify-c10ca71/enable/yaml/assertions_test.go000066400000000000000000000125541520301377500236200ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package yaml import ( "strings" "testing" target "github.com/go-openapi/testify/v2/assert" ) func TestYAMLEq_EqualYAMLString(t *testing.T) { t.Parallel() mock := new(testing.T) target.True(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`)) } func TestYAMLEq_EquivalentButNotEqual(t *testing.T) { t.Parallel() mock := new(testing.T) target.True(t, target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_HashOfArraysAndHashes(t *testing.T) { t.Parallel() mock := new(testing.T) target.True(t, target.YAMLEq(mock, expectedYAML, actualYAML)) } func TestYAMLEq_Array(t *testing.T) { t.Parallel() mock := new(testing.T) target.True(t, target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`)) } func TestYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() mock := new(testing.T) target.False(t, target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`)) } func TestYAMLEq_HashesNotEquivalent(t *testing.T) { t.Parallel() mock := new(testing.T) target.False(t, target.YAMLEq(mock, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_ActualIsSimpleString(t *testing.T) { t.Parallel() mock := new(testing.T) target.False(t, target.YAMLEq(mock, `{"foo": "bar"}`, "Simple String")) } func TestYAMLEq_ExpectedIsSimpleString(t *testing.T) { t.Parallel() mock := new(testing.T) target.False(t, target.YAMLEq(mock, "Simple String", `{"foo": "bar", "hello": "world"}`)) } func TestYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() mock := new(testing.T) target.True(t, target.YAMLEq(mock, "Simple String", "Simple String")) } func TestYAMLEq_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() mock := new(testing.T) target.False(t, target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`)) } func TestYAMLEq_OnlyFirstDocument(t *testing.T) { t.Parallel() mock := new(testing.T) target.True(t, target.YAMLEq(mock, `--- doc1: same --- doc2: different `, `--- doc1: same --- doc2: notsame `, )) } func TestYAMLEq_InvalidIdenticalYAML(t *testing.T) { t.Parallel() mock := new(testing.T) target.False(t, target.YAMLEq(mock, `}`, `}`)) } func TestYAMLUnmarshalAs(t *testing.T) { t.Parallel() mock := new(testing.T) type dummy struct { Hello string `yaml:"hello"` Foo string `yaml:"foo"` } value := dummy{Hello: "world", Foo: "bar"} target.False(t, target.YAMLUnmarshalAsT(mock, value, `{"hello": "buzz", "foo": "lightyear"}`)) target.False(t, target.YAMLMarshalAsT(mock, `{"hello": "buzz", "foo": "lightyear"}`, value)) target.True(t, target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "bar"}`)) target.True(t, target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "bar"}`, value)) } // TestYAMLRedact covers the happy-path Redactor (`func() string` / // `func() []byte`) arms of the [RText] type set for the YAML T-variants, // which can only be exercised once the YAML feature is enabled. The // nil-Redactor panic path is also re-checked here for YAMLMarshalAsT, // since in the internal/assertions package yaml.Marshal panics first // when the feature is disabled and the asBytes guard is unreachable. func TestYAMLRedact(t *testing.T) { t.Parallel() mock := new(testing.T) type dummy struct { Hello string `yaml:"hello"` Foo string `yaml:"foo"` } value := dummy{Hello: "world", Foo: "bar"} const doc = `{"hello": "world", "foo": "bar"}` t.Run("YAMLEqT with func() string Redactor", func(t *testing.T) { t.Parallel() red := func() string { return doc } target.True(t, target.YAMLEqT(mock, red, doc)) }) t.Run("YAMLEqT with func() []byte Redactor on actual", func(t *testing.T) { t.Parallel() red := func() []byte { return []byte(doc) } target.True(t, target.YAMLEqT(mock, doc, red)) }) // Real-world scenario: a redactor normalises a non-deterministic field // before comparison. t.Run("YAMLEqT redactor normalises field", func(t *testing.T) { t.Parallel() raw := `{"id": 42, "ts": "2026-04-26T15:30:45Z"}` red := func() string { return strings.Replace(raw, `"2026-04-26T15:30:45Z"`, `"REDACTED"`, 1) } expected := `{"id": 42, "ts": "REDACTED"}` target.True(t, target.YAMLEqT(mock, expected, red)) }) t.Run("YAMLUnmarshalAsT with Redactor", func(t *testing.T) { t.Parallel() red := func() []byte { return []byte(doc) } target.True(t, target.YAMLUnmarshalAsT(mock, value, red)) }) t.Run("YAMLMarshalAsT with Redactor", func(t *testing.T) { t.Parallel() red := func() string { return doc } target.True(t, target.YAMLMarshalAsT(mock, red, value)) }) t.Run("YAMLMarshalAsT with nil Redactor panics with clear message", func(t *testing.T) { t.Parallel() const wantMsg = "passed Redactor cannot be nil" defer func() { rec := recover() if rec == nil { t.Errorf("expected YAMLMarshalAsT to panic with nil Redactor, got no panic") return } s, _ := rec.(string) if !strings.Contains(s, wantMsg) { t.Errorf("expected panic message to contain %q, got: %v", wantMsg, rec) } }() var nilFn func() string _ = target.YAMLMarshalAsT(mock, nilFn, value) }) } go-openapi-testify-c10ca71/enable/yaml/doc.go000066400000000000000000000006411520301377500211260ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package yaml enables the [YAMLEq] capability in testify. // // # Usage // // To enable YAML assertion features, use a blank import like so: // // import ( // _ "github.com/go-openapi/testify/enable/yaml/v2" // ) // // [YAMLEq]: https://pkg.go.dev/github.com/go-openapi/testify/v2/assert#YAMLEq package yaml go-openapi-testify-c10ca71/enable/yaml/enable_yaml.go000066400000000000000000000006471520301377500226370ustar00rootroot00000000000000// 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 init() { //nolint:gochecknoinits // we precisely want this init to run when importing the package yamlstub.EnableYAMLWithUnmarshal(yaml.Unmarshal) yamlstub.EnableYAMLWithMarshal(yaml.Marshal) } go-openapi-testify-c10ca71/enable/yaml/forward_assertions_test.go000066400000000000000000000111101520301377500253270ustar00rootroot00000000000000// 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 TestYAMLEqWrapper_EqualYAMLString(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) { t.Error("YAMLEq should return true") } } func TestYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { t.Error("YAMLEq should return true") } } func TestYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) expected := ` numeric: 1.5 array: - foo: bar - 1 - "string" - ["nested", "array", 5.5] hash: nested: hash nested_slice: [this, is, nested] string: "foo" ` actual := ` numeric: 1.5 hash: nested: hash nested_slice: [this, is, nested] string: "foo" array: - foo: bar - 1 - "string" - ["nested", "array", 5.5] ` if !assert.YAMLEq(expected, actual) { t.Error("YAMLEq should return true") } } func TestYAMLEqWrapper_Array(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) { t.Error("YAMLEq should return true") } } func TestYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) { t.Error("YAMLEq should return false") } } func TestYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) { t.Error("YAMLEq should return false") } } func TestYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEq(`{"foo": "bar"}`, "Simple String") { t.Error("YAMLEq should return false") } } func TestYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) { t.Error("YAMLEq should return false") } } func TestYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEq("Simple String", "Simple String") { t.Error("YAMLEq should return true") } } func TestYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) { t.Error("YAMLEq should return false") } } func TestYAMLEqBytesWrapper(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEqBytes([]byte(expectedYAML), []byte(actualYAML)) { t.Error("YAMLEqBytes should return true") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEqBytes([]byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) { t.Error("YAMLEqBytes should return false") } }) } func TestYAMLEqfWrapper(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "equivalent") { t.Error("YAMLEqf should return true") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEqf(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "not equivalent") { t.Error("YAMLEqf should return false") } }) } func TestYAMLEqBytesfWrapper(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if !assert.YAMLEqBytesf([]byte(expectedYAML), []byte(actualYAML), yamlCheckMsg, "equivalent bytes") { t.Error("YAMLEqBytesf should return true") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() assert := target.New(new(testing.T)) if assert.YAMLEqBytesf([]byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`), yamlCheckMsg, "not equivalent bytes") { t.Error("YAMLEqBytesf should return false") } }) } go-openapi-testify-c10ca71/enable/yaml/forward_requirements_test.go000066400000000000000000000120611520301377500256660ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package yaml import ( "testing" target "github.com/go-openapi/testify/v2/require" ) func TestRequireYAMLEqWrapper_EqualYAMLString(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEqWrapper_EquivalentButNotEqual(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEqWrapper_HashOfArraysAndHashes(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) expected := ` numeric: 1.5 array: - foo: bar - 1 - "string" - ["nested", "array", 5.5] hash: nested: hash nested_slice: [this, is, nested] string: "foo" ` actual := ` numeric: 1.5 hash: nested: hash nested_slice: [this, is, nested] string: "foo" array: - foo: bar - 1 - "string" - ["nested", "array", 5.5] ` mockRequire.YAMLEq(expected, actual) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEqWrapper_Array(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEqWrapper_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEqWrapper_HashesNotEquivalent(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEqWrapper_ActualIsSimpleString(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`{"foo": "bar"}`, "Simple String") if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEqWrapper_ExpectedIsSimpleString(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq("Simple String", `{"foo": "bar", "hello": "world"}`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEqWrapper_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq("Simple String", "Simple String") if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEqWrapper_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEq(`["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEqBytesWrapper(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEqBytes([]byte(expectedYAML), []byte(actualYAML)) if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEqBytes([]byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLEqfWrapper(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEqf(`{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "equivalent") if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEqf(`{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "not equivalent") if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLEqBytesfWrapper(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEqBytesf([]byte(expectedYAML), []byte(actualYAML), yamlCheckMsg, "equivalent bytes") if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) mockRequire := target.New(mock) mockRequire.YAMLEqBytesf([]byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`), yamlCheckMsg, "not equivalent bytes") if !mock.Failed { t.Error("Check should fail") } }) } go-openapi-testify-c10ca71/enable/yaml/go.mod000066400000000000000000000003031520301377500211330ustar00rootroot00000000000000module github.com/go-openapi/testify/enable/yaml/v2 require ( github.com/go-openapi/testify/v2 v2.5.1 go.yaml.in/yaml/v3 v3.0.4 ) replace github.com/go-openapi/testify/v2 => ../.. go 1.25.0 go-openapi-testify-c10ca71/enable/yaml/go.sum000066400000000000000000000005541520301377500211700ustar00rootroot00000000000000go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= go-openapi-testify-c10ca71/enable/yaml/requirements_test.go000066400000000000000000000173371520301377500241550ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package yaml import ( "testing" target "github.com/go-openapi/testify/v2/require" ) const ( expectedYAML = ` numeric: 1.5 array: - foo: bar - 1 - "string" - ["nested", "array", 5.5] hash: nested: hash nested_slice: [this, is, nested] string: "foo" ` actualYAML = ` numeric: 1.5 hash: nested: hash nested_slice: [this, is, nested] string: "foo" array: - foo: bar - 1 - "string" - ["nested", "array", 5.5] ` yamlCheckMsg = "yaml check: %s" ) func TestRequireYAMLEq_EqualYAMLString(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEq_EquivalentButNotEqual(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEq_HashOfArraysAndHashes(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, expectedYAML, actualYAML) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEq_Array(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`) if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEq_HashAndArrayNotEquivalent(t *testing.T) { t.Parallel() 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") } } func TestRequireYAMLEq_HashesNotEquivalent(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEq_ActualIsSimpleString(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, `{"foo": "bar"}`, "Simple String") if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEq_ExpectedIsSimpleString(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, "Simple String", `{"foo": "bar", "hello": "world"}`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEq_ExpectedAndActualSimpleString(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, "Simple String", "Simple String") if mock.Failed { t.Error("Check should pass") } } func TestRequireYAMLEq_ArraysOfDifferentOrder(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEq(mock, `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`) if !mock.Failed { t.Error("Check should fail") } } func TestRequireYAMLEqf(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqf(mock, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "equivalent") if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqf(mock, `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "not equivalent") if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLEqBytesf(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqBytesf(mock, []byte(expectedYAML), []byte(actualYAML), yamlCheckMsg, "equivalent bytes") if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqBytesf(mock, []byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`), yamlCheckMsg, "not equivalent bytes") if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLEqT(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqT(mock, []byte(expectedYAML), actualYAML) if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqT(mock, []byte(`{"foo": "bar"}`), `{"foo": "bar", "hello": "world"}`) if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLEqBytes(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqBytes(mock, []byte(expectedYAML), []byte(actualYAML)) if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqBytes(mock, []byte(`{"foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLEqTf(t *testing.T) { t.Parallel() t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqTf(mock, []byte(expectedYAML), actualYAML, yamlCheckMsg, "equivalent") if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) target.YAMLEqTf(mock, []byte(`{"foo": "bar"}`), `{"foo": "bar", "hello": "world"}`, yamlCheckMsg, "not equivalent") if !mock.Failed { t.Error("Check should fail") } }) } func TestRequireYAMLMarshalUnmarshalAsTf(t *testing.T) { t.Parallel() type dummy struct { Hello string `yaml:"hello"` Foo string `yaml:"foo"` } t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) value := dummy{Hello: "world", Foo: "bar"} target.YAMLUnmarshalAsTf(mock, value, `{"hello": "world", "foo": "bar"}`, yamlCheckMsg, "unmarshal") if mock.Failed { t.Error("Check should pass") } target.YAMLMarshalAsTf(mock, `{"hello": "world", "foo": "bar"}`, value, yamlCheckMsg, "marshal") if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) value := dummy{Hello: "world", Foo: "bar"} target.YAMLUnmarshalAsTf(mock, value, `{"hello": "world", "foo": "yay"}`, yamlCheckMsg, "unmarshal") if !mock.Failed { t.Error("Check should fail with FailNow") } target.YAMLMarshalAsTf(mock, `{"hello": "world", "foo": "yay"}`, value, yamlCheckMsg, "marshal") if !mock.Failed { t.Error("Check should fail with FailNow") } }) } func TestRequireYAMLUnmarshalAsWrapper(t *testing.T) { t.Parallel() type dummy struct { Hello string `yaml:"hello"` Foo string `yaml:"foo"` } t.Run("should pass", func(t *testing.T) { t.Parallel() mock := new(mockT) value := dummy{Hello: "world", Foo: "bar"} target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "bar"}`) if mock.Failed { t.Error("Check should pass") } target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "bar"}`, value) if mock.Failed { t.Error("Check should pass") } }) t.Run("should fail", func(t *testing.T) { t.Parallel() mock := new(mockT) value := dummy{Hello: "world", Foo: "bar"} target.YAMLUnmarshalAsT(mock, value, `{"hello": "world", "foo": "yay"}`) if !mock.Failed { t.Error("Check should fail with FailNow") } target.YAMLMarshalAsT(mock, `{"hello": "world", "foo": "yay"}`, value) if !mock.Failed { t.Error("Check should fail with FailNow") } }) } type mockT struct { Failed bool } // Helper is like [testing.T.Helper] but does nothing. func (mockT) Helper() {} func (t *mockT) FailNow() { t.Failed = true } func (t *mockT) Errorf(format string, args ...any) { _, _ = format, args } go-openapi-testify-c10ca71/go.mod000066400000000000000000000001031520301377500167410ustar00rootroot00000000000000module github.com/go-openapi/testify/v2 retract v2.0.0 go 1.25.0 go-openapi-testify-c10ca71/go.sum000066400000000000000000000000001520301377500167620ustar00rootroot00000000000000go-openapi-testify-c10ca71/go.work000066400000000000000000000001651520301377500171540ustar00rootroot00000000000000use ( . ./codegen ./enable/colors ./enable/yaml ./hack/migrate-testify ./internal/testintegration ) go 1.25.0 go-openapi-testify-c10ca71/hack/000077500000000000000000000000001520301377500165475ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/000077500000000000000000000000001520301377500202565ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/000077500000000000000000000000001520301377500212205ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/.gitignore000066400000000000000000000000331520301377500232040ustar00rootroot00000000000000public *.lock testify.yaml go-openapi-testify-c10ca71/hack/doc-site/hugo/README.md000066400000000000000000000064021520301377500225010ustar00rootroot00000000000000# Hugo Documentation Site This directory contains the Hugo configuration for the Testify documentation site. ## Structure ``` hugo/ ├── hugo.yaml # Main Hugo configuration ├── testify.yaml.template # Dynamic config template (version info) ├── gendoc.go # Local development server (go run gendoc.go) ├── themes/ │ ├── hugo-relearn/ # Relearn theme (extracted from zip) │ ├── testify-assets/ # Custom SCSS and assets │ │ └── _custom.scss # Theme customization │ └── testify-static/ # Static files (logo, favicon) └── layouts/ # Custom layouts (override theme) ├── shortcodes/ # Custom Hugo shortcodes └── partials/ # Custom partial templates ``` ## Content Generation Documentation content is generated by the `DocGenerator` to: ``` ../../../docs/doc-site/ ``` This directory is mounted as Hugo's content directory via module mounts in `hugo.yaml`. ## Local Development ```bash # Run local Hugo server go run gendoc.go # Site will be available at: # http://localhost:1313/testify/ ``` The `gendoc.go` program: 1. Extracts version info from git tags and go.mod 2. Generates `testify.yaml` from template 3. Starts Hugo server with both config files 4. Enables live reload and draft content ## Theme Uses **Hugo Relearn** theme (dark variant) following go-swagger patterns. Theme customizations: - Custom SCSS in `themes/testify-assets/_custom.scss` - Static branding assets in `themes/testify-static/` - Layout overrides in `layouts/` ## Configuration Two-layer config following go-swagger pattern: 1. **hugo.yaml** - Static configuration (theme, structure, parameters) 2. **testify.yaml** - Dynamic configuration (version, build time) The dynamic config is generated at build time from `testify.yaml.template`. ## URL Structure Following domain-based organization: ``` / # Home page /boolean/ # Boolean assertions (True, False) /collection/ # Collection assertions (Contains, Empty, Len) /compare/ # Comparison assertions (Greater, Less) /equal/ # Equality assertions (Equal, NotEqual, Same) /error/ # Error assertions (Error, NoError, ErrorIs) /file/ # File assertions (FileExists, FileEmpty) /http/ # HTTP assertions (HTTPSuccess, HTTPError) /json/ # JSON assertions (JSONEq) /number/ # Number assertions (InDelta, InEpsilon) /panic/ # Panic assertions (Panics, NotPanics) /string/ # String assertions (Regexp, NotRegexp) /time/ # Time assertions (WithinDuration) /type/ # Type assertions (IsType, Zero, Implements) /yaml/ # YAML assertions (YAMLEq) ``` ## Deployment GitHub Pages deployment will be configured to: - Build from `docs/` directory - Publish to: https://go-openapi.github.io/testify/ ## Notes - **Versioning ignored for now** - Focus on basic structure first - Edit links point to `internal/assertions/` source code - Search enabled via Relearn theme - Breadcrumbs and prev/next navigation enabled go-openapi-testify-c10ca71/hack/doc-site/hugo/gendoc.go000066400000000000000000000076631520301377500230220ustar00rootroot00000000000000//go:build ignore // Local development script for Hugo documentation. // // Usage: go run gendoc.go // // Requires: // * hugo // * git package main import ( "bufio" "context" "fmt" "os" "os/exec" "path/filepath" "regexp" "runtime" "strings" "time" ) //nolint:forbidigo,dogsled func main() { ctx := context.Background() // Change to the directory containing this script. _, thisFile, _, _ := runtime.Caller(0) scriptDir := filepath.Dir(thisFile) if err := os.Chdir(scriptDir); err != nil { fatalf("chdir: %v", err) } fmt.Println("==> Preparing Hugo documentation site...") latestRelease := gitLatestRelease(ctx) requiredGoVersion := goVersionFromMod() buildTime := time.Now().UTC().Format("2006-01-02T15:04:05Z") versionMessage := "Documentation test for latest master" fmt.Printf(" Latest release: %s\n", latestRelease) fmt.Printf(" Go version: %s\n", requiredGoVersion) fmt.Printf(" Build time: %s\n", buildTime) // Generate dynamic config from template. generateTestifyYAML(requiredGoVersion, latestRelease, versionMessage, buildTime) fmt.Println("==> Generated testify.yaml") // Check if theme exists. if _, err := os.Stat("themes/hugo-relearn"); os.IsNotExist(err) { fatalf("Relearn theme not found at themes/hugo-relearn\n" + "Run: unzip hugo-theme-relearn-main.zip -d themes/ && mv themes/hugo-theme-relearn-main themes/hugo-relearn") } // Check if generated docs exist. if _, err := os.Stat("../../../docs/doc-site"); os.IsNotExist(err) { fmt.Println("WARNING: Generated docs not found at ../../../docs/doc-site") fmt.Println("You may need to run: go generate ./...") fmt.Println() fmt.Println("Creating placeholder content directory...") os.MkdirAll("content", 0o755) //nolint:errcheck,mnd } fmt.Println("==> Starting Hugo development server...") fmt.Println(" Visit: http://localhost:1313/testify/") fmt.Println() // Start Hugo server with both configs. cmd := exec.CommandContext(ctx, "hugo", "server", "--config", "hugo.yaml,testify.yaml,metrics.yaml", "--buildDrafts", "--disableFastRender", "--navigateToChanged", "--bind", "0.0.0.0", "--port", "1313", "--baseURL", "http://localhost:1313/testify/", "--appendPort=false", "--logLevel", "info", "--cleanDestinationDir", ) cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr cmd.Stdin = os.Stdin if err := cmd.Run(); err != nil { fatalf("hugo: %v", err) } } // gitLatestRelease returns the latest semver tag, or "dev" if none found. func gitLatestRelease(ctx context.Context) string { out, err := exec.CommandContext(ctx, "git", "tag", "--list", "--sort", "-version:refname", "v*").Output() if err != nil || len(out) == 0 { return "dev" } sc := bufio.NewScanner(strings.NewReader(string(out))) if sc.Scan() { return strings.TrimSpace(sc.Text()) } return "dev" } // goVersionFromMod extracts the go version from the root go.mod. func goVersionFromMod() string { data, err := os.ReadFile("../../../go.mod") if err != nil { fatalf("reading go.mod: %v", err) } re := regexp.MustCompile(`(?m)^go\s+(\S+)`) m := re.FindSubmatch(data) if m == nil { fatalf("could not find go version in go.mod") } return string(m[1]) } // generateTestifyYAML reads the template and writes testify.yaml with substitutions. func generateTestifyYAML(goVersion, latestRelease, versionMessage, buildTime string) { tmpl, err := os.ReadFile("testify.yaml.template") if err != nil { fatalf("reading template: %v", err) } out := string(tmpl) out = strings.ReplaceAll(out, "{{ GO_VERSION }}", goVersion) out = strings.ReplaceAll(out, "{{ LATEST_RELEASE }}", latestRelease) out = strings.ReplaceAll(out, "{{ VERSION_MESSAGE }}", versionMessage) out = strings.ReplaceAll(out, "{{ BUILD_TIME }}", buildTime) if err := os.WriteFile("testify.yaml", []byte(out), 0o600); err != nil { //nolint:mnd fatalf("writing testify.yaml: %v", err) } } func fatalf(format string, args ...any) { fmt.Fprintf(os.Stderr, "ERROR: "+format+"\n", args...) os.Exit(1) } go-openapi-testify-c10ca71/hack/doc-site/hugo/hugo.yaml000066400000000000000000000056711520301377500230570ustar00rootroot00000000000000baseURL: https://go-openapi.github.io/testify/ title: Testify v2 theme: hugo-relearn languageCode: en-us # Enable features enableEmoji: true enableGitInfo: true enableRobotsTXT: true # Content configuration contentDir: content # Output formats outputs: home: - html - rss - print # Markup configuration markup: goldmark: renderer: unsafe: true # Allow raw HTML in markdown parser: attribute: title: true block: true renderHooks: image: useEmbedded: always highlight: codeFences: true guessSyntax: false lineNos: false lineNumbersInTable: false noClasses: true style: monokai tabWidth: 4 # Taxonomies are disabled for now disableKinds: - taxonomy - term # Module mounts # Following go-swagger pattern: simple mounts for content and assets module: mounts: # Custom layouts (override theme if needed) - source: layouts target: layouts # Generated documentation content - source: '../../../docs/doc-site' target: content # Custom SCSS (for theme customization) + logo - source: themes/testify-assets target: assets # Custom static files (favicon) - source: themes/testify-static target: static # Relearn theme parameters params: # Edit links - point to source code since docs are generated editURL: 'https://github.com/go-openapi/testify/edit/master/internal/assertions/' externalLinkTarget: _blank # Repository info sourceRepository: 'https://github.com/go-openapi/testify' # Branding author: name: 'go-openapi maintainers' hideAuthorEmail: true # Theme customization themeVariant: - zen-dark - relearn-dark - relearn-light showVisitedLinks: true collapsibleMenu: true disableBreadcrumb: false disableNextPrev: false disableLandingPageButton: true titleSeparator: '|' # Features search: disable: false index: disable: false page: disable: false adapter: identifier: lunr disableLanguageSwitchingButton: true disableInlineCopyToClipBoard: false # Menu ordering ordersectionsby: 'weight' # Proposal for enhancement: configure versions #versions: #- baseURL: https://go-openapi.github.io/testify/ # identifier: v2.1.0 # isLatest: true #version: 'v2.1.0' # Custom params (populated by testify.yaml) testify: goVersion: '' latestRelease: '' versionMessage: '' buildTime: '' # Generated metrics (populated by metrics.yaml) metrics: domains: 0 functions: 0 assertions: 0 generics: 0 helpers: 0 others: 0 # Menu configuration menu: shortcuts: - name: "GitHub" identifier: github url: "https://github.com/go-openapi/testify" weight: 10 - name: "go-openapi toolkit" identifier: go-openapi url: "https://github.com/go-openapi" weight: 20 # Privacy settings privacy: disqus: disable: true googleAnalytics: disable: true go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/000077500000000000000000000000001520301377500227205ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/partials/000077500000000000000000000000001520301377500245375ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/partials/.gitkeep000066400000000000000000000001051520301377500261640ustar00rootroot00000000000000# Custom partials directory # Override theme partials here if needed go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/partials/bodys/000077500000000000000000000000001520301377500256575ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/partials/bodys/single.html000066400000000000000000000003171520301377500300270ustar00rootroot00000000000000{{- .Store.Set "relearnIsNested" false }} {{- if gt .ReadingTime 1 }}
📖 {{ .ReadingTime }} min read (~ {{ .FuzzyWordCount }} words).
{{- end }} {{- .Render "article" }} go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/partials/content-footer.html000066400000000000000000000027121520301377500303750ustar00rootroot00000000000000{{- $LastModifierDisplayName := "" }} {{- $LastModifierEmail := "" }} {{- $Date := "" }} {{- $dateFormat := site.Params.dateFormat | default ":date_medium" }} {{- with .GitInfo }} {{- with and (not site.Params.hideAuthorName) .AuthorName }} {{- $LastModifierDisplayName = . }} {{- end }} {{- with and (not site.Params.hideAuthorEmail) .AuthorEmail }} {{- $LastModifierEmail = . }} {{- end }} {{- with and (not site.Params.hideAuthorDate) .AuthorDate }} {{- $Date = . | time.Format $dateFormat }} {{- end }} {{- else }} {{- with and (not site.Params.hideAuthorName) .Params.LastModifierDisplayName }} {{- $LastModifierDisplayName = . }} {{- end }} {{- with and (not site.Params.hideAuthorEmail) .Params.LastModifierEmail }} {{- $LastModifierEmail = . }} {{- end }} {{- with and (not site.Params.hideAuthorDate) .Date }} {{- $Date = . | time.Format $dateFormat }} {{- end }} {{- end }} {{- if $LastModifierDisplayName }} Last edited by: {{ with $LastModifierEmail }}{{ end }}{{ $LastModifierDisplayName }}{{ with $LastModifierEmail }}{{ end }} {{- end }} {{- with $Date }} {{ . }} {{- end }}
Copyright 2015-2025 go-openapi maintainers. This documentation is under an Apache 2.0 license. {{- partial "term-list.html" (dict "page" . "taxonomy" "categories" "icon" "layer-group" ) }} go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/partials/custom-header.html000066400000000000000000000017501520301377500301700ustar00rootroot00000000000000 go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/shortcodes/000077500000000000000000000000001520301377500250755ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/layouts/shortcodes/.gitkeep000066400000000000000000000001001520301377500265150ustar00rootroot00000000000000# Custom shortcodes directory # Add custom Hugo shortcodes here go-openapi-testify-c10ca71/hack/doc-site/hugo/metrics.yaml000066400000000000000000000032471520301377500235600ustar00rootroot00000000000000params: metrics: domains: 19 functions: 141 assertions: 137 generics: 53 nongeneric_assertions: 84 helpers: 4 others: 0 by_domain: boolean: name: Boolean count: 4 collection: name: Collection count: 23 common: name: Common count: 0 comparison: name: Comparison count: 12 condition: name: Condition count: 9 equality: name: Equality count: 16 error: name: Error count: 8 file: name: File count: 6 http: name: Http count: 6 json: name: Json count: 5 number: name: Number count: 9 ordering: name: Ordering count: 10 panic: name: Panic count: 4 safety: name: Safety count: 2 string: name: String count: 4 testing: name: Testing count: 2 time: name: Time count: 2 type: name: Type count: 10 yaml: name: Yaml count: 5 package_variants: 442 total_variants: 884 total_functions: 894 go-openapi-testify-c10ca71/hack/doc-site/hugo/testify.yaml.template000066400000000000000000000007021520301377500254040ustar00rootroot00000000000000# Dynamic configuration generated at build time # This file provides version information extracted from the repository params: testify: # Go version requirement (from go.mod) goVersion: '{{ GO_VERSION }}' # Latest release tag (from git tags) latestRelease: '{{ LATEST_RELEASE }}' # Version message for the documentation set versionMessage: '{{ VERSION_MESSAGE }}' # Build timestamp buildTime: '{{ BUILD_TIME }}' go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/000077500000000000000000000000001520301377500225055ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/.gitignore000066400000000000000000000000151520301377500244710ustar00rootroot00000000000000hugo-relearn go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/testify-assets/000077500000000000000000000000001520301377500254745ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/testify-assets/colorized.png000066400000000000000000001666651520301377500302200ustar00rootroot00000000000000PNG  IHDR?PLTE۶mmmIII$$$mI$mI$mmII$$mI$۶mmII$$mmII$$۶mmmIII$$ےmmIIm$$mmII$$mmII$$II$$$$۶۶mmImI$I$ےmmII$m$mmII$$mmII$$II$$$$ےmmIIm$$ImmII$$mmmII$$mmII$$II$$$$۶mmmIII$ےmImm$mI$mI$I$$۶۶mmImI$Iے۶mIm$mm۶I$mI۶$I$$ےmImm$IImI$mmmI$mI$I$$۶۶mmImI$I$ےmmII$m$ےmImm$II$mIm$Im$۶ےmmII$m$ImmII$$mmIm$Im$ImIm$I$mے۶mIm$mII$mےIm$Im$ےmmIIm$$ImmII$$mےmImImI* IDATxw|MϽ7KLik,vR[mEmZBCUKkծ$BB"BAƽɹqS~=>sΓ󜣪j]ƀB!B!x娥 B!B!^M21$B!B񊒉!!B!BW#CJCXJp?ǾYHeOQ3ԯ<65%]ICWMс+8p$-CGUf=Ѥ\O?vu3hOÛ=1'9xyKSyƭ!N]kE?OcNmXw/!V^8B!B!Gx16,TuQ <:B\VHU MrЊj=ZSQΕoJߠ{_y/[bs9Ɍ"UޥAustw}I,^?~`Ӥ:UYqե:0xr愢˵"~ޕy3@fҵ<~i$q B!B<%6L{5Vh dy)/d[6 [e\&8nHK:#O#*[KCO! %Zmȭ?:¾߳xyM$Ns:0뀟9j-q'8%>;>s3&KB!B! )1¦R\ c |P&/*[gR(mߧ'gZ7U'ޝ atq /qlCw\JSl,lNR߲-{ҔŁ0b;k3mE Q;Aѓq5'qu{Yoͻ[*Z#B!BkbHN癃(~}z,"4)<=^ghpXl͗8C_MTmYX"/ټK qb> `H<>5>\7p;?{>s+B1Ǫ3?XN\|09dF%EQVbyGG5$>GʖŬr5}+O{aZ y݅%|1s8֮L;D@.9jfzB[iJ]3IsW!#{bѨ lOK(ܯYґx15svnmP cfc}'+}c:Q?=-ҽ W<|3&zx8(Vn3۷3$lޟ88=RYA[DÎ@{zY}B!Bn=w}*d=vnE2uڞCY}^*,MM g[WjT~c/+OT6y6&xw1,їv+M7sNmRO"]Ɔin/cʳr.CNf׌j3q`G<@L03̰q/> c> !B!x'070;KQ; X"md\nTeqz_^ Gk ID `MYzW_L͞Sׂ(ɱMSg;NrD{CYlJ=4/| ˾񽰋^(aj6szhKmIT160|z_=M@k7L69QT>cLJ!b_7MuY*1xc[;tSIϥtYִi<<+B!Bهor><5g M~,XZ ¦mj|fv=ڏR o-G@Kq fOHDzt-:M9mwW%ٴAi wY+͛$荭Z&AS:n^)/[J q,Y_`< /+MTmYX"/ߝ^w:D cWO9>7|ƝNSS4`&ݻ /˔Zsm|TKl yb}1X_"[!B!B!"߯S9ƣckvvUoAuefN/[{gV]sͺ۬rKൊ~nHp ]Bϴ|x'-De?qX@CgtQтN 2v95/bņ~ܯB!B!rt^Fb͇Fg'rv[B&#Ռ)8|r^sN-Wߩt{CLnnٙgW>uWU׉ztWΜ5a(}F\!~ۚB!B! ׷=Ԩ3`HNAkl8CRZʈgXLtbˎ0VU+| c'2 IR݇3V״UF*7 ?v.EJPEnB!B!^\CbG73$\MnpbhĂ0_K͋aZҪA>.S&s+q+6r'54+Dj|ʗ~S_F. dT!juns.3LIJ.MIHOƐϮ~B!BazfD:E}b_qi=~'!Me_fGǎ^" ;460iõlg]8XДLe}ҔJq+6z+B!B"DŽ3;"=CSb|u(Ya.-i`Pk ؋au331t SDYp֠D';y#|xOshdP} a`pRNC6c~/WQ{ ZrY5C:7_tF$gR!k폼Rec>EH k&{4uV>`ebύI?B!B!+e /z_aKU~/B!B!g&MSaWJ\'j 7p,Wb[\I!!B!Bϋ51dVAӿRnsLɫ~TɓWBlPWR胃O&t=4U)O/d&1Oա!6GM NUꗢR[.wcs$hWQ?M=vV oVe'lݫP5QSk &ļh8U5zuƹҸWo GTD~׷ K؊ZХZ_|C/G}_\pl6 !lv:Yji"q_*wel?3t KTK}_ ~nko /,96LrRe.!Z%CLA䥌Zbôw[c6@VJ ‘<|i܊z=Z 6*n\AٗOGb.eI\RַĽQð?`yÇgSC8;/u+:~!&KɌT5U's!D`t Cs>L͞Sׂ(~Y}]h:o=[‚ Іp40B*6ʫ*zvib*ƶqζ Jmr=ۯ& Ə&AeWcB=lݱ;ۦ5eQ?&#~V{<!Rm蝻=7`uvo.=aN]RU}K\>#nqbDMM>^i46ww(_~ꄻ&| ˾񽰋^(#Ư4^44=>M !(BYn ޭgN7_F{X6O޿5 }r/8W ;%{$|ڭ #',GbrHܧM=Lo?S}Jv̂}uGSN~ݍ%RCSg;NrD{CYlJ=w+EVkJƇ|((ܯ*_iS:~ǯ<|ߞ9ϓF8N+Qohg`Ǭr_4c!"X&0j$W~b.HeٞL#SaS 6@u>3@Ǎs{)7#tYנL[CtoP$&mF\IŚ1iǏ=?8uC!Kp>{ͪ`< /KY;~ߌ9~(U}*h»NA5T(f:W7U:.]j4k0sN|B:kiYo48|ؼPO5z瑟s`W4wo@5y Uv]K¼s@ċiWʏoJχ*Nq*jS_y\8W=s'sVv=cS_ c~NT IDATD((nZ|݇ҁEn21쌏֫i-.,q.3w \?î=',c;kt 7obG <#1`5g_vfuh/Q2މ4nY-u'MM=Lj;#%;l.иy ʟ_@8žzJϡww%[蹶f>KLZ l(?Hxl_=84w6iM2v^8t-B/q<_R>'O%dm0C,D`l]h $6Kԥp2JEN):]k@Mb  .p rTƑ|w:MNрtツȻ9L}]ʶa1\mYX"/ǑFҝ[N<I>p.CީX4xLoF?qgH 辠;{4ט{x:njijy_M?ۋ838|FĽqy-]eqW~\ܖu8{ p7챱[?x1Y~јxi|UP 㛱K%CJW~ngt}lktkr|I` g,+wЅ-zE~гY{n ZcE4Gu趜K fsm<8f'Tl72Mtm2}6vF?6Sx͝k #|rKൊdHp ]B_bɧj1/򼐪0c~h'ӄJQm+=zs3^U>#?w),ʹ6 {hhՅ;gѿ[Mܬq LJ\=xGO[z&p>tu~M)6};zߌ.J%Vw?z>zu)g~t4awQX*?fR}щkauڽq=,#//&?^h/Oy7%tʶ{8-SȧGѳC{,t3j.ɏK(5^y3j |e ț1ƕ çUث_BIB'ܝa~ze;\e>3gs NJ=rMfwŊ |hOf)p[e_fM96~/I ; 7op , 8ԸO=n][ثnr!.MmyCq5]ÈO ?*ߓ ;ث!Jov682u{P KG7e- TgQ&Z挽>e7ܾ-h4FgPV(&4PVϨw}7x$Ƶ깋/MbiOǛU_%=Oog-qr$/ʉb=p^EBKَ.ZZ('ޚmHJ$E>,<,v+%}e f-cv9;_Pp˻7ɧzw),!)}!]mPd=zdDf?k&PeGzMWo>qF^PZ>ÍTnbMa3@s z Q`=:=5jP̌ F)FNE!<4xߌ->z3{?{}v-?\ +=(,q4>>mňqJX_NsGW{$`2.^r/Oe<@U.(SGٿJvw~j4}.dn]P@}d0' QsSkr%5ChعzNGԅX .ݖ[r&|4xߌ.x.ԑ3xriHD>y$3: gw*NEsܹZi&{T7)˖}rQ4bsp2*)v$#suF¨,# ۲pR?Z>S4u"8Tj@/?f*F,<_JǑ#ϬJOбuoeh:j8oeܢw A1tb/Ʈhli+LyQ^('|~;[,njSH<}ܦF1m?֓(m#B)-{cﴋg|flǷ`9H<!gS[s#OFO~VB}bǞ3#o8|Ľ1y-]/FKEܘ_cqD JYŋ}%ÖQr@fmft(g@|/ ,_UcYYTֿJt;Sǩ7SYɼ# 8m+%_$\ZyGP:Zp♎B'>Xx$R)yٰ*ۍb7cQm~].JekUgl Gv9/cHg}҈f ȽRSO]4*q)H7ñ\m:LosYNZīLJH eQpueI]#'q%"^(xiĐآ%5 +Caqؘ.cF)' 3J?c\ȓ~N^3-uf|}2umn}tKfcNOJ啻_)f]&ù (ifaYJ&B!B9 K`E d%]%*!]KL !B!BB!B!&B!B!xEĐB!B!+J&BaZ*sl0BW5(lBPi!t[dOt)~1gVaBN I^EO\n}<"̌wۉ^Լ|'r/q/,37bctBUB]QCClx(_؊ZХZQl훼U [*u}iZ~f2q_IoQ9E1g9=zԈ7Ywz _{3 5I*yʸ,QIݢ浖U1T1{l*C8m4U%<[׻gCL:_vB9?;)g9xq+?tܕ%L@!0[t/JIrd@SXK!0$lf7iwgu/Y཯cҨ:Q<ȟSFCx&5I줥s$\ZB6>h C7ed' Sჶ=)׹%\yBZV~KJPUyy6{*6V&w5![w^7fE\N6õj LvtKAKL !i܊z=K[>~ϬJ_~{$mcɗ[ډHɸ\ДafTʞ[BNhh8ρW M~blsj|^.֔!%U-H2!1w==62yr?~ {RܗCbBLd)BaK˱Â^lC[%{$d[| ˾񽰋^HUa*ɪ9CЍw0S|ubԳnMu'>qg9s+Gu|LJ!b_7MuYMavoĞeoF-ɺ%M㉬Ni@nmR=qI,4yzf`z*OWj</vأ8mpMnS_HCSg;NrD{CYlJeodszhKm<:ݩ¸Wm蝻=7`uvo.TTj~41~0*rRnByL6YUyāqP>AWۯFn|^ph[x `dgsv}1yL *L*&iv/+1xuA * %e=ıW=CncHO']#w !P޷"x _~ɍKq fOHDzt-:M9mMHyhl)B3` ^ߊ[q/KZ j3z|4-N䟙2[FsP¼wلL\ 3~-gڊvx'1o3jN q`L=ψ]>Uax^:Gs.eѱ7=49Pծ(w9kS_ c~D<հV8.]p(L1vkL9 !CB!xmӖxX DKŲ8$foߐsz׏kI_F\)e@ hSKt?8Jd2dzi4;Efҽ "^N](E)m~Nb޳/>GKp(k_cU5De/b}1X_"Ҍ_SǕ-u'MM=Lj;#ߡ?9ϹijypDp|16mo/|Н='DF{@6Kw'[ą^"x ~|)N#dXC5xoNtQтN 2v95/bņ~2MWoӠ-awS ŭ)=zzjTʵ.k@*.f[VhHp ]BF em>l ۷=N꧈'8Jʧkp|wq3.ET%s|ٺ+Ⱥ Z1)TtO~\zƷ>K>WS}s7ۭ6|i?OȚY\EJ|CmũY}^Fho^iB&B;>͚sl;|Fy_N/fTOY|ā泖7szPW$]GG˧.⊣:7sI舽:ĸK'!F N8dۯOX{;})󟼛ZCvYK}ן_~_338s3MJW{?<5i\qp0"ڤߤ-nF?~'N3$brs 4V~jzeيX} GN#V *u|{MQd )s¸՟Oacly|βB5= $45~67/ypGZֽMۭ"\<ݜI\9ȿg0͏͐KxR2!)O횵|2`c|I?kf [vׄ1xF_g(e/jyΨ?/YTꕗp#XS rY`HNAklc=$ܯ>z3{?{uHfŊ}q9I3?êsV7PD_ޫT 5lodqעժxDģ3etF##*Nj;aƽ6wo;3CR")|FaǼVCϣTN+񌝰6-Aן?]R!o\n$RpvAE.c <("X;тF9_ qL,3_#'L󠰾oJ!6<0vTL;Ȫ}7sMx0gb,[jR()CB!S]n`NuD]UycMOL0n-iʗz*i*GTylmi%}JyOɨB\eH ZΠbhĂ0_KOns)8ioP_F9wV0懓FHU4%] %>9Űk8A_-=?eYƖɗGy McVZBy)[{щx[Q;a\'R9G OuwJoL*'èqatri}:r&}"WͦodYAڂuOO-0˿GY<<87\ IDATzǾH&x-}boQMGlDs6!)CB!\86Yc AcLF9:n CЏ*lVI N)y,+sSDDoJŮm(G8kB3 |'aNs.y$~ ϓd8K^q8Ѵ?s&S|ٲv* iZDpԀ._~J͘UXx>_,_qV~KkixzP2ݩܶ;ͳ ' x.g8Vϩ^1îDqThJadiJEGԽ&%[9.Hix_װ}pKfr 1Iw꽏Ʊν*͆ =Lp-G=_SU*}f+/8 qN-{cāGt*GJ4[lNF:E}c.W>Ά+`L\7܊eD@L03̰q/p~ɓ) p6|+-_6e^.%(Z5!%DbB .1cR&B/p'JjXCC6ɷdyƵs^ߧ4)/*HEq+>BeHm4~ vOriOEs=r~TQ?!L !/״[R[̝IOflǷ`9c Y5C:7_tF$gR!k A1tb/Ʈhli+#kt]ϫ=8]q=А|G2߿)+G.KU| ޿gᆪC'( ""ػbذꪻvD,QHAzozޓ {//>d373l)^7B~=БH{پy7} ",b<Gs"U1{̤ _t{{ε8n}CGSey{^{؃aa.ޚ`Mپ=l&c $-$D;UfB*Å_)&qQ4ק\'\2dI !^;Bos0XBgx3(}0E/0oKƟ?_!$1$BxwF#E} {ŕ s ;%;X߹4e=W=X/7I{:|fQ^z,Q:(B!=D%%*XqdgB&@CB!B!-!M B!B2IbH!B!ĐB!B!D %!!B!BJCB˯Mf]Ϲ>IšB!BKZ8xqy'o&"M$ڐ̚yWՠd/2{:.ғʛ"Ӕ!%B!&دcEq%#RV7ME‹8mfr6 SPX"qhKB!B I `R]vi q9Q2Oz0O|a ,wfa!eo=s$Z+1Lٶys_t[)]:GQsgsڔ)]!:h"EhQ< ~̊%|x.zؾ'__|O{7hog<DžcCfwWU㗍v72=)Ϝ_cwq f^NJdVe/c2_h!B!C0r]><{2 (jb#qpx"t裼u'U(;Kǔ/X{Vn$5AONSٴEk#/_Y9oxqF{.F'?ŝ/nYU "<){}/y7x=ffc29NȥadWIpKy8 qd&}E~ #3CM`~ 2&!B!ACD0uW嵮ӹ7W{>{~*27SVi/kh.k2{=u.S䢷dcmxq:)&%=ʈlYv%=I )_*wL'cH `+&mw6c!)%{'j`\ ͰCGhL*GPAy^K9<03>X8^{*'Eo^ɶ}GHV.cŻ׬@*2vkiq!B!8 ?fBݍLpWi!B!hlmB!B!-$B!B!Z(yL!B!;B!B!Z(I !B!BPB!B!h$1$B!BBIbHѴVp!JB!h  @4G#jo"i!DS.ᵴy<6̿~ HXy.^6Waۑj` ?c9;!XV^e,oԒu5ch,kIJBP\eUX_?zaNe@?0dNscigO9pk7$:)#0섲1]yp|: Vy]Cw~1~1hmi[CyunSonC!|w#Г(7tY~pZ h:Y^PU޿at?ʾB"P݃ Ѕ1j̉_߅qZe݃T7c|[xs19e4=u.  E;n7vyjN8O&8n_3R&?o.C'pOUnv۫'pX]BΠ{D@E6_gV3z1} tܚHJANL+?)0昪;UUwm;K؏ n}O]Гj"y>b,w ox)ﰩ1`|sS żd!:Ce pIW'@PTr[}7P^qi`_r}p}g;׮PtG ֥a2*.:3- ! JK1kab"W9cTA8;f阯mDo?1(ԍ=0Έ!`%R*=GSFU}[C.҉CQ5,+ sZtRpA[:Td3`'=P٩Gꃺ-S('UWlE?6=7ǍmuǸ EYL(lWסS5kתuF@[+Sڂ:t4LS 侐9v+i)x|,a^wGO“0~ N_g)1 +:vSw㐏G=GdzݸV;{lJbH-{}/y7x=ffc297/wx^z.[^IY`on0~ V+E9%f )K!˩\x̃{rL:#6Ô)|*LtmEՋ5^7sG0:)|qUa[c.:W"Lr߼?BE\7\v#q g`PpG0T6miUw6?н G?9qQxMU=:Io0D^kZw/Դ>[1XwnB]x˛6p@/ C]ݼ&dXP={ L+̅Gr2ƛ 5  )DO2C 6q'Ns0Yty;<5 ,܂~qG?iaƬRs|,,@?73-[6⺭!/u۶17Ǖ*(:$< )Gzxgx&:\+KQ. :> #ɦҝ :]`6's1[ƃP,1$qM^ k Ţ/[યvMI:61! !UtiM\0 <Xs6a[ёpnW (x8nw[AތSw'IbHȒ/MmosucLڭ{7R0/yvI|+^[;^܆va6YFcYQsCʴe^`P\qgwW*NokIg&n?/2Vr^#!M}9'kWJn,i}o-*U nC`c+zK/V@FY!*^=[0يva>11*ǥ|)}V@1hF:n_wZW(̞>kѫ4dT`M+$NͮC=]Q&;B*D AϪŝvvE]UhΆ@,WA5$jC8bT$BVUަ< /KAO-K$`Ȅ)V˼t{8nX17ɯN C&q@Nz>硭X^G_zV_qRݫ {|&‚t;cJ\APrpc~~ΰzs/` =;/w'5N}~[nƳA;]jn@h@*4S?ggtrn\#(0.b;1 3~?kdflfγhcxPn y^ ܽV_;SN3WfRzO<7r0!ÇcP}]'JsЛ-pVoc,K0@Cdu=cPg۳N( \~t${ПV~!߇j7xT/!g>9.X]c[CL l\K! (ѿNX?xj7bP=5zfj^5B= տ'199o J/BPmDBFYC* 9gk ԭqr?|1ݍnul#cSok rǐ &b;KX>3c0@p/g%d>؋ R2nA~v1KkDME"揻wُɭqi+<l6q`X#^[.( &6!EQHiBOAM0\Uˆ~wu5e*RV45tܴCT}QJLXƷ>m=^mPqAaĎ~ԜGTl۪IpޏoA+Fjh0Όżqaj(@/.qhe @s>8^f4UF?to:Ղ^Suv^gaYz1Ƶ>>]9qu4ѭPЫs8W'c޷ 5ټl/\k|śv)U D.خ>dL"1k˅:F,  btq=ji?uI':@뇻gb<:zŝxv D!Rv\|e?>b9;3с [?gTMeϞFH^~z|ŏ 9?l?Vv,s/L`;3X.bWY kkyi2 }Vf+Du">>?/)@[ʷOψ`Bvk;D}}Akhhh_(Qһupo/?0TQ&z \v>z qV=}m9h1.R@:$)?ۃL ąAa&zccU}DDn*؅egyuqA(ԅQ_jݻ+?na~^qǫO@u)E(<':8uAeGZeC g> ,1)=.u? &qqͣv*DCq]-N*Mf[K`t|ꛘ֥ $WcY6ay'}U'imPux3>>^?-Wqvnx$hL4K^宑8,٘ɺ9n:ţZ3]K\&qM:#'܍$!!8#o(uf$7P^1ƋH5%{ѓQcOPPg5s1n 6c1q53B~BM+ḱ.m0nknaUm-ԔƵ5k-4 Yg`]u/zwV(B!B!D$_W/B!BBIbH!B!ĐB!B!D %!!B!BJCB!D / !h6a "PZȴXᅱh,@PcD@Qg~m2wT~IM7(^KӇ;>/K>gŒCK0Ӌ_Geh`Sl } z1>̹;ӆo-:푽Ype_\Ճ֡ =X|u?7_ӞK TؖYe~ mvj:ObiY5kUyCN$>pb7f / j+;su~6ֱ4/]҃</Kd?Nqk'.Sn^qdCO2c~2.Ϲ^v?(ƛ:7FtLUsU[9o5(wy W`)t/bl͹<šع=-pi͈B{ Em1xge&sJUؚG5( X+lJ?٫G %%rGN R\&*$S{:/Ǎ* /ӣ Sr2YF;K\V?qä o:ٓ]ȴu,;lnvQpgXΈ'`kf>m*`]D\8=o'tEk/?_Hm=낻⹳O4b:؞ ZAS{>ٳxnu=C 5N%1$DOL4FTg8eeaL:#6Ô)|*LtmEg/cw5+730$5AONSٴܣiZmhKT<>31 `Lv4!!g(oɁĨtRjuߠx^H\nSg9O^T?cp8xsm).LM`灋0rp'.)-Uj(9cv;Ҳyi =PE/7/Q B%^5Nw@喺Ϭd^?O;ʮ+卭ӻ/Ĺ{YXY;׿.Xr'rm TЫ_8Ɵ ثݯo\ty}I۝>mx+1cCi+Cذ1GjEXpkKqZ֙1d1eY9Ȩh>!!S*qMZ5|6!\/Nsqڨvm'$XTfQAIP0z⵳yl^q8o!ǖ#=rYrO/!#؟8zXO7%歡18ҲxikPr~|rݍS_[?I9=)\vˮ+t`pWI?]DhXZGېs$N[YٞZ`@UsfemNyo\MVjsE?y<D06 ڷ $JB h=y,ǵ;ՠ•+$Íf5+ y'9K*3y~Zo9&heMiLOx0%e2U4نfG!?g%5n0Nu0%I\kq"i 4\PNO7%LhN|ͫH2>Z^۪]v2M((yF핔R^FY8v1 69z8nmONCBH{&dk4}"#(Mnzo?-S7'vi #R)N> :G #\A"EbR{®ټl"*2e!2r+Q n[sNR~\H4\Azw5x?GE&D[PlfT Ư<8+-_\#KOĭN?i #ПhbCExw5kUE_ L|",D*"fE%i.ڇDGEҫ5 gCxtգ\͎ʦn,o'ԗ뇻Zhr[wca;}?(.jH*'K{n>xmLINu Dݠq-=ƠHKPˆfE9+ t$FĵF|.?t-i[9 R$5iGr~+EJv׏0n:#d,1i&eigeJ aЎqwuaƩ/w˭xu+F,.H.)mX{^XƄ3g>Y8wx"oCPH( Gzƻzxpzߍg]OYHbHF$q۟ᵧ pRQ\DlgH33X\t㌜C0%5Z}H@M_eD?d?K Ѐ5 o䩯%pPEEl4=>*1h]kV ;X58^1w\d{\4^U·hsr<OidŖr~cMt^ kͽsnVY[:l_h>10(?${Ck/T+nچzjI~Q9;5O44Κqy}FSTaeer?T}WCԣ6>$ekx`K!sMJ~R郢hUᠠ^Ͻmp[+%׳sx` lfܦ|:7y%ӭ^I_d% bNzTƋ_E~6ηsSXC # uy)q]g|?N}~[nƳg,URiBQ30DŭطhuﵢoL㚅 J\+>3SS5l`8&}Ů9 nfz~cUؿoqZ?Y|7/lXgGTUĵׄ3BxÝxv HW !đ/2,W] 9].*;'75)Jvh4U_`Ca쎡4_bpٝ\Œ>j' ,&3C\dX] :"ʴ5kׄ3Bx2D݌݆$!!8Jxhh/.dCDDDDDZ%DdL yh+ZӪ>}W46UEDDDD*"kL<6,&N;!s{(1$"uRq-V 6DDo'coQG\[w?/NyL/Ǥ%xp`#C#4{>6N~1Iǩ?+`B|NYpMe} 3W1t_LyI\1z^ļL[?7_9ui=4cHDr.qxp1WG*bWo񛿯ٴ̐u Jɟ~s7!y#8{x^|Xj=:|r=Yʳ*'s/~EWE^M9 38njƑֽ;vI 5|,~^|u~ny|,z^C{x <z%\Đ"J H$s;9׸rس,ɮYo`px'>3>8n]\†C8Pv;,ꔶd*Sf= TYb]÷6uNj {eѦ-WwQ*7d]m>]&}s?zn`@Qa sK RNK#ʼn尗&3uvmS3HvI!5Ւ>Ϙ:7Jbi^em""""""{C"-#:~_am565_rW0ok, >2:@Q1.آBJ%|u(^dT|pK)-5욅C>nKqu=-*č#C%DDDDDRbH%gŌ/ט<]98>ev(y9T&hNϺ7͟bsj]Ky|Ԑ[NiKR,bv/Ә2 u6={U!uai<|* "R40EI!X y.bK3392m}p|(Fr =ő8>s7ƎaE%`KY"gI}c{ ĺrqhr0<'&z1^!ο|(+( uז|#oN!T%6?NM:1} BDjIt.xA1|y{]ߍn~3c~:ҐW*ǵiؕH&~m8zu!Çc`i.kgKDz-.'`~}e~N]7mP>#q1_=p fSאtH7pdo:x.Ⱥ%DDDDDS8jW/""""""{OJ SZJ&"""""""NiƐH;ĐH;ĐH;ĐH;ĐH;Đ}8k1+|=tHDw-]UOrtbfR8ǹ4G#KYp'qvu=EDDDDD@!XTLgQqzb !3w YիXt%֔""""""ĐEFLaƫW!KQXDDDDDDZ;%DZ ]wlrup?>_9TefIǩ?+`B|NYpM3| 3W1t_L߼O\#_琺inZ{ov[9i;>~_H@+*"s ~|.eb&s{ͫ'pgpj<>32GZ%%ʅYٽz\"X%{-e{/m,ڋ#o{%R+("""""R%Dd18<<" ;&Mu$?,@"C{q_rOa 6ȡ)e݊5C+a^Ūۻ0t5})Ft EDDDDDjĐ7*c ˅<W@:15|/N 5XGE7DIn ӵZVDDDDDdgكLֹ~gd3+Ior뜻qIH^.So~ƻKדYjp _}'TK<IfW9tisO>^[^=DDDDDDvA!=M#&G0:h㗫/C넯ͼV/8x}L"n)]pgקpxZ7p( $(m!ΐ &v~ϸMڀe]9﹑(k$"""""Đ-%gqi&*-Xpö3o\9rDw$ͭ_cw&KB~b)#/c_/v}r)-vIE,e`Xt-9Ɇ %oIm[]mY9&tEDDDD}SbHDjy-Ƌ:Quu%+?78<{=Q?IQ5*9U:>G1=*O'aqh,z[_KY_aH|[ԏy&%Ŀ'8%G_2AuYT| ~9s%7w NDDDDDt?0JV-dO)4cZd.'Og^&rs=> [}yqo/ ObL%?OE[=R:w9Y8Q\t9?3_3i^iͲELiprި9zP ų_&1d/^:x%ٜxBwÇ3{v, F1꺳8.hc EDDDD1XHDDDDDDDҳEDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DDDDDDDD)%DZO&)8M[Ixz(#߳GΕY6i% ~d)-H[UD0 s|3_]­/Ift$w[rM!+#zYfqe{7o}U^: 4 oo%ԧ\㋬.;4K0P  6&ֲM1$G?tTG*Ȓ099tB3xUu,W밪VԚo=|hO۰ }ssM܎DDDDDMҌ!=ם{ƕD<=9r |x\oGVˡO|ɔw3=#pԎvDDDDDdϏjDd1 )$,Ux}_\QU{1 np(J70Or۝y ۣ$~{޿XjMWqڱ[J I22S'2ꕡ/WONy} ;29."˚aPVm`|;1W91e充 r0ÿb3?VG[v;t?K!=VSt9S@n/wGuޡd&zH`8^]Κxs9!v>m,' N*fȭ-hZp;ZE? W5كg0N~sGlt=gVPRy9R]5:>\9py?>Q]Ks/M rڨO) W~}:gY/>?9 iG STf v~ """""mC"{=_Nاyⱱ^Y/#h~ .Go)rwq xiFu+&{mH(1$|hn7_ MCs{?Rv~ """""C"-L 9W ,OJ&\PLжgJ Sz\H;ĐH;ĐH;ĐH;ĐHe ٧0H#t%$?UDZ#u^0[=poA{GF*`8dϖ͜MeSc1 Iƽl5p8wtBq,}'Zۺ8WwtUk>ŝid˓ ٸ]Q@0ƹ02ԓq)|ܑ`7Ƈd6Uvv}EDDDDC"p ʉ;wZ؛β@d؇} nGӧc'cĂ?a ?҃0KqSڼ1%_gNXl7=] mW  ۆ_-,e x?bmh) L߾8'&asbL[\زfNHQbHݰ~F:VPrc?mx'5ׄ1 e085>1a捝3~Kf~=c qA`#C)v488oso.nS(l(饉alwx19)53莹`ksSoWWDDDD1_;&o'IfaZ|x`uV'z`68yp̐ í6/9 ?u\b2YӘi%DZn#K͑|^0N9+`;u5؜Sρ`1\xcc[owu}0R 쵸.l201ow_rK?TG{αI`}z.vvS1{Ń?~]]vNL^ F_]_=T xxQ{llq:gaiSM>7q? r:W| a^폙Xۃq^==kX}qJ>9j ~y67籣1f}yNօܭF6lGlE 3Ueub[ *֋+oG}yqRdS]=Vc?ވHWL*lI u__Z!ibɎgXQ`W+""""J //6۞#.6Ȯg@w5)7"Mo.R!ڊ pnYwa;-H"GE?mцY\`Sy79lƹnf! m3#m@;zÜx)eъbl?x츪uf=O] ؋ qkYRgBZn8#׮S5~q}EDDDD@!V@F V|`ڒ,-wo``A=0 a9( (oJ{`x_o[|Xc`d˔#๷+x˱W&ǹCFsRDb0ne%64~hlm9i3O~;g1ihv4C"koZO}Q,<|_J `8۽q`NLͲF4 d IDAT̈#p;j)nts-?$ߎp?3"~{0#N}3Shwk{64~y#v̷cowH(1$jX ns?Z Sb]pZ% b; 5`̚21pZg~:k4tM<=sCPtvrS,lLEu?YWDDDDiE`ؿ5sJ$0ocSqʲq0Fù8]¦%bJ-> 3i6fQN̘A*~(vj96`nD0vOؿ 8۫ +@f"gc vlN)Dj~[6sU[E%9ՍhVh1pb|+`m_.R'""""ҜiMrp[gԁ8Miƹ<܇PTkVbbߛUqR&10k 5` 9؊h~0gι6|ʃe❳J̥}0gs} ? jC{8WgbvN`yف25=?Ou/߿}'""""Ҍ}Hkasq_Q^&΄p'׉Nѫg築M w;|6TK.{y8fDnӶW+""""B ʔa1߁zbbj+h I~Hx~`Ql[Up HCwۧoxkmt̩YEEڞޥ΋bsvljS~4Evu}EDDDDF?VDXs4bӭ33pNj9Sm5aN0-@%OqgZ`~{I u\YL0vJܛ[ſ+mu-'O,-9Lڶ|F,FHghDc Y[\?0#|I)SLaƹRRu@&'t%ݸ/9/u7Ÿ~q]ZBXs*h%Yp+l7iס' MJ2#&fQv!o+bvu+W~J A@9n؜AKrXp͗y,s7Cg?*ncT1q a|f nb> 6]*6=Hf~cjñp^B1q11iiDWe_x#WN,`v6YVƉn5_Wk/9upOFVl5BVӇ:t#30y<}eqؿO/_ODDDDSbH1J[ϒhe2cI V2nE97+fv%0B% ~beܲz{"5<~uD-n8̆/eb8c@ rdjѦY=ʏo!W- ֵn"""""Ҏh!879A!pZq2nywNqzL<'Wb\XĜ'Ϧ891>L9Un-2l:usb ׇo7-d-.# &fMIsüpbGV=kƶk,!Ÿ, n,${H1_rGHDDDD%DZ?NO0w{piOwrPU̮^vN:|xr=To{̨mBXL-ڈKI׉DXM]׆"(Zk;PPjt>+kXEsE^uVV?. lx E)1YX/#ٺiOi VFpk M|Vv%y%Na߬jl]B%vLxɈ}λ[U=%㽉 v|Pj%6 /_VW2paG?+KՎ+Tʓ%gږGXK^ev#~+^ H"6HNK*ɳu/{ ߓŰ|J~,HTw6iirKv1oю;qery\K&xĻj<:p[O?rܪj}?*sO"u 2N܅g{ ?w͠j9Wэn8oKB>N?yKɨ0 2nn!'ٍޚJVXCDX5CFXVľ:q9%!]CZr'Łb\m5yErd>(q9;UN,CN ){'uy}$""""C"m 0cv5k#TNYGŕ?%∬]'p{q曹Lg rHӑHi *$>3,ks5}3cO$(!RKŒ;w0|]NYy1.xCi<\n&t닊ya]=Cuf0xۣ#g$Hu,! 7mSeFn6oY!2+\rVbJp7$i.f`U-~uBwWmo X֎ֵ:IH㺱5\c9s4v L,Czw>> {i%w?0A afWS)#!1>Rz`Jyvy`틼 qT7 )E3 Ĺ8\}>65(i5+"mWeA.O'I_L kpՐę%@yU5s6dsbreS3R80R}SXqo*~&.`!""""/}-%i!c sK4?G!iڬim?J 5J SJ SJ SJ  0$%(-5~3!"""""MD!6}x /=QT-_?;mְ^{S扳'O26|QșY75v o2R){;~[~"""""C"mX2"g|p rir\O#F軗KB=1aUS<+25C^>KWݑo_#>1W\>_c @4jHԞUvJ wV/TGe}氮Tʖ\|1qc)|kYl(.˺p L ݽ>#;OO|$-QAM ڂ6@ QKH6XY<75e|f8_r@hxw-&?3,Xä)<ԃӴ%eene-)1'""""[HKa\N#cI-Pm3Q'UӿKr kqlîcܚ,r">90oY;n:^a|p{EJuID{.W=wXekOѫKXT9;N`zSU,**eKwRç_ǰhll+*SeaVyt1|;sB'!l2'?]`Q~{5G[E$.M'ur8wSx}p*"dq'iWOv"7?/B?r(eٮb+cy~/nۖ/*౓!gj2wO:7<~u箏nNd!R.=1) 9Kbۺ%p+9evuy?i?iR]l$^g1rфx#?ز^c aLJC%'Zc Q䦍s] {XU6V0s;D,qT1. ƥ3zrz9cDfxD.~˖B/D#gӲǮVrщwu)N=ADDDD6J A&9#Or9>\T${K~v%j8vw7}^N9~So.s}|R"eCjٴmbZe $u:#%ɱxU>_k \ Tٺew+X{1gi 5C).;5Æ̣myrX/WbHDDDDdgٓ\C^q?%^֬oS V>!ĀevX6=?/7d'1YjfXp-~]#OONDDDDZ%DڠfK9yh5r~ô>6:(T ~wÒ^;ESS ;ϱה5Qޛ%gstw^ 6?˲(ǞQ}bٿ90"s˿a]} ['wuLG|_LemCCǓ2z˩0% w0BJn:%%r7&wr/Sx&~]1*{~^==筿%*Hc}LXXONHVM7xtH-ɔa1[5Q7߈{%Pe8_-S$;Uwyqrذ*Wư.4ϰrN/K&̩lեv+cO/"R/ f$qYU6~½N-3]r(^geI3|~^&7AWP&""""|i '"0 ;TDڠ+*9|-9z(q$UsEX_zҋ7n/cH Gij.8GįM& sCE\øWRxk7W쑿jY"?gy }XWژ5Ȉ3tpu ^Lαqn7:O]MxLscDk>{,BH+d"kD,^O{){6~"""""@SRhMj8+)$""""(1$"""""""N)1$"""""""N)1$"""""""N)1$H) "#گ4~""""mW/Ҧy#؂R8_7 #O#-~1We^6['u0Osgˏf&z2p[Bj9 `q.S?aY1nn~ {ͧ3-`07syC8O ; 8ZbWF~Iy8LMɔp>oZCK*~;""""C" Z IDATmsw&ﰅ-zn5,`!Ѐc.=S? [|2O,1[?8q4S;~ď-: wjB8(oe̽q }Gt|D\lnž7O6޸,"oa-^8p/o~-Žsᤖ4UcN`+0>[ iݔiC7Q-n c* n#y}c5S5ׄ1 e@A _cǯexԙDXn)S;;x= P~L -Ź7Oy{sqo]u~ 50+T”Lt\s0NJֹ)wxIBH%" QAPRk]Qb **,xED( )=.%BO'!Z'!_i {N>1{p^ Ɓ-_Eb/Uӣλf1W\烚=8WÌXR`ďW.fX}̢U-pn|{G5k|i yf[ ٺTM|T镘o.L0-z=J\lDתb"7`3J~P>O/ocU>O`LrvW)_Qϯ!gLrn xkaX/l rqc6Ú ixzGb<[ׅ9nP/__hS~oy*vnQc$`f5#aG2m3xA;vC<Wi쪭.vG 0#io}bC 9SgvO8Σ5a2 pmB8%||'発 +qZ}}8ޯ 6b{|4Ĕي{CHN,JkzcZ`}RN]e`-OHDoVl4cL3yǘe_ q`־mb_C"OC/i90k\ Nᑚl bS 0HqӉ- p`>(g<ϧ SSNE_צ{Xi!:sxjMr*:S`쓕]( ڇ 5ǟThϋU! uo}|ߋM˃z_hh6휍xzi<6],$׋ŀg@,CRy|,uSSOs~EDDDN3 D8VN1Uؔχ ?xCTeL rSVcv1? LȴP11;k! \mlOh]n*ٷ"~;R<80?m9"+Nӱy878ӲJVc|Nh PwiPa D[J2vJkܜAVYj-Yx?O}g'~8""""C"g ; 61;ObC0%sw0UJkaz2 0ۄ;rGwl0/زe1mc.v,΄Źw6* obڞn`HFF܆;.Hϫ+c' kkݷ$fa~^4O&]燳tPc,~gxY ؿw acvޞGvo8[*c-D*A΋0]kcM_3c|y >χ q"Spda~2Rk?C"rٹxg,8݂!BV.6.rv3s~MLP7{hzL\ds+pW#Ub6Ɖ@jvPJ񃡼dWW<[gh}H߉8Xmz83,ƮޓxDh9l5q^<Bރ]cK#m߄{.̽1]ӳN=U`G15sCۊ78dkk1OȁRC`m#Sp^LZș(,Wc\)͝7\{ߚ}?ޱx<:wac;NnGmOzS?Cq~K.qnYVWkbQ98_^3+N~~p (+98j3/ֽ2  ̢6F4LLd)YN0>?/8NUp= Bb&EC{/6F ՅO;7cEa .qZ`]s8͸oI0W=/55Ww+"""r JSD@|]"!Bda4L(9rq !?`HDDDDDDDO)S DDDDDDDD!?`HDDDDDDDO)S DDDDDDDD!?`HDDDDDDDO)S DDDDDDDD!?`HDDDDDDDO)S DDDDDDDD!?`HDDDDDDDO)S DDDDDDDD!?`HDDDDDDDO)S DDDDDDDD!?`HDDDDDDDO)9]V ՗X`yCe]""""""RLRPQ1;koC23sO\Q؎i^2WiB1+Ǵ\Fh3ZMDDDDDDN!bUx} Fl*Q2j"gg,r,Q;}S,$""""""'GAT+Ӯ9kL ,C*H"""""""rFRDS1˶Xyu4|U+.0ks>p0dמFfMcpㅡ4aԹgŴ,H[Ե>V:)WVGKbff~F27_t~PyW^?|K0xc:43޿o>[wP[y_k~㼥R} I5mZ8ܡ,_&""""""r S0#*ܜ]Azd}=s)N AݩqyXwK9L uϧ\|>e{g*c_e_l9 lx W-} 3<5+4?;gs=/_Qh%]\'KMjB""""""":M% $2$ XϘ_^s101Miv:uyn3bo|!eYܑ7:]NsZ {quI ?!ER.z)41Mhr˂o~aK[8M'tW\HDDDDDDO 9rs,6u"X6zZ^>&<0ҢtY9lD8wwnma/Q,N:VΡrwb%x0&1;7T!$anqkJ"""""""rzTHL.{r|LX!lz =iZw'oK,eԋ64C6w瘒l}/giک>sWr؎b7!D YҪ~~* 'סR;|¿!]y7/䤿)F{Rx{Tj/Oɉ˘a" } 5nX^i ,c€ M?ZY͏}S{mmw/gˇCl(t<ïLJZ f{jndWqǼFc#"7[h8 8i|G_y$fP7Dӥ?`L0viGťYUndLv|Kʈmre?Wb|{waCz/jDxs_bw7;苸w/^[>BAsxk{܊d18=,Ou"pKf]yedVYyu75co3$jwŽ"6+ر7姼ˋ1$q#$)>s{̵d$yRQXSɂdRyӵDKYl:ڻp) y&.ã]0oϾ_/Ǵ/ :tzで~#`ł4BO~ X1yUy~?R׸.7y}Ai248I~s:86oK&lF{bʢ\ɾn[gm7{6f'vD^#[wnrs3 ؈ь߶~Ln0Ч_LuY1-u5 3uD C0k*ro`JJ}6C[DL}3ŴhG =s~PFP'yot:SW0{5sNWbeq{RڭN&S/˦M廑6?0̲㧏>O5lon 6/c~rFo™oQrh5p4/fnj'a/sEA@}z.\Ō8tv[m]q])Tna;_bQ д]}+_oaC6,woKHI9Ղ`ȸT1;yW~H^^L7%) ti ,(CYթBYmQ[?|d7<8ΣYCɗĜl` 3&n͹4d<3vn4nZ [a5Ҡ`}'K_dz˖8/uVƳ>дyu{EدۡP SSîIfuG!O :N5pOXź{]-]a\R0,U,?l[ْc)Lbu9F\s]YF6iOxN _\#Yv.NK(f}}^z~+Ͼbİ_Y=`8ǛIF내tvr.QQG/︧kx#{Ph`] ^=U{ۯqt5UlEU*S?|^.-bˁ?o`3Ujôh@N IDAT4m=n̘ ԩ}5!`u+:pKVZ&֘'2}DKr5Wu{>vϛOwݽEDDDDDJ@4 ɺB،X,_HٽyyT9 AWiJfNIԄMc jy-W%|ܯo|?Kڌ<}YK|l<{=%p=1T@*8KckӞ7w\yӁ.f( M.vYl.1o4S}O^?߷.#6i?DHE iq vj\s3siA.רhlr2iI `7y+HY7Ob0_[lz)QT ԝ[DDDDD?" هM_?i{CطuwwFe$('aſXWDDDDDDJJ42Lu9ʊ%{H)/me3zu]}fhnS \.8D@L]ʣ  !_Hd+j}}w'4 K]6I♹Uk]Խ0s OPt⏶~˞Ed-#[_v ӆQrx|A14Ԃ?zL_NWrxæ#l_:ÌI͸t⽻KdhYJp [5_YÒC|q JUF&"""""RRMe֒`AVaO'asy/:%жya9I;\+W$h|U\YcC-qͼ8"J^M̀b]Bǯѕ&lgӉ<%!?'4*k(SZ:u:_gEC~ÜpSâOs]pmTOVD6`]|yp&7QN4 Nfbl1ˆ+ɹ1 c#I̙Ovdqmi7؄>[Tǫo:P|#:Ə-#qO5n+Ϛw`A\t _W-5 5ic?[w/|G_|-:,6m2?~|9CH]Td1\o+Ge;{ ̯J ;\QV\wJof-"""""CXv@vy7^*{LbӢ;/vZP}]W^x,B_[e۬d|WNTp>;773pBO)yعnoW)739ywYA۶УW{Y=S f%/HacY`,k?>^f^%sѭVz>ϏJhVVNaV&3E4.[?#^"@XP33H[15ۏl?w|uW=߮$ցfɈp=ip˓dO|w{6xsDI>R{$bޞoz?6r$~i7L-1$"""""RbLZf?Ʃv7C`c3eiީ!Q™"^Q-yiG\]9眛yk\8:JX""""""%7.WƕΡJ i9O^ҡ1cS^ 5f ޚƼFӔ/x? KM No3GP㽶eӠ.t|q Nyj|_fuD>忌-b.A7%+!a{vmt(}v*)iJ& ըJ s2mlIUsAo 0|kҠ`HDDDDDDDOi!?`HDDDDDDDO?B9 IENDB`go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/testify-assets/images/000077500000000000000000000000001520301377500267415ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/testify-assets/images/favicon.png000066400000000000000000000031661520301377500311020ustar00rootroot00000000000000PNG  IHDR szz=IDATXkpW9B$$)D$H-&0:L;L cZ@D Qg:h -? VfT6[ I$osisY{][gi{o +A"@_1GZ1~'AN E/z^ S#vE#vn,Rr?jF;^g]JiA5EyKU9#vD'2]Wk#5"B$zwF>;3iD>Cmy=5Mz8Uū;d|{mJ޻BMkBKTr}?ENf!J'NU EbMhI}KcQh#k7qv1&yIoߚК c8t[ u"G9am4bר*?1r]=UEyu6(zވ-h[~ua3lIɞ~9dڴh0^6ueքXވ-DorQk7Shp)=yTcyy("sUxwϼsk#n<<_z# [x * -PUN78JᔿHpNFTclנj;\]}gqSU7mGQU:wy:{d]<\rc{p~sƵ_q)̨=7SCj BI5a 'C6Qڻ)⼥)~{c\~I\Zp6yH<w?y2fcM_9ŏ;5idw/׎}5}FNp 4`[c$G9>EADȦU~b+j6X<^~K-P-F,ք!Ia Co;jsu>C].f y٥):{=W긺$y8A!: SZ9>iK!%b7 h$05a3'<Ʊšd=U8PYYj-p,7TSl9 ),z-޻vvL6e{DVlSl#1xE PIr2 Gmz+_)7u|mff5Pt1i٬>j;8q%rgdՔKV1{+^}{uoF3)Uo;p~T'QYWԎlkˋTƄVUZ.ā;X]ʲwDƘ"rU^[LnΙ4վsQ[ͤg #vExmm#G޿u䭖>WS<Uu U ?hǴ&p᭷5s"!A +_`"uWDl|2ڰppz=$,Tk #`_ಙnn0,h*9ڝp;Df+5v8@PoG dA~AH990C ID{z'*Xn70vvTmۄ< ~@~VɄ9$LFVpkrҐ=s919EFAz:tRe4/Q_@M>NF^6t}t#SƌGs Fo$H'+txT|VPeCj ILn熧]ss!ouIaPaP /%QA+eKlMG_&xs\8btLis}Y0)btTx(_ -A_ƆU`Da\`L͙}&F^W w"E0`y 'FƵ=IN90ړ(';I /ZU]:5Ly9gm=k '`I ,K${K]]9&vXܓCEd[y iuɩ&SJVc0r(W8]fs$ ?*orQ[ ɫIr!Ɵ]^:Bp>r7DeetU,1RZ0܌0RQG] $ /ֳ<N),9QYADjt]pT0:l(\>x[h:X|`۵Y'&LE1o01b6|T UQG *7ohmxZ97ͧQz <8HY@.)*0h#қIq *L>kaGlr{Ѵ50޶ Ek1Çcq4y2^!)\.PXXmX7UT1{ֹH; 004Ol{o` y.1UqɽX,+p5/pq "ZdG\{32΀!* P7kkDg5mUEtO<4%ij4#Z:4Yu{vՕL}{ A) `s=077V s\":15xث;nF9cL4^Cw@Hm6<:ef? Ν&7w]_Iw<@r=X0 uܣ}4AȒ5aM6ebO6"K/>(K>bbi=eϚsD"e>P"Jer0f 1YHNIN!($nVس{76n܈m;w o\EtUT1{sǏ5c驈n:\Yc8TmހROĄ[!.[s<--ƆEޔ`2IB*֕$9m;iOVeOɑ,lU\R?:\ɊcDWwG 3/܂[nQQQݸ4qm##hWH!0,KY ~篰 nGȩ <=_t}&:J~~#2g/n̅/κ#F{UcURGIkDqܰ۽@T!HPKH9g=rƗÈׁ:!x}(x(p>\}V1118wFl܀:5yYyK~˗`[oaʔ)ABB >}2שqZ-brl/T; ۷ cteԿ-H0Ģdp66ɶGvZb$B(49?OAhf$6#QQ;ܼMqbգ!m*= b#/<8j0L7~<^#<n_x1F<Q7to^Gff&Dc|bGl\xVTmt-Ȟy<0K^p,lt[ GGps =ȲXqu(ZjJ톫n)g_9'"KC3 W뼑4FՊqANa9 /^|Y5Jѱ(Aj*vOv}kHFE Tb ,;q@k4xלI Ìf;BHv~.$)% x CwxRBi19ٸ曻=5Y>$@oqw$d6qM83a-M'wN17M @ ZJ >|fLހ6F(U۷R^G "ұϠjwmǕ/; "Fo~DtN~' ^7>LAM׋{bÆ ؿ?hInn69gP1Z]] όO8]8<29-Bu#86]၈ht`~c0i:>}:Mը)؊@UD9dz> oDjݢTo݄CdC|rzFlexvQV˺v R4 tL[[Sr*F^{LI=ff͚cűbҥKD!0aN?tzغu?[% 0۽8 :˲TTmL>v5itU4k7+ylՇll`{Lwc}"Hk%Fu8 1ef'Gm-9ƍ;Fp:? V\IU~ /Ν7|N&5q&U]ISGIHڮz)=}r}xne1t9ncb$Ț~:;VSA#ߕ$La7$ \t{CEZ,X~z|Z5SҐ{yaBAx< >v'G]]nv j`ĉY^nn |}駟)5{a3 yyJC3NAmP<8MM;hhhŋQSSөwFk$J`% | $޷o_F,A5 {К">a -)Dq(_{>$JYd)b-2YWsSQD 39ς.A4"80EV.K򉂑g?, |A .!YGok֬իC8ղ   NǍI賭ڼ2 (x-p[l 9$HҳN}f&ت:nb7]U F Ԏ0~xj9p) ZChj:}dJhZf޾rCR%%%Q'""*++s^֜<|/m!bQ_2?[b݅vU d@7,:}ڃbAwBMݻ]DWmnnn5LFӣCkSO= :q8n:FXR*uu4 [%UW_Qr/ C4ݝ571Qw-;Vu,G|{ՒVM 1,wܪUd:Y[вq`X췕H2C?͙Byu{J3ϸ)):,|1cдd 4}v؁K.ž{6q x}}ґ= jn{e7]v*hY&F\mJNW^zۖaΦF}lcܺ,c/Qm3-5 Bn܌տs*A@$yXk׮m3Aԩj4v7⭷޺Fwe5%2~<ӏHBR:t ]veA6T8p# g^wh^E^[xxHپ%JEtJE~?$P IfVIZ pɧJMLZC]xοK, \eKGuo2$'cݺumFˋhbq nأ"lXdLթ׈ Tٍ_|F @>Vtt4Ć"X,ԥhs9= U< Gţ;$AoϷŇvu8Q%M\~,MB-w|;67s}qXTvBh vaʕ-Ԑ@_wcbb`J5nقCH=ӛ#=r$GHs+ I/`nC ƢKMpD{'M(syoNqѕĸ߿?}Yax'K::1mUן1st&"QinC"j4Eatj`gASᗟh<6k>KJUTīD RQjYwQKRr*+8bO|ߺ$!m4[ش.d@&̅^H_=+V`MZ%zK_> /2VY9/hԆ#2#UUIK *$O0^p)yn Ԋ!сCV;l \tEի z= XߗC%eOKE`!c, %hؽPc[d^&IsP4-[| Q vg2)i4˵"dz)쫪YT=lCFP_ 8 RY%W"܋h}P̍i4Qr{ ->:<r^tN޴A]qG0GxR*6,L:Ux +>AC}1D[0[++и \rΦFhe9 w0~Pkk{M=j_gqq1>CG ep57'puuwfUn\K& 4b4wv[=CRR{,Y+.~&<F^ǫxXfV$[dU}Lmjd1>]#8wdM/AQK/>qڂh^0 U|hega5|H9H˜6 I`uxPRr^gTT휨t`W^]w݅^S1ǐ2v*Ukpq&0"2Z+mܕlSfQ?LExRYXX v{N'Myp=? 0ǐ;\*UhWn+])bS j$N!6^ 3)J-{:gpp '⫯py8o2_)idkR>:*I3kh.".ysC7^ƣ=iS ]!{¿^w/+/GʸI!N'P{FX!.q 戗L$^C1H;o " u8~5jJKP\ufjxT8bCJsO;|`] q$cY0A]j @SX1;Qn5͍Svv6bconGccWUk4!,2L;EzY0 %Cݖuݎo 3`5:e̓Ey\/M7lk #oUf=;JS|^Z_(Kc6$H&v$R$TS9]"걥jH^5MȺ`pȭE4ށC?#S?28kjx>it c壷bG漋|ڜA}[f]@ @H*b,AA$Qj$XC\\HbLhvyl뱊e1;}fC\L3Ћ몖Nせ.߮> Z{k/~?gMrpzMDUSR 8QAEy5,5 \."Jbf]A8':2Qi܉0+L_ cXmm3萞dcl:D$Ř`xy._s#"9c]u]YҨ5wU ZSyo<.a dbퟁQiQM8T^U0[D$E .:g"6:&=|>BƲ7%0uL^C|1~d I)^0/;D1wDvB Uϊsf 0?ˤH$'Da->Wop_/73=v5~i =v?O2Tvmzݜ Ay5\j63ݟB ]8{pfv%H%DE;J΁|651w̟ CC9ʖ~AY}Ndb$&D} D+ρCLoDBDa߾ ڵoubd RF"*WbOxa$Ć}r Κ6 Ѭ ; *:-n0Q&F(ITV }N' [`9;[^KKXBrbiHg$Dz܉C`Yra XNGJ# <^Vmh4aw$XKY "͊~W$%$8^NYbb DM̆^g<$$S`8 bH^jr΢Xj'6l/FiU#]㣐icqa ' v*SٯӽVz;$JpZt ҋ#H9`]xk9HKDZM ނƒUؼe/N;_/EJF-Ki%ÛW#7=> f#>6F˩&\ЋQ2&AX0lYZ#<;?~]dDj)IDuV4F13_}2p9n'Fcc#6oڄ\x O(N7~Y[pƹput$@$FAX}IMr1[J4lߌM?@?nÅ_6uH+GEE:KWll,̝;Q߬ŗcn3 }Y+Gї!Cf_LA#`+ի&A׫z& 6:)D]y-y^vC墅;*Ml< xCmaa.ԍ݊ Ͱ2Y5 Dy] wfqq0gcVCCݐE$H{!}\\$n>|jѷH"GqzDt磕U㸶V+ݤEԫ>KOHHqCRׁo_@GZS FC{osEz}X=@ISSR n9$.:uu-3B"B뉈Bn 2Ifk`$!@z=lG *!IGh/bqGG{%mWYju: Dstv.y&=Չfu\.>u: YC8w#D 1qȚ97lmڒ86m(**P[[ h4}oOM'imCr #J6KQ~5ףl}-1Yx1+ŋK͛71^P8qwF}  11F|s&P}n7} n߶@~ei@ih mZF9#c >}Z-mKjjPBVt@wץW0)(nߏ>i>jU^H]sՍX`D0`Wdj@|#h XRFf=(XuN[ <ε6YTZ#p&eB<YCG[+_=Nbyn;&D r5Qo/vz#HJm, z8^H֖X3,Kc Y@ /lM?ZAI] <ХO@҈ ˣB`VJVj5 ?0BŁ˱'uH>(2Dں! ¢ H,ut1G;U1}OD ljAS2VbL7ߏ B IIq՝q5A'=f>N ˅ODY\|QILJbpQiqީ6H mx},rW]MMX}&F߇թN!6lޅ%|=o=rNN^GM%%=l1&'R=bY~쁇Q pQEmb2yz fuЍS=@IpOPE>k9Oaiz+ Z.7cX|z8z4EH2X G'#F:^S O0bJ-"NTA[Qn% =6Ӡ`b v=Vt ۳ތY6#m@l( u#^ p64kZr ҺAb9%Q.&(à vz'FM S)btD=ۋq7b'HV0рom5S:x%K`ܝH;EH8CM"W Rҧ MyTjŖO!7urGRB gY׬TUM4` W@Dr*LG.i_ o楓 7 ϣpJ[{% HW \Q @Ǎ/BeLK]"H`QQs*˝>@m2u; ` \K|^=ixD]$1>%,Tt j*(@VGC*K`C0 1@/JRJm"cw0C|gHϊyv$AÙ0+YipxPe݂_#dhh!@Z>UUcfh g?,'ٖTY8}-k|`0ݑ.$8]`Ġla2aPf,vDa% F܈9C-b`_P`YiQSkQSÁ#N,$ Nݼуp k)rů; v\'< `Lp xk5{9(IqQ0Γ$Itqk dDϧ/]pQcL(vBScJ-ћ18J:-~.FEHb`];0tDȏ`؝n+.qt\rx,ef_[_L^ɶJ]<)c\b?qqJ^""DQ:Z.r0(_/ND6coAc+bX5HI$`fl}_F4L@k0s 2|NKN܏W6dM!aAM@;wn7;uop{BOۂUu!'-dцX%= Eq,2R֡bO!Us&ab~ @:c̟(AxS]k^}4D :phu,ÿ}H b!5rRTĞ+E&G+: R IR.dsм"NCD$̶E QoEjbn0%Q ms妎둕 KOz|x}A$|h|Z>FM1)|n{P#ݍcs}-zM tI1Hߏ?AX4Z d$sYq5!DV$9jXΛ(21 CvDx<>.&rJ gEhpۍX}QXw,WpXs IauUQMeƖ+8Wz>B1%,}Y}tn_RAdӀ$JCN(N1|@&,ˠ;ߏraD?$D.AՊ?"=I1-!^$VRRIܽvC(zZxdː!>9i>xV5[;]m])Lr|5NoE; |3t3R{0u6ݶd64Yxzr_YU}(J y qu:ވ P'4$~U龜( ,wX 9DnM2S${3y2Fi82&$DX.%;.a7D BWo3mEmض>[~kX)2@*o~d(@6 V,]t4HQԫ كXH9^K“r˜n|s j{2)(P*wZnP2aBN>=hwOǚ?~_f*YrEX^o|>4Fz| #G CRPh0P:. )i!;&, NfZ7 +f~݆|9yF,-2)nyo'JIS mE.z+R8պ<^+r%˟o x۽)'Mm`.M$ar D߫y5׻bFAۤK?¼No<V9ظرy:y2l;sG R=APbo. =^g*:tr)&A8^s!@u]$\\cKBoF-DH2;!`VRC 6A4a&jOsI!& F>a 4<-gQKwu!AY/hrRJ`t`B,/uB5 %FcH6R`d)f@Vjv(X1rQt| ҦYTqL.aPAu!!+)T zNcC%r5p|"\]И*J^u8%nܪ2@cmi4a&px511`"W4Qk}k8v*nY" b:br'T,nv u=" E}uZmfYuLu "A%:6W LI+pL I$YxC 6?D< nXkG7L /j2 vK5 PǷ&A D+* "sL m;֧pzBt޷266H"Ď+܄ Ĭ̐mj G]I *EI=0zfbzL`㧠Tf*RA8cL3%UTb(JI#)w8T_ EI+p ҧtŢ>E]ץ!UC,"GS)~^s˺"tSJ bI[ X<0ZV@eIBܠ'X a^X >ZY'A@^11&/Bq2D hmQ7`Y`A$^24[B0 ʤU8^b8CgcOKj:C-Zh-Ŭc\ڒ'PּJ}KqE1Sɦkfp5IK.+"?'_1A R[ET2CQDQ2P}Pω]zH'eiȑb =6oM-U+]‡HtmXrHH/u ۥ)ЅG sU8t bդyܨ*f2(ዽ#(HzI)#Um^)$RǎgɆ!o DPֺ;ަ8eq?U;m6+!IV:AE83 G#1*:.,sE,Be{$_]*&rp[lrX!}j$ 20Bs%x kB31s[Q32cNGi(\Y3 *D ֠Ҽ fg1!'xvm*Z@[G+#]C]]x(2N[Ÿ=FҘ ,5 u8lhjBM8%Irzj/#l\ayMwjk sdX4IكZ8"*$NT,c}#o md)osK#t@$4/$=7_&AQemXZ &l<Emؗi_k7 :J^BNK7/\ zݣغ|U%{Y?.4BȾ)cE@掵0yfuAƹ7$kh%FV""=cnE՗c"'6>e 2;7"J&6VqB]v65{zR͢$ڧ};v~YI v _1)G{@0h>X)#lU䨫q=["Xm!(\7J Kt8`}c\I3zEawD@L@>bXu^[EDeÀᗏyJ8\2_e;y pˤpC#a?H4aaȘ<L|}؄d)DF{/hH-sU872]i% yكvj+%PM:U풼s.Ő;`KU&6wU_ASBFkC?bPd $4U=";(g67C%M^C6jUjS"EEUU(j&Ғ "BcC1ul/{ww63ՙ=C)PPؙ1O:=3>y.9ٴ%!ސnQ-Ǣ [ڄ p (-3B)P֯AC]Yt%\2W^D91QZ0JADvev 0*9CpPkRq3? }{^BǗX(ڷa`TBD;9X۰Wa^4ou\Bp¤b#MR~Z''|T=i4.פdf 뾵mҩ#lhYaN Q$PDLQ%M0}OoƬ&Ϲ"LPKB V.>h#m.cמ;8 ,0FJE J˩eP0 5Ƭ d7,|z#]=0 [b{G p07 c|鎳 ޡs񘡴J?]PfT/[Q6ObVlJ`ŧX̮a! 57?l'0z~oNT"qQ0,i+"RW}ZU=aa4Z ɑ]%[Q AOA bI,n\Ǡ W".Loa^icD+E8lAJ\0m}gZ0@.-;(4BNCBԽiPXѵyg:kYFO zi~Ns"Xh' s4fѸ 0fg10&7._0& H(09"Ln@G^qJ1"a~LNqo 6Zކ>韽ZF( #MEAD*A*CEA6n41a BdJ`cXRYΪƂJK:?Nb)% b>db ԕJnXVǟ(z"rӢ .DykX1,r表(-kwӿr?,Ѓ@e1֔Yܠðp>F.0L@,uT-փ@@}J{6_L-[* l,S?}+$fp.04 E [ ĻZu&?@ߤFZ jAjvE )D_a1k^߿[o"ϿG+vT^Zb2vjWVy db{oUԸD9)K8kˤHQ ٱfЛ@@ >Z\߸!CYes*r;a$—tVY^E졉k%75Ĭv% 5mF 0, o xqdQf`UN*BoB$ĬZcng:˺ k(tvD*]_(-W!զQ 28R`d2a [`>x`h^td23Tr:7Hq_C= T$~IJwWiI [W]PD*@|ͯ'v Q1EWFY bĈŧSͮs8knYRcs,rZ~?0vcWÿ}XZlπ EW8I?J]]+ xq~+^x)lkoo'{0gqIo Vx}e3X=_mW{d8`uԄ8g 7+6@LIENDB`go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/testify-static/000077500000000000000000000000001520301377500254615ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/doc-site/hugo/themes/testify-static/github.png000066400000000000000000000067341520301377500274630ustar00rootroot00000000000000PNG  IHDR>asBIT|d pHYsvv}ՂtEXtSoftwarewww.inkscape.org< YIDATxiUffP`,AH,bR(.hL*fAHYh4hqĥ\hLL$ "Q˥$"0'?yͻꚁy}۫*uji`4!@4tIG*Uݘ!PHo(Ç??I;ʼ,DU(ZIs3 J,^څQij\ eP_:g#QD9XUy/lƩsQ)DdO%`(Jx-`~չ;?K|(A3CpbvMlzu:7?؆ yrpS3g1- 4'ۀcM^haw ]p $vZjۓCc<Bi=k#q 4d XqGڀ50Y.Ƶ+~)xX{kӞyiӻn~ P#H !oy2F`ǂFr{R9EO;9PvcY63 m>-TL14PvcY6| dB'uTyn%$cȉدk p6tCttpnF[LZgWMHϮ@0`Hif5J .Z@E T㖍Y0Xf`XYǸh؎_`қT:*kQUDdPF U]kjV>T_5?Qzc5㠳 kh3pje.nc3*FX$Y[WCr!l(yP ?ĊAȕkŁ9l םU}xPEsE e O>̺Z s 5]+X9l~wPXaczDz kue6Z^k24ovo%bYM9lz@|,p3Nen=,Skz \iCٵFݵ5ADDdMxcx;0*W3eg8UD jƼn,{jd$p;v+7.6`UNU7vZ2)u&5KUucGdcU}$t Y"/=|6UO@g,ĝa؝>ZM\ dXy2Hchkո*,Y#`"h?I@Or ·<_' Dd$&`-Vw$/ӈW;vgWa{LL>FD\S'9p>&dcQSԙ)Dd^쎉/]Ajrg@ߴjL@~A}XNڊ;m)`\kFuQi;'Mz7͞q lǽ&6(&>?cƾRp;nAAkcp3pDڎ37xݫݾ"F-d|1 1 ӛp_Rvz~zGK% c$ѸhOڎc̳pwYJl/zufcwڀ)"pU6/k%"ZUaשR".wzxR憐%aW8S&d[#(5Я^F@u/N[nmفEa>Oq[4%9pl9i-W2{^DL6y4~G'~쑶{J{a@pbQ oyI@`3Ri!EPdhOl;0D`=) $ VV>BdxVH,!s/`I̮Ny/Ȁ kC]cŠS{!N5vA'D42d_[F2x+a 8k*2uxBNq'/ 'wLZPp1D?ܫ\ ! "kXcftx8IUR H a. 0 FZnDF0l}>Nډӣ8bDUNP#GՙA>bpU@ c!m >]U,7<=+{wC|J{Ƀ p`ڎ璉 #^0u1zf<|_D¾Qd4{q~iOŏS |7v&ϡ0)8@OToyڂ{۽ Ԕ&yO⬃6bUŷ`q )]M/"?gq0IUM.6{Uq)K|p887llE` |7y O^qt0M5|ĎջnQ p;c}˳0.U)z&rbO.FDJ5[qD{YnW՟S=Ad`^[= EjUuꅚRFn^`\  yT#yjm=iMpHɑ 2`ߔvm}=LT`RUtr(txomY{b ySxIENDB`go-openapi-testify-c10ca71/hack/migrate-testify/000077500000000000000000000000001520301377500216645ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/.gitignore000066400000000000000000000000201520301377500236440ustar00rootroot00000000000000migrate-testify go-openapi-testify-c10ca71/hack/migrate-testify/advisory.go000066400000000000000000000045211520301377500240550ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2026 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "fmt" "go/ast" "go/token" ) // eventuallyWithAdvisoryMessage is the advisory text emitted for every // EventuallyWith / EventuallyWithT (pre-rename) call encountered in pass 1. // // It is informational: no source rewrite is performed. The semantics of // CollectT.FailNow in go-openapi/testify/v2.4 match stretchr/testify // (tick-only abort, poller retries), so code migrating from stretchr does // not need to change. The advisory exists to surface the new Cancel() // escape hatch and to flag callers for whom the old fork behavior // (whole-assertion abort on FailNow) may have been load-bearing. const eventuallyWithAdvisoryMessage = "advisory: EventuallyWith — in v2.4, CollectT.FailNow() aborts only the current tick (matches stretchr). " + "Use CollectT.Cancel() if you want to abort the whole assertion immediately. " + "See: https://go-openapi.github.io/testify/usage/migration/index.html#collectt-failnow-vs-cancel" // eventuallyWithNames lists the EventuallyWith-family call names to watch. // Names are checked in their pre-rename form (stretchr spelling) and the // post-rename form, so the advisory fires regardless of migration order. var eventuallyWithNames = map[string]bool{ //nolint:gochecknoglobals // lookup table "EventuallyWithT": true, "EventuallyWithTf": true, "EventuallyWith": true, "EventuallyWithf": true, } // noteEventuallyWithCancel emits an informational diagnostic for every // EventuallyWith-family call found in f that targets a testify package. // // It does not modify the AST. The advisory is emitted once per call site. // //nolint:unparam // return count left for future use. func noteEventuallyWithCancel(f *ast.File, aliases map[string]string, fset *token.FileSet, rpt *report, filename string) int { count := 0 ast.Inspect(f, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return true } if !eventuallyWithNames[sel.Sel.Name] { return true } if !isTestifySelector(sel, aliases) { return true } pos := fset.Position(call.Pos()) rpt.warn(filename, pos.Line, fmt.Sprintf("%s(): %s", sel.Sel.Name, eventuallyWithAdvisoryMessage)) count++ return true }) return count } go-openapi-testify-c10ca71/hack/migrate-testify/constraints.go000066400000000000000000000147541520301377500245750ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "go/types" ) // isComparable reports whether typ satisfies the comparable constraint. func isComparable(typ types.Type) bool { return types.Comparable(typ) } // isDeepComparable reports whether typ is comparable AND the == operator // has the same semantics as reflect.DeepEqual. This is false for: // - Pointer types (== compares addresses, DeepEqual compares targets) // - Struct types containing pointer or interface fields // - Interface types // - Array types of non-deep-comparable elements // // This is critical for safe Equal→EqualT upgrades: EqualT uses == while // Equal uses reflect.DeepEqual. func isDeepComparable(typ types.Type) bool { if !types.Comparable(typ) { return false } return isDeepComparableUnderlying(typ, make(map[types.Type]bool)) } func isDeepComparableUnderlying(typ types.Type, seen map[types.Type]bool) bool { // Prevent infinite recursion on recursive types. if seen[typ] { return true } seen[typ] = true under := typ.Underlying() switch t := under.(type) { case *types.Basic: return true case *types.Pointer: return false case *types.Interface: return false case *types.Struct: for field := range t.Fields() { if !isDeepComparableUnderlying(field.Type(), seen) { return false } } return true case *types.Array: return isDeepComparableUnderlying(t.Elem(), seen) default: return false } } // isOrdered reports whether typ satisfies the Ordered constraint // (cmp.Ordered | []byte | time.Time). func isOrdered(typ types.Type) bool { // Check for time.Time (struct, not ~struct, so check named type). if isTimeTime(typ) { return true } // Check for []byte. if isByteSlice(typ) { return true } // Check if the underlying type is in cmp.Ordered (string, int*, uint*, float*). return isCmpOrdered(typ) } // isCmpOrdered checks if a type satisfies cmp.Ordered (basic ordered types). func isCmpOrdered(typ types.Type) bool { under := typ.Underlying() basic, ok := under.(*types.Basic) if !ok { return false } switch basic.Kind() { case types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Uintptr, types.Float32, types.Float64, types.String: return true default: return false } } // isText reports whether typ satisfies the Text constraint (~string | ~[]byte). func isText(typ types.Type) bool { under := typ.Underlying() // ~string if basic, ok := under.(*types.Basic); ok && basic.Kind() == types.String { return true } // ~[]byte return isByteSlice(typ) } // isSignedNumeric reports whether typ satisfies the SignedNumeric constraint. func isSignedNumeric(typ types.Type) bool { under := typ.Underlying() basic, ok := under.(*types.Basic) if !ok { return false } switch basic.Kind() { case types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Float32, types.Float64: return true default: return false } } // isMeasurable reports whether typ satisfies the Measurable constraint // (SignedNumeric | UnsignedNumeric | ~float32 | ~float64). func isMeasurable(typ types.Type) bool { under := typ.Underlying() basic, ok := under.(*types.Basic) if !ok { return false } switch basic.Kind() { case types.Int, types.Int8, types.Int16, types.Int32, types.Int64, types.Uint, types.Uint8, types.Uint16, types.Uint32, types.Uint64, types.Float32, types.Float64: return true default: return false } } // isBoolean reports whether typ satisfies the Boolean constraint (~bool). func isBoolean(typ types.Type) bool { under := typ.Underlying() basic, ok := under.(*types.Basic) if !ok { return false } return basic.Kind() == types.Bool } // isRegExp reports whether typ satisfies the RegExp constraint // (Text | *regexp.Regexp). func isRegExp(typ types.Type) bool { if isText(typ) { return true } return isRegexpPointer(typ) } // isPointerType reports whether typ is a pointer type. Returns the element type. func isPointerType(typ types.Type) (elem types.Type, ok bool) { ptr, ok := typ.Underlying().(*types.Pointer) if !ok { return nil, false } return ptr.Elem(), true } // isSliceType reports whether typ is a slice type. Returns the element type. func isSliceType(typ types.Type) (elem types.Type, ok bool) { sl, ok := typ.Underlying().(*types.Slice) if !ok { return nil, false } return sl.Elem(), true } // isMapType reports whether typ is a map type. Returns key and value types. func isMapType(typ types.Type) (key, val types.Type, ok bool) { m, ok := typ.Underlying().(*types.Map) if !ok { return nil, nil, false } return m.Key(), m.Elem(), true } // sameType reports whether two types are identical. func sameType(a, b types.Type) bool { return types.Identical(a, b) } // isAnyOrInterface reports whether typ is interface{} or any, which means // we cannot determine the concrete type statically. func isAnyOrInterface(typ types.Type) bool { if typ == nil { return true } iface, ok := typ.Underlying().(*types.Interface) if !ok { return false } return iface.Empty() } // satisfiesConstraint checks whether a type satisfies the given constraint kind. func satisfiesConstraint(typ types.Type, c constraintKind) bool { if isAnyOrInterface(typ) { return false } switch c { case constraintComparable: return isComparable(typ) case constraintDeepComparable: return isDeepComparable(typ) case constraintOrdered: return isOrdered(typ) case constraintText: return isText(typ) case constraintSignedNumeric: return isSignedNumeric(typ) case constraintMeasurable: return isMeasurable(typ) case constraintPointer: _, ok := isPointerType(typ) return ok case constraintBoolean: return isBoolean(typ) case constraintRegExp: return isRegExp(typ) default: return false } } // Helper functions func isByteSlice(typ types.Type) bool { sl, ok := typ.Underlying().(*types.Slice) if !ok { return false } basic, ok := sl.Elem().(*types.Basic) return ok && basic.Kind() == types.Byte } func isTimeTime(typ types.Type) bool { named, ok := typ.(*types.Named) if !ok { return false } obj := named.Obj() return obj.Name() == "Time" && obj.Pkg() != nil && obj.Pkg().Path() == "time" } func isRegexpPointer(typ types.Type) bool { ptr, ok := typ.Underlying().(*types.Pointer) if !ok { return false } named, ok := ptr.Elem().(*types.Named) if !ok { return false } obj := named.Obj() return obj.Name() == "Regexp" && obj.Pkg() != nil && obj.Pkg().Path() == "regexp" } go-openapi-testify-c10ca71/hack/migrate-testify/constraints_test.go000066400000000000000000000162061520301377500256260ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "go/token" "go/types" "iter" "slices" "testing" ) type constraintTestCase struct { name string typ types.Type constraint constraintKind expected bool } func constraintTestCases() iter.Seq[constraintTestCase] { // Create some test types. intType := types.Typ[types.Int] int64Type := types.Typ[types.Int64] float64Type := types.Typ[types.Float64] stringType := types.Typ[types.String] boolType := types.Typ[types.Bool] uint8Type := types.Typ[types.Uint8] complex128Type := types.Typ[types.Complex128] // []byte byteSlice := types.NewSlice(types.Typ[types.Byte]) // *int intPtr := types.NewPointer(intType) // interface{} emptyIface := types.NewInterfaceType(nil, nil) return slices.Values([]constraintTestCase{ // comparable {name: "int is comparable", typ: intType, constraint: constraintComparable, expected: true}, {name: "string is comparable", typ: stringType, constraint: constraintComparable, expected: true}, {name: "bool is comparable", typ: boolType, constraint: constraintComparable, expected: true}, // ordered {name: "int is ordered", typ: intType, constraint: constraintOrdered, expected: true}, {name: "float64 is ordered", typ: float64Type, constraint: constraintOrdered, expected: true}, {name: "string is ordered", typ: stringType, constraint: constraintOrdered, expected: true}, {name: "[]byte is ordered", typ: byteSlice, constraint: constraintOrdered, expected: true}, {name: "bool is NOT ordered", typ: boolType, constraint: constraintOrdered, expected: false}, // text {name: "string is text", typ: stringType, constraint: constraintText, expected: true}, {name: "[]byte is text", typ: byteSlice, constraint: constraintText, expected: true}, {name: "int is NOT text", typ: intType, constraint: constraintText, expected: false}, // signedNumeric {name: "int is signedNumeric", typ: intType, constraint: constraintSignedNumeric, expected: true}, {name: "int64 is signedNumeric", typ: int64Type, constraint: constraintSignedNumeric, expected: true}, {name: "float64 is signedNumeric", typ: float64Type, constraint: constraintSignedNumeric, expected: true}, {name: "uint8 is NOT signedNumeric", typ: uint8Type, constraint: constraintSignedNumeric, expected: false}, // measurable {name: "int is measurable", typ: intType, constraint: constraintMeasurable, expected: true}, {name: "uint8 is measurable", typ: uint8Type, constraint: constraintMeasurable, expected: true}, {name: "float64 is measurable", typ: float64Type, constraint: constraintMeasurable, expected: true}, {name: "complex128 is NOT measurable", typ: complex128Type, constraint: constraintMeasurable, expected: false}, {name: "string is NOT measurable", typ: stringType, constraint: constraintMeasurable, expected: false}, // boolean {name: "bool is boolean", typ: boolType, constraint: constraintBoolean, expected: true}, {name: "int is NOT boolean", typ: intType, constraint: constraintBoolean, expected: false}, // pointer {name: "*int is pointer", typ: intPtr, constraint: constraintPointer, expected: true}, {name: "int is NOT pointer", typ: intType, constraint: constraintPointer, expected: false}, // deepComparable — same as comparable but excludes pointers and structs with pointers {name: "int is deepComparable", typ: intType, constraint: constraintDeepComparable, expected: true}, {name: "string is deepComparable", typ: stringType, constraint: constraintDeepComparable, expected: true}, {name: "bool is deepComparable", typ: boolType, constraint: constraintDeepComparable, expected: true}, {name: "*int is NOT deepComparable", typ: intPtr, constraint: constraintDeepComparable, expected: false}, // any/interface{} should not satisfy anything {name: "interface{} is NOT comparable", typ: emptyIface, constraint: constraintComparable, expected: false}, {name: "interface{} is NOT ordered", typ: emptyIface, constraint: constraintOrdered, expected: false}, {name: "interface{} is NOT deepComparable", typ: emptyIface, constraint: constraintDeepComparable, expected: false}, }) } func TestSatisfiesConstraint(t *testing.T) { t.Parallel() for c := range constraintTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() got := satisfiesConstraint(c.typ, c.constraint) if got != c.expected { t.Errorf("satisfiesConstraint(%v, %v) = %v, want %v", c.typ, c.constraint, got, c.expected) } }) } } func TestSameType(t *testing.T) { t.Parallel() intType := types.Typ[types.Int] int64Type := types.Typ[types.Int64] stringType := types.Typ[types.String] if !sameType(intType, intType) { t.Error("int should be same type as int") } if sameType(intType, int64Type) { t.Error("int should NOT be same type as int64") } if sameType(intType, stringType) { t.Error("int should NOT be same type as string") } } func TestDeepComparable(t *testing.T) { t.Parallel() // Struct with no pointers — deep-comparable. plainStruct := types.NewStruct([]*types.Var{ types.NewVar(token.NoPos, nil, "X", types.Typ[types.Int]), types.NewVar(token.NoPos, nil, "Y", types.Typ[types.String]), }, nil) if !isDeepComparable(plainStruct) { t.Error("struct{X int; Y string} should be deep-comparable") } // Struct with a pointer field — NOT deep-comparable. ptrStruct := types.NewStruct([]*types.Var{ types.NewVar(token.NoPos, nil, "X", types.Typ[types.Int]), types.NewVar(token.NoPos, nil, "P", types.NewPointer(types.Typ[types.Int])), }, nil) if isDeepComparable(ptrStruct) { t.Error("struct{X int; P *int} should NOT be deep-comparable") } // Struct with an interface field — NOT deep-comparable. ifaceStruct := types.NewStruct([]*types.Var{ types.NewVar(token.NoPos, nil, "X", types.Typ[types.Int]), types.NewVar(token.NoPos, nil, "E", types.NewInterfaceType(nil, nil)), }, nil) if isDeepComparable(ifaceStruct) { t.Error("struct{X int; E interface{}} should NOT be deep-comparable") } // Array of int — deep-comparable. intArray := types.NewArray(types.Typ[types.Int], 3) if !isDeepComparable(intArray) { t.Error("[3]int should be deep-comparable") } // Array of *int — NOT deep-comparable. ptrArray := types.NewArray(types.NewPointer(types.Typ[types.Int]), 3) if isDeepComparable(ptrArray) { t.Error("[3]*int should NOT be deep-comparable") } } func TestIsSliceType(t *testing.T) { t.Parallel() intSlice := types.NewSlice(types.Typ[types.Int]) elem, ok := isSliceType(intSlice) if !ok { t.Fatal("expected []int to be a slice type") } if !sameType(elem, types.Typ[types.Int]) { t.Errorf("expected slice element to be int, got %v", elem) } _, ok = isSliceType(types.Typ[types.Int]) if ok { t.Error("int should not be a slice type") } } func TestIsMapType(t *testing.T) { t.Parallel() m := types.NewMap(types.Typ[types.String], types.Typ[types.Int]) key, val, ok := isMapType(m) if !ok { t.Fatal("expected map[string]int to be a map type") } if !sameType(key, types.Typ[types.String]) { t.Errorf("expected key to be string, got %v", key) } if !sameType(val, types.Typ[types.Int]) { t.Errorf("expected val to be int, got %v", val) } } go-openapi-testify-c10ca71/hack/migrate-testify/generics.go000066400000000000000000000421541520301377500240200ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "fmt" "go/ast" "go/token" "go/types" "strings" "golang.org/x/tools/go/packages" ) // minPairArgs is the minimum number of assertion arguments (after t) needed // for binary assertions like Equal(t, expected, actual). const minPairArgs = 2 // runGenericUpgrade executes pass 2: reflection → generic assertion upgrade. func runGenericUpgrade(dir string, opts *options) error { pkgs, fset, err := loadPackages(dir) if err != nil { return err } rpt := &report{} for _, pkg := range pkgs { if pkg.TypesInfo == nil { continue } for i, f := range pkg.Syntax { if i >= len(pkg.GoFiles) { continue } filename := pkg.GoFiles[i] if opts.skipVendor && isVendorPath(filename) { continue } if !fileImportsAny(f, "github.com/go-openapi/testify") { continue } rpt.filesScanned++ changes := upgradeFile(f, pkg, fset, rpt, filename, opts.verbose) if changes == 0 { continue } rpt.filesChanged++ rpt.totalChanges += changes if opts.dryRun { if err := showDiff(fset, f, filename); err != nil { rpt.errorf(filename, 0, err.Error()) } } else { if err := writeFile(fset, f, filename); err != nil { rpt.errorf(filename, 0, err.Error()) } } } } rpt.print(opts.verbose) rpt.printPass2Summary() return nil } // upgradeFile processes a single file for generic upgrades. func upgradeFile(f *ast.File, pkg *packages.Package, fset *token.FileSet, rpt *report, filename string, verbose bool) int { aliases := buildGoOpenapiAliasMap(f) if len(aliases) == 0 { return 0 } changes := 0 ast.Inspect(f, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } sel, funcName, isTestifyCall := extractTestifyCall(call, aliases) if !isTestifyCall { return true } // Strip trailing "f" for format variants. baseName := funcName isFormat := false if strings.HasSuffix(baseName, "f") && baseName != "Equalf" { // Check if baseName without 'f' is in the upgrade table. candidate := baseName[:len(baseName)-1] if _, ok := genericUpgrades[candidate]; ok { baseName = candidate isFormat = true } } // Also check Equalf directly. if baseName == "Equalf" { baseName = "Equal" isFormat = true } rule, ok := genericUpgrades[baseName] if !ok { return true } if rule.manualReview { pos := fset.Position(call.Pos()) rpt.warn(filename, pos.Line, fmt.Sprintf("%s → %s requires manual review (argument count changes)", funcName, rule.target)) return true } if rule.containerUpgrade { if upgraded := tryContainerUpgrade(call, sel, funcName, baseName, isFormat, pkg, fset, rpt, filename, verbose); upgraded { changes++ } return true } if upgraded := trySimpleUpgrade(call, sel, funcName, baseName, isFormat, rule, pkg, fset, rpt, filename, verbose); upgraded { changes++ } return true }) return changes } // buildGoOpenapiAliasMap builds an alias map for go-openapi/testify imports. func buildGoOpenapiAliasMap(f *ast.File) map[string]string { aliases := make(map[string]string) for _, imp := range f.Imports { path := importPath(imp) if !strings.HasPrefix(path, "github.com/go-openapi/testify") { continue } // Skip enable imports. if strings.Contains(path, "/enable/") { continue } var localName string if imp.Name != nil { localName = imp.Name.Name } else { parts := strings.Split(path, "/") localName = parts[len(parts)-1] } if localName != "_" && localName != "." { aliases[localName] = path } } return aliases } // extractTestifyCall checks if a call expression is a testify assertion call. // Returns the selector, function name, and whether it's a testify call. func extractTestifyCall(call *ast.CallExpr, aliases map[string]string) (*ast.SelectorExpr, string, bool) { sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return nil, "", false } funcName := sel.Sel.Name // Package-level call: assert.Equal(t, ...) if ident, ok := sel.X.(*ast.Ident); ok { if _, exists := aliases[ident.Name]; exists { return sel, funcName, true } } return nil, "", false } // checkResult is the outcome of a constraint check for a generic upgrade. type checkResult struct { ok bool // constraints satisfied — upgrade is safe reason skipReason // non-empty: skip with this reason typeInfo string // context for skip message (e.g. type name) } var ( checkOK = checkResult{ok: true} //nolint:gochecknoglobals // convenience value checkInsufficient = checkResult{} //nolint:gochecknoglobals // not enough args, silent skip ) func checkSkip(r skipReason, info string) checkResult { return checkResult{reason: r, typeInfo: info} } // trySimpleUpgrade attempts to upgrade a simple (non-container) assertion. func trySimpleUpgrade( call *ast.CallExpr, sel *ast.SelectorExpr, funcName, baseName string, isFormat bool, rule upgradeRule, pkg *packages.Package, fset *token.FileSet, rpt *report, filename string, verbose bool, ) bool { pos := fset.Position(call.Pos()) skip := func(reason skipReason, typeInfo string) bool { rpt.trackSkip(filename, pos.Line, funcName, reason, verbose, typeInfo) return false } argTypes, argSkipReason := getArgTypesWithReason(call, pkg, 1) if argTypes == nil { return skip(argSkipReason, "") } var result checkResult switch baseName { case "Equal", "NotEqual": result = checkDeepComparablePair(argTypes, rule) if !result.ok { // Fallback: try slice or map equality upgrade. isNot := baseName == "NotEqual" if alt := tryEqualityFallback(argTypes, isNot); alt.ok { return applyUpgrade(sel, funcName, alt.target, isFormat, rpt, filename, pos, verbose) } } case "Greater", "GreaterOrEqual", "Less", "LessOrEqual": result = checkPairConstraint(argTypes, constraintOrdered, skipNotOrdered, rule) case "InDelta", "InEpsilon": result = checkPairConstraint(argTypes, constraintMeasurable, skipNotMeasurable, rule) case "Positive", "Negative": result = checkSingleConstraint(argTypes, constraintSignedNumeric, skipNotSignedNumeric) case "True", "False": result = checkSingleConstraint(argTypes, constraintBoolean, skipNotBoolean) case "Same", "NotSame": result = checkPairConstraint(argTypes, constraintPointer, skipNotPointer, rule) case "ElementsMatch", "NotElementsMatch", "Subset", "NotSubset": result = checkSlicePairConstraint(argTypes, rule) case "IsIncreasing", "IsDecreasing", "IsNonIncreasing", "IsNonDecreasing": result = checkOrderedSliceConstraint(argTypes) case "Regexp", "NotRegexp": result = checkRegexpConstraint(argTypes) case "JSONEq", "YAMLEq": result = checkPairConstraint(argTypes, constraintText, skipNotText, rule) default: return false } if !result.ok { if result.reason != "" { return skip(result.reason, result.typeInfo) } return false } newName := rule.target if isFormat { newName += "f" } if verbose { rpt.info(filename, pos.Line, fmt.Sprintf("upgraded %s → %s", funcName, newName)) } rpt.trackUpgrade(funcName, newName) sel.Sel.Name = newName return true } // applyUpgrade rewrites the selector name and records the upgrade. func applyUpgrade( sel *ast.SelectorExpr, funcName, target string, isFormat bool, rpt *report, filename string, pos token.Position, verbose bool, ) bool { newName := target if isFormat { newName += "f" } if verbose { rpt.info(filename, pos.Line, fmt.Sprintf("upgraded %s → %s", funcName, newName)) } rpt.trackUpgrade(funcName, newName) sel.Sel.Name = newName return true } // tryEqualityFallback attempts slice or map equality upgrades when the // standard Equal → EqualT path is not applicable (e.g., slices and maps // are not deep-comparable scalars). func tryEqualityFallback(argTypes []types.Type, isNot bool) containerCheckResult { if len(argTypes) < minPairArgs { return containerCheckResult{} } if !sameType(argTypes[0], argTypes[1]) { return containerCheckResult{checkResult: checkSkip(skipTypeMismatch, argTypes[0].String()+" vs "+argTypes[1].String())} } // Try slice equality: both args are []E with E comparable. if elem, ok := isSliceType(argTypes[0]); ok { if !isComparable(elem) { return containerCheckResult{checkResult: checkSkip(skipSliceElemNotComparable, elem.String())} } target := "SliceEqualT" if isNot { target = "SliceNotEqualT" } return containerCheckResult{checkResult: checkOK, target: target} } // Try map equality: both args are map[K]V with K and V comparable. if key, val, ok := isMapType(argTypes[0]); ok { if !isComparable(key) { return containerCheckResult{checkResult: checkSkip(skipMapKeyNotComparable, key.String())} } if !isComparable(val) { return containerCheckResult{checkResult: checkSkip(skipMapValNotComparable, val.String())} } target := "MapEqualT" if isNot { target = "MapNotEqualT" } return containerCheckResult{checkResult: checkOK, target: target} } return containerCheckResult{} } // checkDeepComparablePair checks that both arguments are deeply comparable and have matching types. func checkDeepComparablePair(argTypes []types.Type, rule upgradeRule) checkResult { if len(argTypes) < minPairArgs { return checkInsufficient } if reason := deepComparableSkipReason(argTypes[0]); reason != "" { return checkSkip(reason, argTypes[0].String()) } if reason := deepComparableSkipReason(argTypes[1]); reason != "" { return checkSkip(reason, argTypes[1].String()) } if rule.sameType && !sameType(argTypes[0], argTypes[1]) { return checkSkip(skipTypeMismatch, argTypes[0].String()+" vs "+argTypes[1].String()) } return checkOK } // checkPairConstraint checks that both arguments satisfy a constraint and have matching types. func checkPairConstraint(argTypes []types.Type, c constraintKind, failReason skipReason, rule upgradeRule) checkResult { if len(argTypes) < minPairArgs { return checkInsufficient } if !satisfiesConstraint(argTypes[0], c) || !satisfiesConstraint(argTypes[1], c) { return checkSkip(failReason, argTypes[0].String()) } if rule.sameType && !sameType(argTypes[0], argTypes[1]) { return checkSkip(skipTypeMismatch, argTypes[0].String()+" vs "+argTypes[1].String()) } return checkOK } // checkSingleConstraint checks that the first argument satisfies a constraint. func checkSingleConstraint(argTypes []types.Type, c constraintKind, failReason skipReason) checkResult { if len(argTypes) < 1 { return checkInsufficient } if !satisfiesConstraint(argTypes[0], c) { return checkSkip(failReason, argTypes[0].String()) } return checkOK } // checkSlicePairConstraint checks that both arguments are slices with deep-comparable elements. func checkSlicePairConstraint(argTypes []types.Type, rule upgradeRule) checkResult { if len(argTypes) < minPairArgs { return checkInsufficient } elem0, ok0 := isSliceType(argTypes[0]) elem1, ok1 := isSliceType(argTypes[1]) if !ok0 || !ok1 { return checkSkip(skipNotSlice, argTypes[0].String()) } if !isDeepComparable(elem0) || !isDeepComparable(elem1) { return checkSkip(skipSliceElemNotDeepComparable, elem0.String()) } if rule.sameType && !sameType(argTypes[0], argTypes[1]) { return checkSkip(skipTypeMismatch, argTypes[0].String()+" vs "+argTypes[1].String()) } return checkOK } // checkOrderedSliceConstraint checks that the argument is a slice of ordered elements. func checkOrderedSliceConstraint(argTypes []types.Type) checkResult { if len(argTypes) < 1 { return checkInsufficient } elem, ok := isSliceType(argTypes[0]) if !ok { return checkSkip(skipNotSlice, argTypes[0].String()) } if !isOrdered(elem) { return checkSkip(skipSliceElemNotOrdered, elem.String()) } return checkOK } // checkRegexpConstraint checks that the first arg is a RegExp and the second is Text. func checkRegexpConstraint(argTypes []types.Type) checkResult { if len(argTypes) < minPairArgs { return checkInsufficient } if !satisfiesConstraint(argTypes[0], constraintRegExp) { return checkSkip(skipNotRegExp, argTypes[0].String()) } if !satisfiesConstraint(argTypes[1], constraintText) { return checkSkip(skipNotText, argTypes[1].String()) } return checkOK } // deepComparableSkipReason returns the specific skip reason if a type is not deep-comparable, // or an empty string if it is. func deepComparableSkipReason(typ types.Type) skipReason { if !types.Comparable(typ) { return skipNotComparable } under := typ.Underlying() switch under.(type) { case *types.Pointer: return skipPointerSemantics case *types.Interface: return skipInterfaceField case *types.Struct: if !isDeepComparable(typ) { return skipInterfaceField } } if !isDeepComparable(typ) { return skipNotComparable } return "" } // containerCheckResult extends checkResult with the resolved target function name. type containerCheckResult struct { checkResult target string } // tryContainerUpgrade handles Contains/NotContains which dispatch to different // generic variants based on the container type. func tryContainerUpgrade( call *ast.CallExpr, sel *ast.SelectorExpr, funcName, baseName string, isFormat bool, pkg *packages.Package, fset *token.FileSet, rpt *report, filename string, verbose bool, ) bool { pos := fset.Position(call.Pos()) skip := func(reason skipReason, typeInfo string) bool { rpt.trackSkip(filename, pos.Line, funcName, reason, verbose, typeInfo) return false } argTypes, argSkipReason := getArgTypesWithReason(call, pkg, 1) if argTypes == nil { return skip(argSkipReason, "") } if len(argTypes) < minPairArgs { return false } isNot := baseName == "NotContains" var result containerCheckResult switch { case isText(argTypes[0]): result = checkStringContains(argTypes, isNot) case isSliceType2(argTypes[0]): result = checkSliceContains(argTypes, isNot) case isMapType2(argTypes[0]): result = checkMapContains(argTypes, isNot) default: return skip(skipContainerTypeUnknown, argTypes[0].String()) } if !result.ok { if result.reason != "" { return skip(result.reason, result.typeInfo) } return false } target := result.target if isFormat { target += "f" } if verbose { rpt.info(filename, pos.Line, fmt.Sprintf("upgraded %s → %s", funcName, target)) } rpt.trackUpgrade(funcName, target) sel.Sel.Name = target return true } // isSliceType2 reports whether typ is a slice type (bool-only variant for switch). func isSliceType2(typ types.Type) bool { _, ok := typ.Underlying().(*types.Slice) return ok } // isMapType2 reports whether typ is a map type (bool-only variant for switch). func isMapType2(typ types.Type) bool { _, ok := typ.Underlying().(*types.Map) return ok } // pickTarget selects the contains or not-contains variant. func pickTarget(kind containerKind, isNot bool) string { targets := containerUpgradeTargets[kind] if isNot { return targets[1] } return targets[0] } // checkStringContains validates a string Contains/NotContains upgrade. func checkStringContains(argTypes []types.Type, isNot bool) containerCheckResult { if !isText(argTypes[1]) { return containerCheckResult{checkResult: checkSkip(skipNotText, argTypes[1].String())} } return containerCheckResult{checkResult: checkOK, target: pickTarget(containerString, isNot)} } // checkSliceContains validates a slice Contains/NotContains upgrade. func checkSliceContains(argTypes []types.Type, isNot bool) containerCheckResult { elem, _ := isSliceType(argTypes[0]) if !isDeepComparable(elem) { return containerCheckResult{checkResult: checkSkip(skipSliceElemNotDeepComparable, elem.String())} } if isAnyOrInterface(argTypes[1]) { return containerCheckResult{checkResult: checkSkip(skipInterfaceType, argTypes[1].String())} } if !sameType(elem, argTypes[1]) { return containerCheckResult{checkResult: checkSkip(skipTypeMismatch, elem.String()+" vs "+argTypes[1].String())} } return containerCheckResult{checkResult: checkOK, target: pickTarget(containerSlice, isNot)} } // checkMapContains validates a map Contains/NotContains upgrade. func checkMapContains(argTypes []types.Type, isNot bool) containerCheckResult { key, _, _ := isMapType(argTypes[0]) if !isComparable(key) { return containerCheckResult{checkResult: checkSkip(skipNotComparable, key.String())} } if isAnyOrInterface(argTypes[1]) { return containerCheckResult{checkResult: checkSkip(skipInterfaceType, argTypes[1].String())} } if !sameType(key, argTypes[1]) { return containerCheckResult{checkResult: checkSkip(skipTypeMismatch, key.String()+" vs "+argTypes[1].String())} } return containerCheckResult{checkResult: checkOK, target: pickTarget(containerMap, isNot)} } // getArgTypesWithReason extracts the types of call arguments starting from the given offset // (to skip the testing.T parameter). Returns nil and a skip reason if any type cannot be resolved. func getArgTypesWithReason(call *ast.CallExpr, pkg *packages.Package, offset int) ([]types.Type, skipReason) { if len(call.Args) <= offset { return nil, skipUnresolvableType } result := make([]types.Type, 0, len(call.Args)-offset) for _, arg := range call.Args[offset:] { tv, ok := pkg.TypesInfo.Types[arg] if !ok { return nil, skipUnresolvableType } if isAnyOrInterface(tv.Type) { return nil, skipInterfaceType } result = append(result, tv.Type) } return result, "" } go-openapi-testify-c10ca71/hack/migrate-testify/generics_test.go000066400000000000000000000444641520301377500250650ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "go/ast" "go/importer" "go/parser" "go/printer" "go/token" "go/types" "iter" "slices" "strings" "testing" "golang.org/x/tools/go/packages" ) type genericsTestCase struct { name string input string expected string } func genericsTestCases() iter.Seq[genericsTestCase] { return slices.Values([]genericsTestCase{ { name: "equal int upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestEqual(t *testing.T) { assert.Equal(t, 42, 42) }`, expected: `assert.EqualT(t, 42, 42)`, }, { name: "notequal string upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNotEqual(t *testing.T) { assert.NotEqual(t, "a", "b") }`, expected: `assert.NotEqualT(t, "a", "b")`, }, { name: "greater upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestGreater(t *testing.T) { assert.Greater(t, 2, 1) }`, expected: `assert.GreaterT(t, 2, 1)`, }, { name: "positive upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestPositive(t *testing.T) { assert.Positive(t, 42) }`, expected: `assert.PositiveT(t, 42)`, }, { name: "skip any", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSkipAny(t *testing.T) { var x any = 42 assert.Equal(t, x, x) }`, expected: `assert.Equal(t, x, x)`, }, { name: "skip different types", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSkipDifferent(t *testing.T) { assert.Equal(t, int32(1), int64(1)) }`, expected: `assert.Equal(t, int32(1), int64(1))`, }, { name: "contains string upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestContains(t *testing.T) { assert.Contains(t, "hello world", "world") }`, expected: `assert.StringContainsT(t, "hello world", "world")`, }, { name: "contains slice upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestContains(t *testing.T) { assert.Contains(t, []int{1, 2, 3}, 2) }`, expected: `assert.SliceContainsT(t, []int{1, 2, 3}, 2)`, }, { name: "contains map upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestContains(t *testing.T) { assert.Contains(t, map[string]int{"a": 1}, "a") }`, expected: `assert.MapContainsT(t, map[string]int{"a": 1}, "a")`, }, { name: "equal slice int upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSliceEqual(t *testing.T) { assert.Equal(t, []int{1, 2}, []int{3, 4}) }`, expected: `assert.SliceEqualT(t, []int{1, 2}, []int{3, 4})`, }, { name: "notequal slice string upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSliceNotEqual(t *testing.T) { assert.NotEqual(t, []string{"a"}, []string{"b"}) }`, expected: `assert.SliceNotEqualT(t, []string{"a"}, []string{"b"})`, }, { name: "equal map upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestMapEqual(t *testing.T) { assert.Equal(t, map[string]int{"a": 1}, map[string]int{"b": 2}) }`, expected: `assert.MapEqualT(t, map[string]int{"a": 1}, map[string]int{"b": 2})`, }, { name: "notequal map upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestMapNotEqual(t *testing.T) { assert.NotEqual(t, map[string]int{"a": 1}, map[string]int{"b": 2}) }`, expected: `assert.MapNotEqualT(t, map[string]int{"a": 1}, map[string]int{"b": 2})`, }, { name: "equalf slice format variant", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSliceEqualf(t *testing.T) { assert.Equalf(t, []int{1, 2}, []int{3, 4}, "should match") }`, expected: `assert.SliceEqualTf(t, []int{1, 2}, []int{3, 4}, "should match")`, }, { name: "skip slice non-comparable element", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSkipSliceNonComparable(t *testing.T) { assert.Equal(t, [][]int{{1}}, [][]int{{2}}) }`, expected: `assert.Equal(t, [][]int{{1}}, [][]int{{2}})`, }, { name: "true/false bool upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestBool(t *testing.T) { assert.True(t, true) assert.False(t, false) }`, expected: "assert.TrueT(t, true)\n\tassert.FalseT(t, false)", }, { name: "elementsmatch slice upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestElementsMatch(t *testing.T) { assert.ElementsMatch(t, []int{1, 2, 3}, []int{3, 2, 1}) }`, expected: `assert.ElementsMatchT(t, []int{1, 2, 3}, []int{3, 2, 1})`, }, { name: "notelementsmatch slice upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNotElementsMatch(t *testing.T) { assert.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) }`, expected: `assert.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4})`, }, { name: "notelementsmatchf slice format variant", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNotElementsMatchf(t *testing.T) { assert.NotElementsMatchf(t, []int{1, 2, 3}, []int{1, 2, 4}, "should differ") }`, expected: `assert.NotElementsMatchTf(t, []int{1, 2, 3}, []int{1, 2, 4}, "should differ")`, }, { name: "subset slice upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSubset(t *testing.T) { assert.Subset(t, []int{1, 2, 3}, []int{1, 2}) }`, expected: `assert.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2})`, }, { name: "notsubset slice upgrade", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNotSubset(t *testing.T) { assert.NotSubset(t, []int{1, 2, 3}, []int{4, 5}) }`, expected: `assert.SliceNotSubsetT(t, []int{1, 2, 3}, []int{4, 5})`, }, { name: "notsubsetf slice format variant", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNotSubsetf(t *testing.T) { assert.NotSubsetf(t, []int{1, 2, 3}, []int{4, 5}, "should not contain") }`, expected: `assert.SliceNotSubsetTf(t, []int{1, 2, 3}, []int{4, 5}, "should not contain")`, }, { name: "skip notsubset map (not a slice)", input: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestNotSubsetMap(t *testing.T) { assert.NotSubset(t, map[string]int{"a": 1}, map[string]int{"b": 2}) }`, expected: `assert.NotSubset(t, map[string]int{"a": 1}, map[string]int{"b": 2})`, }, }) } func TestGenericUpgradeUnit(t *testing.T) { t.Parallel() for c := range genericsTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", c.input, parser.ParseComments) if err != nil { t.Fatalf("parse: %v", err) } // Create a mock assert package so type-checking succeeds. info := typeCheckWithMockAssert(t, fset, f) if info == nil { t.Fatal("type-check failed") } // Build a fake *packages.Package to pass to upgradeFile. pkg := &packages.Package{ TypesInfo: info, Syntax: []*ast.File{f}, GoFiles: []string{"test.go"}, } rpt := &report{} upgradeFile(f, pkg, fset, rpt, "test.go", true) // Extract the assertion call(s) from the output. var buf strings.Builder pcfg := &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} if err := pcfg.Fprint(&buf, fset, f); err != nil { t.Fatalf("print: %v", err) } got := buf.String() if !strings.Contains(got, c.expected) { t.Errorf("expected output to contain:\n %s\ngot:\n%s", c.expected, got) } }) } } func TestGenericUpgradeTracksUpgrades(t *testing.T) { t.Parallel() input := `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestEqual(t *testing.T) { assert.Equal(t, 42, 42) assert.True(t, true) }` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", input, parser.ParseComments) if err != nil { t.Fatalf("parse: %v", err) } info := typeCheckWithMockAssert(t, fset, f) if info == nil { t.Fatal("type-check failed") } pkg := &packages.Package{ TypesInfo: info, Syntax: []*ast.File{f}, GoFiles: []string{"test.go"}, } rpt := &report{} changes := upgradeFile(f, pkg, fset, rpt, "test.go", false) if changes != 2 { t.Errorf("expected 2 changes, got %d", changes) } if len(rpt.upgraded) != 2 { t.Errorf("expected 2 upgrade entries, got %d", len(rpt.upgraded)) } if rpt.upgraded["Equal → EqualT"] != 1 { t.Errorf("expected Equal → EqualT upgrade, got %v", rpt.upgraded) } if rpt.upgraded["True → TrueT"] != 1 { t.Errorf("expected True → TrueT upgrade, got %v", rpt.upgraded) } } func TestGenericUpgradeTracksSkips(t *testing.T) { t.Parallel() // Test with any type — should be skipped with skipInterfaceType. input := `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSkipAny(t *testing.T) { var x any = 42 assert.Equal(t, x, x) }` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", input, parser.ParseComments) if err != nil { t.Fatalf("parse: %v", err) } info := typeCheckWithMockAssert(t, fset, f) if info == nil { t.Fatal("type-check failed") } pkg := &packages.Package{ TypesInfo: info, Syntax: []*ast.File{f}, GoFiles: []string{"test.go"}, } rpt := &report{} changes := upgradeFile(f, pkg, fset, rpt, "test.go", true) if changes != 0 { t.Errorf("expected 0 changes for any type, got %d", changes) } if len(rpt.skipped) == 0 { t.Error("expected skip to be tracked for any type") } if rpt.skipped[string(skipInterfaceType)] != 1 { t.Errorf("expected skipInterfaceType, got %v", rpt.skipped) } } func TestGenericUpgradeTracksDifferentTypeSkip(t *testing.T) { t.Parallel() input := `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestSkipDifferent(t *testing.T) { assert.Equal(t, int32(1), int64(1)) }` fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", input, parser.ParseComments) if err != nil { t.Fatalf("parse: %v", err) } info := typeCheckWithMockAssert(t, fset, f) if info == nil { t.Fatal("type-check failed") } pkg := &packages.Package{ TypesInfo: info, Syntax: []*ast.File{f}, GoFiles: []string{"test.go"}, } rpt := &report{} changes := upgradeFile(f, pkg, fset, rpt, "test.go", true) if changes != 0 { t.Errorf("expected 0 changes for mismatched types, got %d", changes) } if len(rpt.skipped) == 0 { t.Error("expected skip to be tracked for type mismatch") } if rpt.skipped[string(skipTypeMismatch)] != 1 { t.Errorf("expected skipTypeMismatch, got %v", rpt.skipped) } } func TestGenericUpgradeManualReview(t *testing.T) { t.Parallel() // IsType and IsNotType both change argument count when upgraded to the // generic form (IsOfTypeT / IsNotOfTypeT take the expected type as a // type parameter). The tool must not rewrite the call; it must emit a // manual-review warning. cases := []struct { name string source string wantTarget string }{ { name: "IsType", source: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) type MyType struct{} func TestIsType(t *testing.T) { assert.IsType(t, &MyType{}, &MyType{}) }`, wantTarget: "IsOfTypeT", }, { name: "IsNotType", source: `package p import ( "testing" "github.com/go-openapi/testify/v2/assert" ) type MyType struct{} func TestIsNotType(t *testing.T) { assert.IsNotType(t, &MyType{}, 42) }`, wantTarget: "IsNotOfTypeT", }, } for _, tc := range cases { t.Run(tc.name, func(t *testing.T) { t.Parallel() fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", tc.source, parser.ParseComments) if err != nil { t.Fatalf("parse: %v", err) } info := typeCheckWithMockAssert(t, fset, f) if info == nil { t.Fatal("type-check failed") } pkg := &packages.Package{ TypesInfo: info, Syntax: []*ast.File{f}, GoFiles: []string{"test.go"}, } rpt := &report{} changes := upgradeFile(f, pkg, fset, rpt, "test.go", false) if changes != 0 { t.Errorf("expected 0 changes (manual review), got %d", changes) } found := false for _, d := range rpt.diagnostics { if d.kind == "warning" && strings.Contains(d.message, tc.wantTarget) && strings.Contains(d.message, "manual review") { found = true break } } if !found { t.Errorf("expected manual-review warning mentioning %s, got diagnostics: %v", tc.wantTarget, rpt.diagnostics) } }) } } // typeCheckWithMockAssert creates a mock "assert" package that has a few functions // taking (testing.T, any...) and type-checks the file against it. func typeCheckWithMockAssert(t *testing.T, fset *token.FileSet, f *ast.File) *types.Info { t.Helper() // We need to provide packages that the source imports. // Use the default importer for stdlib. stdImporter := importer.ForCompiler(fset, "source", nil) // Create a fake "assert" package with the functions we need. assertPkg := types.NewPackage("github.com/go-openapi/testify/v2/assert", "assert") // T interface (just needs Errorf and Helper). tParam := types.NewVar(token.NoPos, assertPkg, "t", types.NewInterfaceType(nil, nil)) anyType := types.NewInterfaceType(nil, nil) // Create variadic msgAndArgs param. msgAndArgsVar := types.NewVar(token.NoPos, assertPkg, "msgAndArgs", types.NewSlice(anyType)) // Helper to create assertion function signatures. makeAssertFunc := func(name string, params ...*types.Var) { allParams := make([]*types.Var, 0, 1+len(params)+1) allParams = append(allParams, tParam) allParams = append(allParams, params...) allParams = append(allParams, msgAndArgsVar) sig := types.NewSignatureType(nil, nil, nil, types.NewTuple(allParams...), types.NewTuple(types.NewVar(token.NoPos, assertPkg, "", types.Typ[types.Bool])), true, // variadic ) assertPkg.Scope().Insert(types.NewFunc(token.NoPos, assertPkg, name, sig)) } // Add known assertion functions to the fake package. anyParam := func(name string) *types.Var { return types.NewVar(token.NoPos, assertPkg, name, anyType) } // Equal(t, expected any, actual any, ...) makeAssertFunc("Equal", anyParam("expected"), anyParam("actual")) makeAssertFunc("EqualT", anyParam("expected"), anyParam("actual")) makeAssertFunc("NotEqual", anyParam("expected"), anyParam("actual")) makeAssertFunc("NotEqualT", anyParam("expected"), anyParam("actual")) makeAssertFunc("Greater", anyParam("e1"), anyParam("e2")) makeAssertFunc("GreaterT", anyParam("e1"), anyParam("e2")) makeAssertFunc("Less", anyParam("e1"), anyParam("e2")) makeAssertFunc("LessT", anyParam("e1"), anyParam("e2")) makeAssertFunc("Positive", anyParam("e")) makeAssertFunc("PositiveT", anyParam("e")) makeAssertFunc("Negative", anyParam("e")) makeAssertFunc("NegativeT", anyParam("e")) makeAssertFunc("Contains", anyParam("s"), anyParam("contains")) makeAssertFunc("StringContainsT", anyParam("s"), anyParam("contains")) makeAssertFunc("SliceContainsT", anyParam("s"), anyParam("contains")) makeAssertFunc("MapContainsT", anyParam("s"), anyParam("contains")) makeAssertFunc("Equalf", anyParam("expected"), anyParam("actual")) makeAssertFunc("NotEqualf", anyParam("expected"), anyParam("actual")) makeAssertFunc("SliceEqualT", anyParam("expected"), anyParam("actual")) makeAssertFunc("SliceEqualTf", anyParam("expected"), anyParam("actual")) makeAssertFunc("SliceNotEqualT", anyParam("expected"), anyParam("actual")) makeAssertFunc("SliceNotEqualTf", anyParam("expected"), anyParam("actual")) makeAssertFunc("MapEqualT", anyParam("expected"), anyParam("actual")) makeAssertFunc("MapEqualTf", anyParam("expected"), anyParam("actual")) makeAssertFunc("MapNotEqualT", anyParam("expected"), anyParam("actual")) makeAssertFunc("MapNotEqualTf", anyParam("expected"), anyParam("actual")) makeAssertFunc("True", anyParam("value")) makeAssertFunc("TrueT", anyParam("value")) makeAssertFunc("False", anyParam("value")) makeAssertFunc("FalseT", anyParam("value")) makeAssertFunc("ElementsMatch", anyParam("listA"), anyParam("listB")) makeAssertFunc("ElementsMatchT", anyParam("listA"), anyParam("listB")) makeAssertFunc("NotElementsMatch", anyParam("listA"), anyParam("listB")) makeAssertFunc("NotElementsMatchT", anyParam("listA"), anyParam("listB")) makeAssertFunc("NotElementsMatchf", anyParam("listA"), anyParam("listB")) makeAssertFunc("NotElementsMatchTf", anyParam("listA"), anyParam("listB")) makeAssertFunc("Subset", anyParam("list"), anyParam("subset")) makeAssertFunc("SliceSubsetT", anyParam("list"), anyParam("subset")) makeAssertFunc("NotSubset", anyParam("list"), anyParam("subset")) makeAssertFunc("NotSubsetf", anyParam("list"), anyParam("subset")) makeAssertFunc("SliceNotSubsetT", anyParam("list"), anyParam("subset")) makeAssertFunc("SliceNotSubsetTf", anyParam("list"), anyParam("subset")) makeAssertFunc("IsType", anyParam("expectedType"), anyParam("object")) makeAssertFunc("IsNotType", anyParam("theType"), anyParam("object")) assertPkg.MarkComplete() // Custom importer that returns our mock assert package. customImporter := &mockImporter{ base: stdImporter, extra: map[string]*types.Package{ "github.com/go-openapi/testify/v2/assert": assertPkg, }, } conf := &types.Config{ Importer: customImporter, } info := &types.Info{ Types: make(map[ast.Expr]types.TypeAndValue), Defs: make(map[*ast.Ident]types.Object), Uses: make(map[*ast.Ident]types.Object), } _, err := conf.Check("test", fset, []*ast.File{f}, info) if err != nil { t.Logf("type-check error (may be expected): %v", err) // Even with errors, the info map may have partial results. // Check if we have any type info at all. if len(info.Types) == 0 { return nil } } return info } type mockImporter struct { base types.Importer extra map[string]*types.Package } func (m *mockImporter) Import(path string) (*types.Package, error) { if pkg, ok := m.extra[path]; ok { return pkg, nil } return m.base.Import(path) } go-openapi-testify-c10ca71/hack/migrate-testify/go.mod000066400000000000000000000002721520301377500227730ustar00rootroot00000000000000module github.com/go-openapi/testify/hack/migrate-testify/v2 go 1.25.0 require ( golang.org/x/mod v0.33.0 golang.org/x/tools v0.42.0 ) require golang.org/x/sync v0.19.0 // indirect go-openapi-testify-c10ca71/hack/migrate-testify/go.sum000066400000000000000000000011701520301377500230160ustar00rootroot00000000000000github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= golang.org/x/mod v0.33.0 h1:tHFzIWbBifEmbwtGz65eaWyGiGZatSrT9prnU8DbVL8= golang.org/x/mod v0.33.0/go.mod h1:swjeQEj+6r7fODbD2cqrnje9PnziFuw4bmLbBZFrQ5w= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k= golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0= go-openapi-testify-c10ca71/hack/migrate-testify/gomod.go000066400000000000000000000033631520301377500233250ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "context" "fmt" "os" "os/exec" "strings" ) // updateGoMod updates the target project's go.mod to replace stretchr/testify // with go-openapi/testify/v2. func updateGoMod(dir, version string, dryRun, verbose bool) error { // Check go.mod exists. if _, err := os.Stat(dir + "/go.mod"); err != nil { return fmt.Errorf("no go.mod found in %s", dir) } if dryRun { fmt.Println("would run: go mod edit -droprequire github.com/stretchr/testify") //nolint:forbidigo // CLI output fmt.Printf("would run: go mod edit -require github.com/go-openapi/testify/v2@%s\n", version) //nolint:forbidigo // CLI output fmt.Println("would run: go mod tidy") //nolint:forbidigo // CLI output return nil } commands := []struct { name string args []string }{ { name: "drop stretchr/testify require", args: []string{"go", "mod", "edit", "-droprequire", "github.com/stretchr/testify"}, }, { name: "add go-openapi/testify/v2 require", args: []string{"go", "mod", "edit", "-require", "github.com/go-openapi/testify/v2@" + version}, }, { name: "go mod tidy", args: []string{"go", "mod", "tidy"}, }, } for _, c := range commands { if verbose { fmt.Printf("running: %s\n", strings.Join(c.args, " ")) //nolint:forbidigo // CLI output } cmd := exec.CommandContext(context.Background(), c.args[0], c.args[1:]...) //nolint:gosec // args are constructed from controlled constants cmd.Dir = dir cmd.Stdout = os.Stdout cmd.Stderr = os.Stderr if err := cmd.Run(); err != nil { return fmt.Errorf("%s: %w", c.name, err) } } return nil } go-openapi-testify-c10ca71/hack/migrate-testify/loader.go000066400000000000000000000114461520301377500234670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "fmt" "go/ast" "go/token" "os" "path/filepath" "strings" "golang.org/x/mod/modfile" "golang.org/x/tools/go/packages" ) // loadPackages loads all Go packages under dir with full type information. // If a go.work file is present, it loads packages from all workspace modules. // Otherwise, it loads packages from the single module at dir. func loadPackages(dir string) ([]*packages.Package, *token.FileSet, error) { absDir, err := filepath.Abs(dir) if err != nil { return nil, nil, fmt.Errorf("resolving path: %w", err) } patterns := workspacePatterns(absDir) fset := token.NewFileSet() cfg := &packages.Config{ Mode: packages.NeedName | packages.NeedFiles | packages.NeedSyntax | packages.NeedTypes | packages.NeedTypesInfo | packages.NeedImports | packages.NeedDeps, Dir: absDir, Fset: fset, Tests: true, } pkgs, err := packages.Load(cfg, patterns...) if err != nil { return nil, nil, fmt.Errorf("loading packages: %w", err) } // Report loading errors but don't fail — partial results are useful. var errs []string for _, pkg := range pkgs { for _, e := range pkg.Errors { errs = append(errs, e.Error()) } } if len(errs) > 0 { // Log but continue — we can still transform files that loaded. fmt.Printf("warning: %d package loading errors (some files may be skipped):\n", len(errs)) //nolint:forbidigo // CLI output for _, e := range errs { fmt.Printf(" %s\n", e) //nolint:forbidigo // CLI output } } return pkgs, fset, nil } // workspacePatterns returns the load patterns for packages.Load. // If go.work exists, it returns a pattern per workspace module (e.g. "./conv/...", "./mangling/..."). // If go.work does not exist, it returns ["./..."] and warns if sub-modules are detected. func workspacePatterns(absDir string) []string { goworkPath := filepath.Join(absDir, "go.work") data, err := os.ReadFile(goworkPath) if err != nil { // No go.work — check for sub-modules and warn. warnIfSubModules(absDir) return []string{"./..."} } wf, err := modfile.ParseWork(goworkPath, data, nil) if err != nil { fmt.Fprintf(os.Stderr, "warning: failed to parse go.work: %v; falling back to ./...\n", err) return []string{"./..."} } patterns := make([]string, 0, len(wf.Use)) for _, use := range wf.Use { dir := use.Path if dir == "." { patterns = append(patterns, "./...") } else { // Normalize: strip leading "./" if present, ensure trailing "/...". dir = strings.TrimPrefix(dir, "./") patterns = append(patterns, "./"+dir+"/...") } } if len(patterns) == 0 { return []string{"./..."} } fmt.Printf("go.work: loading %d workspace modules\n", len(patterns)) //nolint:forbidigo // CLI output return patterns } // warnIfSubModules scans for nested go.mod files and warns if found. func warnIfSubModules(absDir string) { var subModules []string _ = filepath.Walk(absDir, func(path string, info os.FileInfo, err error) error { if err != nil { return nil //nolint:nilerr // intentionally swallow walk errors for best-effort scan } if info.IsDir() { base := filepath.Base(path) if base == "vendor" || base == ".git" || base == "node_modules" { return filepath.SkipDir } return nil } if info.Name() == "go.mod" && path != filepath.Join(absDir, "go.mod") { rel, _ := filepath.Rel(absDir, filepath.Dir(path)) subModules = append(subModules, rel) } return nil }) if len(subModules) == 0 { return } fmt.Fprintf(os.Stderr, "warning: found %d sub-modules without go.work; pass 2 will only cover the root module\n", len(subModules)) fmt.Fprintf(os.Stderr, " → Create go.work with: go work init . %s\n", strings.Join(prefixDot(subModules), " ")) } // prefixDot adds "./" prefix to each path. func prefixDot(paths []string) []string { result := make([]string, len(paths)) for i, p := range paths { result[i] = "./" + p } return result } // fileImportsPath reports whether the given file has an import with the specified path. func fileImportsPath(f *ast.File, path string) bool { for _, imp := range f.Imports { if importPath(imp) == path { return true } } return false } // importPath returns the unquoted import path from an ImportSpec. func importPath(imp *ast.ImportSpec) string { return strings.Trim(imp.Path.Value, `"`) } // fileImportsAny reports whether the file imports any path with the given prefix. func fileImportsAny(f *ast.File, prefix string) bool { for _, imp := range f.Imports { if strings.HasPrefix(importPath(imp), prefix) { return true } } return false } // isVendorPath reports whether the file path is inside a vendor/ directory. func isVendorPath(path string) bool { return strings.Contains(path, "/vendor/") || strings.HasPrefix(path, "vendor/") } go-openapi-testify-c10ca71/hack/migrate-testify/main.go000066400000000000000000000101561520301377500231420ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package main implements a migration tool for converting stretchr/testify // usage to go-openapi/testify/v2, and upgrading reflection-based assertions // to generic variants where type information permits. // // Usage: // // go run ./hack/migrate-testify [flags] [directory] package main import ( "context" "flag" "fmt" "os" "os/exec" "strings" ) type options struct { migrate bool upgradeGenerics bool all bool dryRun bool verbose bool skipGomod bool skipVendor bool version string } func main() { opts := &options{} flag.BoolVar(&opts.migrate, "migrate", false, "Run pass 1: stretchr/testify → go-openapi/testify/v2") flag.BoolVar(&opts.upgradeGenerics, "upgrade-generics", false, "Run pass 2: reflection → generic assertions") flag.BoolVar(&opts.all, "all", false, "Run both passes sequentially") flag.BoolVar(&opts.dryRun, "dry-run", false, "Show diffs without modifying files") flag.BoolVar(&opts.verbose, "verbose", false, "Print detailed transformation info") flag.BoolVar(&opts.skipGomod, "skip-gomod", false, "Skip go.mod changes") flag.BoolVar(&opts.skipVendor, "skip-vendor", true, "Skip vendor/ directory") flag.StringVar(&opts.version, "version", "v2.3.0", "Target testify version") flag.Usage = func() { fmt.Fprintf(os.Stderr, "Usage: go run ./hack/migrate-testify [flags] [directory]\n\n") fmt.Fprintf(os.Stderr, "Migrate stretchr/testify to go-openapi/testify/v2 and upgrade to generic assertions.\n\n") fmt.Fprintf(os.Stderr, "Flags:\n") flag.PrintDefaults() fmt.Fprintf(os.Stderr, "\nAt least one of --migrate, --upgrade-generics, or --all is required.\n") fmt.Fprintf(os.Stderr, "\nMono-repo support:\n") fmt.Fprintf(os.Stderr, " Pass 1 walks the filesystem and works across module boundaries.\n") fmt.Fprintf(os.Stderr, " Pass 2 requires type information and uses go/packages to load code.\n") fmt.Fprintf(os.Stderr, " For multi-module repos, a go.work file must be present so that pass 2\n") fmt.Fprintf(os.Stderr, " can load all workspace modules. Create one with:\n") fmt.Fprintf(os.Stderr, " go work init . ./sub/module1 ./sub/module2 ...\n") fmt.Fprintf(os.Stderr, "\nPost-migration checklist:\n") fmt.Fprintf(os.Stderr, " - Run your linter: the migration may surface pre-existing unchecked linting issues.\n") fmt.Fprintf(os.Stderr, " - Run your test suite to verify all tests still pass.\n") fmt.Fprintf(os.Stderr, " - Review any 'advisory:' warnings — no rewrite is applied, but they may require manual attention\n") fmt.Fprintf(os.Stderr, " (e.g. EventuallyWith advisory: v2.4 splits CollectT.FailNow and CollectT.Cancel).\n") } flag.Parse() if opts.all { opts.migrate = true opts.upgradeGenerics = true } if !opts.migrate && !opts.upgradeGenerics { flag.Usage() os.Exit(2) //nolint:mnd // standard exit code for usage error } dir := "." if flag.NArg() > 0 { dir = flag.Arg(0) } // Pre-flight: warn if git is dirty. checkGitDirty(dir) if opts.migrate { fmt.Println("=== Pass 1: Migration (stretchr/testify → go-openapi/testify/v2) ===") //nolint:forbidigo // CLI output if err := runMigration(dir, opts); err != nil { fmt.Fprintf(os.Stderr, "error: migration: %v\n", err) os.Exit(1) } } if opts.upgradeGenerics { fmt.Println("=== Pass 2: Generic Upgrade (reflection → generic assertions) ===") //nolint:forbidigo // CLI output if err := runGenericUpgrade(dir, opts); err != nil { fmt.Fprintf(os.Stderr, "error: generic upgrade: %v\n", err) os.Exit(1) } } fmt.Println("Done.") //nolint:forbidigo // CLI output } // checkGitDirty warns if the working directory has uncommitted changes. func checkGitDirty(dir string) { cmd := exec.CommandContext(context.Background(), "git", "status", "--porcelain") cmd.Dir = dir out, err := cmd.Output() if err != nil { return // Not a git repo or git not available — skip check. } output := strings.TrimSpace(string(out)) if output != "" { fmt.Fprintf(os.Stderr, "warning: working directory has uncommitted changes\n") } } go-openapi-testify-c10ca71/hack/migrate-testify/migrate.go000066400000000000000000000162411520301377500236470ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "fmt" "go/ast" "go/parser" "go/token" "os" "path/filepath" "strings" "golang.org/x/tools/go/ast/astutil" ) // runMigration executes pass 1: stretchr/testify → go-openapi/testify/v2. func runMigration(dir string, opts *options) error { fset := token.NewFileSet() rpt := &report{} // Walk all .go files in the directory tree. err := filepath.Walk(dir, func(path string, info os.FileInfo, err error) error { if err != nil { return err } if info.IsDir() { base := filepath.Base(path) if base == "vendor" && opts.skipVendor { return filepath.SkipDir } if base == ".git" || base == "node_modules" { return filepath.SkipDir } return nil } if !strings.HasSuffix(path, ".go") { return nil } rpt.filesScanned++ return migrateFile(fset, path, opts, rpt) }) if err != nil { return fmt.Errorf("walking directory: %w", err) } rpt.print(opts.verbose) rpt.printPass1Summary() if !opts.dryRun && !opts.skipGomod { if err := updateGoMod(dir, opts.version, false, opts.verbose); err != nil { return fmt.Errorf("updating go.mod: %w", err) } } else if opts.dryRun && !opts.skipGomod { if err := updateGoMod(dir, opts.version, true, opts.verbose); err != nil { return fmt.Errorf("updating go.mod: %w", err) } } return nil } // migrateFile processes a single Go file for pass 1 transformations. func migrateFile(fset *token.FileSet, filename string, opts *options, rpt *report) error { src, err := os.ReadFile(filename) if err != nil { return err } f, err := parser.ParseFile(fset, filename, src, parser.ParseComments) if err != nil { return fmt.Errorf("parsing %s: %w", filename, err) } if !fileImportsAny(f, "github.com/stretchr/testify") { return nil } changed := false // 1. Detect incompatible imports. for imp, msg := range incompatibleImports { if fileImportsPath(f, imp) { rpt.warn(filename, 0, msg) } } // 2. Build alias map before rewriting imports. aliases := buildAliasMap(f) // 2b. Count API usage for the summary report. countAPIUsage(f, aliases, rpt) // 3. Rewrite imports. for old, replacement := range importRewrites { if astutil.RewriteImport(fset, f, old, replacement) { changed = true rpt.totalChanges++ if opts.verbose { rpt.info(filename, 0, fmt.Sprintf("rewrote import %q → %q", old, replacement)) } } } // 4. Advisory: note the new CollectT.Cancel() semantics for every // EventuallyWith-family call. Runs BEFORE rename so stretchr spellings // (EventuallyWithT) are also picked up in the diagnostic position. _ = noteEventuallyWithCancel(f, aliases, fset, rpt, filename) // 5. Rename functions and replace PanicTestFunc. changes := renameFunctions(f, aliases, fset, rpt, filename, opts.verbose) if changes > 0 { changed = true rpt.totalChanges += changes } // 5. Replace PanicTestFunc type references. ptfChanges := replacePanicTestFunc(f, aliases, fset, rpt, filename, opts.verbose) if ptfChanges > 0 { changed = true rpt.totalChanges += ptfChanges } // 6. Detect YAML usage and inject enable import. if needsYAMLEnable(f, aliases) { if !fileImportsPath(f, goopenapiYAMLEnable) { astutil.AddNamedImport(fset, f, "_", goopenapiYAMLEnable) changed = true rpt.totalChanges++ if opts.verbose { rpt.info(filename, 0, "injected enable/yaml import") } } } if !changed { return nil } rpt.filesChanged++ if opts.dryRun { return showDiff(fset, f, filename) } return writeFile(fset, f, filename) } // buildAliasMap builds a map from import alias (or default package name) to import path // for stretchr/testify packages. func buildAliasMap(f *ast.File) map[string]string { aliases := make(map[string]string) for _, imp := range f.Imports { path := importPath(imp) if !strings.HasPrefix(path, "github.com/stretchr/testify") { continue } var localName string if imp.Name != nil { localName = imp.Name.Name } else { // Default: last path element. parts := strings.Split(path, "/") localName = parts[len(parts)-1] } if localName != "_" && localName != "." { aliases[localName] = path } } return aliases } // renameFunctions walks the AST and renames function calls that changed names // between stretchr/testify and go-openapi/testify/v2. func renameFunctions(f *ast.File, aliases map[string]string, fset *token.FileSet, rpt *report, filename string, verbose bool) int { changes := 0 ast.Inspect(f, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } // Handle pkg.Func() calls. sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return true } funcName := sel.Sel.Name newName, exists := migrationRenames[funcName] if !exists { return true } // Verify this is a call on a testify package. if !isTestifySelector(sel, aliases) { return true } sel.Sel.Name = newName changes++ pos := fset.Position(call.Pos()) if verbose { rpt.info(filename, pos.Line, fmt.Sprintf("renamed %s → %s", funcName, newName)) } return true }) return changes } // isTestifySelector checks if a selector expression refers to a stretchr/testify package. func isTestifySelector(sel *ast.SelectorExpr, aliases map[string]string) bool { ident, ok := sel.X.(*ast.Ident) if !ok { return false } _, exists := aliases[ident.Name] return exists } // replacePanicTestFunc replaces PanicTestFunc type references with func(). func replacePanicTestFunc(f *ast.File, aliases map[string]string, fset *token.FileSet, rpt *report, filename string, verbose bool) int { changes := 0 astutil.Apply(f, func(c *astutil.Cursor) bool { sel, ok := c.Node().(*ast.SelectorExpr) if !ok { return true } if sel.Sel.Name != "PanicTestFunc" { return true } if !isTestifySelector(sel, aliases) { return true } // Replace with func() — an *ast.FuncType with no params and no results. c.Replace(&ast.FuncType{ Params: &ast.FieldList{}, }) changes++ pos := fset.Position(sel.Pos()) if verbose { rpt.info(filename, pos.Line, "replaced PanicTestFunc with func()") } return true }, nil) return changes } // countAPIUsage walks the AST and counts every testify assertion call for reporting. func countAPIUsage(f *ast.File, aliases map[string]string, rpt *report) { ast.Inspect(f, func(n ast.Node) bool { call, ok := n.(*ast.CallExpr) if !ok { return true } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return true } if !isTestifySelector(sel, aliases) { return true } ident, ok := sel.X.(*ast.Ident) if !ok { return true } rpt.trackAPIUsage(ident.Name + "." + sel.Sel.Name) return true }) } // needsYAMLEnable checks if any YAML assertion functions are called in the file. func needsYAMLEnable(f *ast.File, aliases map[string]string) bool { found := false ast.Inspect(f, func(n ast.Node) bool { if found { return false } call, ok := n.(*ast.CallExpr) if !ok { return true } sel, ok := call.Fun.(*ast.SelectorExpr) if !ok { return true } if yamlFunctions[sel.Sel.Name] && isTestifySelector(sel, aliases) { found = true return false } return true }) return found } go-openapi-testify-c10ca71/hack/migrate-testify/migrate_test.go000066400000000000000000000072401520301377500247050ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "bytes" "go/parser" "go/printer" "go/token" "iter" "os" "slices" "strings" "testing" "golang.org/x/tools/go/ast/astutil" ) type migrateTestCase struct { name string input string expected string // warnContains, if non-empty, checks that a warning was emitted containing this string. warnContains string } func migrateTestCases() iter.Seq[migrateTestCase] { return slices.Values([]migrateTestCase{ { name: "basic import rewrite", input: readTestdata("migrate_basic/input.go.txt"), expected: readTestdata("migrate_basic/expected.go.txt"), }, { name: "yaml enable injection", input: readTestdata("migrate_yaml/input.go.txt"), expected: readTestdata("migrate_yaml/expected.go.txt"), }, { name: "aliased import with rename", input: readTestdata("migrate_alias/input.go.txt"), expected: readTestdata("migrate_alias/expected.go.txt"), }, { name: "incompatible imports warn", input: readTestdata("migrate_incompatible/input.go.txt"), warnContains: "mock package is not available", }, { name: "EventuallyWith advisory", input: readTestdata("migrate_eventuallywith/input.go.txt"), warnContains: "CollectT.Cancel()", }, { name: "PanicTestFunc replacement", input: readTestdata("migrate_panic_func/input.go.txt"), expected: readTestdata("migrate_panic_func/expected.go.txt"), }, }) } func TestMigrateFile(t *testing.T) { t.Parallel() for c := range migrateTestCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() runMigrateSubtest(t, c) }) } } func runMigrateSubtest(t *testing.T, c migrateTestCase) { t.Helper() fset := token.NewFileSet() f, err := parser.ParseFile(fset, "test.go", c.input, parser.ParseComments) if err != nil { t.Fatalf("parse: %v", err) } rpt := &report{} // Build aliases before rewriting imports. aliases := buildAliasMap(f) // Detect incompatible imports. for imp, msg := range incompatibleImports { if fileImportsPath(f, imp) { rpt.warn("test.go", 0, msg) } } // Rewrite imports. for old, replacement := range importRewrites { astutil.RewriteImport(fset, f, old, replacement) } // Advisory: note CollectT.Cancel semantics for EventuallyWith-family calls. noteEventuallyWithCancel(f, aliases, fset, rpt, "test.go") // Rename functions. renameFunctions(f, aliases, fset, rpt, "test.go", true) // Replace PanicTestFunc. replacePanicTestFunc(f, aliases, fset, rpt, "test.go", true) // YAML injection. if needsYAMLEnable(f, aliases) { astutil.AddNamedImport(fset, f, "_", goopenapiYAMLEnable) } // Check warnings. if c.warnContains != "" { found := false for _, d := range rpt.diagnostics { if d.kind == warn && strings.Contains(d.message, c.warnContains) { found = true break } } if !found { t.Errorf("expected warning containing %q, got: %v", c.warnContains, rpt.diagnostics) } return } // Compare output. var buf strings.Builder cfg := &printer.Config{Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8} if err := cfg.Fprint(&buf, fset, f); err != nil { t.Fatalf("print: %v", err) } got := strings.TrimSpace(buf.String()) want := strings.TrimSpace(c.expected) if got != want { t.Errorf("output mismatch:\n--- got ---\n%s\n--- want ---\n%s", got, want) } } func readTestdata(path string) string { data, err := os.ReadFile("testdata/" + path) if err != nil { panic("reading testdata: " + err.Error()) } return string(bytes.ReplaceAll(data, []byte{'\r'}, []byte{})) // on windows, remove the \r from \n\r sequences } go-openapi-testify-c10ca71/hack/migrate-testify/rename_map.go000066400000000000000000000244371520301377500243310ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main // stretchrAssertPath and related constants define the import paths for // stretchr/testify and their go-openapi/testify/v2 replacements. const ( stretchrAssertPath = "github.com/stretchr/testify/assert" stretchrRequirePath = "github.com/stretchr/testify/require" stretchrRootPath = "github.com/stretchr/testify" stretchrMockPath = "github.com/stretchr/testify/mock" stretchrSuitePath = "github.com/stretchr/testify/suite" stretchrHTTPPath = "github.com/stretchr/testify/http" goopenapiAssertPath = "github.com/go-openapi/testify/v2/assert" goopenapiRequirePath = "github.com/go-openapi/testify/v2/require" goopenapiRootPath = "github.com/go-openapi/testify/v2" goopenapiYAMLEnable = "github.com/go-openapi/testify/v2/enable/yaml" ) // importRewrites maps stretchr import paths to go-openapi replacements. var importRewrites = map[string]string{ //nolint:gochecknoglobals // lookup table stretchrAssertPath: goopenapiAssertPath, stretchrRequirePath: goopenapiRequirePath, stretchrRootPath: goopenapiRootPath, } // incompatibleImports are stretchr import paths with no go-openapi equivalent. var incompatibleImports = map[string]string{ //nolint:gochecknoglobals // lookup table stretchrMockPath: "mock package is not available in go-openapi/testify/v2.\n" + " → Use github.com/vektra/mockery or hand-written mocks.\n" + " → See: https://go-openapi.github.io/testify/usage/migration/index.html#5-remove-suitemock-usage", stretchrSuitePath: "suite package is not available in go-openapi/testify/v2.\n" + " → Use standard Go subtests (t.Run) and TestMain.\n" + " → See: https://go-openapi.github.io/testify/usage/migration/index.html#5-remove-suitemock-usage", stretchrHTTPPath: "http package is not available in go-openapi/testify/v2.\n" + " → Use net/http/httptest from the standard library.\n" + " → See: https://go-openapi.github.io/testify/usage/migration/index.html#6-remove-use-of-the-testifyhttp-package", } // migrationRenames maps stretchr function names to their go-openapi equivalents // where the names differ. var migrationRenames = map[string]string{ //nolint:gochecknoglobals // lookup table "EventuallyWithT": "EventuallyWith", "EventuallyWithTf": "EventuallyWithf", "NoDirExists": "DirNotExists", "NoDirExistsf": "DirNotExistsf", "NoFileExists": "FileNotExists", "NoFileExistsf": "FileNotExistsf", } // yamlFunctions lists assertion function names that use YAML, triggering // injection of the enable/yaml import. var yamlFunctions = map[string]bool{ //nolint:gochecknoglobals // lookup table "YAMLEq": true, "YAMLEqf": true, "YAMLEqT": true, "YAMLEqTf": true, "YAMLEqBytes": true, "YAMLEqBytesf": true, } // constraintKind classifies the type constraint needed for a generic upgrade. type constraintKind int const ( constraintComparable constraintKind = iota constraintDeepComparable // comparable AND == has same semantics as reflect.DeepEqual constraintOrdered constraintText constraintSignedNumeric constraintMeasurable constraintPointer constraintBoolean constraintRegExp ) // containerKind classifies the container type for Contains upgrades. type containerKind int const ( containerString containerKind = iota containerSlice containerMap ) // upgradeRule defines how to upgrade a reflection-based assertion to a generic variant. type upgradeRule struct { // target is the generic function name to upgrade to. target string // argConstraints defines the constraint required for each argument // (excluding t and msgAndArgs). For single-constraint functions, only // one entry is needed. argConstraints []constraintKind // sameType requires that the relevant arguments have identical types. sameType bool // containerUpgrade means the function dispatches to different generic // variants depending on the container type (string, slice, map). containerUpgrade bool // manualReview flags the upgrade as requiring manual review due to // signature changes (e.g., IsType → IsOfTypeT changes arg count). manualReview bool } // genericUpgrades maps reflection-based assertion names to their generic upgrade rules. var genericUpgrades = map[string]upgradeRule{ //nolint:gochecknoglobals // lookup table // Equality — must be deep-comparable (== same as reflect.DeepEqual) // to preserve semantics. Pointer types are excluded because EqualT // uses == (address comparison) while Equal uses reflect.DeepEqual. "Equal": { target: "EqualT", argConstraints: []constraintKind{constraintDeepComparable}, sameType: true, }, "NotEqual": { target: "NotEqualT", argConstraints: []constraintKind{constraintDeepComparable}, sameType: true, }, // Comparison / ordering "Greater": { target: "GreaterT", argConstraints: []constraintKind{constraintOrdered}, sameType: true, }, "GreaterOrEqual": { target: "GreaterOrEqualT", argConstraints: []constraintKind{constraintOrdered}, sameType: true, }, "Less": { target: "LessT", argConstraints: []constraintKind{constraintOrdered}, sameType: true, }, "LessOrEqual": { target: "LessOrEqualT", argConstraints: []constraintKind{constraintOrdered}, sameType: true, }, // Numeric "Positive": { target: "PositiveT", argConstraints: []constraintKind{constraintSignedNumeric}, }, "Negative": { target: "NegativeT", argConstraints: []constraintKind{constraintSignedNumeric}, }, "InDelta": { target: "InDeltaT", argConstraints: []constraintKind{constraintMeasurable}, sameType: true, }, "InEpsilon": { target: "InEpsilonT", argConstraints: []constraintKind{constraintMeasurable}, sameType: true, }, // Container (dispatches to different targets) "Contains": { containerUpgrade: true, }, "NotContains": { containerUpgrade: true, }, // Collection "ElementsMatch": { target: "ElementsMatchT", argConstraints: []constraintKind{constraintComparable}, sameType: true, }, "NotElementsMatch": { target: "NotElementsMatchT", argConstraints: []constraintKind{constraintComparable}, sameType: true, }, "Subset": { target: "SliceSubsetT", argConstraints: []constraintKind{constraintComparable}, sameType: true, }, "NotSubset": { target: "SliceNotSubsetT", argConstraints: []constraintKind{constraintComparable}, sameType: true, }, // Ordering (slice) "IsIncreasing": { target: "IsIncreasingT", argConstraints: []constraintKind{constraintOrdered}, }, "IsDecreasing": { target: "IsDecreasingT", argConstraints: []constraintKind{constraintOrdered}, }, "IsNonIncreasing": { target: "IsNonIncreasingT", argConstraints: []constraintKind{constraintOrdered}, }, "IsNonDecreasing": { target: "IsNonDecreasingT", argConstraints: []constraintKind{constraintOrdered}, }, // String / regex "Regexp": { target: "RegexpT", argConstraints: []constraintKind{constraintRegExp, constraintText}, }, "NotRegexp": { target: "NotRegexpT", argConstraints: []constraintKind{constraintRegExp, constraintText}, }, // JSON / YAML "JSONEq": { target: "JSONEqT", argConstraints: []constraintKind{constraintText}, sameType: true, }, "YAMLEq": { target: "YAMLEqT", argConstraints: []constraintKind{constraintText}, sameType: true, }, // Pointer "Same": { target: "SameT", argConstraints: []constraintKind{constraintPointer}, sameType: true, }, "NotSame": { target: "NotSameT", argConstraints: []constraintKind{constraintPointer}, sameType: true, }, // Type (manual review — arg count changes) "IsType": { target: "IsOfTypeT", manualReview: true, argConstraints: []constraintKind{}, }, "IsNotType": { target: "IsNotOfTypeT", manualReview: true, argConstraints: []constraintKind{}, }, // Boolean "True": { target: "TrueT", argConstraints: []constraintKind{constraintBoolean}, }, "False": { target: "FalseT", argConstraints: []constraintKind{constraintBoolean}, }, } // containerUpgradeTargets maps container kind to (funcName, notFuncName) pairs. var containerUpgradeTargets = map[containerKind][2]string{ //nolint:gochecknoglobals // lookup table containerString: {"StringContainsT", "StringNotContainsT"}, containerSlice: {"SliceContainsT", "SliceNotContainsT"}, containerMap: {"MapContainsT", "MapNotContainsT"}, } // skipReason describes why a generic upgrade was skipped for an assertion call. type skipReason string const ( skipPointerSemantics skipReason = "pointer type (== compares addresses, not values)" skipInterfaceField skipReason = "struct contains pointer/interface fields" skipUnresolvableType skipReason = "type not statically resolvable" skipInterfaceType skipReason = "argument is interface{}/any" skipTypeMismatch skipReason = "arguments have different types" skipNotOrdered skipReason = "type does not satisfy Ordered constraint" skipNotText skipReason = "type does not satisfy Text constraint" skipNotSignedNumeric skipReason = "type does not satisfy SignedNumeric constraint" skipNotMeasurable skipReason = "type does not satisfy Measurable constraint" skipNotComparable skipReason = "type does not satisfy comparable constraint" skipNotBoolean skipReason = "type does not satisfy Boolean constraint" skipNotPointer skipReason = "type is not a pointer" skipNotSlice skipReason = "argument is not a slice type" skipNotRegExp skipReason = "type does not satisfy RegExp constraint" skipSliceElemNotDeepComparable skipReason = "slice element not deeply comparable" skipSliceElemNotComparable skipReason = "slice element not comparable" skipSliceElemNotOrdered skipReason = "slice element not ordered" skipMapKeyNotComparable skipReason = "map key not comparable" skipMapValNotComparable skipReason = "map value not comparable" skipContainerTypeUnknown skipReason = "container is not string, slice, or map" ) go-openapi-testify-c10ca71/hack/migrate-testify/report.go000066400000000000000000000156651520301377500235430ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "cmp" "fmt" "go/ast" "go/printer" "go/token" "os" "slices" "strings" ) const warn = "warning" // diagnostic represents a single warning or info message from the migration tool. type diagnostic struct { file string line int message string kind string // "warning", "info", "error" } func (d diagnostic) String() string { if d.line > 0 { return fmt.Sprintf("%s:%d: %s: %s", d.file, d.line, d.kind, d.message) } return fmt.Sprintf("%s: %s: %s", d.file, d.kind, d.message) } // report collects diagnostics and file changes during a migration run. type report struct { diagnostics []diagnostic filesChanged int totalChanges int filesScanned int // Pass 1 stats: funcName → call count (e.g. "assert.Equal" → 42). apiUsage map[string]int // Pass 2 stats: "Equal→EqualT" → count. upgraded map[string]int // Pass 2 stats: skipReason → count. skipped map[string]int } func (r *report) warn(file string, line int, msg string) { r.diagnostics = append(r.diagnostics, diagnostic{file: file, line: line, message: msg, kind: warn}) } func (r *report) info(file string, line int, msg string) { r.diagnostics = append(r.diagnostics, diagnostic{file: file, line: line, message: msg, kind: "info"}) } func (r *report) errorf(file string, line int, msg string) { r.diagnostics = append(r.diagnostics, diagnostic{file: file, line: line, message: msg, kind: "error"}) } // trackAPIUsage increments the usage counter for a testify API call. func (r *report) trackAPIUsage(qualifiedName string) { if r.apiUsage == nil { r.apiUsage = make(map[string]int) } r.apiUsage[qualifiedName]++ } // trackUpgrade increments the upgrade counter for a successful generic upgrade. func (r *report) trackUpgrade(from, to string) { if r.upgraded == nil { r.upgraded = make(map[string]int) } r.upgraded[from+" → "+to]++ } // trackSkip records a skipped generic upgrade and emits an info diagnostic. func (r *report) trackSkip(file string, line int, funcName string, reason skipReason, verbose bool, typeInfo string) { if r.skipped == nil { r.skipped = make(map[string]int) } r.skipped[string(reason)]++ if verbose { msg := fmt.Sprintf("skipped %s: %s", funcName, reason) if typeInfo != "" { msg += " [" + typeInfo + "]" } r.info(file, line, msg) } } // print outputs all collected diagnostics. func (r *report) print(verbose bool) { if verbose { for _, d := range r.diagnostics { fmt.Println(d) //nolint:forbidigo // CLI output } } else { // Only print warnings and errors, not info diagnostics. for _, d := range r.diagnostics { if d.kind != "info" { fmt.Println(d) //nolint:forbidigo // CLI output } } } } // printPass1Summary outputs the Pass 1 structured summary. func (r *report) printPass1Summary() { fmt.Printf("\n=== Pass 1 Summary ===\n") //nolint:forbidigo // CLI output fmt.Printf("Files scanned: %d | Files changed: %d | Transformations: %d\n", //nolint:forbidigo // CLI output r.filesScanned, r.filesChanged, r.totalChanges) if len(r.apiUsage) > 0 { fmt.Printf("\nAPI usage across migrated scope:\n") //nolint:forbidigo // CLI output printCountTable(r.apiUsage) } warnings := 0 for _, d := range r.diagnostics { if d.kind == warn { warnings++ } } fmt.Printf("\nWarnings: %d\n", warnings) //nolint:forbidigo // CLI output } // printPass2Summary outputs the Pass 2 structured summary. func (r *report) printPass2Summary() { fmt.Printf("\n=== Pass 2 Summary ===\n") //nolint:forbidigo // CLI output fmt.Printf("Files scanned: %d | Files changed: %d | Upgrades: %d\n", //nolint:forbidigo // CLI output r.filesScanned, r.filesChanged, r.totalChanges) if len(r.upgraded) > 0 { fmt.Printf("\nUpgraded assertions:\n") //nolint:forbidigo // CLI output printCountTable(r.upgraded) } if len(r.skipped) > 0 { total := 0 for _, v := range r.skipped { total += v } fmt.Printf("\nSkipped (generic alternative exists but cannot upgrade): %d\n", total) //nolint:forbidigo // CLI output printCountTable(r.skipped) } } // printCountTable prints a map of label→count as a right-aligned table with two columns. func printCountTable(m map[string]int) { type entry struct { label string count int } entries := make([]entry, 0, len(m)) for k, v := range m { entries = append(entries, entry{k, v}) } slices.SortFunc(entries, func(a, b entry) int { if c := cmp.Compare(b.count, a.count); c != 0 { return c } return cmp.Compare(a.label, b.label) }) // Find max label width for alignment. maxLabel := 0 for _, e := range entries { if len(e.label) > maxLabel { maxLabel = len(e.label) } } // Print in two columns if there are enough entries. for i := 0; i < len(entries); i += 2 { left := entries[i] if i+1 < len(entries) { right := entries[i+1] fmt.Printf(" %-*s %5d %-*s %5d\n", maxLabel, left.label, left.count, maxLabel, right.label, right.count) //nolint:forbidigo // CLI output } else { fmt.Printf(" %-*s %5d\n", maxLabel, left.label, left.count) //nolint:forbidigo // CLI output } } } // writeFile writes a modified AST back to disk via go/printer. func writeFile(fset *token.FileSet, f *ast.File, filename string) error { out, err := os.Create(filename) if err != nil { return fmt.Errorf("creating %s: %w", filename, err) } defer out.Close() cfg := &printer.Config{ Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8, //nolint:mnd // standard Go tabwidth } if err := cfg.Fprint(out, fset, f); err != nil { return fmt.Errorf("writing %s: %w", filename, err) } return nil } // showDiff displays a simple diff-like output showing what would change. func showDiff(fset *token.FileSet, f *ast.File, filename string) error { original, err := os.ReadFile(filename) if err != nil { return err } var buf strings.Builder cfg := &printer.Config{ Mode: printer.UseSpaces | printer.TabIndent, Tabwidth: 8, //nolint:mnd // standard Go tabwidth } if err := cfg.Fprint(&buf, fset, f); err != nil { return err } modified := buf.String() if string(original) == modified { return nil } fmt.Printf("--- %s\n+++ %s (modified)\n", filename, filename) //nolint:forbidigo // CLI output origLines := strings.Split(string(original), "\n") modLines := strings.Split(modified, "\n") // Simple line-by-line diff — not a real unified diff, but helpful for dry-run. maxLines := max(len(origLines), len(modLines)) for i := range maxLines { var origLine, modLine string if i < len(origLines) { origLine = origLines[i] } if i < len(modLines) { modLine = modLines[i] } if origLine != modLine { if origLine != "" { fmt.Printf("-%s\n", origLine) //nolint:forbidigo // CLI output } if modLine != "" { fmt.Printf("+%s\n", modLine) //nolint:forbidigo // CLI output } } } fmt.Println() //nolint:forbidigo // CLI output return nil } go-openapi-testify-c10ca71/hack/migrate-testify/report_test.go000066400000000000000000000120231520301377500245630ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package main import ( "iter" "slices" "testing" ) type trackAPIUsageCase struct { name string calls []string expected map[string]int } func trackAPIUsageCases() iter.Seq[trackAPIUsageCase] { return slices.Values([]trackAPIUsageCase{ { name: "single call", calls: []string{"assert.Equal"}, expected: map[string]int{ "assert.Equal": 1, }, }, { name: "multiple calls same function", calls: []string{"assert.Equal", "assert.Equal", "assert.Equal"}, expected: map[string]int{ "assert.Equal": 3, }, }, { name: "multiple different functions", calls: []string{"assert.Equal", "require.NoError", "assert.True"}, expected: map[string]int{ "assert.Equal": 1, "require.NoError": 1, "assert.True": 1, }, }, }) } func TestTrackAPIUsage(t *testing.T) { t.Parallel() for c := range trackAPIUsageCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() rpt := &report{} for _, call := range c.calls { rpt.trackAPIUsage(call) } if len(rpt.apiUsage) != len(c.expected) { t.Errorf("expected %d entries, got %d", len(c.expected), len(rpt.apiUsage)) } for k, v := range c.expected { if rpt.apiUsage[k] != v { t.Errorf("expected %s=%d, got %d", k, v, rpt.apiUsage[k]) } } }) } } type trackUpgradeCase struct { name string upgrades [][2]string // from, to pairs expected map[string]int } func trackUpgradeCases() iter.Seq[trackUpgradeCase] { return slices.Values([]trackUpgradeCase{ { name: "single upgrade", upgrades: [][2]string{{"Equal", "EqualT"}}, expected: map[string]int{ "Equal → EqualT": 1, }, }, { name: "multiple upgrades", upgrades: [][2]string{ {"Equal", "EqualT"}, {"Equal", "EqualT"}, {"True", "TrueT"}, }, expected: map[string]int{ "Equal → EqualT": 2, "True → TrueT": 1, }, }, }) } func TestTrackUpgrade(t *testing.T) { t.Parallel() for c := range trackUpgradeCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() rpt := &report{} for _, u := range c.upgrades { rpt.trackUpgrade(u[0], u[1]) } if len(rpt.upgraded) != len(c.expected) { t.Errorf("expected %d entries, got %d", len(c.expected), len(rpt.upgraded)) } for k, v := range c.expected { if rpt.upgraded[k] != v { t.Errorf("expected %s=%d, got %d", k, v, rpt.upgraded[k]) } } }) } } type trackSkipCase struct { name string skips []skipReason expectedSkipped map[string]int verbose bool expectDiag bool } func trackSkipCases() iter.Seq[trackSkipCase] { return slices.Values([]trackSkipCase{ { name: "single skip", skips: []skipReason{skipPointerSemantics}, expectedSkipped: map[string]int{ string(skipPointerSemantics): 1, }, verbose: false, expectDiag: false, }, { name: "verbose emits diagnostic", skips: []skipReason{skipInterfaceType}, expectedSkipped: map[string]int{ string(skipInterfaceType): 1, }, verbose: true, expectDiag: true, }, { name: "multiple skips aggregated", skips: []skipReason{skipPointerSemantics, skipPointerSemantics, skipTypeMismatch}, expectedSkipped: map[string]int{ string(skipPointerSemantics): 2, string(skipTypeMismatch): 1, }, verbose: false, expectDiag: false, }, }) } func TestTrackSkip(t *testing.T) { t.Parallel() for c := range trackSkipCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() rpt := &report{} for _, reason := range c.skips { rpt.trackSkip("test.go", 42, "Equal", reason, c.verbose, "*float64") } if len(rpt.skipped) != len(c.expectedSkipped) { t.Errorf("expected %d skip entries, got %d", len(c.expectedSkipped), len(rpt.skipped)) } for k, v := range c.expectedSkipped { if rpt.skipped[k] != v { t.Errorf("expected %s=%d, got %d", k, v, rpt.skipped[k]) } } hasDiag := len(rpt.diagnostics) > 0 if c.expectDiag && !hasDiag { t.Error("expected diagnostic to be emitted in verbose mode") } if !c.expectDiag && hasDiag { t.Errorf("expected no diagnostic in non-verbose mode, got %d", len(rpt.diagnostics)) } }) } } func TestReportInitializesLazily(t *testing.T) { t.Parallel() rpt := &report{} // All maps should be nil initially. if rpt.apiUsage != nil { t.Error("apiUsage should be nil initially") } if rpt.upgraded != nil { t.Error("upgraded should be nil initially") } if rpt.skipped != nil { t.Error("skipped should be nil initially") } // After tracking, maps should be initialized. rpt.trackAPIUsage("assert.Equal") rpt.trackUpgrade("Equal", "EqualT") rpt.trackSkip("test.go", 1, "Equal", skipPointerSemantics, false, "") if rpt.apiUsage == nil { t.Error("apiUsage should be initialized after tracking") } if rpt.upgraded == nil { t.Error("upgraded should be initialized after tracking") } if rpt.skipped == nil { t.Error("skipped should be initialized after tracking") } } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/000077500000000000000000000000001520301377500234755ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_alias/000077500000000000000000000000001520301377500262765ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_alias/expected.go.txt000066400000000000000000000002571520301377500312500ustar00rootroot00000000000000package example import ( "testing" ta "github.com/go-openapi/testify/v2/assert" ) func TestAlias(t *testing.T) { ta.Equal(t, 1, 1) ta.DirNotExists(t, "/nonexistent") } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_alias/input.go.txt000066400000000000000000000002511520301377500306000ustar00rootroot00000000000000package example import ( "testing" ta "github.com/stretchr/testify/assert" ) func TestAlias(t *testing.T) { ta.Equal(t, 1, 1) ta.NoDirExists(t, "/nonexistent") } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_basic/000077500000000000000000000000001520301377500262665ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_basic/expected.go.txt000066400000000000000000000003211520301377500312300ustar00rootroot00000000000000package example import ( "testing" "github.com/go-openapi/testify/v2/assert" "github.com/go-openapi/testify/v2/require" ) func TestBasic(t *testing.T) { assert.Equal(t, 1, 1) require.NoError(t, nil) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_basic/input.go.txt000066400000000000000000000003071520301377500305720ustar00rootroot00000000000000package example import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) func TestBasic(t *testing.T) { assert.Equal(t, 1, 1) require.NoError(t, nil) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_eventuallywith/000077500000000000000000000000001520301377500302715ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_eventuallywith/input.go.txt000066400000000000000000000003751520301377500326020ustar00rootroot00000000000000package example import ( "testing" "time" "github.com/stretchr/testify/assert" ) func TestEventuallyWith(t *testing.T) { assert.EventuallyWithT(t, func(c *assert.CollectT) { assert.True(c, true) }, 100*time.Millisecond, 20*time.Millisecond) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_incompatible/000077500000000000000000000000001520301377500276535ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_incompatible/input.go.txt000066400000000000000000000004331520301377500321570ustar00rootroot00000000000000package example import ( "testing" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" "github.com/stretchr/testify/suite" ) type MySuite struct { suite.Suite } type MyMock struct { mock.Mock } func TestIncompat(t *testing.T) { assert.True(t, true) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_panic_func/000077500000000000000000000000001520301377500273125ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_panic_func/expected.go.txt000066400000000000000000000002671520301377500322650ustar00rootroot00000000000000package example import ( "testing" "github.com/go-openapi/testify/v2/assert" ) func TestPanicFunc(t *testing.T) { var f func() = func() { panic("boom") } assert.Panics(t, f) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_panic_func/input.go.txt000066400000000000000000000003001520301377500316070ustar00rootroot00000000000000package example import ( "testing" "github.com/stretchr/testify/assert" ) func TestPanicFunc(t *testing.T) { var f assert.PanicTestFunc = func() { panic("boom") } assert.Panics(t, f) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_yaml/000077500000000000000000000000001520301377500261475ustar00rootroot00000000000000go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_yaml/expected.go.txt000066400000000000000000000003221520301377500311120ustar00rootroot00000000000000package example import ( "testing" "github.com/go-openapi/testify/v2/assert" _ "github.com/go-openapi/testify/v2/enable/yaml" ) func TestYAML(t *testing.T) { assert.YAMLEq(t, `name: foo`, `name: foo`) } go-openapi-testify-c10ca71/hack/migrate-testify/testdata/migrate_yaml/input.go.txt000066400000000000000000000002331520301377500304510ustar00rootroot00000000000000package example import ( "testing" "github.com/stretchr/testify/assert" ) func TestYAML(t *testing.T) { assert.YAMLEq(t, `name: foo`, `name: foo`) } go-openapi-testify-c10ca71/internal/000077500000000000000000000000001520301377500174555ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/000077500000000000000000000000001520301377500216475ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/benchmarks_test.go000066400000000000000000000312411520301377500253530ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "slices" "testing" ) // Helper functions to reduce duplication in benchmarks // benchmarkComparison runs a benchmark comparing reflection vs generic implementations. func benchmarkComparison( b *testing.B, name string, reflectFn func(*mockT), genericFn func(*mockT), ) { b.Helper() mockT := &mockT{} b.Run("reflect/"+name, func(b *testing.B) { b.ReportAllocs() for b.Loop() { reflectFn(mockT) } }) b.Run("generic/"+name, func(b *testing.B) { b.ReportAllocs() for b.Loop() { genericFn(mockT) } }) } // benchmarkGenericOnly runs a benchmark for generic-only functions (no reflection equivalent). func benchmarkGenericOnly(b *testing.B, genericFn func(*mockT)) { b.Helper() mockT := &mockT{} b.Run("generic", func(b *testing.B) { b.ReportAllocs() for b.Loop() { genericFn(mockT) } }) } func Benchmark_isEmpty(b *testing.B) { b.ReportAllocs() v := new(int) for b.Loop() { isEmpty("") isEmpty(42) isEmpty(v) } } func BenchmarkNotNil(b *testing.B) { for b.Loop() { NotNil(b, b) } } func BenchmarkBytesEqual(b *testing.B) { const size = 1024 * 8 s := make([]byte, size) for i := range s { s[i] = byte(i % 255) } s2 := make([]byte, size) copy(s2, s) mockT := &mockFailNowT{} for b.Loop() { Equal(mockT, s, s2) } } /* * Benchmarks comparing reflect-based vs generic implementations. * * These benchmarks measure the performance difference between: * - Reflect-based versions (e.g., Greater, ElementsMatch) * - Generic versions (e.g., GreaterT, ElementsMatchT) */ // BenchmarkGreater compares Greater (reflect) vs GreaterT (generic). func BenchmarkGreater(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { Greater(t, 100, 50) }, func(t *mockT) { GreaterT(t, 100, 50) }, ) benchmarkComparison(b, "float64", func(t *mockT) { Greater(t, 100.5, 50.5) }, func(t *mockT) { GreaterT(t, 100.5, 50.5) }, ) benchmarkComparison(b, "string", func(t *mockT) { Greater(t, "beta", "alpha") }, func(t *mockT) { GreaterT(t, "beta", "alpha") }, ) } // BenchmarkLess compares Less (reflect) vs LessT (generic). func BenchmarkLess(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { Less(t, 50, 100) }, func(t *mockT) { LessT(t, 50, 100) }, ) } // BenchmarkElementsMatch compares ElementsMatch (reflect) vs ElementsMatchT (generic). func BenchmarkElementsMatch(b *testing.B) { // Small slices (10 elements) smallA := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} smallB := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} benchmarkComparison(b, "small_10", func(t *mockT) { ElementsMatch(t, smallA, smallB) }, func(t *mockT) { ElementsMatchT(t, smallA, smallB) }, ) // Medium slices (100 elements) mediumA := make([]int, 100) mediumB := make([]int, 100) for i := range mediumA { mediumA[i] = i mediumB[99-i] = i } benchmarkComparison(b, "medium_100", func(t *mockT) { ElementsMatch(t, mediumA, mediumB) }, func(t *mockT) { ElementsMatchT(t, mediumA, mediumB) }, ) // Large slices (1000 elements) largeA := make([]int, 1000) largeB := make([]int, 1000) for i := range largeA { largeA[i] = i largeB[999-i] = i } benchmarkComparison(b, "large_1000", func(t *mockT) { ElementsMatch(t, largeA, largeB) }, func(t *mockT) { ElementsMatchT(t, largeA, largeB) }, ) // String slices stringsA := []string{"apple", "banana", "cherry", "date", "elderberry"} stringsB := []string{"elderberry", "date", "cherry", "banana", "apple"} benchmarkComparison(b, "strings_5", func(t *mockT) { ElementsMatch(t, stringsA, stringsB) }, func(t *mockT) { ElementsMatchT(t, stringsA, stringsB) }, ) } // BenchmarkNotElementsMatch compares NotElementsMatch (reflect) vs NotElementsMatchT (generic). func BenchmarkNotElementsMatch(b *testing.B) { sliceA := []int{1, 2, 3, 4, 5} sliceB := []int{1, 2, 3, 4, 6} benchmarkComparison(b, "", func(t *mockT) { NotElementsMatch(t, sliceA, sliceB) }, func(t *mockT) { NotElementsMatchT(t, sliceA, sliceB) }, ) } // BenchmarkPositive compares Positive (reflect) vs PositiveT (generic). func BenchmarkPositive(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { Positive(t, 42) }, func(t *mockT) { PositiveT(t, 42) }, ) benchmarkComparison(b, "float64", func(t *mockT) { Positive(t, 42.5) }, func(t *mockT) { PositiveT(t, 42.5) }, ) } // BenchmarkNegative compares Negative (reflect) vs NegativeT (generic). func BenchmarkNegative(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { Negative(t, -42) }, func(t *mockT) { NegativeT(t, -42) }, ) } // BenchmarkEqual compares Equal (reflect) vs EqualT (generic). func BenchmarkEqual(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { Equal(t, 42, 42) }, func(t *mockT) { EqualT(t, 42, 42) }, ) benchmarkComparison(b, "string", func(t *mockT) { Equal(t, "hello", "hello") }, func(t *mockT) { EqualT(t, "hello", "hello") }, ) benchmarkComparison(b, "float64", func(t *mockT) { Equal(t, 3.14, 3.14) }, func(t *mockT) { EqualT(t, 3.14, 3.14) }, ) } // BenchmarkNotEqual compares NotEqual (reflect) vs NotEqualT (generic). func BenchmarkNotEqual(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { NotEqual(t, 42, 43) }, func(t *mockT) { NotEqualT(t, 42, 43) }, ) } // BenchmarkSame compares Same (reflect) vs SameT (generic). func BenchmarkSame(b *testing.B) { v := 42 p := &v benchmarkComparison(b, "", func(t *mockT) { Same(t, p, p) }, func(t *mockT) { SameT(t, p, p) }, ) } // BenchmarkNotSame compares NotSame (reflect) vs NotSameT (generic). func BenchmarkNotSame(b *testing.B) { v1, v2 := 42, 42 p1, p2 := &v1, &v2 benchmarkComparison(b, "", func(t *mockT) { NotSame(t, p1, p2) }, func(t *mockT) { NotSameT(t, p1, p2) }, ) } // BenchmarkGreaterOrEqual compares GreaterOrEqual (reflect) vs GreaterOrEqualT (generic). func BenchmarkGreaterOrEqual(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { GreaterOrEqual(t, 100, 50) }, func(t *mockT) { GreaterOrEqualT(t, 100, 50) }, ) } // BenchmarkLessOrEqual compares LessOrEqual (reflect) vs LessOrEqualT (generic). func BenchmarkLessOrEqual(b *testing.B) { benchmarkComparison(b, "int", func(t *mockT) { LessOrEqual(t, 50, 100) }, func(t *mockT) { LessOrEqualT(t, 50, 100) }, ) } // BenchmarkIsIncreasing compares IsIncreasing (reflect) vs IsIncreasingT (generic). func BenchmarkIsIncreasing(b *testing.B) { slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} benchmarkComparison(b, "", func(t *mockT) { IsIncreasing(t, slice) }, func(t *mockT) { IsIncreasingT(t, slice) }, ) } // BenchmarkIsNonIncreasing compares IsNonIncreasing (reflect) vs IsNonIncreasingT (generic). func BenchmarkIsNonIncreasing(b *testing.B) { slice := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} benchmarkComparison(b, "", func(t *mockT) { IsNonIncreasing(t, slice) }, func(t *mockT) { IsNonIncreasingT(t, slice) }, ) } // BenchmarkIsDecreasing compares IsDecreasing (reflect) vs IsDecreasingT (generic). func BenchmarkIsDecreasing(b *testing.B) { slice := []int{10, 9, 8, 7, 6, 5, 4, 3, 2, 1} benchmarkComparison(b, "", func(t *mockT) { IsDecreasing(t, slice) }, func(t *mockT) { IsDecreasingT(t, slice) }, ) } // BenchmarkIsNonDecreasing compares IsNonDecreasing (reflect) vs IsNonDecreasingT (generic). func BenchmarkIsNonDecreasing(b *testing.B) { slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} benchmarkComparison(b, "", func(t *mockT) { IsNonDecreasing(t, slice) }, func(t *mockT) { IsNonDecreasingT(t, slice) }, ) } // BenchmarkContains compares Contains (reflect) vs various ContainsT generics. func BenchmarkContains(b *testing.B) { benchmarkComparison(b, "string", func(t *mockT) { Contains(t, "hello world", "world") }, func(t *mockT) { StringContainsT(t, "hello world", "world") }, ) slice := []int{1, 2, 3, 4, 5} benchmarkComparison(b, "slice", func(t *mockT) { Contains(t, slice, 3) }, func(t *mockT) { SliceContainsT(t, slice, 3) }, ) m := map[string]int{"a": 1, "b": 2, "c": 3} benchmarkComparison(b, "map", func(t *mockT) { Contains(t, m, "b") }, func(t *mockT) { MapContainsT(t, m, "b") }, ) } // BenchmarkNotContains compares NotContains (reflect) vs various NotContainsT generics. func BenchmarkNotContains(b *testing.B) { benchmarkComparison(b, "string", func(t *mockT) { NotContains(t, "hello world", "xyz") }, func(t *mockT) { StringNotContainsT(t, "hello world", "xyz") }, ) slice := []int{1, 2, 3, 4, 5} benchmarkComparison(b, "slice", func(t *mockT) { NotContains(t, slice, 99) }, func(t *mockT) { SliceNotContainsT(t, slice, 99) }, ) } // BenchmarkSubset compares Subset (reflect) vs SubsetT (generic). func BenchmarkSubset(b *testing.B) { list := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} subset := []int{2, 4, 6} benchmarkComparison(b, "", func(t *mockT) { Subset(t, list, subset) }, func(t *mockT) { SliceSubsetT(t, list, subset) }, ) } // BenchmarkNotSubset compares NotSubset (reflect) vs NotSubsetT (generic). func BenchmarkNotSubset(b *testing.B) { list := []int{1, 2, 3, 4, 5} subset := []int{1, 2, 99} benchmarkComparison(b, "", func(t *mockT) { NotSubset(t, list, subset) }, func(t *mockT) { SliceNotSubsetT(t, list, subset) }, ) } // BenchmarkInDelta compares InDelta (reflect) vs InDeltaT (generic). func BenchmarkInDelta(b *testing.B) { benchmarkComparison(b, "float64", func(t *mockT) { InDelta(t, 3.14, 3.15, 0.02) }, func(t *mockT) { InDeltaT(t, 3.14, 3.15, 0.02) }, ) benchmarkComparison(b, "int", func(t *mockT) { InDelta(t, 100, 102, 5) }, func(t *mockT) { InDeltaT(t, 100, 102, 5) }, ) } // BenchmarkInEpsilon compares InEpsilon (reflect) vs InEpsilonT (generic). func BenchmarkInEpsilon(b *testing.B) { benchmarkComparison(b, "float64", func(t *mockT) { InEpsilon(t, 100.0, 101.0, 0.02) }, func(t *mockT) { InEpsilonT(t, 100.0, 101.0, 0.02) }, ) } // BenchmarkTrue compares True (reflect) vs TrueT (generic). func BenchmarkTrue(b *testing.B) { benchmarkComparison(b, "", func(t *mockT) { True(t, true) }, func(t *mockT) { TrueT(t, true) }, ) } // BenchmarkFalse compares False (reflect) vs FalseT (generic). func BenchmarkFalse(b *testing.B) { benchmarkComparison(b, "", func(t *mockT) { False(t, false) }, func(t *mockT) { FalseT(t, false) }, ) } // BenchmarkRegexp compares Regexp (reflect) vs RegexpT (generic). func BenchmarkRegexp(b *testing.B) { benchmarkComparison(b, "string", func(t *mockT) { Regexp(t, `^\d{3}-\d{4}$`, "123-4567") }, func(t *mockT) { RegexpT(t, `^\d{3}-\d{4}$`, "123-4567") }, ) } // BenchmarkNotRegexp compares NotRegexp (reflect) vs NotRegexpT (generic). func BenchmarkNotRegexp(b *testing.B) { benchmarkComparison(b, "string", func(t *mockT) { NotRegexp(t, `^\d{3}-\d{4}$`, "hello") }, func(t *mockT) { NotRegexpT(t, `^\d{3}-\d{4}$`, "hello") }, ) } // BenchmarkSorted benchmarks SortedT (generic-only, no reflection version). func BenchmarkSorted(b *testing.B) { slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} benchmarkGenericOnly(b, func(t *mockT) { SortedT(t, slice) }) } // BenchmarkNotSorted benchmarks NotSortedT (generic-only, no reflection version). func BenchmarkNotSorted(b *testing.B) { slice := []int{1, 3, 2, 4, 5} benchmarkGenericOnly(b, func(t *mockT) { NotSortedT(t, slice) }) } // BenchmarkSeqContains compares Contains (reflect) vs SeqContainsT (generic) for iterators. func BenchmarkSeqContains(b *testing.B) { slice := []int{1, 2, 3, 4, 5, 6, 7, 8, 9, 10} benchmarkComparison(b, "", func(t *mockT) { Contains(t, slices.Values(slice), 5) }, func(t *mockT) { SeqContainsT(t, slices.Values(slice), 5) }, ) } // BenchmarkSeqNotContains compares NotContains (reflect) vs SeqNotContainsT (generic) for iterators. func BenchmarkSeqNotContains(b *testing.B) { slice := []int{1, 2, 3, 4, 5} benchmarkComparison(b, "", func(t *mockT) { NotContains(t, slices.Values(slice), 99) }, func(t *mockT) { SeqNotContainsT(t, slices.Values(slice), 99) }, ) } // BenchmarkJSONEq compares JSONEq (reflect) vs JSONEqT (generic). func BenchmarkJSONEq(b *testing.B) { expected := `{"name":"John","age":30}` actual := `{"age":30,"name":"John"}` benchmarkComparison(b, "", func(t *mockT) { JSONEq(t, expected, actual) }, func(t *mockT) { JSONEqT(t, expected, actual) }, ) } // BenchmarkIsOfType compares IsType (reflect) vs IsOfTypeT (generic). func BenchmarkIsOfType(b *testing.B) { var expected int actual := 42 benchmarkComparison(b, "", func(t *mockT) { IsType(t, expected, actual) }, func(t *mockT) { IsOfTypeT[int](t, actual) }, ) } // BenchmarkIsNotOfType compares IsNotType (reflect) vs IsNotOfTypeT (generic). func BenchmarkIsNotOfType(b *testing.B) { var expected int actual := "string" benchmarkComparison(b, "", func(t *mockT) { IsNotType(t, expected, actual) }, func(t *mockT) { IsNotOfTypeT[int](t, actual) }, ) } go-openapi-testify-c10ca71/internal/assertions/boolean.go000066400000000000000000000035001520301377500236130ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions // True asserts that the specified value is true. // // # Usage // // assertions.True(t, myBool) // // # Examples // // success: 1 == 1 // failure: 1 == 0 func True(t T, value bool, msgAndArgs ...any) bool { // Domain: boolean // Opposite: False if !value { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Should be true", msgAndArgs...) } return true } // TrueT asserts that the specified value is true. // // The type constraint [Boolean] accepts any type which underlying type is bool. // // # Usage // // type B bool // var b B = true // // assertions.True(t, b) // // # Examples // // success: 1 == 1 // failure: 1 == 0 func TrueT[B Boolean](t T, value B, msgAndArgs ...any) bool { // Domain: boolean // Opposite: FalseT if !bool(value) { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Should be true", msgAndArgs...) } return true } // False asserts that the specified value is false. // // # Usage // // assertions.False(t, myBool) // // # Examples // // success: 1 == 0 // failure: 1 == 1 func False(t T, value bool, msgAndArgs ...any) bool { // Domain: boolean if value { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Should be false", msgAndArgs...) } return true } // FalseT asserts that the specified value is false. // // The type constraint [Boolean] accepts any type which underlying type is bool. // // # Usage // // type B bool // var b B = true // // assertions.FalseT(t, b) // // # Examples // // success: 1 == 0 // failure: 1 == 1 func FalseT[B Boolean](t T, value B, msgAndArgs ...any) bool { // Domain: boolean if bool(value) { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Should be false", msgAndArgs...) } return true } go-openapi-testify-c10ca71/internal/assertions/boolean_test.go000066400000000000000000000035771520301377500246700ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "slices" "testing" ) func TestBooleanTrue(t *testing.T) { t.Parallel() mock := new(mockT) if !True(mock, true) { t.Error("True should return true") } if True(mock, false) { t.Error("True should return false") } } func TestBooleanFalse(t *testing.T) { t.Parallel() mock := new(mockT) if !False(mock, false) { t.Error("False should return true") } if False(mock, true) { t.Error("False should return false") } } func TestBooleanTrueTFalseT(t *testing.T) { t.Parallel() type X bool var truthy X = true var falsy X = false t.Run("with TrueT on redeclared bool type", func(t *testing.T) { t.Parallel() mock := new(mockT) if !TrueT(mock, truthy) { t.Error("TrueT should return true") } if TrueT(mock, falsy) { t.Error("TrueT should return false") } }) t.Run("with FalseT on redeclared bool type", func(t *testing.T) { t.Parallel() mock := new(mockT) if FalseT(mock, truthy) { t.Error("TrueT should return true") } if !FalseT(mock, falsy) { t.Error("FalseT should return false") } }) } func TestBooleanErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, booleanFailCases()) } func booleanFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "True/false-value", assertion: func(t T) bool { return True(t, false) }, wantError: "Should be true", }, { name: "False/true-value", assertion: func(t T) bool { return False(t, true) }, wantError: "Should be false", }, { name: "TrueT/false-value", assertion: func(t T) bool { return TrueT(t, false) }, wantError: "Should be true", }, { name: "FalseT/true-value", assertion: func(t T) bool { return FalseT(t, true) }, wantError: "Should be false", }, }) } go-openapi-testify-c10ca71/internal/assertions/collection.go000066400000000000000000000661761520301377500243510ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "bytes" "fmt" "iter" "maps" "reflect" "slices" "strings" ) // Len asserts that the specified object has specific length. // // Len also fails if the object has a type that len() does not accept. // // The asserted object can be a string, a slice, a map, an array, pointer to array or a channel. // // # Usage // // assertions.Len(t, mySlice, 3) // assertions.Len(t, myString, 4) // assertions.Len(t, myMap, 5) // // # Examples // // success: []string{"A","B"}, 2 // failure: []string{"A","B"}, 1 func Len(t T, object any, length int, msgAndArgs ...any) bool { // Domain: collection // // NOTE: (proposal for enhancement) this does not currently support iterators, or collection objects that have a Len() method. if h, ok := t.(H); ok { h.Helper() } l, ok := getLen(object) if !ok { return Fail(t, fmt.Sprintf("%q could not be applied builtin len()", truncatingFormat("%v", object)), msgAndArgs...) } if l != length { return Fail(t, fmt.Sprintf("%q should have %d item(s), but has %d", truncatingFormat("%v", object), length, l), msgAndArgs...) } return true } // Contains asserts that the specified string, list(array, slice...) or map contains the // specified substring or element. // // # Usage // // assertions.Contains(t, "Hello World", "World") // assertions.Contains(t, []string{"Hello", "World"}, "World") // assertions.Contains(t, map[string]string{"Hello": "World"}, "Hello") // // # Examples // // success: []string{"A","B"}, "A" // failure: []string{"A","B"}, "C" func Contains(t T, s, contains any, msgAndArgs ...any) bool { // Domain: collection // Opposite: NotContains if h, ok := t.(H); ok { h.Helper() } ok, found := containsElement(s, contains) if !ok { return Fail(t, truncatingFormat("%#v", s)+" could not be applied builtin len()", msgAndArgs...) } if !found { return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true } // StringContainsT asserts that a string contains the specified substring. // // Strings may be go strings or []byte according to the type constraint [Text]. // // # Usage // // assertions.StringContainsT(t, "Hello World", "World") // // # Examples // // success: "AB", "A" // failure: "AB", "C" func StringContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { // Domain: collection // Opposite: StringNotContainsT if h, ok := t.(H); ok { h.Helper() } if !strings.Contains(string(str), string(substring)) { return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", str), substring), msgAndArgs...) } return true } // SliceContainsT asserts that the specified slice contains a comparable element. // // Go native comparable types are explained there: [comparable-types]. // // # Usage // // assertions.SliceContainsT(t, []{"Hello","World"}, "World") // // # Examples // // success: []string{"A","B"}, "A" // failure: []string{"A","B"}, "C" // // [comparable-types]: https://go.dev/blog/comparable func SliceContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { // Domain: collection // Opposite: SliceNotContainsT if h, ok := t.(H); ok { h.Helper() } if !slices.Contains(s, element) { return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", s), element), msgAndArgs...) } return true } // SeqContainsT asserts that the specified iterator contains a comparable element. // // The sequence may not be consumed entirely: the iteration stops as soon as the specified element is found. // // Go native comparable types are explained there: [comparable-types]. // // # Usage // // assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") // // # Examples // // success: slices.Values([]string{"A","B"}), "A" // failure: slices.Values([]string{"A","B"}), "C" // // [comparable-types]: https://go.dev/blog/comparable func SeqContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { // Domain: collection // Opposite: SeqNotContainsT if h, ok := t.(H); ok { h.Helper() } for iterated := range iter { if iterated == element { return true } } return Fail(t, fmt.Sprintf("sequence does not contain %#v", element), msgAndArgs...) } // MapContainsT asserts that the specified map contains a key. // // Go native comparable types are explained there: [comparable-types]. // // # Usage // // assertions.MapContainsT(t, map[string]string{"Hello": "x","World": "y"}, "World") // // # Examples // // success: map[string]string{"A": "B"}, "A" // failure: map[string]string{"A": "B"}, "C" // // [comparable-types]: https://go.dev/blog/comparable func MapContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { // Domain: collection // Opposite: MapNotContainsT if h, ok := t.(H); ok { h.Helper() } _, ok := m[key] if !ok { return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", m), key), msgAndArgs...) } return true } // NotContains asserts that the specified string, list(array, slice...) or map does NOT contain the // specified substring or element. // // # Usage // // assertions.NotContains(t, "Hello World", "Earth") // assertions.NotContains(t, ["Hello", "World"], "Earth") // assertions.NotContains(t, {"Hello": "World"}, "Earth") // // # Examples // // success: []string{"A","B"}, "C" // failure: []string{"A","B"}, "B" func NotContains(t T, s, contains any, msgAndArgs ...any) bool { // Domain: collection if h, ok := t.(H); ok { h.Helper() } ok, found := containsElement(s, contains) if !ok { return Fail(t, truncatingFormat("%#v", s)+" could not be applied builtin len()", msgAndArgs...) } if found { return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), contains), msgAndArgs...) } return true } // StringNotContainsT asserts that a string does not contain the specified substring. // // See [StringContainsT]. // // # Usage // // assertions.StringNotContainsT(t, "Hello World", "hi") // // # Examples // // success: "AB", "C" // failure: "AB", "A" func StringNotContainsT[ADoc, EDoc Text](t T, str ADoc, substring EDoc, msgAndArgs ...any) bool { // Domain: collection if h, ok := t.(H); ok { h.Helper() } if strings.Contains(string(str), string(substring)) { return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", str), substring), msgAndArgs...) } return true } // SliceNotContainsT asserts that the specified slice does not contain a comparable element. // // See [SliceContainsT]. // // # Usage // // assertions.SliceNotContainsT(t, []{"Hello","World"}, "hi") // // # Examples // // success: []string{"A","B"}, "C" // failure: []string{"A","B"}, "A" func SliceNotContainsT[Slice ~[]E, E comparable](t T, s Slice, element E, msgAndArgs ...any) bool { // Domain: collection if h, ok := t.(H); ok { h.Helper() } if slices.Contains(s, element) { return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", s), element), msgAndArgs...) } return true } // SeqNotContainsT asserts that the specified iterator does not contain a comparable element. // // See [SeqContainsT]. // // # Usage // // assertions.SeqContainsT(t, slices.Values([]{"Hello","World"}), "World") // // # Examples // // success: slices.Values([]string{"A","B"}), "C" // failure: slices.Values([]string{"A","B"}), "A" func SeqNotContainsT[E comparable](t T, iter iter.Seq[E], element E, msgAndArgs ...any) bool { // Domain: collection if h, ok := t.(H); ok { h.Helper() } for iterated := range iter { if iterated == element { return Fail(t, fmt.Sprintf("sequence does not contain %#v", element), msgAndArgs...) } } return true } // MapNotContainsT asserts that the specified map does not contain a key. // // # Usage // // assertions.MapNotContainsT(t, map[string]string{"Hello": "x","World": "y"}, "hi") // // # Examples // // success: map[string]string{"A": "B"}, "C" // failure: map[string]string{"A": "B"}, "A" func MapNotContainsT[Map ~map[K]V, K comparable, V any](t T, m Map, key K, msgAndArgs ...any) bool { // Domain: collection if h, ok := t.(H); ok { h.Helper() } _, ok := m[key] if ok { return Fail(t, fmt.Sprintf("%s should not contain %#v", truncatingFormat("%#v", m), key), msgAndArgs...) } return true } const unsupportedCollectionType = "%q has an unsupported type %s" // Subset asserts that the list (array, slice, or map) contains all elements // given in the subset (array, slice, or map). // // Map elements are key-value pairs unless compared with an array or slice where // only the map key is evaluated. // // nil values are considered as empty sets. // // # Usage // // assertions.Subset(t, []int{1, 2, 3}, []int{1, 2}) // assertions.Subset(t, []string{"x": 1, "y": 2}, []string{"x": 1}) // assertions.Subset(t, []int{1, 2, 3}, map[int]string{1: "one", 2: "two"}) // assertions.Subset(t, map[string]int{"x": 1, "y": 2}, []string{"x"}) // // # Examples // // success: []int{1, 2, 3}, []int{1, 2} // failure: []int{1, 2, 3}, []int{4, 5} func Subset(t T, list, subset any, msgAndArgs ...any) (ok bool) { // Domain: collection // Opposite: NotSubset if h, ok := t.(H); ok { h.Helper() } subsetType := reflect.TypeOf(subset) listType := reflect.TypeOf(list) if subsetType == nil { if listType == nil { // ∅ ⊂ ∅ return true // we consider nil to be equal to the nil set } listKind := listType.Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf(unsupportedCollectionType, list, listKind), msgAndArgs...) } return true } subsetKind := subsetType.Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf(unsupportedCollectionType, subset, subsetKind), msgAndArgs...) } if listType == nil { subsetList := reflect.ValueOf(subset) if subsetList.Len() == 0 { return true } return Fail(t, fmt.Sprintf("%q is not a subset of the empty set", subset), msgAndArgs...) } listKind := listType.Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf(unsupportedCollectionType, list, listKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { subsetMap := reflect.ValueOf(subset) actualMap := reflect.ValueOf(list) return isSubsetMap(t, list, subset, subsetMap, actualMap, msgAndArgs...) } subsetList := reflect.ValueOf(subset) if subsetKind == reflect.Map { keys := make([]any, subsetList.Len()) for idx, key := range subsetList.MapKeys() { keys[idx] = key.Interface() } subsetList = reflect.ValueOf(keys) } return isSubsetList(t, list, subsetList, msgAndArgs...) } // SliceSubsetT asserts that a slice of comparable elements contains all the elements given in the subset. // // # Usage // // assertions.SliceSubsetT(t, []int{1, 2, 3}, []int{1, 2}) // // # Examples // // success: []int{1, 2, 3}, []int{1, 2} // failure: []int{1, 2, 3}, []int{4, 5} func SliceSubsetT[Slice ~[]E, E comparable](t T, list, subset Slice, msgAndArgs ...any) (ok bool) { // Domain: collection // Opposite: SliceNotSubsetT if h, ok := t.(H); ok { h.Helper() } for _, element := range subset { if !slices.Contains(list, element) { return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) } } return true } // NotSubset asserts that the list (array, slice, or map) does NOT contain all // elements given in the subset (array, slice, or map). // Map elements are key-value pairs unless compared with an array or slice where // only the map key is evaluated. // // # Usage // // assertions.NotSubset(t, [1, 3, 4], [1, 2]) // assertions.NotSubset(t, {"x": 1, "y": 2}, {"z": 3}) // assertions.NotSubset(t, [1, 3, 4], {1: "one", 2: "two"}) // assertions.NotSubset(t, {"x": 1, "y": 2}, ["z"]) // // # Examples // // success: []int{1, 2, 3}, []int{4, 5} // failure: []int{1, 2, 3}, []int{1, 2} func NotSubset(t T, list, subset any, msgAndArgs ...any) (ok bool) { // Domain: collection if h, ok := t.(H); ok { h.Helper() } const emptySetMessage = "nil is the empty set which is a subset of every set" subsetType := reflect.TypeOf(subset) listType := reflect.TypeOf(list) if subsetType == nil { return Fail(t, emptySetMessage, msgAndArgs...) } if listType == nil { subsetKind := subsetType.Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf(unsupportedCollectionType, subset, subsetKind), msgAndArgs...) } subsetList := reflect.ValueOf(subset) if subsetList.Len() != 0 { return true } return Fail(t, emptySetMessage, msgAndArgs...) } listKind := listType.Kind() if listKind != reflect.Array && listKind != reflect.Slice && listKind != reflect.Map { return Fail(t, fmt.Sprintf(unsupportedCollectionType, list, listKind), msgAndArgs...) } subsetKind := subsetType.Kind() if subsetKind != reflect.Array && subsetKind != reflect.Slice && subsetKind != reflect.Map { return Fail(t, fmt.Sprintf(unsupportedCollectionType, subset, subsetKind), msgAndArgs...) } if subsetKind == reflect.Map && listKind == reflect.Map { subsetMap := reflect.ValueOf(subset) actualMap := reflect.ValueOf(list) return isNotSubsetMap(t, list, subset, subsetMap, actualMap, msgAndArgs...) } subsetList := reflect.ValueOf(subset) if subsetKind == reflect.Map { keys := make([]any, subsetList.Len()) for idx, key := range subsetList.MapKeys() { keys[idx] = key.Interface() } subsetList = reflect.ValueOf(keys) } return isNotSubsetList(t, list, subset, subsetList, msgAndArgs...) } // SliceNotSubsetT asserts that a slice of comparable elements does not contain all the elements given in the subset. // // # Usage // // assertions.SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 4}) // // # Examples // // success: []int{1, 2, 3}, []int{4, 5} // failure: []int{1, 2, 3}, []int{1, 2} func SliceNotSubsetT[Slice ~[]E, E comparable](t T, list, subset Slice, msgAndArgs ...any) (ok bool) { // Domain: collection if h, ok := t.(H); ok { h.Helper() } for _, element := range subset { if !slices.Contains(list, element) { return true } } return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } // ElementsMatch asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // # Usage // // assertions.ElementsMatch(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // // success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} // failure: []int{1, 2, 3}, []int{1, 2, 4} func ElementsMatch(t T, listA, listB any, msgAndArgs ...any) (ok bool) { // Domain: collection // Opposite: NotElementsMatch if h, ok := t.(H); ok { h.Helper() } if isEmpty(listA) && isEmpty(listB) { return true } if !isList(t, listA, msgAndArgs...) || !isList(t, listB, msgAndArgs...) { return false } extraA, extraB := diffLists(listA, listB) if len(extraA) == 0 && len(extraB) == 0 { return true } return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) } // NotElementsMatch asserts that the specified listA(array, slice...) is NOT equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should not match. // This is an inverse of [ElementsMatch]. // // # Usage // // assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false // assertions.NotElementsMatch(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true // assertions.NotElementsMatch(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true // // # Examples // // success: []int{1, 2, 3}, []int{1, 2, 4} // failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} func NotElementsMatch(t T, listA, listB any, msgAndArgs ...any) (ok bool) { // Domain: collection if h, ok := t.(H); ok { h.Helper() } if isEmpty(listA) && isEmpty(listB) { return Fail(t, "listA and listB contain the same elements", msgAndArgs) } if !isList(t, listA, msgAndArgs...) { return Fail(t, "listA is not a list type", msgAndArgs...) } if !isList(t, listB, msgAndArgs...) { return Fail(t, "listB is not a list type", msgAndArgs...) } extraA, extraB := diffLists(listA, listB) if len(extraA) == 0 && len(extraB) == 0 { return Fail(t, "listA and listB contain the same elements", msgAndArgs) } return true } // ElementsMatchT asserts that the specified listA(array, slice...) is equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should match. // // # Usage // // assertions.ElementsMatchT(t, []int{1, 3, 2, 3}, []int{1, 3, 3, 2}) // // # Examples // // success: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} // failure: []int{1, 2, 3}, []int{1, 2, 4} func ElementsMatchT[E comparable](t T, listA, listB []E, msgAndArgs ...any) bool { // Domain: collection // Opposite: NotElementsMatchT if h, ok := t.(H); ok { h.Helper() } lA := len(listA) lB := len(listB) if lA == 0 && lB == 0 { return true } extraA, extraB := diffListsT(listA, listB) if len(extraA) == 0 && len(extraB) == 0 { return true } return Fail(t, formatListDiff(listA, listB, extraA, extraB), msgAndArgs...) } // NotElementsMatchT asserts that the specified listA(array, slice...) is NOT equal to specified // listB(array, slice...) ignoring the order of the elements. If there are duplicate elements, // the number of appearances of each of them in both lists should not match. // This is an inverse of [ElementsMatch]. // // # Usage // // assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 1, 2, 3}) -> false // assertions.NotElementsMatchT(t, []int{1, 1, 2, 3}, []int{1, 2, 3}) -> true // assertions.NotElementsMatchT(t, []int{1, 2, 3}, []int{1, 2, 4}) -> true // // # Examples // // success: []int{1, 2, 3}, []int{1, 2, 4} // failure: []int{1, 3, 2, 3}, []int{1, 3, 3, 2} func NotElementsMatchT[E comparable](t T, listA, listB []E, msgAndArgs ...any) (ok bool) { // Domain: collection if h, ok := t.(H); ok { h.Helper() } lA := len(listA) lB := len(listB) if lA == 0 && lB == 0 { return Fail(t, "listA and listB contain the same elements", msgAndArgs) } extraA, extraB := diffListsT(listA, listB) if len(extraA) == 0 && len(extraB) == 0 { return Fail(t, "listA and listB contain the same elements", msgAndArgs) } return true } // SliceEqualT asserts that 2 slices of comparable elements are equal, // that is have the same length and contain the same elements in the same order. // // See also [slices.Equal]. // // # Usage // // assertions.SliceEqualT(t, []string{"Hello","World"}, []string{"Hello","World"}) // // # Examples // // success: []string{"Hello","World"}, []string{"Hello","World"} // failure: []string{"Hello","World"}, []string{"Hello"} // // [comparable-types]: https://go.dev/blog/comparable func SliceEqualT[E comparable](t T, listA, listB []E, msgAndArgs ...any) (ok bool) { // Domain: collection // Opposite: SliceNotEqualT ok = slices.Equal(listA, listB) if !ok { return Fail(t, "listA and listB are not equal", msgAndArgs) } return true } // SliceNotEqualT asserts that 2 slices of comparable elements are not equal. // // See also [SliceEqualT]. // // # Usage // // assertions.SliceNotEqualT(t, []string{"Hello","World"}, []string{"Hello"}) // // # Examples // // success: []string{"Hello","World"}, []string{"Hello"} // failure: []string{"Hello","World"}, []string{"Hello","World"} // // [comparable-types]: https://go.dev/blog/comparable func SliceNotEqualT[E comparable](t T, listA, listB []E, msgAndArgs ...any) (ok bool) { // Domain: collection ok = slices.Equal(listA, listB) if ok { return Fail(t, "listA and listB are equal", msgAndArgs) } return true } // MapEqualT asserts that 2 maps of comparable elements are equal, // that is have the same length and contain the keys with the same elements. // // See also [maps.Equal]. // // # Usage // // assertions.MapEqualT(t, map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"}) // // # Examples // // success: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} // failure: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} // // [comparable-types]: https://go.dev/blog/comparable func MapEqualT[K, V comparable](t T, listA, listB map[K]V, msgAndArgs ...any) (ok bool) { // Domain: collection // Opposite: MapNotEqualT ok = maps.Equal(listA, listB) if !ok { return Fail(t, "listA and listB are not equal", msgAndArgs) } return true } // MapNotEqualT asserts that 2 maps of comparable elements are not equal. // // See also [MapEqualT]. // // # Usage // // assertions.MapNotEqualT(t, map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"}) // // # Examples // // success: map[string]string{"2": "Hello", "1": "World"}, map[string]string{"1": "Hello", "2": "World"} // failure: map[string]string{"1": "Hello", "2": "World"}, map[string]string{"1": "Hello", "2": "World"} // // [comparable-types]: https://go.dev/blog/comparable func MapNotEqualT[K, V comparable](t T, listA, listB map[K]V, msgAndArgs ...any) (ok bool) { // Domain: collection ok = maps.Equal(listA, listB) if ok { return Fail(t, "listA and listB are equal", msgAndArgs) } return true } func isSubsetMap(t T, list, subset any, subsetMap, actualMap reflect.Value, msgAndArgs ...any) bool { for _, k := range subsetMap.MapKeys() { ev := subsetMap.MapIndex(k) av := actualMap.MapIndex(k) if !av.IsValid() { return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return Fail(t, fmt.Sprintf("%s does not contain %s", truncatingFormat("%#v", list), truncatingFormat("%#v", subset)), msgAndArgs...) } } return true } func isNotSubsetMap(t T, list, subset any, subsetMap, actualMap reflect.Value, msgAndArgs ...any) bool { for _, k := range subsetMap.MapKeys() { ev := subsetMap.MapIndex(k) av := actualMap.MapIndex(k) if !av.IsValid() { return true } if !ObjectsAreEqual(ev.Interface(), av.Interface()) { return true } } return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } func isSubsetList(t T, list any, subsetList reflect.Value, msgAndArgs ...any) bool { for i := range subsetList.Len() { element := subsetList.Index(i).Interface() _, found := containsElement(list, element) // containsElement will work for this type: no need to check the ok bool if !found { return Fail(t, fmt.Sprintf("%s does not contain %#v", truncatingFormat("%#v", list), element), msgAndArgs...) } } return true } func isNotSubsetList(t T, list, subset any, subsetList reflect.Value, msgAndArgs ...any) bool { for i := range subsetList.Len() { element := subsetList.Index(i).Interface() _, found := containsElement(list, element) if !found { return true } } return Fail(t, fmt.Sprintf("%s is a subset of %s", truncatingFormat("%#v", subset), truncatingFormat("%#v", list)), msgAndArgs...) } // containsElement tries to loop over the list check if the list includes the element. // // return (false, false) if impossible. // return (true, false) if element was not found. // return (true, true) if element was found. func containsElement(list any, element any) (ok, found bool) { listValue := reflect.ValueOf(list) listType := reflect.TypeOf(list) if listType == nil { return false, false } listKind := listType.Kind() defer func() { if e := recover(); e != nil { ok = false found = false } }() if listKind == reflect.String { elementValue := reflect.ValueOf(element) return true, strings.Contains(listValue.String(), elementValue.String()) } if listKind == reflect.Map { mapKeys := listValue.MapKeys() for i := range mapKeys { if ObjectsAreEqual(mapKeys[i].Interface(), element) { return true, true } } return true, false } for i := range listValue.Len() { if ObjectsAreEqual(listValue.Index(i).Interface(), element) { return true, true } } return true, false } // isList checks that the provided value is array or slice. func isList(t T, list any, msgAndArgs ...any) (ok bool) { kind := reflect.TypeOf(list).Kind() if kind != reflect.Array && kind != reflect.Slice { return Fail(t, fmt.Sprintf("%q has an unsupported type %s, expecting array or slice", list, kind), msgAndArgs...) } return true } // diffLists diffs two arrays/slices and returns slices of elements that are only in A and only in B. // // If some element is present multiple times, each instance is counted separately (e.g. if something is 2x in A and // 5x in B, it will be 0x in extraA and 3x in extraB). The order of items in both lists is ignored. func diffLists(listA, listB any) (extraA, extraB []any) { aValue := reflect.ValueOf(listA) bValue := reflect.ValueOf(listB) aLen := aValue.Len() bLen := bValue.Len() // Mark indexes in bValue that we already used visited := make([]bool, bLen) for i := range aLen { element := aValue.Index(i).Interface() found := false for j := range bLen { if visited[j] { continue } if ObjectsAreEqual(bValue.Index(j).Interface(), element) { visited[j] = true found = true break } } if !found { extraA = append(extraA, element) } } for j := range bLen { if visited[j] { continue } extraB = append(extraB, bValue.Index(j).Interface()) } return extraA, extraB } func diffListsT[E comparable](listA, listB []E) (extraA, extraB []any) { aLen := len(listA) bLen := len(listB) extraA = make([]any, 0, aLen) extraB = make([]any, 0, bLen) visited := make([]bool, bLen) for i := range aLen { element := listA[i] found := false for j := range bLen { if visited[j] { continue } if element == listB[j] { visited[j] = true found = true break } } if !found { extraA = append(extraA, element) } } for j := range bLen { if visited[j] { continue } extraB = append(extraB, listB[j]) } return extraA, extraB } func formatListDiff(listA, listB any, extraA, extraB []any) string { var msg bytes.Buffer msg.WriteString("elements differ") if len(extraA) > 0 { msg.WriteString("\n\nextra elements in list A:\n") msg.WriteString(dumper(extraA)) } if len(extraB) > 0 { msg.WriteString("\n\nextra elements in list B:\n") msg.WriteString(dumper(extraB)) } msg.WriteString("\n\nlistA:\n") msg.WriteString(dumper(listA)) msg.WriteString("\n\nlistB:\n") msg.WriteString(dumper(listB)) return msg.String() } // getLen tries to get the length of an object. // // It returns (0, false) if impossible. func getLen(x any) (length int, ok bool) { v := reflect.ValueOf(x) switch v.Kind() { case reflect.Array, reflect.Chan, reflect.Map, reflect.Slice, reflect.String: return v.Len(), true case reflect.Pointer: v = v.Elem() if v.Kind() != reflect.Array { return 0, false } return v.Len(), true default: return 0, false } } go-openapi-testify-c10ca71/internal/assertions/collection_impl_test.go000066400000000000000000000051401520301377500264110ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "slices" "testing" ) func TestCollectionUnexportedImplementationDetails(t *testing.T) { t.Parallel() t.Run("containsElement", testContainsElement()) t.Run("getLen", testGetLen()) } func testContainsElement() func(*testing.T) { return func(t *testing.T) { t.Parallel() list1 := []string{"Foo", "Bar"} list2 := []int{1, 2} simpleMap := map[any]any{"Foo": "Bar"} checkContains := func(list any, elem any, expectOK, expectFound bool) { t.Helper() ok, found := containsElement(list, elem) if ok != expectOK { t.Errorf("containsElement(%v, %v): expected ok=%v, got %v", list, elem, expectOK, ok) } if found != expectFound { t.Errorf("containsElement(%v, %v): expected found=%v, got %v", list, elem, expectFound, found) } } checkContains("Hello World", "World", true, true) checkContains(list1, "Foo", true, true) checkContains(list1, "Bar", true, true) checkContains(list2, 1, true, true) checkContains(list2, 2, true, true) checkContains(list1, "Foo!", true, false) checkContains(list2, 3, true, false) checkContains(list2, "1", true, false) checkContains(simpleMap, "Foo", true, true) checkContains(simpleMap, "Bar", true, false) checkContains(1433, "1", false, false) } } func testGetLen() func(*testing.T) { return func(t *testing.T) { t.Parallel() for v := range collectionImplGetLenFalseCases() { l, ok := getLen(v) if ok { t.Errorf("expected getLen to fail for %#v", v) } if l != 0 { t.Errorf("expected getLen to return 0 for %#v, got %d", v, l) } } for c := range collectionImplGetLenTrueCases() { l, ok := getLen(c.v) if !ok { t.Errorf("expected getLen to succeed for %#v", c.v) } if c.l != l { t.Errorf("expected length %d for %#v, got %d", c.l, c.v, l) } } } } type collectionImplGetLenCase = any func collectionImplGetLenFalseCases() iter.Seq[collectionImplGetLenCase] { return slices.Values([]collectionImplGetLenCase{ nil, 0, true, false, 'A', struct{}{}, }) } type collectionImplGetLenTrueCase struct { v any l int } func collectionImplGetLenTrueCases() iter.Seq[collectionImplGetLenTrueCase] { ch := make(chan int, 5) ch <- 1 ch <- 2 ch <- 3 return slices.Values([]collectionImplGetLenTrueCase{ {[]int{1, 2, 3}, 3}, {[...]int{1, 2, 3}, 3}, {"ABC", 3}, {map[int]int{1: 2, 2: 4, 3: 6}, 3}, {ch, 3}, {[]int{}, 0}, {map[int]int{}, 0}, {make(chan int), 0}, {[]int(nil), 0}, {map[int]int(nil), 0}, {(chan int)(nil), 0}, }) } go-openapi-testify-c10ca71/internal/assertions/collection_test.go000066400000000000000000001240561520301377500254000ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "fmt" "iter" "reflect" "slices" "strings" "testing" ) // TestCollectionLen tests the Len assertion. func TestCollectionLen(t *testing.T) { t.Parallel() for tc := range collectionLenCases() { t.Run(tc.name, testLen(tc)) } } // TestCollectionContains tests both Contains and NotContains with reflection-based // and generic variants using unified test cases. // // For slices, also tests SeqContains // and SeqNotContains since slices can be converted to iter.Seq via slices.Values. func TestCollectionContains(t *testing.T) { t.Parallel() for tc := range unifiedContainsCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() container, element := tc.makeValues() if !tc.reflectionOnly { // Test reflection-based variants t.Run("with Contains", testContainsAssertion(tc, containsKind, Contains, container, element)) t.Run("with NotContains", testContainsAssertion(tc, notContainsKind, NotContains, container, element)) // Test generic variants (type dispatch) t.Run("with generic Contains", testContainsAssertionT(tc, containsTKind, container, element)) t.Run("with generic NotContains", testContainsAssertionT(tc, notContainsTKind, container, element)) // For slices, also test Seq variants (slices can be converted to iter.Seq via slices.Values) if isSliceType(container) { t.Run("with generic SeqContains", testContainsAssertionT(tc, seqContainsTKind, container, element)) t.Run("with generic SeqNotContains", testContainsAssertionT(tc, seqNotContainsTKind, container, element)) } return } // Reflection-only cases t.Run("with Contains (reflection)", testContainsAssertion(tc, containsKind, Contains, container, element)) t.Run("with NotContains (reflection)", testContainsAssertion(tc, notContainsKind, NotContains, container, element)) }) } } // TestCollectionSubset tests both Subset and NotSubset with reflection-based // and generic variants using unified test cases. func TestCollectionSubset(t *testing.T) { t.Parallel() for tc := range unifiedSubsetCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() list, subset := tc.makeValues() if !tc.reflectionOnly { // Test reflection-based variants t.Run("with Subset", testSubsetAssertion(tc, subsetKind, Subset, list, subset)) t.Run("with NotSubset", testSubsetAssertion(tc, notSubsetKind, NotSubset, list, subset)) // Test generic variants (type dispatch) t.Run("with generic Subset", testSubsetAssertionT(tc, subsetTKind, list, subset)) t.Run("with generic NotSubset", testSubsetAssertionT(tc, notSubsetTKind, list, subset)) } else { // Reflection-only cases t.Run("with Subset (reflection)", testSubsetAssertion(tc, subsetKind, Subset, list, subset)) t.Run("with NotSubset (reflection)", testSubsetAssertion(tc, notSubsetKind, NotSubset, list, subset)) } }) } } // TestCollectionElementsMatch tests both ElementsMatch and NotElementsMatch // with reflection-based and generic variants using unified test cases. func TestCollectionElementsMatch(t *testing.T) { t.Parallel() for tc := range unifiedElementsMatchCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() actual, expected := tc.makeValues() if !tc.reflectionOnly { // Test reflection-based variants t.Run("with ElementsMatch", testElementsMatchAssertion(tc, elementsMatchKind, ElementsMatch, actual, expected)) t.Run("with NotElementsMatch", testElementsMatchAssertion(tc, notElementsMatchKind, NotElementsMatch, actual, expected)) // Test generic variants (type dispatch) t.Run("with generic ElementsMatch", testElementsMatchAssertionT(tc, elementsMatchTKind, actual, expected)) t.Run("with generic NotElementsMatch", testElementsMatchAssertionT(tc, notElementsMatchTKind, actual, expected)) } else { // Reflection-only cases t.Run("with ElementsMatch (reflection)", testElementsMatchAssertion(tc, elementsMatchKind, ElementsMatch, actual, expected)) t.Run("with NotElementsMatch (reflection)", testElementsMatchAssertion(tc, notElementsMatchKind, NotElementsMatch, actual, expected)) } }) } } // TestCollectionSliceEqual tests SliceEqualT and SliceNotEqualT. func TestCollectionSliceEqual(t *testing.T) { t.Parallel() for tc := range sliceEqualCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() listA, listB := tc.makeValues() t.Run("with SliceEqualT", testSliceEqualAssertion(tc, sliceEqualKind, listA, listB)) t.Run("with SliceNotEqualT", testSliceEqualAssertion(tc, sliceNotEqualKind, listA, listB)) }) } } // TestCollectionMapEqual tests MapEqualT and MapNotEqualT. func TestCollectionMapEqual(t *testing.T) { t.Parallel() for tc := range mapEqualCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() listA, listB := tc.makeValues() t.Run("with MapEqualT", testMapEqualAssertion(tc, mapEqualKind, listA, listB)) t.Run("with MapNotEqualT", testMapEqualAssertion(tc, mapNotEqualKind, listA, listB)) }) } } // TestCollectionErrorMessages tests error message formatting for collection assertions. func TestCollectionErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, collectionErrorMessageCases()) } // ============================================================================ // TestCollectionLen // ============================================================================ func testLen(tc collectionLenCase) func(*testing.T) { if !tc.valid { return func(t *testing.T) { t.Run("with invalid type", func(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() mock := new(mockT) res := Len(mock, tc.v, tc.len) if res { t.Errorf("Len should not work for type %T", tc.v) return } if tc.expectedMsg == "" { return // skip error message check } // check error message if !strings.Contains(mock.errorString(), tc.expectedMsg) { t.Errorf("expected error message to contain %q but got: %q", tc.expectedMsg, mock.errorString()) } }) }) } } return func(t *testing.T) { t.Run("with expected length", func(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() mock := new(mockT) res := Len(mock, tc.v, tc.len) if !res { t.Errorf("%#v should have %d items", tc.v, tc.len) } }) }) t.Run("with unexpected length", func(t *testing.T) { t.Run(tc.name, func(t *testing.T) { t.Parallel() mock := new(mockT) res := Len(mock, tc.v, tc.len+1) if res { t.Errorf("%#v should not have %d items", tc.v, tc.len+1) return } if tc.expectedMsg == "" { return // skip error message check } // check error message if !strings.Contains(mock.errorString(), tc.expectedMsg) { t.Errorf("expected error message to contain %q but got: %q", tc.expectedMsg, mock.errorString()) } }) }) } } type collectionLenCase struct { name string v any len int expectedMsg string // message when expecting len+1 items valid bool } func collectionLenCases() iter.Seq[collectionLenCase] { ch := make(chan int, 5) ch <- 1 ch <- 2 ch <- 3 longSlice := make([]int, 1_000_000) arr := [3]int{1, 2, 3} return slices.Values([]collectionLenCase{ {"slice/int", []int{1, 2, 3}, 3, `"[1 2 3]" should have 4 item(s), but has 3`, true}, {"array/int", [...]int{1, 2, 3}, 3, `"[1 2 3]" should have 4 item(s), but has 3`, true}, {"ptr-to-array/int", &arr, 3, `"&[1 2 3]" should have 4 item(s), but has 3`, true}, {"string", "ABC", 3, `"ABC" should have 4 item(s), but has 3`, true}, {"map/int", map[int]int{1: 2, 2: 4, 3: 6}, 3, `"map[1:2 2:4 3:6]" should have 4 item(s), but has 3`, true}, {"channel", ch, 3, "", true}, {"empty slice", []int{}, 0, `"[]" should have 1 item(s), but has 0`, true}, {"empty map", map[int]int{}, 0, `"map[]" should have 1 item(s), but has 0`, true}, {"empty channel", make(chan int), 0, "", true}, {"nil slice", []int(nil), 0, `"[]" should have 1 item(s), but has 0`, true}, {"nil map", map[int]int(nil), 0, `"map[]" should have 1 item(s), but has 0`, true}, {"nil chan", (chan int)(nil), 0, `"" should have 1 item(s), but has 0`, true}, // Unsupported types {"invalid type/nil", nil, 0, `"" could not be applied builtin len()`, false}, {"invalid type/int", 0, 0, "", false}, {"invalid type/bool", true, 0, "", false}, {"invalid type/rune", 'A', 0, "", false}, {"invalid type/struct", struct{}{}, 0, "", false}, {"invalid type/ptr-not-array", &longSlice, 1_000_000, `<... truncated>" could not be applied builtin len()`, false}, {"invalid type/ptr-anything", ptr(1), 0, `" could not be applied builtin len()`, false}, // Truncated message {"truncated message/long slice", longSlice, 1_000_000, `<... truncated>" should have 1000001 item(s), but has 1000000`, true}, }) } // ============================================================================ // TestCollectionContains // ============================================================================ // containsRelationship describes the relationship between a container and an element. type containsRelationship int const ( crContains containsRelationship = iota crNotContains crInvalidContainer // container type doesn't support Contains ) type containsAssertionKind int const ( containsKind containsAssertionKind = iota notContainsKind containsTKind // generic string/slice/map variants notContainsTKind // generic not-contains variants seqContainsTKind // generic seq contains seqNotContainsTKind // generic seq not-contains ) func testContainsAssertion(tc containsTestCase, kind containsAssertionKind, assertion func(T, any, any, ...any) bool, container, element any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := assertion(mock, container, element) shouldPass := expectedStatusForContainsAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } //nolint:gocognit,gocyclo,cyclop // type dispatch requires large switch statement func testContainsAssertionT(tc containsTestCase, kind containsAssertionKind, container, element any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) stop := func(expected string, actual any) { t.Fatalf("test case error: expected container=%s, actual=%T", expected, actual) } var result bool // Type switch based on container type switch cont := container.(type) { case string: elem, ok := element.(string) if !ok { t.Fatalf("test case error: string container requires string element, got %T", element) } result = testStringContainsGeneric(mock, kind, cont, elem) case []byte: elem, ok := element.([]byte) if !ok { t.Fatalf("test case error: []byte container requires []byte element, got %T", element) } result = testStringContainsGeneric(mock, kind, cont, elem) case []int: elem, ok := element.(int) if !ok { t.Fatalf("test case error: []int container requires int element, got %T", element) } if kind == seqContainsTKind || kind == seqNotContainsTKind { result = testSeqContainsGeneric(mock, kind, cont, elem) } else { result = testSliceContainsGeneric(mock, kind, cont, elem) } case []string: elem, ok := element.(string) if !ok { t.Fatalf("test case error: []string container requires string element, got %T", element) } if kind == seqContainsTKind || kind == seqNotContainsTKind { result = testSeqContainsGeneric(mock, kind, cont, elem) } else { result = testSliceContainsGeneric(mock, kind, cont, elem) } case []float64: elem, ok := element.(float64) if !ok { t.Fatalf("test case error: []float64 container requires float64 element, got %T", element) } if kind == seqContainsTKind || kind == seqNotContainsTKind { result = testSeqContainsGeneric(mock, kind, cont, elem) } else { result = testSliceContainsGeneric(mock, kind, cont, elem) } case []*containsStruct: elem, ok := element.(*containsStruct) if !ok { t.Fatalf("test case error: []*containsStruct container requires *containsStruct element, got %T", element) } if kind == seqContainsTKind || kind == seqNotContainsTKind { result = testSeqContainsGeneric(mock, kind, cont, elem) } else { result = testSliceContainsGeneric(mock, kind, cont, elem) } case map[string]int: elem, ok := element.(string) if !ok { t.Fatalf("test case error: map[string]int container requires string element, got %T", element) } result = testMapContainsGeneric(mock, kind, cont, elem) case map[int]string: elem, ok := element.(int) if !ok { t.Fatalf("test case error: map[int]string container requires int element, got %T", element) } result = testMapContainsGeneric(mock, kind, cont, elem) case map[any]any: result = testMapContainsGeneric(mock, kind, cont, element) default: stop(fmt.Sprintf("%T", container), container) } shouldPass := expectedStatusForContainsAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testStringContainsGeneric[S, E Text](mock T, kind containsAssertionKind, container S, element E) bool { switch kind { case containsTKind: return StringContainsT(mock, container, element) case notContainsTKind: return StringNotContainsT(mock, container, element) default: panic(fmt.Errorf("test case configuration error: invalid containsAssertionKind for string generic: %d", kind)) } } func testSliceContainsGeneric[Slice ~[]E, E comparable](mock T, kind containsAssertionKind, container Slice, element E) bool { switch kind { case containsTKind: return SliceContainsT(mock, container, element) case notContainsTKind: return SliceNotContainsT(mock, container, element) default: panic(fmt.Errorf("test case configuration error: invalid containsAssertionKind for slice generic: %d", kind)) } } func testMapContainsGeneric[Map ~map[K]V, K comparable, V any](mock T, kind containsAssertionKind, container Map, element K) bool { switch kind { case containsTKind: return MapContainsT(mock, container, element) case notContainsTKind: return MapNotContainsT(mock, container, element) default: panic(fmt.Errorf("test case configuration error: invalid containsAssertionKind for map generic: %d", kind)) } } func testSeqContainsGeneric[Slice ~[]E, E comparable](mock T, kind containsAssertionKind, container Slice, element E) bool { seq := slices.Values(container) switch kind { case seqContainsTKind: return SeqContainsT(mock, seq, element) case seqNotContainsTKind: return SeqNotContainsT(mock, seq, element) default: panic(fmt.Errorf("test case configuration error: invalid containsAssertionKind for seq generic: %d", kind)) } } func expectedStatusForContainsAssertion(kind containsAssertionKind, relationship containsRelationship) bool { positive := kind == containsKind || kind == containsTKind || kind == seqContainsTKind switch relationship { case crContains: return positive case crNotContains: return !positive case crInvalidContainer: return false default: panic(fmt.Errorf("test case configuration error: invalid containsRelationship: %d", relationship)) } } func isSliceType(v any) bool { return reflect.TypeOf(v).Kind() == reflect.Slice } type containsTestCase struct { name string makeValues func() (container, element any) relationship containsRelationship reflectionOnly bool } type containsStruct struct { Name, Value string } const ( testStringBar = "Bar" testStringFoo = "Foo" ) func unifiedContainsCases() iter.Seq[containsTestCase] { list := []string{testStringFoo, testStringBar} complexList := []*containsStruct{ {"b", "c"}, {"d", "e"}, {"g", "h"}, {"j", "k"}, } simpleMap := map[any]any{testStringFoo: testStringBar} var zeroMap map[any]any return slices.Values([]containsTestCase{ // String contains {"string/contains", func() (any, any) { return "Hello World", "Hello" }, crContains, false}, {"string/not-contains", func() (any, any) { return "Hello World", "Salut" }, crNotContains, false}, // Slice contains {"slice-string/contains", func() (any, any) { return list, testStringBar }, crContains, false}, {"slice-string/not-contains", func() (any, any) { return list, "Salut" }, crNotContains, false}, // Struct pointers use reflection-only since generic uses pointer equality, not deep equality {"slice-struct/contains", func() (any, any) { return complexList, &containsStruct{"g", "h"} }, crContains, true}, {"slice-struct/not-contains", func() (any, any) { return complexList, &containsStruct{"g", "e"} }, crNotContains, true}, // Map contains (key lookup) {"map/contains-key", func() (any, any) { return simpleMap, testStringFoo }, crContains, false}, {"map/not-contains-key", func() (any, any) { return simpleMap, testStringBar }, crNotContains, false}, {"map-zero/not-contains", func() (any, any) { return zeroMap, testStringBar }, crNotContains, false}, // Invalid container (reflection only) { "invalid/non-container-struct", func() (any, any) { type nonContainer struct{ Value string } return nonContainer{Value: "Hello"}, "Hello" }, crInvalidContainer, true, }, { "invalid/nil", func() (any, any) { return nil, "key" }, crInvalidContainer, true, }, }) } // ============================================================================ // TestCollectionSubset // ============================================================================ // subsetRelationship describes the relationship between a list and a subset. type subsetRelationship int const ( srSubset subsetRelationship = iota srNotSubset srInvalidType // types don't support subset comparison ) type subsetAssertionKind int const ( subsetKind subsetAssertionKind = iota notSubsetKind subsetTKind // generic slice variant notSubsetTKind // generic not-subset variant ) func testSubsetAssertion(tc subsetTestCase, kind subsetAssertionKind, assertion func(T, any, any, ...any) bool, list, subset any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := assertion(mock, list, subset) shouldPass := expectedStatusForSubsetAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testSubsetAssertionT(tc subsetTestCase, kind subsetAssertionKind, list, subset any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) var result bool // Type switch based on list type switch lst := list.(type) { case []int: sub, ok := subset.([]int) if !ok { t.Fatalf("test case error: []int list requires []int subset, got %T", subset) } result = testSliceSubsetGeneric(mock, kind, lst, sub) case []string: sub, ok := subset.([]string) if !ok { t.Fatalf("test case error: []string list requires []string subset, got %T", subset) } result = testSliceSubsetGeneric(mock, kind, lst, sub) case []float64: sub, ok := subset.([]float64) if !ok { t.Fatalf("test case error: []float64 list requires []float64 subset, got %T", subset) } result = testSliceSubsetGeneric(mock, kind, lst, sub) default: t.Fatalf("unsupported type for generic subset: %T", list) } shouldPass := expectedStatusForSubsetAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testSliceSubsetGeneric[Slice ~[]E, E comparable](mock T, kind subsetAssertionKind, list, subset Slice) bool { switch kind { case subsetTKind: return SliceSubsetT(mock, list, subset) case notSubsetTKind: return SliceNotSubsetT(mock, list, subset) default: panic(fmt.Errorf("test case configuration error: invalid subsetAssertionKind for generic: %d", kind)) } } func expectedStatusForSubsetAssertion(kind subsetAssertionKind, relationship subsetRelationship) bool { positive := kind == subsetKind || kind == subsetTKind switch relationship { case srSubset: return positive case srNotSubset: return !positive case srInvalidType: return false default: panic(fmt.Errorf("test case configuration error: invalid subsetRelationship: %d", relationship)) } } type subsetTestCase struct { name string makeValues func() (list, subset any) relationship subsetRelationship reflectionOnly bool } func unifiedSubsetCases() iter.Seq[subsetTestCase] { return slices.Values([]subsetTestCase{ // Subset cases {"int/nil-subset", func() (any, any) { return []int{1, 2, 3}, ([]int)(nil) }, srSubset, false}, {"int/empty-subset", func() (any, any) { return []int{1, 2, 3}, []int{} }, srSubset, false}, {"int/proper-subset", func() (any, any) { return []int{1, 2, 3}, []int{1, 2} }, srSubset, false}, {"int/equal-sets", func() (any, any) { return []int{1, 2, 3}, []int{1, 2, 3} }, srSubset, false}, {"string/subset", func() (any, any) { return []string{"hello", "world"}, []string{"hello"} }, srSubset, false}, {"float64/subset", func() (any, any) { return []float64{1.1, 2.2, 3.3}, []float64{2.2} }, srSubset, false}, { "map-string/subset", func() (any, any) { return map[string]string{"a": "x", "b": "y", "c": "z"}, map[string]string{"a": "x", "b": "y"} }, srSubset, true, }, { "slice-map-mixed/subset", func() (any, any) { return []string{"a", "b", "c"}, map[string]int{"a": 1, "c": 3} }, srSubset, true, }, // Not subset cases { "string/not-subset", func() (any, any) { return []string{"hello", "world"}, []string{"hello", "testify"} }, srNotSubset, false, }, {"int/not-subset", func() (any, any) { return []int{1, 2, 3}, []int{4, 5} }, srNotSubset, false}, {"int/partial-not-subset", func() (any, any) { return []int{1, 2, 3}, []int{1, 5} }, srNotSubset, false}, { "map-string/not-subset", func() (any, any) { return map[string]string{"a": "x", "b": "y", "c": "z"}, map[string]string{"a": "x", "b": "z"} }, srNotSubset, true, }, { "map-string/superset-not-subset", func() (any, any) { return map[string]string{"a": "x", "b": "y"}, map[string]string{"a": "x", "b": "y", "c": "z"} }, srNotSubset, true, }, { "slice-map-mixed/not-subset", func() (any, any) { return []string{"a", "b", "c"}, map[string]int{"c": 3, "d": 4} }, srNotSubset, true, }, { "subset/nil", func() (any, any) { return []string{"a", "b", "c"}, nil }, srSubset, true, }, { "subset/empty", func() (any, any) { return []string{"a", "b", "c"}, []string{} }, srSubset, true, }, { "not-subset/nil", func() (any, any) { return nil, []string{"a", "b", "c"} }, srNotSubset, true, }, { "not-subset/empty", func() (any, any) { return []string{}, []string{"a", "b", "c"} }, srNotSubset, true, }, { "subset/nil-nil", func() (any, any) { return nil, nil }, srSubset, true, }, { "subset/empty-nil", func() (any, any) { return []int{}, nil }, srSubset, true, }, { "subset/nil-empty", func() (any, any) { return nil, []int{} }, srSubset, true, }, { "subset/empty-empty", func() (any, any) { return []int{}, []int{} }, srSubset, true, }, { "invalid-type/[]int-invalid", func() (any, any) { return []int{}, 1 }, srInvalidType, true, }, { "invalid-type/invalid-[]int", func() (any, any) { return 1, []int{} }, srInvalidType, true, }, { "invalid-type/invalid-nil", func() (any, any) { return 1, nil }, srInvalidType, true, }, { "invalid-type/nil-invalid", func() (any, any) { return nil, 1 }, srInvalidType, true, }, }) } // ============================================================================ // TestCollectionElementsMatch // ============================================================================ // elementsMatchRelationship describes the relationship between two collections. type elementsMatchRelationship int const ( emMatch elementsMatchRelationship = iota emNotMatch emInvalidType // types don't support elements matching ) type elementsMatchAssertionKind int const ( elementsMatchKind elementsMatchAssertionKind = iota notElementsMatchKind elementsMatchTKind // generic variant notElementsMatchTKind // generic not-match variant ) func testElementsMatchAssertion(tc elementsMatchTestCase, kind elementsMatchAssertionKind, assertion func(T, any, any, ...any) bool, actual, expected any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := assertion(mock, actual, expected) shouldPass := expectedStatusForElementsMatchAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testElementsMatchAssertionT(tc elementsMatchTestCase, kind elementsMatchAssertionKind, actual, expected any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) var result bool // Type switch based on actual type switch act := actual.(type) { case []int: exp, ok := expected.([]int) if !ok { t.Fatalf("test case error: []int actual requires []int expected, got %T", expected) } result = testElementsMatchGeneric(mock, kind, act, exp) case [2]int: exp, ok := expected.([2]int) if !ok { t.Fatalf("test case error: [2]int actual requires [2]int expected, got %T", expected) } result = testElementsMatchGeneric(mock, kind, act[:], exp[:]) case []string: exp, ok := expected.([]string) if !ok { t.Fatalf("test case error: []string actual requires []string expected, got %T", expected) } result = testElementsMatchGeneric(mock, kind, act, exp) case [3]string: exp, ok := expected.([3]string) if !ok { t.Fatalf("test case error: [3]string actual requires [3]string expected, got %T", expected) } result = testElementsMatchGeneric(mock, kind, act[:], exp[:]) case nil: if expected != nil { t.Fatalf("test case error: nil actual requires nil expected, got %T", expected) } result = testElementsMatchGeneric(mock, kind, ([]int)(nil), ([]int)(nil)) default: t.Fatalf("unsupported type for generic ElementsMatch: %T", actual) } shouldPass := expectedStatusForElementsMatchAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testElementsMatchGeneric[E comparable](mock T, kind elementsMatchAssertionKind, actual, expected []E) bool { switch kind { case elementsMatchTKind: return ElementsMatchT(mock, actual, expected) case notElementsMatchTKind: return NotElementsMatchT(mock, actual, expected) default: panic(fmt.Errorf("test case configuration error: invalid elementsMatchAssertionKind for generic: %d", kind)) } } func expectedStatusForElementsMatchAssertion(kind elementsMatchAssertionKind, relationship elementsMatchRelationship) bool { positive := kind == elementsMatchKind || kind == elementsMatchTKind switch relationship { case emMatch: return positive case emNotMatch: return !positive case emInvalidType: return false default: panic(fmt.Errorf("test case configuration error: invalid elementsMatchRelationship: %d", relationship)) } } type elementsMatchTestCase struct { name string makeValues func() (actual, expected any) relationship elementsMatchRelationship reflectionOnly bool } func unifiedElementsMatchCases() iter.Seq[elementsMatchTestCase] { return slices.Values([]elementsMatchTestCase{ // Matching cases {"nil-nil", func() (any, any) { return nil, nil }, emMatch, true}, // reflection only - generic can't infer type from nil {"empty-empty", func() (any, any) { return []int{}, []int{} }, emMatch, false}, {"single-element", func() (any, any) { return []int{1}, []int{1} }, emMatch, false}, {"duplicates-same", func() (any, any) { return []int{1, 1}, []int{1, 1} }, emMatch, false}, {"reordered", func() (any, any) { return []int{1, 2}, []int{2, 1} }, emMatch, false}, {"array-reordered", func() (any, any) { return [2]int{1, 2}, [2]int{2, 1} }, emMatch, false}, {"string-reordered", func() (any, any) { return []string{"hello", "world"}, []string{"world", "hello"} }, emMatch, false}, {"string-duplicates", func() (any, any) { return []string{"hello", "hello"}, []string{"hello", "hello"} }, emMatch, false}, { "string-complex-reordered", func() (any, any) { return []string{"hello", "hello", "world"}, []string{"hello", "world", "hello"} }, emMatch, false, }, { "array-string-reordered", func() (any, any) { return [3]string{"hello", "hello", "world"}, [3]string{"hello", "world", "hello"} }, emMatch, false, }, {"empty-nil", func() (any, any) { return []int{}, nil }, emMatch, true}, // reflection only - nil type inference // Not matching cases {"different-count", func() (any, any) { return []int{1}, []int{1, 1} }, emNotMatch, false}, {"different-values", func() (any, any) { return []int{1, 2}, []int{2, 2} }, emNotMatch, false}, {"string-different", func() (any, any) { return []string{"hello", "hello"}, []string{"hello"} }, emNotMatch, false}, // Invalid types (reflection only) {"invalid type/[]int-invalid", func() (any, any) { return []int{}, 1 }, emInvalidType, true}, {"invalid type/invalid-[]int", func() (any, any) { return 1, []int{} }, emInvalidType, true}, }) } // ============================================================================ // TestCollectionSliceEqual // ============================================================================ // sliceEqualRelationship describes the equality relationship between two slices. type sliceEqualRelationship int const ( seEqual sliceEqualRelationship = iota seNotEqual ) type sliceEqualAssertionKind int const ( sliceEqualKind sliceEqualAssertionKind = iota sliceNotEqualKind ) type sliceEqualTestCase struct { name string makeValues func() (listA, listB any) relationship sliceEqualRelationship } func testSliceEqualAssertion(tc sliceEqualTestCase, kind sliceEqualAssertionKind, listA, listB any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) var result bool switch a := listA.(type) { case []int: b, ok := listB.([]int) if !ok { t.Fatalf("test case error: []int listA requires []int listB, got %T", listB) } result = testSliceEqualGeneric(mock, kind, a, b) case []string: b, ok := listB.([]string) if !ok { t.Fatalf("test case error: []string listA requires []string listB, got %T", listB) } result = testSliceEqualGeneric(mock, kind, a, b) case []float64: b, ok := listB.([]float64) if !ok { t.Fatalf("test case error: []float64 listA requires []float64 listB, got %T", listB) } result = testSliceEqualGeneric(mock, kind, a, b) default: t.Fatalf("unsupported type for SliceEqualT: %T", listA) } shouldPass := expectedStatusForSliceEqualAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testSliceEqualGeneric[E comparable](mock T, kind sliceEqualAssertionKind, listA, listB []E) bool { switch kind { case sliceEqualKind: return SliceEqualT(mock, listA, listB) case sliceNotEqualKind: return SliceNotEqualT(mock, listA, listB) default: panic(fmt.Errorf("test case configuration error: invalid sliceEqualAssertionKind: %d", kind)) } } func expectedStatusForSliceEqualAssertion(kind sliceEqualAssertionKind, relationship sliceEqualRelationship) bool { positive := kind == sliceEqualKind switch relationship { case seEqual: return positive case seNotEqual: return !positive default: panic(fmt.Errorf("test case configuration error: invalid sliceEqualRelationship: %d", relationship)) } } func sliceEqualCases() iter.Seq[sliceEqualTestCase] { return slices.Values([]sliceEqualTestCase{ // Equal cases {"empty-empty", func() (any, any) { return []int{}, []int{} }, seEqual}, {"single-element", func() (any, any) { return []int{1}, []int{1} }, seEqual}, {"multiple-elements", func() (any, any) { return []int{1, 2, 3}, []int{1, 2, 3} }, seEqual}, {"strings-equal", func() (any, any) { return []string{"a", "b"}, []string{"a", "b"} }, seEqual}, {"nil-nil", func() (any, any) { return []int(nil), []int(nil) }, seEqual}, {"nil-empty", func() (any, any) { return []int(nil), []int{} }, seEqual}, {"empty-nil", func() (any, any) { return []int{}, []int(nil) }, seEqual}, {"duplicates", func() (any, any) { return []int{1, 1, 1}, []int{1, 1, 1} }, seEqual}, {"floats-equal", func() (any, any) { return []float64{1.1, 2.2}, []float64{1.1, 2.2} }, seEqual}, // Not equal cases {"different-length", func() (any, any) { return []int{1, 2}, []int{1} }, seNotEqual}, {"different-values", func() (any, any) { return []int{1, 2}, []int{1, 3} }, seNotEqual}, {"different-order", func() (any, any) { return []int{1, 2}, []int{2, 1} }, seNotEqual}, {"strings-different", func() (any, any) { return []string{"a", "b"}, []string{"a", "c"} }, seNotEqual}, {"one-empty", func() (any, any) { return []int{1}, []int{} }, seNotEqual}, {"empty-one", func() (any, any) { return []int{}, []int{1} }, seNotEqual}, {"different-duplicates", func() (any, any) { return []int{1, 1}, []int{1, 2} }, seNotEqual}, }) } // ============================================================================ // TestCollectionMapEqual // ============================================================================ // mapEqualRelationship describes the equality relationship between two maps. type mapEqualRelationship int const ( meEqual mapEqualRelationship = iota meNotEqual ) type mapEqualAssertionKind int const ( mapEqualKind mapEqualAssertionKind = iota mapNotEqualKind ) type mapEqualTestCase struct { name string makeValues func() (listA, listB any) relationship mapEqualRelationship } func testMapEqualAssertion(tc mapEqualTestCase, kind mapEqualAssertionKind, listA, listB any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) var result bool switch a := listA.(type) { case map[string]int: b, ok := listB.(map[string]int) if !ok { t.Fatalf("test case error: map[string]int listA requires map[string]int listB, got %T", listB) } result = testMapEqualGeneric(mock, kind, a, b) case map[int]string: b, ok := listB.(map[int]string) if !ok { t.Fatalf("test case error: map[int]string listA requires map[int]string listB, got %T", listB) } result = testMapEqualGeneric(mock, kind, a, b) case map[string]string: b, ok := listB.(map[string]string) if !ok { t.Fatalf("test case error: map[string]string listA requires map[string]string listB, got %T", listB) } result = testMapEqualGeneric(mock, kind, a, b) default: t.Fatalf("unsupported type for MapEqualT: %T", listA) } shouldPass := expectedStatusForMapEqualAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testMapEqualGeneric[K, V comparable](mock T, kind mapEqualAssertionKind, listA, listB map[K]V) bool { switch kind { case mapEqualKind: return MapEqualT(mock, listA, listB) case mapNotEqualKind: return MapNotEqualT(mock, listA, listB) default: panic(fmt.Errorf("test case configuration error: invalid mapEqualAssertionKind: %d", kind)) } } func expectedStatusForMapEqualAssertion(kind mapEqualAssertionKind, relationship mapEqualRelationship) bool { positive := kind == mapEqualKind switch relationship { case meEqual: return positive case meNotEqual: return !positive default: panic(fmt.Errorf("test case configuration error: invalid mapEqualRelationship: %d", relationship)) } } func mapEqualCases() iter.Seq[mapEqualTestCase] { return slices.Values([]mapEqualTestCase{ // Equal cases {"empty-empty", func() (any, any) { return map[string]int{}, map[string]int{} }, meEqual}, {"single-entry", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"a": 1} }, meEqual}, { "multiple-entries", func() (any, any) { return map[string]int{"a": 1, "b": 2, "c": 3}, map[string]int{"a": 1, "b": 2, "c": 3} }, meEqual, }, {"nil-nil", func() (any, any) { return map[string]int(nil), map[string]int(nil) }, meEqual}, {"nil-empty", func() (any, any) { return map[string]int(nil), map[string]int{} }, meEqual}, {"empty-nil", func() (any, any) { return map[string]int{}, map[string]int(nil) }, meEqual}, { "string-values", func() (any, any) { return map[int]string{1: "a", 2: "b"}, map[int]string{1: "a", 2: "b"} }, meEqual, }, { "string-string", func() (any, any) { return map[string]string{"k1": "v1", "k2": "v2"}, map[string]string{"k1": "v1", "k2": "v2"} }, meEqual, }, // Not equal cases {"different-values", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"a": 2} }, meNotEqual}, {"different-keys", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"b": 1} }, meNotEqual}, {"different-length", func() (any, any) { return map[string]int{"a": 1, "b": 2}, map[string]int{"a": 1} }, meNotEqual}, {"one-empty", func() (any, any) { return map[string]int{"a": 1}, map[string]int{} }, meNotEqual}, {"empty-one", func() (any, any) { return map[string]int{}, map[string]int{"a": 1} }, meNotEqual}, {"extra-key", func() (any, any) { return map[string]int{"a": 1}, map[string]int{"a": 1, "b": 2} }, meNotEqual}, { "string-different-values", func() (any, any) { return map[string]string{"k": "v1"}, map[string]string{"k": "v2"} }, meNotEqual, }, }) } // ============================================================================ // TestCollectionErrorMessages // ============================================================================ func collectionErrorMessageCases() iter.Seq[failCase] { const pkg = "assertions" type nonContainer struct { Value string } longSlice := make([]int, 1_000_000) return slices.Values([]failCase{ // Contains/NotContains fail messages { name: "Contains(string, error)", assertion: func(t T) bool { return Contains(t, "Hello World", errors.New("Hello")) }, wantContains: []string{`"Hello World" does not contain &errors.errorString{s:"Hello"}`}, }, { name: "Contains(map, missing-key)", assertion: func(t T) bool { return Contains(t, map[string]int{"one": 1}, "two") }, wantContains: []string{`map[string]int{"one":1} does not contain "two"`}, }, { name: "NotContains(map, present-key)", assertion: func(t T) bool { return NotContains(t, map[string]int{"one": 1}, "one") }, wantContains: []string{`map[string]int{"one":1} should not contain "one"`}, }, { name: "Contains(nonContainer, string)", assertion: func(t T) bool { return Contains(t, nonContainer{Value: "Hello"}, "Hello") }, wantContains: []string{pkg + `.nonContainer{Value:"Hello"} could not be applied builtin len()`}, }, { name: "NotContains(nonContainer, string)", assertion: func(t T) bool { return NotContains(t, nonContainer{Value: "Hello"}, "Hello") }, wantContains: []string{pkg + `.nonContainer{Value:"Hello"} could not be applied builtin len()`}, }, // nil container { name: "Contains(nil, key)", assertion: func(t T) bool { return Contains(t, nil, "key") }, wantContains: []string{" could not be applied builtin len()"}, }, { name: "NotContains(nil, key)", assertion: func(t T) bool { return NotContains(t, nil, "key") }, wantContains: []string{" could not be applied builtin len()"}, }, // truncation: too long to print truncationCase("truncation/Nil(longSlice)", func(t T) bool { return Nil(t, &longSlice) }), truncationCase("truncation/Empty(longSlice)", func(t T) bool { return Empty(t, longSlice) }), { name: "truncation/Contains(longSlice, 1)", assertion: func(t T) bool { return Contains(t, longSlice, 1) }, wantContains: []string{ `[]int{0, 0, 0,`, `<... truncated> does not contain 1`, }, }, { name: "truncation/NotContains(longSlice, 0)", assertion: func(t T) bool { return NotContains(t, longSlice, 0) }, wantContains: []string{ `[]int{0, 0, 0,`, `<... truncated> should not contain 0`, }, }, { name: "truncation/Subset(longSlice, [1])", assertion: func(t T) bool { return Subset(t, longSlice, []int{1}) }, wantContains: []string{ `[]int{0, 0, 0,`, `<... truncated> does not contain 1`, }, }, { name: "truncation/Subset(map-longSlice)", assertion: func(t T) bool { return Subset(t, map[bool][]int{true: longSlice}, map[bool][]int{false: longSlice}) }, wantContains: []string{ `map[bool][]int{true:[]int{0, 0, 0,`, `<... truncated> does not contain map[bool][]int{false:[]int{0, 0, 0,`, }, }, { name: "truncation/NotSubset(longSlice)", assertion: func(t T) bool { return NotSubset(t, longSlice, longSlice) }, wantContains: []string{ `[]int{0, 0, 0,`, `<... truncated> is a subset of []int{0, 0, 0,`, }, }, { name: "truncation/NotSubset(map-longSlice)", assertion: func(t T) bool { return NotSubset(t, map[int][]int{1: longSlice}, map[int][]int{1: longSlice}) }, wantContains: []string{ `map[int][]int{1:[]int{0, 0, 0,`, `<... truncated> is a subset of map[int][]int{1:[]int{0, 0, 0,`, }, }, // NotSubset/SliceNotSubsetT fail messages — regression coverage for // upstream stretchr/testify#1800 / #1848: previously %q produced // broken output like "%!q(bool=true)" or "'\x01'" for non-string types. { name: "NotSubset(bools)", assertion: func(t T) bool { return NotSubset(t, []bool{true}, []bool{true}) }, wantContains: []string{`[]bool{true} is a subset of []bool{true}`}, }, { name: "NotSubset(ints)", assertion: func(t T) bool { return NotSubset(t, []int{1, 2, 3}, []int{1, 2}) }, wantContains: []string{`[]int{1, 2} is a subset of []int{1, 2, 3}`}, }, { name: "NotSubset(map-int)", assertion: func(t T) bool { return NotSubset(t, map[int]string{1: "one"}, map[int]string{1: "one"}) }, wantContains: []string{`map[int]string{1:"one"} is a subset of map[int]string{1:"one"}`}, }, { name: "SliceNotSubsetT(bools)", assertion: func(t T) bool { return SliceNotSubsetT(t, []bool{true}, []bool{true}) }, wantContains: []string{`[]bool{true} is a subset of []bool{true}`}, }, { name: "SliceNotSubsetT(ints)", assertion: func(t T) bool { return SliceNotSubsetT(t, []int{1, 2, 3}, []int{1, 2}) }, wantContains: []string{`[]int{1, 2} is a subset of []int{1, 2, 3}`}, }, }) } go-openapi-testify-c10ca71/internal/assertions/compare.go000066400000000000000000000416051520301377500236320ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "bytes" "cmp" "fmt" "reflect" "slices" "time" ) // Greater asserts that the first element is strictly greater than the second. // // Both elements must be of the same type in the [reflect.Kind] sense. // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.Greater(t, 2, 1) // assertions.Greater(t, float64(2), float64(1)) // assertions.Greater(t, "b", "a") // // # Examples // // success: 2, 1 // failure: 1, 2 func Greater(t T, e1 any, e2 any, msgAndArgs ...any) bool { // Domain: comparison // Opposite: LessOrEqual if h, ok := t.(H); ok { h.Helper() } failMessage := fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2) return compareTwoValues(t, e1, e2, []compareResult{compareGreater}, failMessage, msgAndArgs...) } // GreaterT asserts that for two elements of the same type, // the first element is strictly greater than the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterT] with [*time.Time]. // // [GreaterT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, use [Greater] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.GreaterT(t, 2, 1) // assertions.GreaterT(t, float64(2), float64(1)) // assertions.GreaterT(t, "b", "a") // assertions.GreaterT(t, time.Date(2026,1,1,0,0,0,0,nil), time.Now()) // // # Examples // // success: 2, 1 // failure: 1, 2 func GreaterT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { // Domain: comparison // Opposite: LessOrEqualT if h, ok := t.(H); ok { h.Helper() } result := compareOrderedWithAny(e1, e2) if result > 0 { return true } return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", e1, e2), msgAndArgs...) } // GreaterOrEqual asserts that the first element is greater than or equal to the second. // // See also [Greater]. // // # Usage // // assertions.GreaterOrEqual(t, 2, 1) // assertions.GreaterOrEqual(t, 2, 2) // assertions.GreaterOrEqual(t, "b", "a") // assertions.GreaterOrEqual(t, "b", "b") // // # Examples // // success: 2, 1 // failure: 1, 2 func GreaterOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { // Domain: comparison // Opposite: Less if h, ok := t.(H); ok { h.Helper() } failMessage := fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2) return compareTwoValues(t, e1, e2, []compareResult{compareGreater, compareEqual}, failMessage, msgAndArgs...) } // GreaterOrEqualT asserts that for two elements of the same type, // the first element is greater than or equal to the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [GreaterOrEqualT] with [*time.Time]. // // [GreaterOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [GreaterOrEqual] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.GreaterOrEqualT(t, 2, 1) // assertions.GreaterOrEqualT(t, 2, 2) // assertions.GreaterOrEqualT(t, "b", "a") // assertions.GreaterOrEqualT(t, "b", "b") // // # Examples // // success: 2, 1 // failure: 1, 2 func GreaterOrEqualT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { // Domain: comparison // Opposite: LessT if h, ok := t.(H); ok { h.Helper() } result := compareOrderedWithAny(e1, e2) if result >= 0 { return true } return Fail(t, fmt.Sprintf("\"%v\" is not greater than or equal to \"%v\"", e1, e2), msgAndArgs...) } // Less asserts that the first element is strictly less than the second. // // Both elements must be of the same type in the [reflect.Kind] sense. // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.Less(t, 1, 2) // assertions.Less(t, float64(1), float64(2)) // assertions.Less(t, "a", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 func Less(t T, e1 any, e2 any, msgAndArgs ...any) bool { // Domain: comparison if h, ok := t.(H); ok { h.Helper() } failMessage := fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2) return compareTwoValues(t, e1, e2, []compareResult{compareLess}, failMessage, msgAndArgs...) } // LessT asserts that for two elements of the same type, the first element is strictly less than the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessT] with [*time.Time]. // // [LessT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [Less] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you need to convert types beforehand. // // # Usage // // assertions.LessT(t, 1, 2) // assertions.LessT(t, float64(1), float64(2)) // assertions.LessT(t, "a", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 func LessT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { // Domain: comparison if h, ok := t.(H); ok { h.Helper() } result := compareOrderedWithAny(e1, e2) if result < 0 { return true } return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", e1, e2), msgAndArgs...) } // LessOrEqual asserts that the first element is less than or equal to the second. // // # Usage // // assertions.LessOrEqual(t, 1, 2) // assertions.LessOrEqual(t, 2, 2) // assertions.LessOrEqual(t, "a", "b") // assertions.LessOrEqual(t, "b", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 func LessOrEqual(t T, e1 any, e2 any, msgAndArgs ...any) bool { // Domain: comparison if h, ok := t.(H); ok { h.Helper() } failMessage := fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2) return compareTwoValues(t, e1, e2, []compareResult{compareLess, compareEqual}, failMessage, msgAndArgs...) } // LessOrEqualT asserts that for two elements of the same type, the first element is less than or equal to the second. // // The [Ordered] type can be any of Go's [cmp.Ordered] (strings, numeric types), // []byte (uses [bytes.Compare]) and [time.Time] (uses [time.Time.Compare]. // // Notice that pointers are not [Ordered], but uintptr are. So you can't call [LessOrEqualT] with [*time.Time]. // // [LessOrEqualT] ensures type safety at build time. If you need to compare values with a dynamically assigned type, // use [LessOrEqual] instead. // // To compare values that need a type conversion (e.g. float32 against float64), you should use [LessOrEqual] instead. // // # Usage // // assertions.LessOrEqualT(t, 1, 2) // assertions.LessOrEqualT(t, 2, 2) // assertions.LessOrEqualT(t, "a", "b") // assertions.LessOrEqualT(t, "b", "b") // // # Examples // // success: 1, 2 // failure: 2, 1 func LessOrEqualT[Orderable Ordered](t T, e1, e2 Orderable, msgAndArgs ...any) bool { // Domain: comparison if h, ok := t.(H); ok { h.Helper() } result := compareOrderedWithAny(e1, e2) if result <= 0 { return true } return Fail(t, fmt.Sprintf("\"%v\" is not less than or equal to \"%v\"", e1, e2), msgAndArgs...) } // Positive asserts that the specified element is strictly positive. // // # Usage // // assertions.Positive(t, 1) // assertions.Positive(t, 1.23) // // # Examples // // success: 1 // failure: -1 func Positive(t T, e any, msgAndArgs ...any) bool { // Domain: comparison // Opposite: Negative if h, ok := t.(H); ok { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) failMessage := fmt.Sprintf("\"%v\" is not positive", e) return compareTwoValues(t, e, zero.Interface(), []compareResult{compareGreater}, failMessage, msgAndArgs...) } // PositiveT asserts that the specified element of a signed numeric type is strictly positive. // // # Usage // // assertions.PositiveT(t, 1) // assertions.PositiveT(t, 1.23) // // # Examples // // success: 1 // failure: -1 func PositiveT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { // Domain: comparison // Opposite: NegativeT if h, ok := t.(H); ok { h.Helper() } result := cmp.Compare(e, SignedNumber(0)) if result > 0 { return true } return Fail(t, fmt.Sprintf("\"%v\" is not positive", e), msgAndArgs...) } // Negative asserts that the specified element is strictly negative. // // # Usage // // assertions.Negative(t, -1) // assertions.Negative(t, -1.23) // // # Examples // // success: -1 // failure: 1 func Negative(t T, e any, msgAndArgs ...any) bool { // Domain: comparison if h, ok := t.(H); ok { h.Helper() } zero := reflect.Zero(reflect.TypeOf(e)) failMessage := fmt.Sprintf("\"%v\" is not negative", e) return compareTwoValues(t, e, zero.Interface(), []compareResult{compareLess}, failMessage, msgAndArgs...) } // NegativeT asserts that the specified element of a signed numeric type is strictly negative. // // # Usage // // assertions.NegativeT(t, -1) // assertions.NegativeT(t, -1.23) // // # Examples // // success: -1 // failure: 1 func NegativeT[SignedNumber SignedNumeric](t T, e SignedNumber, msgAndArgs ...any) bool { // Domain: comparison if h, ok := t.(H); ok { h.Helper() } result := cmp.Compare(e, SignedNumber(0)) if result < 0 { return true } return Fail(t, fmt.Sprintf("\"%v\" is not negative", e), msgAndArgs...) } type compareResult = int const ( compareLess compareResult = iota - 1 compareEqual compareGreater ) func compareTwoValues(t T, e1 any, e2 any, allowedComparesResults []compareResult, failMessage string, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } e1Kind := reflect.ValueOf(e1).Kind() e2Kind := reflect.ValueOf(e2).Kind() if e1Kind != e2Kind { return Fail(t, "Elements should be the same type", msgAndArgs...) } result, isComparable := compare(e1, e2, e1Kind) if !isComparable { return Fail(t, fmt.Sprintf(`Can not compare type "%T"`, e1), msgAndArgs...) } if !containsValue(allowedComparesResults, result) { return Fail(t, failMessage, msgAndArgs...) } return true } func containsValue(values []compareResult, value compareResult) bool { return slices.Contains(values, value) } func compare(obj1, obj2 any, kind reflect.Kind) (compareResult, bool) { obj1Value := reflect.ValueOf(obj1) obj2Value := reflect.ValueOf(obj2) switch kind { case reflect.Int: intobj1 := convertReflectValue[int](obj1, obj1Value) intobj2 := convertReflectValue[int](obj2, obj2Value) return cmp.Compare(intobj1, intobj2), true case reflect.Int8: int8obj1 := convertReflectValue[int8](obj1, obj1Value) int8obj2 := convertReflectValue[int8](obj2, obj2Value) return cmp.Compare(int8obj1, int8obj2), true case reflect.Int16: int16obj1 := convertReflectValue[int16](obj1, obj1Value) int16obj2 := convertReflectValue[int16](obj2, obj2Value) return cmp.Compare(int16obj1, int16obj2), true case reflect.Int32: int32obj1 := convertReflectValue[int32](obj1, obj1Value) int32obj2 := convertReflectValue[int32](obj2, obj2Value) return cmp.Compare(int32obj1, int32obj2), true case reflect.Int64: int64obj1 := convertReflectValue[int64](obj1, obj1Value) int64obj2 := convertReflectValue[int64](obj2, obj2Value) return cmp.Compare(int64obj1, int64obj2), true case reflect.Uint: uintobj1 := convertReflectValue[uint](obj1, obj1Value) uintobj2 := convertReflectValue[uint](obj2, obj2Value) return cmp.Compare(uintobj1, uintobj2), true case reflect.Uint8: uint8obj1 := convertReflectValue[uint8](obj1, obj1Value) uint8obj2 := convertReflectValue[uint8](obj2, obj2Value) return cmp.Compare(uint8obj1, uint8obj2), true case reflect.Uint16: uint16obj1 := convertReflectValue[uint16](obj1, obj1Value) uint16obj2 := convertReflectValue[uint16](obj2, obj2Value) return cmp.Compare(uint16obj1, uint16obj2), true case reflect.Uint32: uint32obj1 := convertReflectValue[uint32](obj1, obj1Value) uint32obj2 := convertReflectValue[uint32](obj2, obj2Value) return cmp.Compare(uint32obj1, uint32obj2), true case reflect.Uint64: uint64obj1 := convertReflectValue[uint64](obj1, obj1Value) uint64obj2 := convertReflectValue[uint64](obj2, obj2Value) return cmp.Compare(uint64obj1, uint64obj2), true case reflect.Float32: float32obj1 := convertReflectValue[float32](obj1, obj1Value) float32obj2 := convertReflectValue[float32](obj2, obj2Value) return cmp.Compare(float32obj1, float32obj2), true case reflect.Float64: float64obj1 := convertReflectValue[float64](obj1, obj1Value) float64obj2 := convertReflectValue[float64](obj2, obj2Value) return cmp.Compare(float64obj1, float64obj2), true case reflect.String: stringobj1 := convertReflectValue[string](obj1, obj1Value) stringobj2 := convertReflectValue[string](obj2, obj2Value) return cmp.Compare(stringobj1, stringobj2), true // Check for known struct types we can check for compare results. case reflect.Struct: return compareStruct(obj1, obj2, obj1Value, obj2Value) case reflect.Slice: return compareSlice(obj1, obj2, obj1Value, obj2Value) case reflect.Uintptr: uintptrobj1 := convertReflectValue[uintptr](obj1, obj1Value) uintptrobj2 := convertReflectValue[uintptr](obj2, obj2Value) return cmp.Compare(uintptrobj1, uintptrobj2), true default: return compareEqual, false } } func compareStruct(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareResult, bool) { // all structs enter here. We're not interested in most types. if !obj1Value.CanConvert(reflect.TypeFor[time.Time]()) { return compareEqual, false } // time.Time can be compared timeobj1 := convertReflectValue[time.Time](obj1, obj1Value) timeobj2 := convertReflectValue[time.Time](obj2, obj2Value) return timeobj1.Compare(timeobj2), true } func compareSlice(obj1, obj2 any, obj1Value, obj2Value reflect.Value) (compareResult, bool) { // we only care about the []byte type. if !obj1Value.CanConvert(reflect.TypeFor[[]byte]()) { return compareEqual, false } // []byte can be compared bytesobj1 := convertReflectValue[[]byte](obj1, obj1Value) bytesobj2 := convertReflectValue[[]byte](obj2, obj2Value) return bytes.Compare(bytesobj1, bytesobj2), true } func convertReflectValue[V any](obj any, value reflect.Value) V { //nolint:ireturn // false positive // we try and avoid calling [reflect.Value.Convert()] whenever possible, // as this has a pretty big performance impact converted, ok := obj.(V) if !ok { converted, ok = value.Convert(reflect.TypeFor[V]()).Interface().(V) if !ok { // should never get there panic("internal error: expected that reflect.Value.Convert yields its target type") } } return converted } // compareOrderedWithAny compares two [Ordered] values. // // This is an internal function that should only be called with actually [Ordered] types, // even though it doesn't enforce this a build time. // //nolint:forcetypeassert // e2 is guaranteed to be of the same type as e1 func compareOrderedWithAny[Orderable any](e1, e2 Orderable) int { v := any(e1) o := any(e2) switch value := v.(type) { case time.Time: other := o.(time.Time) return value.Compare(other) case []byte: other := o.([]byte) return bytes.Compare(value, other) case string: other := o.(string) return cmp.Compare(value, other) case int: other := o.(int) return cmp.Compare(value, other) case int8: other := o.(int8) return cmp.Compare(value, other) case int16: other := o.(int16) return cmp.Compare(value, other) case int32: other := o.(int32) return cmp.Compare(value, other) case int64: other := o.(int64) return cmp.Compare(value, other) case uint: other := o.(uint) return cmp.Compare(value, other) case uint8: other := o.(uint8) return cmp.Compare(value, other) case uint16: other := o.(uint16) return cmp.Compare(value, other) case uint32: other := o.(uint32) return cmp.Compare(value, other) case uint64: other := o.(uint64) return cmp.Compare(value, other) case float32: other := o.(float32) return cmp.Compare(value, other) case float64: other := o.(float64) return cmp.Compare(value, other) case uintptr: other := o.(uintptr) return cmp.Compare(value, other) default: // we have a custom type: convert with reflection. // We have less edge cases to guard than when comparing with the purely reflection-based call. e1Kind := reflect.ValueOf(e1).Kind() result, ok := compare(e1, e2, e1Kind) if !ok { // should never get there panic(fmt.Errorf("internal error: expected that reflect.Value.Convert yields its target type for %T", e1)) } return result } } go-openapi-testify-c10ca71/internal/assertions/compare_impl_test.go000066400000000000000000000205221520301377500257050ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "reflect" "slices" "testing" "time" ) func TestCompareUnexportedImplementationDetails(t *testing.T) { t.Parallel() t.Run("compare", testCompare()) t.Run("containsValue", testContainsValue()) t.Run("compareTwoValues - different types", testCompareTwoValuesDifferentTypes()) t.Run("compareTwoValues - not comparable", testCompareTwoValuesNotComparable()) t.Run("compareTwoValues - compare result", testCompareTwoValuesCorrectCompareResult()) } func testCompare() func(*testing.T) { return func(t *testing.T) { for currCase := range compareImplCompareCases() { t.Run("compare", func(t *testing.T) { t.Parallel() resLess, isComparable := compare(currCase.less, currCase.greater, reflect.ValueOf(currCase.less).Kind()) if !isComparable { t.Error("object should be comparable for type " + currCase.cType) } if resLess != compareLess { t.Errorf("object less (%v) should be less than greater (%v) for type "+currCase.cType, currCase.less, currCase.greater) } resGreater, isComparable := compare(currCase.greater, currCase.less, reflect.ValueOf(currCase.less).Kind()) if !isComparable { t.Error("object are comparable for type " + currCase.cType) } if resGreater != compareGreater { t.Errorf("object greater should be greater than less for type %s", currCase.cType) } resEqual, isComparable := compare(currCase.less, currCase.less, reflect.ValueOf(currCase.less).Kind()) if !isComparable { t.Errorf("object are comparable for type %s", currCase.cType) } if resEqual != 0 { t.Errorf("objects should be equal for type %s", currCase.cType) } }) } } } func testContainsValue() func(*testing.T) { return func(t *testing.T) { t.Parallel() for currCase := range compareContainsValueCases() { result := containsValue(currCase.values, currCase.value) if currCase.result != result { t.Errorf("containsValue(%v, %v): expected %v, got %v", currCase.values, currCase.value, currCase.result, result) } } } } func testCompareTwoValuesDifferentTypes() func(*testing.T) { return func(t *testing.T) { for currCase := range compareTwoValuesDifferentTypesCases() { t.Run("different types should not be comparable", func(t *testing.T) { t.Parallel() mock := new(mockT) result := compareTwoValues(mock, currCase.v1, currCase.v2, []compareResult{compareLess, compareEqual, compareGreater}, "testFailMessage") if result { t.Error("expected compareTwoValues to return false for different types") } }) } } } func testCompareTwoValuesNotComparable() func(*testing.T) { return func(t *testing.T) { for currCase := range compareTwoValuesNotComparableCases() { t.Run("should not be comparable", func(t *testing.T) { t.Parallel() mock := new(mockT) result := compareTwoValues(mock, currCase.v1, currCase.v2, []compareResult{compareLess, compareEqual, compareGreater}, "testFailMessage") if result { t.Error("expected compareTwoValues to return false for non-comparable types") } }) } } } func testCompareTwoValuesCorrectCompareResult() func(*testing.T) { return func(t *testing.T) { for currCase := range compareTwoValuesCorrectResultCases() { t.Run("should be comparable", func(t *testing.T) { t.Parallel() mock := new(mockT) result := compareTwoValues(mock, currCase.v1, currCase.v2, currCase.allowedResults, "testFailMessage") if !result { t.Errorf("expected compareTwoValues to return true for %v vs %v with allowed %v", currCase.v1, currCase.v2, currCase.allowedResults) } }) } } } type compareImplCompareCase struct { less any greater any cType string } func compareImplCompareCases() iter.Seq[compareImplCompareCase] { type customString string type customInt int type customInt8 int8 type customInt16 int16 type customInt32 int32 type customInt64 int64 type customUInt uint type customUInt8 uint8 type customUInt16 uint16 type customUInt32 uint32 type customUInt64 uint64 type customFloat32 float32 type customFloat64 float64 type customUintptr uintptr type customTime time.Time type customBytes []byte return slices.Values([]compareImplCompareCase{ {less: customString("a"), greater: customString("b"), cType: "string"}, {less: "a", greater: "b", cType: "string"}, {less: customInt(1), greater: customInt(2), cType: "int"}, {less: int(1), greater: int(2), cType: "int"}, {less: customInt8(1), greater: customInt8(2), cType: "int8"}, {less: int8(1), greater: int8(2), cType: "int8"}, {less: customInt16(1), greater: customInt16(2), cType: "int16"}, {less: int16(1), greater: int16(2), cType: "int16"}, {less: customInt32(1), greater: customInt32(2), cType: "int32"}, {less: int32(1), greater: int32(2), cType: "int32"}, {less: customInt64(1), greater: customInt64(2), cType: "int64"}, {less: int64(1), greater: int64(2), cType: "int64"}, {less: customUInt(1), greater: customUInt(2), cType: "uint"}, {less: uint8(1), greater: uint8(2), cType: "uint8"}, {less: customUInt8(1), greater: customUInt8(2), cType: "uint8"}, {less: uint16(1), greater: uint16(2), cType: "uint16"}, {less: customUInt16(1), greater: customUInt16(2), cType: "uint16"}, {less: uint32(1), greater: uint32(2), cType: "uint32"}, {less: customUInt32(1), greater: customUInt32(2), cType: "uint32"}, {less: uint64(1), greater: uint64(2), cType: "uint64"}, {less: customUInt64(1), greater: customUInt64(2), cType: "uint64"}, {less: float32(1.23), greater: float32(2.34), cType: "float32"}, {less: customFloat32(1.23), greater: customFloat32(2.23), cType: "float32"}, {less: float64(1.23), greater: float64(2.34), cType: "float64"}, {less: customFloat64(1.23), greater: customFloat64(2.34), cType: "float64"}, {less: uintptr(1), greater: uintptr(2), cType: "uintptr"}, {less: customUintptr(1), greater: customUintptr(2), cType: "uint64"}, {less: time.Now(), greater: time.Now().Add(time.Hour), cType: "time.Time"}, { // using time.Local is ok in this context: this is precisely the goal of this test less: time.Date(2024, 0, 0, 0, 0, 0, 0, time.Local), //nolint:gosmopolitan // ok. See above greater: time.Date(2263, 0, 0, 0, 0, 0, 0, time.Local), //nolint:gosmopolitan // ok. See above cType: "time.Time", }, {less: customTime(time.Now()), greater: customTime(time.Now().Add(time.Hour)), cType: "time.Time"}, {less: []byte{1, 1}, greater: []byte{1, 2}, cType: "[]byte"}, {less: customBytes([]byte{1, 1}), greater: customBytes([]byte{1, 2}), cType: "[]byte"}, }) } type compareTwoValuesCase struct { v1 any v2 any // compareResult bool allowedResults []compareResult } func compareTwoValuesDifferentTypesCases() iter.Seq[compareTwoValuesCase] { return slices.Values([]compareTwoValuesCase{ {v1: 123, v2: "abc"}, {v1: "abc", v2: 123456}, {v1: float64(12), v2: "123"}, {v1: "float(12)", v2: float64(1)}, }) } func compareTwoValuesNotComparableCases() iter.Seq[compareTwoValuesCase] { type CompareStruct struct{} return slices.Values([]compareTwoValuesCase{ {v1: CompareStruct{}, v2: CompareStruct{}}, {v1: map[string]int{}, v2: map[string]int{}}, {v1: make([]int, 5), v2: make([]int, 5)}, }) } func compareTwoValuesCorrectResultCases() iter.Seq[compareTwoValuesCase] { return slices.Values([]compareTwoValuesCase{ {v1: 1, v2: 2, allowedResults: []compareResult{compareLess}}, {v1: 1, v2: 2, allowedResults: []compareResult{compareLess, compareEqual}}, {v1: 2, v2: 2, allowedResults: []compareResult{compareGreater, compareEqual}}, {v1: 2, v2: 2, allowedResults: []compareResult{compareEqual}}, {v1: 2, v2: 1, allowedResults: []compareResult{compareEqual, compareGreater}}, {v1: 2, v2: 1, allowedResults: []compareResult{compareGreater}}, }) } type compareContainsValueCase struct { values []compareResult value compareResult result bool } func compareContainsValueCases() iter.Seq[compareContainsValueCase] { return slices.Values([]compareContainsValueCase{ {values: []compareResult{compareGreater}, value: compareGreater, result: true}, {values: []compareResult{compareGreater, compareLess}, value: compareGreater, result: true}, {values: []compareResult{compareGreater, compareLess}, value: compareLess, result: true}, {values: []compareResult{compareGreater, compareLess}, value: compareEqual, result: false}, }) } go-openapi-testify-c10ca71/internal/assertions/compare_test.go000066400000000000000000000401121520301377500246610ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "slices" "testing" "time" ) func TestCompareGreaterAndLess(t *testing.T) { t.Parallel() // Unified tests with all comparison functions, reflection-based or generic for tc := range comparisonCases() { t.Run(tc.name+"/unified", testAllComparison(tc)) } } func TestCompareGreaterAndLessT(t *testing.T) { t.Parallel() // Unified tests with all comparison functions for tc := range comparisonCases() { t.Run(tc.name, func(t *testing.T) { // Dispatch to type-specific test based on the type of tc.less switch tc.less.(type) { case string: testAllComparisonT[string](tc)(t) case int: testAllComparisonT[int](tc)(t) case int8: testAllComparisonT[int8](tc)(t) case int16: testAllComparisonT[int16](tc)(t) case int32: testAllComparisonT[int32](tc)(t) case int64: testAllComparisonT[int64](tc)(t) case uint: testAllComparisonT[uint](tc)(t) case uint8: testAllComparisonT[uint8](tc)(t) case uint16: testAllComparisonT[uint16](tc)(t) case uint32: testAllComparisonT[uint32](tc)(t) case uint64: testAllComparisonT[uint64](tc)(t) case float32: testAllComparisonT[float32](tc)(t) case float64: testAllComparisonT[float64](tc)(t) case uintptr: testAllComparisonT[uintptr](tc)(t) case time.Time: testAllComparisonT[time.Time](tc)(t) case []byte: testAllComparisonT[[]byte](tc)(t) default: // Custom types (like redefined uintptr) - skip, they're tested separately t.Logf("%s: custom types tested separately (got: %T)", t.Name(), tc.less) } }) } // Additional type-specific tests t.Run("custom int type", testGreaterTCustomInt()) } func TestComparePositiveT(t *testing.T) { t.Parallel() // Unified tests with both Positive and Negative functions for tc := range signCases() { t.Run(tc.name, func(t *testing.T) { // Dispatch to type-specific test based on the type of tc.positive switch tc.positive.(type) { case int: testAllSignT[int](tc)(t) case int8: testAllSignT[int8](tc)(t) case int16: testAllSignT[int16](tc)(t) case int32: testAllSignT[int32](tc)(t) case int64: testAllSignT[int64](tc)(t) case float32: testAllSignT[float32](tc)(t) case float64: testAllSignT[float64](tc)(t) } }) } } func TestComparePositive(t *testing.T) { t.Parallel() // Unified tests with both Positive and Negative functions for tc := range signCases() { t.Run(tc.name+"/unified", testAllSign(tc)) } } func TestCompareErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, compareFailCases()) } // genericTestCase wraps a test function with its name for table-driven tests of generic functions. // Kept for compatibility with existing special-case tests. type genericTestCase struct { name string test func(*testing.T) } // Special-case test helpers for types that need custom handling func testGreaterTCustomInt() func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) type MyInt int if !GreaterT(mock, MyInt(2), MyInt(1)) { t.Error("expected GreaterT(2, 1) to pass") } if GreaterT(mock, MyInt(1), MyInt(2)) { t.Error("expected GreaterT(1, 2) to fail") } } } // Unified test helpers for comparison functions // comparisonTestCase represents a test case for comparison functions. type comparisonTestCase struct { name string less any greater any equal bool // if true, less == greater (for testing equal values) } // comparisonCases returns unified test data for all comparison functions. func comparisonCases() iter.Seq[comparisonTestCase] { type redefinedUintptr uintptr return slices.Values([]comparisonTestCase{ // Strict inequality cases {name: "string", less: "a", greater: "b", equal: false}, {name: "int", less: int(1), greater: int(2), equal: false}, {name: "int8", less: int8(1), greater: int8(2), equal: false}, {name: "int16", less: int16(1), greater: int16(2), equal: false}, {name: "int32", less: int32(1), greater: int32(2), equal: false}, {name: "int64", less: int64(1), greater: int64(2), equal: false}, {name: "uint", less: uint(1), greater: uint(2), equal: false}, {name: "uint8", less: uint8(1), greater: uint8(2), equal: false}, {name: "uint16", less: uint16(1), greater: uint16(2), equal: false}, {name: "uint32", less: uint32(1), greater: uint32(2), equal: false}, {name: "uint64", less: uint64(1), greater: uint64(2), equal: false}, {name: "float32", less: float32(1.23), greater: float32(2.34), equal: false}, {name: "float64", less: float64(1.23), greater: float64(2.34), equal: false}, {name: "uintptr", less: uintptr(1), greater: uintptr(2), equal: false}, {name: "uintptr/9-10", less: uintptr(9), greater: uintptr(10), equal: false}, {name: "redefined-uintptr", less: redefinedUintptr(9), greater: redefinedUintptr(10), equal: false}, {name: "time.Time", less: time.Time{}, greater: time.Time{}.Add(time.Hour), equal: false}, {name: "[]byte", less: []byte{1, 1}, greater: []byte{1, 2}, equal: false}, // Equality cases {name: "string/equal", less: "a", greater: "a", equal: true}, {name: "int/equal", less: int(1), greater: int(1), equal: true}, {name: "int8/equal", less: int8(1), greater: int8(1), equal: true}, {name: "int16/equal", less: int16(1), greater: int16(1), equal: true}, {name: "int32/equal", less: int32(1), greater: int32(1), equal: true}, {name: "int64/equal", less: int64(1), greater: int64(1), equal: true}, {name: "uint/equal", less: uint(1), greater: uint(1), equal: true}, {name: "uint8/equal", less: uint8(1), greater: uint8(1), equal: true}, {name: "uint16/equal", less: uint16(1), greater: uint16(1), equal: true}, {name: "uint32/equal", less: uint32(1), greater: uint32(1), equal: true}, {name: "uint64/equal", less: uint64(1), greater: uint64(1), equal: true}, {name: "float32/equal", less: float32(1.23), greater: float32(1.23), equal: true}, {name: "float64/equal", less: float64(1.23), greater: float64(1.23), equal: true}, {name: "uintptr/equal", less: uintptr(1), greater: uintptr(1), equal: true}, {name: "time.Time/equal", less: time.Time{}, greater: time.Time{}, equal: true}, {name: "[]byte/equal", less: []byte{1, 1}, greater: []byte{1, 1}, equal: true}, }) } // testAllComparison tests all four comparison functions with the same test data. func testAllComparison(tc comparisonTestCase) func(*testing.T) { return func(t *testing.T) { t.Parallel() if tc.equal { // For equal values: // - Greater and Less should return false // - GreaterOrEqual and LessOrEqual should return true t.Run("with equal values", func(t *testing.T) { t.Run("Greater should fail", testComparison(Greater, tc.less, tc.greater, false)) t.Run("GreaterOrEqual should pass", testComparison(GreaterOrEqual, tc.less, tc.greater, true)) t.Run("Less should fail", testComparison(Less, tc.less, tc.greater, false)) t.Run("LessOrEqual should pass", testComparison(LessOrEqual, tc.less, tc.greater, true)) }) return } // For strict inequality: // Test both directions to verify the inverse relationships t.Run("with strict inequality", func(t *testing.T) { t.Run("Greater", func(t *testing.T) { t.Run("should pass (greater > less)", testComparison(Greater, tc.greater, tc.less, true)) t.Run("should fail (less > greater)", testComparison(Greater, tc.less, tc.greater, false)) }) t.Run("GreaterOrEqual", func(t *testing.T) { t.Run("should pass (greater >= less)", testComparison(GreaterOrEqual, tc.greater, tc.less, true)) t.Run("should fail (less >= greater)", testComparison(GreaterOrEqual, tc.less, tc.greater, false)) }) t.Run("Less", func(t *testing.T) { t.Run("should pass (less < greater)", testComparison(Less, tc.less, tc.greater, true)) t.Run("should fail (greater < less)", testComparison(Less, tc.greater, tc.less, false)) }) t.Run("LessOrEqual", func(t *testing.T) { t.Run("should pass (less <= greater)", testComparison(LessOrEqual, tc.less, tc.greater, true)) t.Run("should fail (greater <= less)", testComparison(LessOrEqual, tc.greater, tc.less, false)) }) }) } } // testComparison is a helper that tests a comparison function. func testComparison(cmp func(T, any, any, ...any) bool, e1, e2 any, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := cmp(mock, e1, e2) shouldPassOrFail(t, mock, result, shouldPass) } } // testAllComparisonT tests all four generic comparison functions with the same test data. func testAllComparisonT[V Ordered](tc comparisonTestCase) func(*testing.T) { return func(t *testing.T) { t.Parallel() // Type assert the values less, ok1 := tc.less.(V) greater, ok2 := tc.greater.(V) if !ok1 || !ok2 { t.Fatalf("type mismatch in testcase: expected %T, got less=%T, greater=%T", *new(V), tc.less, tc.greater) } if tc.equal { // For equal values: // - GreaterT and LessT should return false // - GreaterOrEqualT and LessOrEqualT should return true t.Run("with equal values", func(t *testing.T) { t.Run("GreaterT should fail", testComparisonT(GreaterT[V], less, greater, false)) t.Run("GreaterOrEqualT should pass", testComparisonT(GreaterOrEqualT[V], less, greater, true)) t.Run("LessT should fail", testComparisonT(LessT[V], less, greater, false)) t.Run("LessOrEqualT should pass", testComparisonT(LessOrEqualT[V], less, greater, true)) }) return } // For strict inequality: // Test both directions to verify the inverse relationships t.Run("with strict inequality", func(t *testing.T) { t.Run("GreaterT", func(t *testing.T) { t.Run("should pass (greater > less)", testComparisonT(GreaterT[V], greater, less, true)) t.Run("should fail (less > greater)", testComparisonT(GreaterT[V], less, greater, false)) }) t.Run("GreaterOrEqualT", func(t *testing.T) { t.Run("should pass (greater >= less)", testComparisonT(GreaterOrEqualT[V], greater, less, true)) t.Run("should fail (less >= greater)", testComparisonT(GreaterOrEqualT[V], less, greater, false)) }) t.Run("LessT", func(t *testing.T) { t.Run("should pass (less < greater)", testComparisonT(LessT[V], less, greater, true)) t.Run("should fail (greater < less)", testComparisonT(LessT[V], greater, less, false)) }) t.Run("LessOrEqualT", func(t *testing.T) { t.Run("should pass (less <= greater)", testComparisonT(LessOrEqualT[V], less, greater, true)) t.Run("should fail (greater <= less)", testComparisonT(LessOrEqualT[V], greater, less, false)) }) }) } } // testComparisonT is a helper that tests a generic comparison function. func testComparisonT[V Ordered](cmp func(T, V, V, ...any) bool, e1, e2 V, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := cmp(mock, e1, e2) shouldPassOrFail(t, mock, result, shouldPass) } } // Unified test helpers for sign functions (Positive/Negative) // signTestCase represents a test case for Positive/Negative functions. type signTestCase struct { name string positive any negative any isZero bool // if true, positive is zero (both Positive and Negative should fail) } // signCases returns unified test data for Positive/Negative functions. func signCases() iter.Seq[signTestCase] { return slices.Values([]signTestCase{ {name: "int", positive: int(1), negative: int(-1), isZero: false}, {name: "int8", positive: int8(1), negative: int8(-1), isZero: false}, {name: "int16", positive: int16(1), negative: int16(-1), isZero: false}, {name: "int32", positive: int32(1), negative: int32(-1), isZero: false}, {name: "int64", positive: int64(1), negative: int64(-1), isZero: false}, {name: "float32", positive: float32(1.5), negative: float32(-1.5), isZero: false}, {name: "float64", positive: float64(1.5), negative: float64(-1.5), isZero: false}, // Zero cases - both Positive and Negative should fail {name: "int/zero", positive: int(0), negative: int(0), isZero: true}, {name: "float64/zero", positive: float64(0.0), negative: float64(0.0), isZero: true}, }) } // testAllSign tests both Positive and Negative functions with the same test data. func testAllSign(tc signTestCase) func(*testing.T) { return func(t *testing.T) { t.Parallel() if tc.isZero { // Zero should fail both Positive and Negative t.Run("zero should fail both", func(t *testing.T) { t.Run("Positive should fail", testSign(Positive, tc.positive, false)) t.Run("Negative should fail", testSign(Negative, tc.positive, false)) }) return } // Test positive and negative values t.Run("with positive/negative values", func(t *testing.T) { t.Run("Positive", func(t *testing.T) { t.Run("should pass (positive value)", testSign(Positive, tc.positive, true)) t.Run("should fail (negative value)", testSign(Positive, tc.negative, false)) }) t.Run("Negative", func(t *testing.T) { t.Run("should pass (negative value)", testSign(Negative, tc.negative, true)) t.Run("should fail (positive value)", testSign(Negative, tc.positive, false)) }) }) } } // testSign is a helper that tests a sign function. func testSign(sign func(T, any, ...any) bool, e any, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := sign(mock, e) shouldPassOrFail(t, mock, result, shouldPass) } } // testAllSignT tests both PositiveT and NegativeT functions with the same test data. func testAllSignT[V SignedNumeric](tc signTestCase) func(*testing.T) { return func(t *testing.T) { t.Parallel() // Type assert the values positive, ok1 := tc.positive.(V) negative, ok2 := tc.negative.(V) if !ok1 || !ok2 { t.Fatalf("type mismatch: expected %T, got positive=%T, negative=%T", *new(V), tc.positive, tc.negative) } if tc.isZero { // Zero should fail both PositiveT and NegativeT t.Run("zero should fail both", func(t *testing.T) { t.Run("PositiveT should fail", testSignT(PositiveT[V], positive, false)) t.Run("NegativeT should fail", testSignT(NegativeT[V], positive, false)) }) return } // Test positive and negative values t.Run("with positive/negative values", func(t *testing.T) { t.Run("PositiveT", func(t *testing.T) { t.Run("should pass (positive value)", testSignT(PositiveT[V], positive, true)) t.Run("should fail (negative value)", testSignT(PositiveT[V], negative, false)) }) t.Run("NegativeT", func(t *testing.T) { t.Run("should pass (negative value)", testSignT(NegativeT[V], negative, true)) t.Run("should fail (positive value)", testSignT(NegativeT[V], positive, false)) }) }) } } // testSignT is a helper that tests a generic sign function. func testSignT[V SignedNumeric](sign func(T, V, ...any) bool, e V, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := sign(mock, e) shouldPassOrFail(t, mock, result, shouldPass) } } // ============================================================================ // TestCompareErrorMessages // ============================================================================ func compareFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "Greater/int", assertion: func(t T) bool { return Greater(t, 1, 2) }, wantContains: []string{`"1" is not greater than "2"`}, }, { name: "GreaterOrEqual/int", assertion: func(t T) bool { return GreaterOrEqual(t, 1, 2) }, wantContains: []string{`"1" is not greater than or equal to "2"`}, }, { name: "Less/int", assertion: func(t T) bool { return Less(t, 2, 1) }, wantContains: []string{`"2" is not less than "1"`}, }, { name: "LessOrEqual/int", assertion: func(t T) bool { return LessOrEqual(t, 2, 1) }, wantContains: []string{`"2" is not less than or equal to "1"`}, }, { name: "Positive/negative-value", assertion: func(t T) bool { return Positive(t, -1) }, wantContains: []string{`"-1" is not positive`}, }, { name: "Negative/positive-value", assertion: func(t T) bool { return Negative(t, 1) }, wantContains: []string{`"1" is not negative`}, }, }) } go-openapi-testify-c10ca71/internal/assertions/condition.go000066400000000000000000001016121520301377500241650ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "context" "errors" "fmt" "reflect" "runtime" "sync" "sync/atomic" "testing" "testing/synctest" "time" ) // Condition uses a comparison function to assert a complex condition. // // # Usage // // assertions.Condition(t, func() bool { return myCondition }) // // # Examples // // success: func() bool { return true } // failure: func() bool { return false } func Condition(t T, comp func() bool, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() } result := comp() if !result { Fail(t, "condition failed", msgAndArgs...) } return result } // Blocked asserts that a channel is blocked on receive. // // It always fails if the operand is not a channel, or if the channel is send-only. // // # Usage // // ch := make(chan struct{}) // assertions.Blocked(t, ch) // // # Examples // // success: make(chan struct{}) // failure: sendChanMessage() func Blocked(t T, ch any, msgAndArgs ...any) bool { // Domain: condition // Opposite: NotBlocked if h, ok := t.(H); ok { h.Helper() } chanType := reflect.TypeOf(ch) if chanType == nil || chanType.Kind() != reflect.Chan { return Fail(t, fmt.Sprintf("Expected a channel but got: %T", ch), msgAndArgs...) } if chanType.ChanDir()&reflect.RecvDir == 0 { return Fail(t, "Expected channel direction to allow receive", msgAndArgs...) } chanValue := reflect.ValueOf(ch) chosen, recv, ok := reflect.Select([]reflect.SelectCase{ { Dir: reflect.SelectRecv, Chan: chanValue, }, { Dir: reflect.SelectDefault, }, }) if chosen == 0 { if ok { return Fail(t, fmt.Sprintf("Channel receive should have blocked, but got: %v", recv), msgAndArgs...) } return Fail(t, "Channel receive should have blocked, but channel was closed", msgAndArgs...) } return true } // BlockedT asserts that a channel is blocked on receive. // // # Usage // // ch := make(chan struct{}) // assertions.BlockedT(t, ch) // // # Examples // // success: make(chan struct{}) // failure: sendChanMessage() func BlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool { // Domain: condition // Opposite: NotBlockedT if h, ok := t.(H); ok { h.Helper() } rch := (<-chan E)(ch) // impose compile-time check that the channel is indeed open for receive select { case r, ok := <-rch: if ok { return Fail(t, fmt.Sprintf("Channel receive should have blocked, but got: %v", r), msgAndArgs...) } return Fail(t, "Channel receive should have blocked, but channel was closed", msgAndArgs...) default: return true } } // NotBlocked asserts that a channel is not blocked on receive. // // It always fails if the operand is not a channel, or if the channel is send-only. // // A closed channel doesn't block and returns true. // Notice that this consumes any message available in the channel. // // # Usage // // ch := make(chan struct{}, 1) // ch <- struct{}{} // assertions.NotBlocked(t, ch) // // # Examples // // success: sendChanMessage() // failure: make(chan struct{}) func NotBlocked(t T, ch any, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() } chanType := reflect.TypeOf(ch) if chanType == nil || chanType.Kind() != reflect.Chan { return Fail(t, fmt.Sprintf("Expected a channel but got: %T", ch), msgAndArgs...) } if chanType.ChanDir()&reflect.RecvDir == 0 { return Fail(t, "Expected channel direction to allow receive", msgAndArgs...) } chanValue := reflect.ValueOf(ch) chosen, _, _ := reflect.Select([]reflect.SelectCase{ { Dir: reflect.SelectRecv, Chan: chanValue, }, { Dir: reflect.SelectDefault, }, }) if chosen == 0 { return true } return Fail(t, "Channel receive should not have blocked", msgAndArgs...) } // NotBlockedT asserts that a channel is not blocked on receive. // // A closed channel doesn't block and returns true. // Notice that this consumes any message available in the channel. // // # Usage // // ch := make(chan struct{}, 1) // ch <- struct{}{} // assertions.NotBlockedT(t, ch) // // # Examples // // success: sendChanMessage() // failure: make(chan struct{}) func NotBlockedT[E any, CHAN ~chan E](t T, ch CHAN, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() } rch := (<-chan E)(ch) // impose compile-time check that the channel is indeed open for receive select { case <-rch: return true default: return Fail(t, "Channel receive should not have blocked", msgAndArgs...) } } // Eventually asserts that the given condition will be met before timeout, // periodically checking the target function on each tick. // // [Eventually] waits until the condition returns true, at most until timeout, // or until the parent context of the test is cancelled. // // If the condition takes longer than the timeout to complete, [Eventually] fails // but waits for the current condition execution to finish before returning. // // For long-running conditions to be interrupted early, check [testing.T.Context] // which is cancelled on test failure. // // # Usage // // assertions.Eventually(t, func() bool { return true }, time.Second, 10*time.Millisecond) // // # Alternative condition signature // // The simplest form of condition is: // // func() bool // // To build more complex cases, a condition may also be defined as: // // func(context.Context) error // // It fails when an error has always been returned up to timeout (equivalent semantics to func() bool returns false), // expressing "eventually returns no error (nil)". // // It will be executed with the context of the assertion, which inherits the [testing.T.Context] and // is cancelled on timeout. // // The semantics of the three available async assertions read as follows. // // - [Eventually] (func() bool) : "eventually returns true" // // - [Never] (func() bool) : "never returns true" // // - [Consistently] (func() bool): "always returns true" // // - [Eventually] (func(ctx) error) : "eventually returns nil" // // - [Never] (func(ctx) error) : not supported, use [Consistently] instead (avoids confusion with double negation) // // - [Consistently] (func(ctx) error): "always returns nil" // // # Concurrency // // The condition function is always executed serially by a single goroutine. It is always executed at least once. // // It may thus write to variables outside its scope without triggering race conditions. // // A blocking condition will cause [Eventually] to hang until it returns. // // Notice that time ticks may be skipped if the condition takes longer than the tick interval. // // # Panic recovery // // If the condition panics, the panic is recovered and treated as a failed tick // (equivalent to returning false or a non-nil error). For [Eventually], this means // the poller retries on the next tick — if a later tick succeeds, the assertion // succeeds. For [Never] and [Consistently], a panic is treated as the condition // erroring, which causes immediate failure. // // The recovered panic is wrapped as an error with the sentinel [errConditionPanicked], // detectable with [errors.Is]. // // # Attention point // // Time-based tests may be flaky in a resource-constrained environment such as a CI runner and may produce // counter-intuitive results, such as ticks or timeouts not firing in time as expected. // // To avoid flaky tests, always make sure that ticks and timeouts differ by at least an order of magnitude (tick << // timeout). // // # Synctest (opt-in) // // Wrap the condition with [WithSynctest] (or [WithSynctestContext]) to run // the polling loop inside a [testing/synctest] bubble, which uses a fake // clock. This eliminates timing-induced flakiness and makes the tick count // deterministic. See [WithSynctest] for the constraints (no real I/O in // the condition, requires `*testing.T`). // // # Examples // // success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond func Eventually[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { // Domain: condition // Opposite: Never if h, ok := t.(H); ok { h.Helper() } return eventually(t, condition, timeout, tick, msgAndArgs...) } // Never asserts that the given condition is never satisfied until timeout, // periodically checking the target function at each tick. // // [Never] is the opposite of [Eventually] ("at least once"). // It succeeds if the timeout is reached without the condition ever returning true. // // If the parent context is cancelled before the timeout, [Never] fails. // // # Usage // // assertions.Never(t, func() bool { return false }, time.Second, 10*time.Millisecond) // // See also [Eventually] for details about using context, concurrency, and panic recovery. // // # Alternative condition signature // // The simplest form of condition is: // // func() bool // // Use [Consistently] instead if you want to use a condition returning an error. // // # Panic recovery // // A panicking condition is treated as an error, causing [Never] to fail immediately. // See [Eventually] for details. // // # Concurrency // // See [Eventually]. // // # Attention point // // See [Eventually]. // // # Synctest (opt-in) // // Wrap the condition with [WithSynctest] to run the polling loop inside a // [testing/synctest] bubble, which uses a fake clock. This eliminates // timing-induced flakiness and makes the tick count deterministic. See // [WithSynctest] for the constraints (no real I/O in the condition, // requires [*testing.T]). Note: [Never] does not accept the context/error // form of condition, so [WithSynctestContext] does not apply here. // // # Examples // // success: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond func Never[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() } return never(t, condition, timeout, tick, msgAndArgs...) } // Consistently asserts that the given condition is always satisfied until timeout, // periodically checking the target function at each tick. // // [Consistently] ("always") imposes a stronger constraint than [Eventually] ("at least once"): // it checks at every tick that every occurrence of the condition is satisfied, whereas // [Eventually] succeeds on the first occurrence of a successful condition. // // # Usage // // assertions.Consistently(t, func() bool { return true }, time.Second, 10*time.Millisecond) // // See also [Eventually] for details about using context, concurrency, and panic recovery. // // # Alternative condition signature // // The simplest form of condition is: // // func() bool // // The semantics of the assertion are "always returns true". // // To build more complex cases, a condition may also be defined as: // // func(context.Context) error // // It fails as soon as an error is returned before timeout expressing "always returns no error (nil)" // // This is consistent with [Eventually] expressing "eventually returns no error (nil)". // // It will be executed with the context of the assertion, which inherits the [testing.T.Context] and // is cancelled on timeout. // // # Panic recovery // // A panicking condition is treated as an error, causing [Consistently] to fail immediately. // See [Eventually] for details. // // # Concurrency // // See [Eventually]. // // # Attention point // // See [Eventually]. // // # Synctest (opt-in) // // Wrap the condition with [WithSynctest] (or [WithSynctestContext]) to run // the polling loop inside a [testing/synctest] bubble, which uses a fake // clock. This eliminates timing-induced flakiness and makes the tick count // deterministic. See [WithSynctest] for the constraints (no real I/O in // the condition, requires [*testing.T]). // // # Examples // // success: func() bool { return true }, 100*time.Millisecond, 20*time.Millisecond // failure: func() bool { return false }, 100*time.Millisecond, 20*time.Millisecond func Consistently[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() } return consistently(t, condition, timeout, tick, msgAndArgs...) } // EventuallyWith asserts that the given condition will be met before the timeout, // periodically checking the target function at each tick. // // In contrast to [Eventually], the condition function is supplied with a [CollectT] // to accumulate errors from calling other assertions. // // The condition is considered "met" if no errors are raised in a tick. // The supplied [CollectT] collects all errors from one tick. // // If the condition is not met before the timeout, the collected errors from the // last tick are copied to t. // // Calling [CollectT.FailNow] (directly, or transitively through [require] assertions) // fails the current tick only: the poller will retry on the next tick. This means // [require]-style assertions inside [EventuallyWith] behave naturally — they abort // the current evaluation and let the polling loop converge. // // To abort the whole assertion immediately (e.g. when the condition can no longer // be expected to succeed), call [CollectT.Cancel]. // // # Usage // // externalValue := false // go func() { // time.Sleep(8*time.Second) // externalValue = true // }() // // assertions.EventuallyWith(t, func(c *assertions.CollectT) { // // add assertions as needed; any assertion failure will fail the current tick // assertions.True(c, externalValue, "expected 'externalValue' to be true") // }, // 10*time.Second, // 1*time.Second, // "external state has not changed to 'true'; still false", // ) // // # Concurrency // // The condition function is never executed in parallel: only one goroutine executes it. // It may write to variables outside its scope without triggering race conditions. // // The condition is wrapped in its own goroutine, so a call to [runtime.Goexit] // (e.g. via [require] assertions or [CollectT.FailNow]) cleanly aborts only the // current tick. // // # Panic recovery // // If the condition panics, the panic is recovered and recorded as an error in the // [CollectT] for that tick. The poller treats it as a failed tick and retries on the // next one. If the assertion times out, the panic error is included in the collected // errors reported on the parent t. // // See [Eventually] for the general panic recovery semantics. // // # Synctest (opt-in) // // Wrap the condition with [WithSynctestCollect] (or [WithSynctestCollectContext]) // to run the polling loop inside a [testing/synctest] bubble, which uses // a fake clock. This eliminates timing-induced flakiness and makes the // tick count deterministic. See [WithSynctest] for the constraints (no // real I/O in the condition, requires [*testing.T]). // // # Examples // // success: func(c *CollectT) { True(c,true) }, 100*time.Millisecond, 20*time.Millisecond // failure: func(c *CollectT) { False(c,true) }, 100*time.Millisecond, 20*time.Millisecond // failure: func(c *CollectT) { c.Cancel() }, 100*time.Millisecond, 20*time.Millisecond func EventuallyWith[C CollectibleConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { // Domain: condition if h, ok := t.(H); ok { h.Helper() } return eventuallyWithT(t, condition, timeout, tick, msgAndArgs...) } func eventually[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } wantsBubble, cond := makeCondition(condition, false) p := newConditionPoller(pollOptions{ mode: pollUntilTrue, failMessage: "condition never satisfied", }) return runPoller(t, p, cond, timeout, tick, wantsBubble, msgAndArgs...) } func never[C NeverConditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } wantsBubble, cond := makeCondition(condition, true) p := newConditionPoller(pollOptions{ mode: pollUntilTimeout, failMessage: "condition satisfied", }) return runPoller(t, p, cond, timeout, tick, wantsBubble, msgAndArgs...) } func consistently[C Conditioner](t T, condition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } wantsBubble, cond := makeCondition(condition, false) p := newConditionPoller(pollOptions{ mode: pollUntilTimeout, failMessage: "condition failed once", }) return runPoller(t, p, cond, timeout, tick, wantsBubble, msgAndArgs...) } func eventuallyWithT[C CollectibleConditioner](t T, collectCondition C, timeout time.Duration, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } var lastCollectedErrors []error var cancelFunc func() // will be set by pollCondition via onSetup wantsBubble, fn := makeCollectibleCondition(collectCondition) condition := func(ctx context.Context) (err error) { collector := new(CollectT).withCancelFunc(cancelFunc) defer func() { if r := recover(); r != nil { err = fmt.Errorf("%w: %v", errConditionPanicked, r) collector.errors = append(collector.errors, err) } if collector.failed() { lastCollectedErrors = collector.collected() err = collector.last() } }() fn(ctx, collector) return nil } copyCollected := func(tt T) { for _, err := range lastCollectedErrors { tt.Errorf("%v", err) } } p := newConditionPoller(pollOptions{ mode: pollUntilTrue, failMessage: "condition never satisfied", onFailure: copyCollected, onSetup: func(cancel func()) { cancelFunc = cancel }, }) return runPoller(t, p, condition, timeout, tick, wantsBubble, msgAndArgs...) } // runPoller dispatches the polling to either the real-time or the // [synctest] bubble-wrapped path, based on whether the condition opted into // fake time AND the caller passed a concrete [*testing.T]. // // When `wantsBubble` is true but `t` is not a `*testing.T` (e.g. a mock or // [CollectT]), the call silently falls back to real-time polling. The // synctest bubble requires a real `*testing.T`. func runPoller(t T, p *conditionPoller, cond func(context.Context) error, timeout, tick time.Duration, wantsBubble bool, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } testingT, canBubble := t.(*testing.T) if !wantsBubble || !canBubble { return p.pollCondition(t, cond, timeout, tick, msgAndArgs...) } var result bool synctest.Test(testingT, func(inner *testing.T) { result = p.pollCondition(inner, cond, timeout, tick, msgAndArgs...) }) return result } // makeCondition normalizes any variant from [Conditioner] or [NeverConditioner] // into the unified `func(context.Context) error` form used by [pollCondition], // and reports whether the caller opted into synctest-bubble polling. // // [WithSynctest] and [WithSynctestContext] are recognized as their underlying // `func() bool` and `func(context.Context) error` forms with `wantsBubble = true`. func makeCondition(condition any, reverse bool) (wantsBubble bool, cond func(context.Context) error) { switch typed := condition.(type) { case WithSynctest: _, cond = makeCondition((func() bool)(typed), reverse) return true, cond case WithSynctestContext: _, cond = makeCondition((func(context.Context) error)(typed), reverse) return true, cond case func() bool: if !reverse { return false, func(ctx context.Context) error { select { case <-ctx.Done(): return ctx.Err() default: if res := typed(); !res { return errors.New("condition returned false") } return nil } } } // inverse bool <-> error logic for Never return false, func(ctx context.Context) error { select { case <-ctx.Done(): return nil default: if res := typed(); res { return errors.New("condition returned true") } return nil } } case func(context.Context) error: // No reversal needed: the poller already uses err != nil as "condition happened". // For Eventually: err == nil = success. For Never: err != nil = failure. // Both align with the natural error semantics without inversion. return false, typed default: // unreachable panic(fmt.Errorf("unsupported Conditioner type. Mismatch with type constraint: %T", condition)) } } // makeCollectibleCondition normalizes any [CollectibleConditioner] variant // into the unified `func(context.Context, *CollectT)` form, and reports // whether the caller opted into synctest-bubble polling. func makeCollectibleCondition(condition any) (wantsBubble bool, fn func(context.Context, *CollectT)) { switch typed := condition.(type) { case WithSynctestCollect: _, fn = makeCollectibleCondition((func(*CollectT))(typed)) return true, fn case WithSynctestCollectContext: _, fn = makeCollectibleCondition((func(context.Context, *CollectT))(typed)) return true, fn case func(*CollectT): return false, func(ctx context.Context, collector *CollectT) { select { case <-ctx.Done(): collector.Errorf("%v", ctx.Err()) default: typed(collector) } } case func(context.Context, *CollectT): return false, typed default: // unreachable panic(fmt.Errorf("unsupported CollectibleConditioner type. Mismatch with type constraint: %T", condition)) } } func recoverCondition(fn func(context.Context) error) func(context.Context) error { return func(ctx context.Context) (err error) { defer func() { if r := recover(); r != nil { err = fmt.Errorf("%w: %v", errConditionPanicked, r) } }() return fn(ctx) } } type conditionPoller struct { pollOptions ticker *time.Ticker reported atomic.Bool conditionChan chan func(context.Context) error doneChan chan struct{} } func newConditionPoller(o pollOptions) *conditionPoller { return &conditionPoller{ pollOptions: o, } } // initChannels creates the polling channels. MUST be called from inside // [pollCondition] so that — when the caller activated a [synctest] bubble // — the channels are bubble-owned. Receives on channels created outside // the bubble do NOT count as durably blocking, which stalls the fake clock. func (p *conditionPoller) initChannels() { p.conditionChan = make(chan func(context.Context) error, 1) p.doneChan = make(chan struct{}) } // pollMode determines how the condition polling should behave. type pollMode int const ( // pollUntilTrue succeeds when condition returns true (for Eventually). pollUntilTrue pollMode = iota // pollUntilTimeout succeeds when timeout is reached without condition being true (for Never/Consistently). pollUntilTimeout ) // pollOptions configures the condition polling behavior. type pollOptions struct { mode pollMode failMessage string // error message added at the end of the stack onFailure func(t T) // called on failure (e.g., to copy collected errors) onSetup func(cancel func()) // called after context setup to expose cancel function } // pollCondition is the common implementation for eventually, never, and eventuallyWithT. // // It polls a condition function at regular intervals until success or timeout. func (p *conditionPoller) pollCondition(t T, condition func(context.Context) error, timeout, tick time.Duration, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } parentCtx := p.parentContextFromT(t) ctx, cancel := p.cancellableContext(parentCtx, timeout) defer cancel() failFunc := p.failFunc(t, msgAndArgs...) // Allow caller to capture the cancel function (for eventuallyWithT's CollectT) if p.onSetup != nil { p.onSetup(cancel) } condition = recoverCondition(condition) // Channels and ticker MUST be created inside pollCondition so that, // when the caller activated a synctest bubble, they are bubble-owned // primitives. Channels created outside the bubble do not count as // durably blocking and would stall the fake clock. p.initChannels() p.ticker = time.NewTicker(tick) defer p.ticker.Stop() // Check the condition once first on the initial call. p.conditionChan <- condition var wg sync.WaitGroup // Goroutine 1: Poll for the condition at every tick wg.Add(1) go p.pollAtTickFunc(parentCtx, ctx, condition, failFunc, &wg)() // 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(context.Context) error, 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 <-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 <-parentCtx.Done(): failFunc(parentCtx.Err().Error()) return case <-ctx.Done(): return case <-p.doneChan: return case p.conditionChan <- condition: } } } } } // 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 <-p.doneChan: return case p.conditionChan <- condition: } } } } } func (p *conditionPoller) executeCondition(parentCtx, ctx context.Context, failFunc func(string), wg *sync.WaitGroup) func() { if p.mode == pollUntilTimeout { // For Never and Consistently return func() { defer wg.Done() for { select { case <-parentCtx.Done(): failFunc(parentCtx.Err().Error()) return case <-ctx.Done(): return // timeout = success case fn := <-p.conditionChan: var conditionWg sync.WaitGroup conditionWg.Go(func() { // guards against the condition issue an early GoExit if err := fn(ctx); err != nil { close(p.doneChan) // (condition true <=> returns error) = failure for Never and Consistently } }) conditionWg.Wait() select { case <-p.doneChan: // done: early exit return default: } } } } } // For Eventually return func() { defer wg.Done() for { select { case <-ctx.Done(): failFunc(ctx.Err().Error()) return case fn := <-p.conditionChan: var conditionWg sync.WaitGroup conditionWg.Go(func() { // guards against the condition issue an early GoExit if err := fn(ctx); err == nil { close(p.doneChan) // (condition true <=> err == nil) = success for Eventually } }) conditionWg.Wait() select { case <-p.doneChan: // done: early exit return default: } } } } } 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 } } } return func() bool { select { case <-p.doneChan: // For Eventually: doneChan closed means condition became true if ctx.Err() != nil { // Timeout occurred before or during success if p.onFailure != nil { p.onFailure(t) } return false } 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 } } } 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 parentCtx } func (p *conditionPoller) cancellableContext(parentCtx context.Context, timeout 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), timeout) } else { ctx, cancel = context.WithTimeout(parentCtx, timeout) } return ctx, cancel } // Sentinel errors recorded by async condition assertions. // Kept package-private: callers should rely on observable behavior, not on // the marker shape. They are distinguishable so future tooling can tell apart // "tick aborted by require", "user explicitly cancelled the assertion", // and "condition panicked". var ( errFailNow = errors.New("collect: failed now (tick aborted)") errCancelled = errors.New("collect: cancelled (assertion aborted)") errConditionPanicked = errors.New("condition panicked") ) // CollectT implements the [T] interface and collects all errors. // // [CollectT] is specifically intended to be used with [EventuallyWith] and // should not be used outside of that context. type CollectT struct { // Domain: condition // // Maintainer: // 1. FailNow() exits the current tick goroutine via runtime.Goexit (matching // stretchr/testify semantics): require-style assertions abort the current // evaluation and the poller retries on the next tick. It does NOT cancel // the EventuallyWith context. // 2. Cancel() is the explicit escape hatch: it cancels the EventuallyWith // context before exiting via runtime.Goexit, aborting the whole assertion. // 3. We no longer establish the distinction between c.errors nil or empty. // Non-empty is an error, full stop. // 4. Deprecated methods have been removed. // A slice of errors. Non-empty slice denotes a failure. errors []error // cancelContext cancels the parent EventuallyWith context on Cancel(). cancelContext func() } // Helper is like [testing.T.Helper] but does nothing. func (*CollectT) Helper() {} // Errorf collects the error. func (c *CollectT) Errorf(format string, args ...any) { c.errors = append(c.errors, fmt.Errorf(format, args...)) } // FailNow records a failure for the current tick and exits the condition // goroutine via [runtime.Goexit]. // // It does NOT cancel the [EventuallyWith] context: the poller will retry on // the next tick. If a later tick succeeds, the assertion succeeds. If no tick // ever succeeds before the timeout, the errors collected during the LAST tick // (the one which most recently called FailNow) are reported on the parent t. // // To abort the whole assertion immediately, use [CollectT.Cancel]. func (c *CollectT) FailNow() { c.errors = append(c.errors, errFailNow) runtime.Goexit() } // Cancel records a failure, cancels the [EventuallyWith] context, then exits // the condition goroutine via [runtime.Goexit]. // // This aborts the whole assertion immediately, without waiting for the timeout. // The errors collected during the cancelled tick are reported on the parent t. // // Use this when the condition can no longer be expected to succeed (e.g. an // upstream resource has been observed in an unrecoverable state). For ordinary // per-tick failures (e.g. "value not yet ready"), use [CollectT.FailNow] // directly or transitively through [require] assertions. func (c *CollectT) Cancel() { c.errors = append(c.errors, errCancelled) c.cancelContext() runtime.Goexit() } // Cancelf records a failure like [Cancel], with an additional custom message recorded. func (c *CollectT) Cancelf(format string, msgAndArgs ...any) { c.errors = append(c.errors, fmt.Errorf(format, msgAndArgs...)) c.Cancel() } func (c *CollectT) failed() bool { return len(c.errors) != 0 } func (c *CollectT) collected() []error { return c.errors } func (c *CollectT) last() error { if len(c.errors) == 0 { return nil } return c.errors[len(c.errors)-1] } func (c *CollectT) withCancelFunc(cancel func()) *CollectT { c.cancelContext = cancel return c } go-openapi-testify-c10ca71/internal/assertions/condition_synctest_test.go000066400000000000000000000666561520301377500272020ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "context" "errors" "fmt" "strings" "sync" "testing" "testing/synctest" "time" ) // =========================================================================== // Dual-path test runner. // =========================================================================== // runDualPath runs fn twice: once with real time, once inside a synctest // bubble. fn should use plain (non-wrapped) conditions and a mock T so // that failures can be verified without polluting the outer test. // // NOTE: fn MUST NOT call t.Parallel(). [synctest.Test] forbids t.Parallel() // inside a bubble. This restriction is transparent to real users of the // async assertions — users do not call t.Parallel() inside condition // functions. func runDualPath(t *testing.T, name string, fn func(t *testing.T)) { t.Helper() t.Run(name+"/real-time", fn) t.Run(name+"/synctest", func(t *testing.T) { synctest.Test(t, fn) }) } // =========================================================================== // Dual-path tests. // =========================================================================== // TestConditionDualPath_EventuallyBehavior exercises [Eventually]'s core // behavior through both real-time and bubble-wrapped test harnesses. // Using the harness-level bubble means the mock captures failures even // under fake time — the best of both worlds for behavior parity tests. func TestConditionDualPath_EventuallyBehavior(t *testing.T) { runDualPath(t, "succeeds on first call", func(t *testing.T) { mock := new(errorsCapturingT) if !Eventually(mock, func() bool { return true }, testTimeout, testTick) { t.Error("expected success") } if len(mock.errors) != 0 { t.Errorf("expected no errors, got %d", len(mock.errors)) } }) runDualPath(t, "fails on persistent false", func(t *testing.T) { mock := new(errorsCapturingT) if Eventually(mock, func() bool { return false }, testTimeout, testTick) { t.Error("expected failure") } if len(mock.errors) == 0 { t.Error("expected mock to capture at least one error") } }) runDualPath(t, "succeeds after a few ticks", func(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := func() bool { mu.Lock() defer mu.Unlock() counter++ return counter >= 3 } if !Eventually(mock, cond, testTimeout, testTick) { t.Error("expected success") } mu.Lock() got := counter mu.Unlock() if got < 3 { t.Errorf("expected at least 3 calls, got %d", got) } }) } // TestConditionDualPath_EventuallyWithErrorBehavior exercises the // context/error-returning variant of [Eventually] through both paths. func TestConditionDualPath_EventuallyWithErrorBehavior(t *testing.T) { runDualPath(t, "succeeds after returning transient errors", func(t *testing.T) { mock := new(errorsCapturingT) state := 0 cond := func(_ context.Context) error { defer func() { state++ }() if state < 2 { return errors.New("not ready yet") } return nil } if !Eventually(mock, cond, testTimeout, testTick) { t.Error("expected Eventually to return true") } }) runDualPath(t, "fails on persistent error", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(_ context.Context) error { return errors.New("persistent error") } if Eventually(mock, cond, testTimeout, testTick) { t.Error("expected Eventually to return false") } }) runDualPath(t, "receives non-nil context", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(ctx context.Context) error { if ctx == nil { return errors.New("expected non-nil context") } return nil } if !Eventually(mock, cond, testTimeout, testTick) { t.Error("expected Eventually to return true") } }) } // TestConditionDualPath_ConsistentlyWithErrorBehavior exercises the // context/error-returning variant of [Consistently] through both paths. func TestConditionDualPath_ConsistentlyWithErrorBehavior(t *testing.T) { runDualPath(t, "succeeds when always nil", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(_ context.Context) error { return nil } if !Consistently(mock, cond, testTimeout, testTick) { t.Error("expected Consistently to return true") } }) runDualPath(t, "fails on persistent error", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(_ context.Context) error { return errors.New("something went wrong") } if Consistently(mock, cond, testTimeout, testTick) { t.Error("expected Consistently to return false") } }) runDualPath(t, "fails when error appears on second call", func(t *testing.T) { mock := new(errorsCapturingT) // Channel created inside fn — under synctest, it is bubble-owned. returns := make(chan error, 2) returns <- nil returns <- errors.New("something went wrong") defer close(returns) cond := func(_ context.Context) error { return <-returns } if Consistently(mock, cond, testTimeout, testTick) { t.Error("expected Consistently to return false") } }) } // TestConditionDualPath_NeverBehavior exercises [Never] through both paths. func TestConditionDualPath_NeverBehavior(t *testing.T) { runDualPath(t, "succeeds when condition never true", func(t *testing.T) { mock := new(errorsCapturingT) if !Never(mock, func() bool { return false }, testTimeout, testTick) { t.Error("expected Never to return true") } }) runDualPath(t, "fails when condition becomes true", func(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := func() bool { mu.Lock() defer mu.Unlock() counter++ return counter == 2 } if Never(mock, cond, testTimeout, testTick) { t.Error("expected Never to return false") } }) } // TestConditionDualPath_ConsistentlyBehavior exercises [Consistently] through both paths. func TestConditionDualPath_ConsistentlyBehavior(t *testing.T) { runDualPath(t, "succeeds when condition always true", func(t *testing.T) { mock := new(errorsCapturingT) if !Consistently(mock, func() bool { return true }, testTimeout, testTick) { t.Error("expected Consistently to return true") } }) runDualPath(t, "fails when condition becomes false", func(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := func() bool { mu.Lock() defer mu.Unlock() counter++ return counter < 3 } if Consistently(mock, cond, testTimeout, testTick) { t.Error("expected Consistently to return false") } }) } // TestConditionDualPath_EventuallyWithBehavior exercises [EventuallyWith] through both paths. func TestConditionDualPath_EventuallyWithBehavior(t *testing.T) { runDualPath(t, "succeeds when no errors collected", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(_ *CollectT) {} if !EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return true") } }) runDualPath(t, "fails when errors persistently collected", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(c *CollectT) { Fail(c, "boom") } if EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return false") } }) } // TestConditionDualPath_EventuallyWithContextBehavior exercises the // context variant of [EventuallyWith] (`func(ctx, *CollectT)`) through // both paths. func TestConditionDualPath_EventuallyWithContextBehavior(t *testing.T) { runDualPath(t, "succeeds after a few calls via context variant", func(t *testing.T) { mock := new(errorsCapturingT) counter := 0 cond := func(_ context.Context, c *CollectT) { counter++ True(c, counter == 2) } if !EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return true") } if len(mock.errors) != 0 { t.Errorf("expected 0 errors, got %d", len(mock.errors)) } if counter != 2 { t.Errorf("expected exactly 2 calls, got %d", counter) } }) runDualPath(t, "fails on persistent collected failure via context variant", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(_ context.Context, c *CollectT) { Fail(c, "fixed failure") } if EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return false") } }) runDualPath(t, "receives non-nil context", func(t *testing.T) { mock := new(errorsCapturingT) cond := func(ctx context.Context, c *CollectT) { if ctx == nil { Fail(c, "expected non-nil context") } } if !EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return true") } }) } // TestConditionDualPath_EventuallySucceedQuickly verifies that Eventually // checks the condition BEFORE the first tick — by using a tick longer than // the total duration, only the initial-check path can succeed. func TestConditionDualPath_EventuallySucceedQuickly(t *testing.T) { t.Parallel() runDualPath(t, "should succeed before the first tick", dualEventuallySucceedBeforeFirstTick) } func dualEventuallySucceedBeforeFirstTick(t *testing.T) { mock := new(errorsCapturingT) cond := func() bool { return true } // Tick longer than the total duration: only the initial check can succeed. if !Eventually(mock, cond, testTimeout, 1*time.Second) { t.Error("expected Eventually to return true before first tick") } } // TestConditionDualPath_EventuallyTimeoutBehavior verifies that Eventually // fails correctly when the condition is slower than the timeout (issue 805) // and when the parent context is cancelled. Both subtests run under real // time and inside a synctest bubble — reassurance that no semantic shift // occurs when switching modes. func TestConditionDualPath_EventuallyTimeoutBehavior(t *testing.T) { t.Parallel() runDualPath(t, "should fail on timeout", dualEventuallyTimeoutOnSlowCondition) runDualPath(t, "should fail when parent context is cancelled", dualEventuallyTimeoutOnParentCancellation) } func dualEventuallyTimeoutOnSlowCondition(t *testing.T) { mock := new(errorsCapturingT) // Condition returns long after the Eventually timeout. cond := func() bool { time.Sleep(100 * time.Millisecond) return true } if Eventually(mock, cond, time.Millisecond, time.Microsecond) { t.Error("expected Eventually to return false on timeout") } } func dualEventuallyTimeoutOnParentCancellation(t *testing.T) { parentCtx, failParent := context.WithCancel(context.WithoutCancel(t.Context())) mock := new(errorsCapturingT).WithContext(parentCtx) cond := func() bool { time.Sleep(testTick) failParent() // cancels the parent context mid-assertion time.Sleep(2 * testTick) return true } if Eventually(mock, cond, testTimeout, testTick) { t.Error("expected Eventually to return false when parent context is cancelled") } // Flattened from the original nested t.Run: nested subtests are forbidden // inside a synctest bubble. These assertions verify that the reported // errors include both the context-cancellation cause and the // "never satisfied" marker. if len(mock.errors) != 2 { t.Errorf("expected 2 error messages (1 for context canceled, 1 for never satisfied), got %d", len(mock.errors)) } var hasContextCancelled, hasFailedCondition bool for _, err := range mock.errors { msg := err.Error() switch { case strings.Contains(msg, "context canceled"): hasContextCancelled = true case strings.Contains(msg, "never satisfied"): hasFailedCondition = true } } if !hasContextCancelled { t.Error("expected a context cancelled error") } if !hasFailedCondition { t.Error("expected a condition never satisfied error") } } // TestConditionDualPath_PollUntilTimeoutBehavior exercises the shared // poll-until-timeout subtests for [Never] and [Consistently] through both // real-time and synctest paths. These subtests cover timing-independent // invariants (initial-check before first tick, flipped-condition failure, // parent-context cancellation) and benefit from dual-path for determinism. func TestConditionDualPath_PollUntilTimeoutBehavior(t *testing.T) { t.Parallel() for c := range pollUntilTimeoutCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() runDualPath(t, "succeed with constant good value", dualPollCaseConstantGood(c)) runDualPath(t, "succeed on timeout with slow bad value", dualPollCaseSlowBad(c)) runDualPath(t, "fail when condition flips on second call", dualPollCaseFlipOnSecond(c)) runDualPath(t, "fail before first tick with constant bad value", dualPollCaseBadBeforeFirstTick(c)) runDualPath(t, "fail when parent context is cancelled", dualPollCaseParentCancelled(c)) }) } } // --------------------------------------------------------------------------- // Dual-path subtest bodies for PollUntilTimeout (Never / Consistently). // --------------------------------------------------------------------------- func dualPollCaseConstantGood(c pollUntilTimeoutCase) func(*testing.T) { return func(t *testing.T) { mock := new(errorsCapturingT) if !c.assertion(mock, func() bool { return c.goodValue }, testTimeout, testTick) { t.Errorf("expected %s to return true", c.name) } } } func dualPollCaseSlowBad(c pollUntilTimeoutCase) func(*testing.T) { return func(t *testing.T) { mock := new(errorsCapturingT) badValue := !c.goodValue cond := func() bool { time.Sleep(2 * testTick) return badValue // returns bad value, but only after timeout } if !c.assertion(mock, cond, testTick, 1*time.Millisecond) { t.Errorf("expected %s to return true on timeout", c.name) } } } func dualPollCaseFlipOnSecond(c pollUntilTimeoutCase) func(*testing.T) { return func(t *testing.T) { mock := new(errorsCapturingT) // Channel created inside fn — bubble-owned under synctest. badValue := !c.goodValue returns := make(chan bool, 2) returns <- c.goodValue returns <- badValue defer close(returns) cond := func() bool { return <-returns } if c.assertion(mock, cond, testTimeout, testTick) { t.Errorf("expected %s to return false", c.name) } } } func dualPollCaseBadBeforeFirstTick(c pollUntilTimeoutCase) func(*testing.T) { return func(t *testing.T) { mock := new(errorsCapturingT) badValue := !c.goodValue // Tick longer than total duration: the initial-check path must detect // the bad value before any tick elapses. if c.assertion(mock, func() bool { return badValue }, testTimeout, time.Second) { t.Errorf("expected %s to return false", c.name) } } } func dualPollCaseParentCancelled(c pollUntilTimeoutCase) func(*testing.T) { return func(t *testing.T) { parentCtx, failParent := context.WithCancel(context.WithoutCancel(t.Context())) mock := new(errorsCapturingT).WithContext(parentCtx) cond := func() bool { failParent() // cancels the parent context return c.goodValue } if c.assertion(mock, cond, testTimeout, time.Second) { t.Errorf("expected %s to return false when parent context is cancelled", c.name) } } } // TestConditionDualPath_EventuallyWithCollectBehavior exercises the // behavioral subtests of [EventuallyWith] through both paths. The // [CollectT]-based invariants (FailNow retries, Cancel short-circuits, // initial-check before first tick, etc.) are independent of timing and // work identically under real time and fake time. // // The nanosecond-tick "race trigger" subtest of [EventuallyWith] is NOT // migrated here — see [TestConditionEventuallyWith] for the rationale. func TestConditionDualPath_EventuallyWithCollectBehavior(t *testing.T) { runDualPath(t, "should complete with false (tolerant count)", dualEventuallyWithCompleteFalse) runDualPath(t, "should complete with true", dualEventuallyWithCompleteTrue) runDualPath(t, "should complete with fail, with latest failed condition", dualEventuallyWithFailLatest) runDualPath(t, "should complete with success, with the ticker never used", dualEventuallyWithCompleteSuccess) runDualPath(t, "collect.FailNow only fails the current tick (poller retries)", dualEventuallyWithFailNowRetries) runDualPath(t, "collect.FailNow allows convergence on a later tick", dualEventuallyWithFailNowConverges) runDualPath(t, "collect.Cancel aborts the whole assertion immediately", dualEventuallyWithCancelAborts) runDualPath(t, "collect.Cancelf aborts with a custom message", dualEventuallyWithCancelfAborts) } // --------------------------------------------------------------------------- // Dual-path subtest bodies for EventuallyWith. // // These are invoked by runDualPath — both under real time and inside a // synctest bubble. They MUST NOT call t.Parallel(): synctest forbids it. // --------------------------------------------------------------------------- func dualEventuallyWithCompleteFalse(t *testing.T) { mock := new(errorsCapturingT) counter := 0 cond := func(c *CollectT) { counter++ Fail(c, "condition fixed failure") Fail(c, "another condition fixed failure") } if EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return false") } // Real-time path has scheduler jitter; synctest path is exact. // Both paths fall within this tolerance. const expectedErrors = 4 if len(mock.errors) < expectedErrors-1 || len(mock.errors) > expectedErrors { t.Errorf("expected %d errors (2 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) } expectedCalls := int(testTimeout / testTick) if counter < expectedCalls-1 || counter > expectedCalls+1 { t.Errorf("expected %d calls, got %d", expectedCalls, counter) } } func dualEventuallyWithCompleteTrue(t *testing.T) { mock := new(errorsCapturingT) counter := 0 cond := func(c *CollectT) { counter++ True(c, counter == 2) } if !EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return true") } if len(mock.errors) != 0 { t.Errorf("expected 0 errors, got %d", len(mock.errors)) } if counter != 2 { t.Errorf("expected condition to be called 2 times, got %d", counter) } } func dualEventuallyWithFailLatest(t *testing.T) { mock := new(errorsCapturingT) // Channel created inside fn — bubble-owned under synctest. mustSleep := make(chan bool, 2) mustSleep <- false mustSleep <- true close(mustSleep) cond := func(c *CollectT) { if <-mustSleep { // Ensure the second condition runs longer than the timeout. // Real time: 1s real sleep. Fake time: 1s fake sleep. time.Sleep(time.Second) return } Fail(c, "condition fixed failure") } if EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return false") } const expectedErrors = 3 if len(mock.errors) != expectedErrors { t.Errorf("expected %d errors (1 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) } } func dualEventuallyWithCompleteSuccess(t *testing.T) { mock := new(errorsCapturingT) cond := func(*CollectT) {} // Tick longer than total duration: the initial-check path must succeed. if !EventuallyWith(mock, cond, testTimeout, time.Second) { t.Error("expected EventuallyWith to return true") } } func dualEventuallyWithFailNowRetries(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := func(c *CollectT) { mu.Lock() counter++ mu.Unlock() c.FailNow() } if EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to return false") } mu.Lock() got := counter mu.Unlock() if got < 2 { t.Errorf("expected the condition to be retried multiple times, got %d call(s)", got) } } func dualEventuallyWithFailNowConverges(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := func(c *CollectT) { mu.Lock() counter++ n := counter mu.Unlock() if n < 3 { c.FailNow() } } if !EventuallyWith(mock, cond, testTimeout, testTick) { t.Error("expected EventuallyWith to eventually return true") } if len(mock.errors) != 0 { t.Errorf("expected no errors reported on parent t after success, got %d: %v", len(mock.errors), mock.errors) } } func dualEventuallyWithCancelAborts(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex // The 30-minute timeout must NOT be waited on: in the real-time path // Cancel must short-circuit quickly; in the synctest path the fake // timeout costs zero real time anyway. cond := func(c *CollectT) { mu.Lock() counter++ mu.Unlock() c.Cancel() } start := time.Now() if EventuallyWith(mock, cond, 30*time.Minute, testTick) { t.Error("expected EventuallyWith to return false") } if elapsed := time.Since(start); elapsed > 5*time.Second { t.Errorf("expected Cancel to short-circuit, but EventuallyWith took %s", elapsed) } mu.Lock() got := counter mu.Unlock() if got != 1 { t.Errorf("expected the condition to be called only once, got: %d", got) } if len(mock.errors) == 0 { t.Error("expected at least one error reported on parent t after Cancel") } } func dualEventuallyWithCancelfAborts(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := func(c *CollectT) { mu.Lock() counter++ mu.Unlock() c.Cancelf("upstream %s is gone", "service-x") } start := time.Now() if EventuallyWith(mock, cond, 30*time.Minute, testTick) { t.Error("expected EventuallyWith to return false") } if elapsed := time.Since(start); elapsed > 5*time.Second { t.Errorf("expected Cancelf to short-circuit, but EventuallyWith took %s", elapsed) } mu.Lock() got := counter mu.Unlock() if got != 1 { t.Errorf("expected the condition to be called once, got %d", got) } foundCustom := false for _, err := range mock.errors { if strings.Contains(err.Error(), "upstream service-x is gone") { foundCustom = true break } } if !foundCustom { t.Errorf("expected custom Cancelf message in errors, got: %v", mock.errors) } } // =========================================================================== // API-level tests — verify the WithSynctest wrapper types activate the // internal bubble when t is a concrete *testing.T. // =========================================================================== // TestSynctest_EventuallyDeterministicCount verifies that the WithSynctest // wrapper activates a bubble, enabling exact tick counts under fake time. func TestSynctest_EventuallyDeterministicCount(t *testing.T) { var counter int var mu sync.Mutex const target = 3 cond := WithSynctest(func() bool { mu.Lock() defer mu.Unlock() counter++ return counter == target }) if !Eventually(t, cond, testTimeout, testTick) { t.Fatal("expected Eventually to succeed") } mu.Lock() got := counter mu.Unlock() if got != target { t.Errorf("expected exactly %d calls under fake time, got %d", target, got) } } // TestSynctest_EventuallyHugeTimeoutInstant verifies that a 1-hour timeout // with a 1-minute tick completes in milliseconds of real wall time when // the condition succeeds — proving fake time is used. func TestSynctest_EventuallyHugeTimeoutInstant(t *testing.T) { var counter int var mu sync.Mutex cond := WithSynctest(func() bool { mu.Lock() defer mu.Unlock() counter++ return counter == 5 }) start := time.Now() if !Eventually(t, cond, 1*time.Hour, 1*time.Minute) { t.Fatal("expected success") } if elapsed := time.Since(start); elapsed > 1*time.Second { t.Errorf("expected fake time to be instant, took %s", elapsed) } } // TestSynctest_EventuallyContextVariant verifies the [WithSynctestContext] // wrapper (context/error condition form). func TestSynctest_EventuallyContextVariant(t *testing.T) { var counter int var mu sync.Mutex cond := WithSynctestContext(func(_ context.Context) error { mu.Lock() defer mu.Unlock() counter++ if counter < 3 { return errors.New("not yet") } return nil }) if !Eventually(t, cond, testTimeout, testTick) { t.Fatal("expected success") } mu.Lock() got := counter mu.Unlock() if got != 3 { t.Errorf("expected exactly 3 calls, got %d", got) } } // TestSynctest_NeverUsesBubble verifies [Never] activates a bubble with the // [WithSynctest] wrapper. func TestSynctest_NeverUsesBubble(t *testing.T) { cond := WithSynctest(func() bool { return false }) start := time.Now() if !Never(t, cond, 1*time.Hour, 1*time.Minute) { t.Fatal("expected Never to succeed") } if elapsed := time.Since(start); elapsed > 1*time.Second { t.Errorf("expected fake time to be instant, took %s", elapsed) } } // TestSynctest_ConsistentlyUsesBubble verifies [Consistently] activates a bubble. func TestSynctest_ConsistentlyUsesBubble(t *testing.T) { cond := WithSynctest(func() bool { return true }) start := time.Now() if !Consistently(t, cond, 1*time.Hour, 1*time.Minute) { t.Fatal("expected Consistently to succeed") } if elapsed := time.Since(start); elapsed > 1*time.Second { t.Errorf("expected fake time to be instant, took %s", elapsed) } } // TestSynctest_EventuallyWithUsesBubble verifies [EventuallyWith] activates // a bubble with the [WithSynctestCollect] wrapper. func TestSynctest_EventuallyWithUsesBubble(t *testing.T) { var counter int var mu sync.Mutex cond := WithSynctestCollect(func(c *CollectT) { mu.Lock() counter++ n := counter mu.Unlock() True(c, n >= 3) }) if !EventuallyWith(t, cond, testTimeout, testTick) { t.Fatal("expected EventuallyWith to succeed") } mu.Lock() got := counter mu.Unlock() if got != 3 { t.Errorf("expected exactly 3 calls, got %d", got) } } // TestSynctest_EventuallyWithContextVariant verifies the // [WithSynctestCollectContext] wrapper. func TestSynctest_EventuallyWithContextVariant(t *testing.T) { var counter int var mu sync.Mutex cond := WithSynctestCollectContext(func(_ context.Context, c *CollectT) { mu.Lock() counter++ n := counter mu.Unlock() True(c, n >= 3) }) if !EventuallyWith(t, cond, testTimeout, testTick) { t.Fatal("expected EventuallyWith to succeed") } } // TestSynctest_FallbackOnMock verifies that passing a non-*testing.T mock // with WithSynctest falls back to real-time polling (no bubble). func TestSynctest_FallbackOnMock(t *testing.T) { mock := new(errorsCapturingT) var counter int var mu sync.Mutex cond := WithSynctest(func() bool { mu.Lock() defer mu.Unlock() counter++ return false }) start := time.Now() if Eventually(mock, cond, testTimeout, testTick) { t.Error("expected Eventually to return false on mock") } elapsed := time.Since(start) // Real-time polling: should take close to testTimeout. if elapsed < testTimeout/2 { t.Errorf("expected real-time polling on mock path, took only %s", elapsed) } } // TestSynctest_PanicRecovery verifies panic recovery works through the // bubble — recoverCondition treats a panic as a failed tick, and the // poller retries. func TestSynctest_PanicRecovery(t *testing.T) { var counter int var mu sync.Mutex cond := WithSynctest(func() bool { mu.Lock() counter++ n := counter mu.Unlock() if n < 3 { panic(fmt.Sprintf("boom %d", n)) } return true }) if !Eventually(t, cond, testTimeout, testTick) { t.Fatal("expected Eventually to succeed after recovering panics") } } // TestSynctest_SlowConditionNoLeak verifies no goroutine leak when the // condition sleeps longer than the tick interval. If a goroutine leaked, // synctest.Test would deadlock waiting for it to exit. func TestSynctest_SlowConditionNoLeak(t *testing.T) { var counter int var mu sync.Mutex cond := WithSynctest(func() bool { mu.Lock() counter++ n := counter mu.Unlock() // time.Sleep advances fake clock when all goroutines block durably. time.Sleep(2 * testTick) return n >= 2 }) start := time.Now() if !Eventually(t, cond, testTimeout, testTick) { t.Fatal("expected success") } if elapsed := time.Since(start); elapsed > 1*time.Second { t.Errorf("expected fake-time sleeps to cost no real time, took %s", elapsed) } } go-openapi-testify-c10ca71/internal/assertions/condition_test.go000066400000000000000000000470121520301377500252270ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "fmt" "iter" "slices" "sort" "sync" "testing" "time" ) const ( testTimeout = 100 * time.Millisecond testTick = 20 * time.Millisecond ) func TestCondition(t *testing.T) { t.Parallel() t.Run("condition should be true", func(t *testing.T) { t.Parallel() mock := new(mockT) if !Condition(mock, func() bool { return true }, "Truth") { t.Error("condition should return true") } }) t.Run("condition should be false", func(t *testing.T) { t.Parallel() mock := new(mockT) if Condition(mock, func() bool { return false }, "Lie") { t.Error("condition should return false") } }) } // This test is deliberately NOT dual-path: it asserts that there are no leaking go routines // when real time tickers are used. This is naturally verified when running in a syntest bubble. func TestConditionEventuallyNoLeak(t *testing.T) { t.Parallel() t.Run("should output messages in a determined order", func(t *testing.T) { t.Parallel() /* Original output (replaced by integers) from https://github.com/stretchr/testify/issues/1611 condition_test.go:150: 2026-01-11 12:11:49.34854116 +0100 CET m=+0.000641595 Condition: inEventually = true condition_test.go:152: 2026-01-11 12:11:49.84944055 +0100 CET m=+0.501540975 Condition: inEventually = true condition_test.go:147: 2026-01-11 12:11:49.849484723 +0100 CET m=+0.501585149 Condition: end. condition_test.go:160: 2026-01-11 12:11:49.849500022 +0100 CET m=+0.501600447 Eventually done condition_test.go:163: 2026-01-11 12:11:49.849508218 +0100 CET m=+0.501608643 End of TestConditionEventuallyNoLeak/should_output_messages_in_a_determined_order */ mock := new(errorsCapturingT) done := make(chan struct{}, 1) recordedActions := make([]int, 0, 5) var mx sync.Mutex record := func(action int) { mx.Lock() defer mx.Unlock() recordedActions = append(recordedActions, action) } inEventually := true Eventually(mock, func() bool { defer func() { record(2) done <- struct{}{} }() if inEventually { record(0) } time.Sleep(5 * testTimeout) if inEventually { record(1) } return true }, testTimeout, testTick, ) inEventually = false record(3) <-done record(4) record(5) const expectedActions = 6 if len(recordedActions) != expectedActions { t.Errorf("expected %d actions to be recorded, got %d", expectedActions, len(recordedActions)) } if !sort.IntsAreSorted(recordedActions) { t.Errorf("expected recorded actions to be ordered, got %v", recordedActions) } }) t.Run("should not leak a go routine for condition execution", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) done := make(chan bool, 1) inEventually := true Eventually(mock, func() bool { defer func() { done <- inEventually }() time.Sleep(5 * testTimeout) return true }, testTimeout, testTick, ) inEventually = false result := <-done if !result { t.Error("Condition should end while Eventually still runs.") } }) } // TestConditionEventuallyWith keeps only the nanosecond-tick "race trigger" // subtest of [EventuallyWith]. All behavior-oriented subtests have been // migrated to [TestConditionDualPath_EventuallyWithCollectBehavior] in // condition_synctest_test.go, where they run under both real time and a // synctest bubble. // // This test is deliberately NOT dual-path: it uses a nanosecond tick // to force real-time scheduling races between the poller, the ticker, // and the condition goroutine. Under synctest, ticks fire deterministically // from a fake clock — so there are no real races to exercise. Keeping this // test real-time-only preserves its purpose as a smoke test against // concurrency regressions that only manifest under real scheduler pressure. func TestConditionEventuallyWith(t *testing.T) { t.Parallel() t.Run("should complete with fail, on a nanosecond tick (real-time race trigger)", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) cond := func(c *CollectT) { Fail(c, "condition fixed failure") } // Nanosecond tick to provoke real-time scheduling races. if EventuallyWith(mock, cond, testTimeout, time.Nanosecond) { t.Error("expected EventuallyWith to return false") } const expectedErrors = 3 if len(mock.errors) != expectedErrors { t.Errorf("expected %d errors (1 from condition, 2 from Eventually), got %d", expectedErrors, len(mock.errors)) } }) } func TestConditionErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, conditionFailCases()) } func conditionFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "Condition/false", assertion: func(t T) bool { return Condition(t, func() bool { return false }) }, wantError: "condition failed", }, { name: "Blocked/non-channel", assertion: func(t T) bool { return Blocked(t, "not a channel") }, wantContains: []string{"Expected a channel but got"}, }, { name: "Blocked/nil-interface", assertion: func(t T) bool { return Blocked(t, nil) }, wantContains: []string{"Expected a channel but got"}, }, { name: "Blocked/buffered-with-value", assertion: func(t T) bool { ch := make(chan int, 1) ch <- 42 return Blocked(t, ch) }, wantContains: []string{"Channel receive should have blocked", "42"}, }, { name: "BlockedT/buffered-with-value", assertion: func(t T) bool { ch := make(chan int, 1) ch <- 42 return BlockedT(t, ch) }, wantContains: []string{"Channel receive should have blocked", "42"}, }, { name: "NotBlocked/empty-unbuffered", assertion: func(t T) bool { return NotBlocked(t, make(chan int)) }, wantContains: []string{"Channel receive should not have blocked"}, }, { name: "NotBlockedT/empty-unbuffered", assertion: func(t T) bool { return NotBlockedT(t, make(chan int)) }, wantContains: []string{"Channel receive should not have blocked"}, }, { name: "Blocked/closed-channel", assertion: func(t T) bool { ch := make(chan int) close(ch) return Blocked(t, ch) }, wantContains: []string{"channel was closed"}, }, { name: "BlockedT/closed-channel", assertion: func(t T) bool { ch := make(chan int) close(ch) return BlockedT(t, ch) }, wantContains: []string{"channel was closed"}, }, { name: "Blocked/send-only-rejected", assertion: func(t T) bool { ch := make(chan int) var so chan<- int = ch return Blocked(t, so) }, wantContains: []string{"channel direction"}, }, { name: "NotBlocked/send-only-rejected", assertion: func(t T) bool { ch := make(chan int) var so chan<- int = ch return NotBlocked(t, so) }, wantContains: []string{"channel direction"}, }, }) } // pollUntilTimeoutAssertion is the common signature for Never and Consistently, // both of which poll until timeout using func() bool conditions. type pollUntilTimeoutAssertion func(T, func() bool, time.Duration, time.Duration, ...any) bool // pollUntilTimeoutCase parameterizes the shared tests for Never and Consistently. type pollUntilTimeoutCase struct { name string assertion pollUntilTimeoutAssertion goodValue bool // the value the condition returns when "holding": false for Never, true for Consistently } func pollUntilTimeoutCases() iter.Seq[pollUntilTimeoutCase] { return slices.Values([]pollUntilTimeoutCase{ { name: "Never", assertion: Never[func() bool], goodValue: false, // Never succeeds when the condition always returns false ("never true") }, { name: "Consistently", assertion: Consistently[func() bool], goodValue: true, // Consistently succeeds when the condition always returns true ("always true") }, }) } func TestConditionPanicRecovery(t *testing.T) { t.Parallel() t.Run("Eventually survives a panicking condition and retries", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) var counter int var mu sync.Mutex condition := func() bool { mu.Lock() counter++ n := counter mu.Unlock() if n < 3 { panic("boom") } return true } if !Eventually(mock, condition, testTimeout, testTick) { t.Error("expected Eventually to return true after recovering from panics") } mu.Lock() got := counter mu.Unlock() if got < 3 { t.Errorf("expected at least 3 calls, got %d", got) } }) t.Run("Eventually fails when condition always panics", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) condition := func() bool { panic("persistent failure") } if Eventually(mock, condition, testTimeout, testTick) { t.Error("expected Eventually to return false when condition always panics") } }) t.Run("Never fails when condition panics", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) condition := func() bool { panic("unexpected") } if Never(mock, condition, testTimeout, testTick) { t.Error("expected Never to return false when condition panics") } }) t.Run("Consistently fails when condition panics", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) condition := func() bool { panic("unexpected") } if Consistently(mock, condition, testTimeout, testTick) { t.Error("expected Consistently to return false when condition panics") } }) t.Run("EventuallyWith survives a panicking condition and retries", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) var counter int var mu sync.Mutex condition := func(_ *CollectT) { mu.Lock() counter++ n := counter mu.Unlock() if n < 3 { panic("boom in collect") } } if !EventuallyWith(mock, condition, testTimeout, testTick) { t.Error("expected EventuallyWith to return true after recovering from panics") } mu.Lock() got := counter mu.Unlock() if got < 3 { t.Errorf("expected at least 3 calls, got %d", got) } }) t.Run("EventuallyWith fails when condition always panics", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) condition := func(_ *CollectT) { panic("always panics") } if EventuallyWith(mock, condition, testTimeout, testTick) { t.Error("expected EventuallyWith to return false when condition always panics") } }) t.Run("EventuallyWith collects panic error via sentinel", func(t *testing.T) { t.Parallel() mock := new(errorsCapturingT) var counter int var mu sync.Mutex condition := func(collect *CollectT) { mu.Lock() counter++ n := counter mu.Unlock() if n == 1 { panic("boom on first tick") } // Subsequent ticks fail normally, preserving the panic error // from the first tick in lastCollectedErrors. Fail(collect, "still failing") } if EventuallyWith(mock, condition, testTimeout, testTick) { t.Error("expected EventuallyWith to return false") } }) t.Run("errConditionPanicked sentinel is detectable with errors.Is", func(t *testing.T) { t.Parallel() err := fmt.Errorf("%w: %v", errConditionPanicked, "test panic") if !errors.Is(err, errConditionPanicked) { t.Error("expected errors.Is to detect errConditionPanicked sentinel") } }) } // ======================================= // Test ConditionBlocked / ConditionNotBlocked // ======================================= func TestConditionBlocked(t *testing.T) { t.Parallel() for tc := range blockedCases() { t.Run(tc.name, tc.test) } // Reflect-only inputs (cannot be expressed via the generic [BlockedT] constraint). t.Run("reflect-only/non-channel-string", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, Blocked(mock, "not a channel"), false) }) t.Run("reflect-only/non-channel-int", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, Blocked(mock, 42), false) }) t.Run("reflect-only/non-channel-slice", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, Blocked(mock, []int{1, 2, 3}), false) }) t.Run("reflect-only/nil-interface", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, Blocked(mock, nil), false) }) t.Run("reflect-only/recv-only-empty-blocks", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int) var ro <-chan int = ch shouldPassOrFail(t, mock, Blocked(mock, ro), true) }) t.Run("reflect-only/recv-only-with-value-fails", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int, 1) ch <- 7 var ro <-chan int = ch shouldPassOrFail(t, mock, Blocked(mock, ro), false) }) // Send-only must be rejected cleanly: passing it to reflect.Select with // a Recv direction would otherwise panic at runtime. t.Run("reflect-only/send-only-rejected", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int) var so chan<- int = ch shouldPassOrFail(t, mock, Blocked(mock, so), false) }) } func TestConditionNotBlocked(t *testing.T) { t.Parallel() for tc := range notBlockedCases() { t.Run(tc.name, tc.test) } // Multi-value buffer: NotBlocked consumes exactly one element per call. t.Run("buffered-multi-consumes-one", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int, 3) ch <- 1 ch <- 2 ch <- 3 shouldPassOrFail(t, mock, NotBlocked(mock, ch), true) // Two values must remain. if got := len(ch); got != 2 { t.Errorf("expected 2 values left in channel, got %d", got) } }) // Reflect-only inputs (cannot be expressed via the generic [NotBlockedT] constraint). t.Run("reflect-only/non-channel-string", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, NotBlocked(mock, "not a channel"), false) }) t.Run("reflect-only/non-channel-int", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, NotBlocked(mock, 42), false) }) t.Run("reflect-only/nil-interface", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, NotBlocked(mock, nil), false) }) t.Run("reflect-only/recv-only-with-value-passes", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int, 1) ch <- 7 var ro <-chan int = ch shouldPassOrFail(t, mock, NotBlocked(mock, ro), true) }) t.Run("reflect-only/recv-only-empty-fails", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int) var ro <-chan int = ch shouldPassOrFail(t, mock, NotBlocked(mock, ro), false) }) t.Run("reflect-only/send-only-rejected", func(t *testing.T) { t.Parallel() mock := new(mockT) ch := make(chan int) var so chan<- int = ch shouldPassOrFail(t, mock, NotBlocked(mock, so), false) }) } // blockedCases exercises both Blocked and BlockedT against the same input. // Channels are produced by a factory: each subtest gets its own fresh channel // so that the X variant cannot drain the buffer and silently turn the XT // variant into a different scenario. // // The generic CHAN constraint of BlockedT is `~chan E`, so named bidirectional // channel types are accepted, but recv-only / send-only channels must be tested // against the reflect-based [Blocked] only — see the reflect-only subtests in // [TestConditionBlocked]. func blockedCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Empty channels block on receive. {"unbuffered-empty/blocks", testAllBlocked(func() chan int { return make(chan int) }, true)}, {"buffered-empty/blocks", testAllBlocked(func() chan int { return make(chan int, 4) }, true)}, {"unbuffered-struct/blocks", testAllBlocked(func() chan struct{} { return make(chan struct{}) }, true)}, {"unbuffered-string/blocks", testAllBlocked(func() chan string { return make(chan string) }, true)}, {"unbuffered-error/blocks", testAllBlocked(func() chan error { return make(chan error) }, true)}, // Typed nil channels block forever (default branch fires => "blocked"). {"typed-nil-int/blocks", testAllBlocked(func() chan int { return nil }, true)}, {"typed-nil-struct/blocks", testAllBlocked(func() chan struct{} { return nil }, true)}, // Non-empty channels do not block. {"buffered-with-value/fails", testAllBlocked(filledChanFactory(42), false)}, {"buffered-with-string/fails", testAllBlocked(filledChanFactory("hello"), false)}, {"buffered-with-zero-struct/fails", testAllBlocked(filledChanFactory(struct{}{}), false)}, // Closed channels do NOT block (zero value is received immediately). // The Fail message currently shows the zero value, which is mildly // misleading; pinned here so the behavior is intentional. {"closed-int/fails", testAllBlocked(closedChanFactory[int](), false)}, {"closed-struct/fails", testAllBlocked(closedChanFactory[struct{}](), false)}, }) } // notBlockedCases is the dual of blockedCases — same input shape, opposite expected outcome. func notBlockedCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Empty channels block => NotBlocked fails. {"unbuffered-empty/fails", testAllNotBlocked(func() chan int { return make(chan int) }, false)}, {"buffered-empty/fails", testAllNotBlocked(func() chan int { return make(chan int, 4) }, false)}, {"unbuffered-struct/fails", testAllNotBlocked(func() chan struct{} { return make(chan struct{}) }, false)}, {"unbuffered-string/fails", testAllNotBlocked(func() chan string { return make(chan string) }, false)}, // Typed nil channels block forever => NotBlocked fails. {"typed-nil-int/fails", testAllNotBlocked(func() chan int { return nil }, false)}, {"typed-nil-struct/fails", testAllNotBlocked(func() chan struct{} { return nil }, false)}, // Non-empty channels do not block => NotBlocked passes. {"buffered-with-value/passes", testAllNotBlocked(filledChanFactory(42), true)}, {"buffered-with-string/passes", testAllNotBlocked(filledChanFactory("hello"), true)}, {"buffered-with-zero-struct/passes", testAllNotBlocked(filledChanFactory(struct{}{}), true)}, // Closed channels are not blocked (zero value is received immediately). {"closed-int/passes", testAllNotBlocked(closedChanFactory[int](), true)}, {"closed-struct/passes", testAllNotBlocked(closedChanFactory[struct{}](), true)}, }) } // testAllBlocked tests both Blocked and BlockedT against fresh channels // produced by the factory. func testAllBlocked[E any](newCh func() chan E, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() label := "should fail" if shouldPass { label = "should pass" } t.Run(label, func(t *testing.T) { t.Run("with Blocked", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, Blocked(mock, newCh()), shouldPass) }) t.Run("with BlockedT", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, BlockedT(mock, newCh()), shouldPass) }) }) } } func testAllNotBlocked[E any](newCh func() chan E, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() label := "should fail" if shouldPass { label = "should pass" } t.Run(label, func(t *testing.T) { t.Run("with NotBlocked", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, NotBlocked(mock, newCh()), shouldPass) }) t.Run("with NotBlockedT", func(t *testing.T) { t.Parallel() mock := new(mockT) shouldPassOrFail(t, mock, NotBlockedT(mock, newCh()), shouldPass) }) }) } } func filledChanFactory[E any](v E) func() chan E { return func() chan E { ch := make(chan E, 1) ch <- v return ch } } func closedChanFactory[E any]() func() chan E { return func() chan E { ch := make(chan E) close(ch) return ch } } go-openapi-testify-c10ca71/internal/assertions/diff.go000066400000000000000000000030421520301377500231050ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "reflect" "github.com/go-openapi/testify/v2/internal/assertions/enable/colors" "github.com/go-openapi/testify/v2/internal/difflib" ) // diff returns a diff of both values as long as both are of the same type and // are a struct, map, slice, array or string. Otherwise it returns an empty string. func diff(expected any, actual any) string { if expected == nil || actual == nil { return "" } et, ek := typeAndKind(expected) at, _ := typeAndKind(actual) if et != at { return "" } if ek != reflect.Struct && ek != reflect.Map && ek != reflect.Slice && ek != reflect.Array && ek != reflect.String { return "" } var e, a string switch et { case reflect.TypeFor[string](): // short-circuit for plain strings e = reflect.ValueOf(expected).String() a = reflect.ValueOf(actual).String() default: e = dumper(expected) a = dumper(actual) } unified := difflib.UnifiedDiff{ A: difflib.SplitLines(e), B: difflib.SplitLines(a), FromFile: "Expected", FromDate: "", ToFile: "Actual", ToDate: "", Context: 1, } if colors.Enabled() { unified.Options = colors.Options() } diff, _ := difflib.GetUnifiedDiffString(unified) return "\n\nDiff:\n" + diff } func typeAndKind(v any) (reflect.Type, reflect.Kind) { t := reflect.TypeOf(v) if t == nil { return nil, reflect.Invalid } k := t.Kind() if k == reflect.Pointer { t = t.Elem() k = t.Kind() } return t, k } go-openapi-testify-c10ca71/internal/assertions/diff_test.go000066400000000000000000000211251520301377500241460ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "iter" "reflect" "slices" "strings" "testing" "time" ) func TestHelpersUnexportedImplementationDetails(t *testing.T) { t.Parallel() t.Run("diff", testDiff()) t.Run("diffList", testDiffList()) } 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) } }) } // Ensure there are no data races with diff. func TestTypeDiffRace(t *testing.T) { t.Parallel() expected := map[string]string{ "a": "A", "b": "B", "c": "C", } actual := map[string]string{ "d": "D", "e": "E", "f": "F", } // run diffs in parallel simulating tests with t.Parallel() numRoutines := 10 rChans := make([]chan string, numRoutines) for idx := range rChans { rChans[idx] = make(chan string) go func(ch chan string) { defer close(ch) ch <- diff(expected, actual) }(rChans[idx]) } for _, ch := range rChans { for msg := range ch { if msg == "" { t.Error("expected non-empty diff result") } } } } func testDiff() func(*testing.T) { return func(t *testing.T) { t.Parallel() for tt := range diffCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() for range min(1, tt.repeat) { // for tests on maps, need to verify the ordering is stable actual := diff( tt.valueA, tt.valueB, ) if tt.expected != actual { t.Errorf("expected diff:\n%s\ngot:\n%s", tt.expected, actual) } } }) } } } func testDiffList() func(*testing.T) { return func(t *testing.T) { t.Parallel() for test := range compareDiffListCases() { t.Run(test.name, func(t *testing.T) { t.Parallel() actualExtraA, actualExtraB := diffLists(test.listA, test.listB) if !reflect.DeepEqual(test.extraA, actualExtraA) { t.Errorf("extra A does not match for listA=%v listB=%v: expected %v, got %v", test.listA, test.listB, test.extraA, actualExtraA) } if !reflect.DeepEqual(test.extraB, actualExtraB) { t.Errorf("extra B does not match for listA=%v listB=%v: expected %v, got %v", test.listA, test.listB, test.extraB, actualExtraB) } }) } } } type diffCase struct { name string repeat int valueA any valueB any expected string } type diffTestingStruct struct { A string B int } func (d *diffTestingStruct) String() string { return d.A } func diffCases() iter.Seq[diffCase] { const n = 5 type Key struct { x int } return slices.Values([]diffCase{ { name: "with diff struct", valueA: struct{ foo string }{"hello"}, valueB: struct{ foo string }{"bar"}, expected: ` Diff: --- Expected +++ Actual @@ -1,3 +1,3 @@ (struct { foo string }) { - foo: (string) (len=5) "hello" + foo: (string) (len=3) "bar" } `, }, { name: "with diff slice", valueA: []int{1, 2, 3, 4}, valueB: []int{1, 3, 5, 7}, expected: ` Diff: --- Expected +++ Actual @@ -2,5 +2,5 @@ (int) 1, - (int) 2, (int) 3, - (int) 4 + (int) 5, + (int) 7 } `, }, { name: "with diff slice (sliced)", valueA: []int{1, 2, 3, 4}[0:3], valueB: []int{1, 3, 5, 7}[0:3], expected: ` Diff: --- Expected +++ Actual @@ -2,4 +2,4 @@ (int) 1, - (int) 2, - (int) 3 + (int) 3, + (int) 5 } `, }, { name: "with string keys map keys should be rendered deterministically in diffs", repeat: n, valueA: map[string]int{"one": 1, "two": 2, "three": 3, "four": 4}, valueB: map[string]int{"one": 1, "three": 3, "five": 5, "seven": 7}, expected: ` Diff: --- Expected +++ Actual @@ -1,6 +1,6 @@ (map[string]int) (len=4) { - (string) (len=4) "four": (int) 4, + (string) (len=4) "five": (int) 5, (string) (len=3) "one": (int) 1, - (string) (len=5) "three": (int) 3, - (string) (len=3) "two": (int) 2 + (string) (len=5) "seven": (int) 7, + (string) (len=5) "three": (int) 3 } `, }, { name: "with diff error", valueA: errors.New("some expected error"), valueB: errors.New("actual error"), expected: ` Diff: --- Expected +++ Actual @@ -1,3 +1,3 @@ (*errors.errorString)({ - s: (string) (len=19) "some expected error" + s: (string) (len=12) "actual error" }) `, }, { name: "with arbitrary comparable keys map keys should be rendered deterministically in diffs", repeat: n, valueA: map[Key]int{{1}: 1, {2}: 2, {3}: 3, {4}: 4}, valueB: map[Key]int{{1}: 1, {2}: 2, {3}: 3, {4}: 999}, expected: ` Diff: --- Expected +++ Actual @@ -12,3 +12,3 @@ x: (int) 4 - }: (int) 4 + }: (int) 999 } `, }, { name: "with diff unexported struct", valueA: diffTestingStruct{A: "some string", B: 10}, valueB: diffTestingStruct{A: "some string", B: 15}, expected: ` Diff: --- Expected +++ Actual @@ -2,3 +2,3 @@ A: (string) (len=11) "some string", - B: (int) 10 + B: (int) 15 } `, }, { name: "with diff date", valueA: time.Date(2020, 9, 24, 0, 0, 0, 0, time.UTC), valueB: time.Date(2020, 9, 25, 0, 0, 0, 0, time.UTC), expected: ` Diff: --- Expected +++ Actual @@ -1,2 +1,2 @@ -(time.Time) 2020-09-24 00:00:00 +0000 UTC +(time.Time) 2020-09-25 00:00:00 +0000 UTC `, }, }) } type compareDiffListCase struct { name string listA any listB any extraA []any extraB []any } func compareDiffListCases() iter.Seq[compareDiffListCase] { return slices.Values([]compareDiffListCase{ { name: "equal empty", listA: []string{}, listB: []string{}, extraA: nil, extraB: nil, }, { name: "equal same order", listA: []string{"hello", "world"}, listB: []string{"hello", "world"}, extraA: nil, extraB: nil, }, { name: "equal different order", listA: []string{"hello", "world"}, listB: []string{"world", "hello"}, extraA: nil, extraB: nil, }, { name: "extra A", listA: []string{"hello", "hello", "world"}, listB: []string{"hello", "world"}, extraA: []any{"hello"}, extraB: nil, }, { name: "extra A twice", listA: []string{"hello", "hello", "hello", "world"}, listB: []string{"hello", "world"}, extraA: []any{"hello", "hello"}, extraB: nil, }, { name: "extra B", listA: []string{"hello", "world"}, listB: []string{"hello", "hello", "world"}, extraA: nil, extraB: []any{"hello"}, }, { name: "extra B twice", listA: []string{"hello", "world"}, listB: []string{"hello", "hello", "world", "hello"}, extraA: nil, extraB: []any{"hello", "hello"}, }, { name: "integers 1", listA: []int{1, 2, 3, 4, 5}, listB: []int{5, 4, 3, 2, 1}, extraA: nil, extraB: nil, }, { name: "integers 2", listA: []int{1, 2, 1, 2, 1}, listB: []int{2, 1, 2, 1, 2}, extraA: []any{1}, extraB: []any{2}, }, }) } go-openapi-testify-c10ca71/internal/assertions/doc.go000066400000000000000000000025511520301377500227460ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package assertions holds the internal implementation // of all the assertion and helper functions exposed by testify. // // It serves as a base to develop and test testify, // whereas the publicly exposed API (in packages assert and require) // is fully generated. // // For convenience, assertion functions are classified by domain. // The entire API can be searched by domain at https://go-openapi.github.io/testify/api. // // # Domains // // - boolean: asserting boolean values // - collection: asserting slices and maps // - comparison: comparing ordered values // - condition: expressing assertions using conditions // - equality: asserting two things are equal // - error: asserting errors // - file: asserting OS files // - http: asserting HTTP response and body // - json: asserting JSON documents // - number: asserting numbers // - ordering: asserting how collections are ordered // - panic: asserting a panic behavior // - safety: checks against leaked resources (goroutines, file descriptors) // - string: asserting strings // - testing: mimics methods from the testing standard library // - time: asserting times and durations // - type: asserting types rather than values // - yaml: asserting yaml documents package assertions go-openapi-testify-c10ca71/internal/assertions/enable/000077500000000000000000000000001520301377500230755ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/enable/colors/000077500000000000000000000000001520301377500243765ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/enable/colors/colors.go000066400000000000000000000056711520301377500262370ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import ( "bufio" "github.com/go-openapi/testify/v2/internal/difflib" ) const ( greenMark = "\033[0;32m" redMark = "\033[0;31m" yellowMark = "\033[0;33m" // aka orange cyanMark = "\033[0;36m" brightGreenMark = "\033[0;92m" brightRedMark = "\033[0;91m" brightYellowMark = "\033[0;93m" // aka yellow brightCyanMark = "\033[0;96m" // aka turquoise // color codes for future use. // blackMark = "\033[0;30m" // blueMark = "\033[0;34m" // magentaMark = "\033[0;35m" // greyMark = "\033[0;37m". // darkGreyMark = "\033[0;90m" // brightBlueMark = "\033[0;94m" // brightMagentaMark = "\033[0;95m" // brightWhiteMark = "\033[0;97m". endMark = "\033[0m" ) // StringColorizer wraps a string with ANSI escape codes. // // This is a simpler alternative to [difflib.PrinterBuilder] for cases // where streaming to a [bufio.Writer] is not needed. type StringColorizer func(string) string func makeColorizer(mark string) StringColorizer { return func(s string) string { return mark + s + endMark } } // noopColorizer returns the input string unchanged. func noopColorizer(s string) string { return s } //nolint:gochecknoglobals // internal colorizers may safely be shared at the package-level var ( greenColorizer = makeColorizer(greenMark) redColorizer = makeColorizer(redMark) // yellowColorizer = makeColorizer(yellowMark) // cyanColorizer = makeColorizer(cyanMark). brightGreenColorizer = makeColorizer(brightGreenMark) brightRedColorizer = makeColorizer(brightRedMark) // brightYellowColorizer = makeColorizer(brightYellowMark) // brightCyanColorizer = makeColorizer(brightCyanMark). ) //nolint:gochecknoglobals // internal printer builders may safely be shared at the package-level var ( greenPrinterBuilder = ansiPrinterBuilder(greenMark) redPrinterBuilder = ansiPrinterBuilder(redMark) yellowPrinterBuilder = ansiPrinterBuilder(yellowMark) cyanPrinterBuilder = ansiPrinterBuilder(cyanMark) brightGreenPrinterBuilder = ansiPrinterBuilder(brightGreenMark) brightRedPrinterBuilder = ansiPrinterBuilder(brightRedMark) brightYellowPrinterBuilder = ansiPrinterBuilder(brightYellowMark) brightCyanPrinterBuilder = ansiPrinterBuilder(brightCyanMark) // magentaPrinterBuilder = ansiPrinterBuilder(magentaMark) // bluePrinterBuilder = ansiPrinterBuilder(blueMark). // brightMagentaPrinterBuilder = ansiPrinterBuilder(brightMagentaMark) // brightBluePrinterBuilder = ansiPrinterBuilder(brightBlueMark). ) func ansiPrinterBuilder(mark string) difflib.PrinterBuilder { return func(w *bufio.Writer) difflib.Printer { return func(str string) (err error) { _, err = w.WriteString(mark) if err != nil { return } _, err = w.WriteString(str) if err != nil { return } _, err = w.WriteString(endMark) if err != nil { return } return nil } } } go-openapi-testify-c10ca71/internal/assertions/enable/colors/colors_test.go000066400000000000000000000060171520301377500272710ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import ( "iter" "slices" "testing" ) func TestStringColorizer(t *testing.T) { t.Parallel() for tc := range colorizerTestCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() result := tc.colorizer(tc.input) if result != tc.expected { t.Errorf("expected %q, got %q", tc.expected, result) } }) } } func TestMakeColorizer(t *testing.T) { t.Parallel() t.Run("creates colorizer with custom mark", func(t *testing.T) { t.Parallel() customMark := "\033[1;35m" // bold magenta colorizer := makeColorizer(customMark) result := colorizer("test") expected := "\033[1;35mtest\033[0m" if result != expected { t.Errorf("expected %q, got %q", expected, result) } }) t.Run("colorizer is reusable", func(t *testing.T) { t.Parallel() colorizer := makeColorizer(greenMark) result1 := colorizer("first") result2 := colorizer("second") expected1 := "\033[0;32mfirst\033[0m" expected2 := "\033[0;32msecond\033[0m" if result1 != expected1 { t.Errorf("first call: expected %q, got %q", expected1, result1) } if result2 != expected2 { t.Errorf("second call: expected %q, got %q", expected2, result2) } }) } func TestNoopColorizer(t *testing.T) { t.Parallel() inputs := []string{ "", "simple", "with\nnewline", "\033[0;31malready colored\033[0m", } for _, input := range inputs { t.Run(input, func(t *testing.T) { t.Parallel() result := noopColorizer(input) if result != input { t.Errorf("noopColorizer should return input unchanged: expected %q, got %q", input, result) } }) } } type colorizerTestCase struct { name string colorizer StringColorizer input string expected string } func colorizerTestCases() iter.Seq[colorizerTestCase] { return slices.Values([]colorizerTestCase{ { name: "green colorizer", colorizer: greenColorizer, input: "hello", expected: "\033[0;32mhello\033[0m", }, { name: "red colorizer", colorizer: redColorizer, input: "world", expected: "\033[0;31mworld\033[0m", }, { name: "bright green colorizer", colorizer: brightGreenColorizer, input: "expected", expected: "\033[0;92mexpected\033[0m", }, { name: "bright red colorizer", colorizer: brightRedColorizer, input: "actual", expected: "\033[0;91mactual\033[0m", }, { name: "noop colorizer", colorizer: noopColorizer, input: "unchanged", expected: "unchanged", }, { name: "empty string", colorizer: greenColorizer, input: "", expected: "\033[0;32m\033[0m", }, { name: "string with special characters", colorizer: redColorizer, input: "line1\nline2\ttab", expected: "\033[0;31mline1\nline2\ttab\033[0m", }, { name: "string with existing ANSI codes", colorizer: greenColorizer, input: "\033[1mbold\033[0m", expected: "\033[0;32m\033[1mbold\033[0m\033[0m", }, }) } go-openapi-testify-c10ca71/internal/assertions/enable/colors/enable_colors.go000066400000000000000000000037031520301377500275370ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import ( "sync" "github.com/go-openapi/testify/v2/internal/difflib" ) //nolint:gochecknoglobals // in this particular case, we need a global to enable the feature from another module var ( resolveOptionsOnce sync.Once optionsEnabler func() []Option colorOptions *difflib.Options stringColorizers colorizers ) // Enable registers colorized options for pretty-printing the output of assertions. // // The argument passed is a function that is executed after package initialization and CLI arg parsing. // // This is not intended for concurrent use as it sets a package-level state. func Enable(enabler func() []Option) { optionsEnabler = enabler } // Enabled indicates if a global color options setting has been enabled. func Enabled() bool { return optionsEnabler != nil } // Options returns the colorization options for [difflib]. // // It yields nil if the colorization feature is not enabled (non blocking: colors just won't display). func Options() *difflib.Options { resolveOptions() return colorOptions } // ExpectedColorizer returns a colorizer for expected values. // // It returns a no-op colorizer if colorization is not enabled. func ExpectedColorizer() StringColorizer { resolveOptions() return stringColorizers.expected } // ActualColorizer returns a colorizer for actual values. // // It returns a no-op colorizer if colorization is not enabled. func ActualColorizer() StringColorizer { resolveOptions() return stringColorizers.actual } func resolveOptions() { resolveOptionsOnce.Do(func() { // defers the resolution of options until first usage if optionsEnabler == nil { stringColorizers = colorizers{ expected: noopColorizer, actual: noopColorizer, } return } o := optionsWithDefaults(optionsEnabler()) colorOptions = makeDiffOptions(o) stringColorizers = setColorizers(o) }) } go-openapi-testify-c10ca71/internal/assertions/enable/colors/options.go000066400000000000000000000026431520301377500264250ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors // Theme is a colorization theme for testify output. type Theme string func (t Theme) String() string { return string(t) } const ( // ThemeLight uses normal ANSI colors. ThemeLight Theme = "light" // ThemeDark uses bright ANSI colors. ThemeDark Theme = "dark" ) // Option is a colorization option. type Option func(o *options) type options struct { enabled bool theme Theme } // WithSanitizedTheme sets a colorization theme from its name or does nothing if the theme is not supported. func WithSanitizedTheme(theme string) Option { th := Theme(theme) switch th { case ThemeDark, ThemeLight: return WithTheme(th) default: return func(*options) { // noop } } } // WithEnable enables colorized output. func WithEnable(enabled bool) Option { return func(o *options) { o.enabled = enabled } } // WithTheme sets a colorization theme. func WithTheme(theme Theme) Option { return func(o *options) { o.theme = theme } } // WithDark sets the [ThemeDark] color theme. func WithDark() Option { return WithTheme(ThemeDark) } // WithLight sets the [ThemeLight] color theme. func WithLight() Option { return WithTheme(ThemeLight) } func optionsWithDefaults(opts []Option) options { o := options{ theme: ThemeDark, // default theme } for _, apply := range opts { apply(&o) } return o } go-openapi-testify-c10ca71/internal/assertions/enable/colors/options_test.go000066400000000000000000000034531520301377500274640ustar00rootroot00000000000000// 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) } }) } go-openapi-testify-c10ca71/internal/assertions/enable/colors/themes.go000066400000000000000000000027261520301377500262210ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package colors import "github.com/go-openapi/testify/v2/internal/difflib" // colorizers holds string colorizers for a theme. type colorizers struct { expected StringColorizer actual StringColorizer } // makeDiffOptions transforms preset options into the // detailed options for difflib. func makeDiffOptions(o options) *difflib.Options { if !o.enabled { return nil } switch o.theme { case ThemeLight: return &difflib.Options{ EqualPrinter: greenPrinterBuilder, DeletePrinter: redPrinterBuilder, UpdatePrinter: cyanPrinterBuilder, InsertPrinter: yellowPrinterBuilder, } case ThemeDark: return &difflib.Options{ EqualPrinter: brightGreenPrinterBuilder, DeletePrinter: brightRedPrinterBuilder, UpdatePrinter: brightCyanPrinterBuilder, InsertPrinter: brightYellowPrinterBuilder, } default: return nil } } // setColorizers returns string colorizers for the given options. func setColorizers(o options) colorizers { if !o.enabled { return colorizers{ expected: noopColorizer, actual: noopColorizer, } } switch o.theme { case ThemeLight: return colorizers{ expected: greenColorizer, actual: redColorizer, } case ThemeDark: return colorizers{ expected: brightGreenColorizer, actual: brightRedColorizer, } default: return colorizers{ expected: noopColorizer, actual: noopColorizer, } } } go-openapi-testify-c10ca71/internal/assertions/enable/colors/themes_test.go000066400000000000000000000143401520301377500272530ustar00rootroot00000000000000// 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") }, }, }) } go-openapi-testify-c10ca71/internal/assertions/enable/doc.go000066400000000000000000000004471520301377500241760ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package enable allows to register extra features. // // # Available extensions // // - yaml: enable the YAML assertions // - colors: enable colorization of diff output package enable go-openapi-testify-c10ca71/internal/assertions/enable/yaml/000077500000000000000000000000001520301377500240375ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/enable/yaml/enable_yaml.go000066400000000000000000000032601520301377500266370ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package yaml is an indirection to handle YAML serialization/deserialization. // // This package allows the builder to override the indirection with an alternative implementation // of YAML serialization. package yaml //nolint:gochecknoglobals // in this particular case, we need a global to enable the feature from another module var ( enableYAMLUnmarshal func([]byte, any) error enableYAMLMarshal func(any) ([]byte, error) ) // EnableYAMLWithUnmarshal registers a YAML-capable unmarshaler. // // This is not intended for concurrent use. func EnableYAMLWithUnmarshal(unmarshaler func([]byte, any) error) { enableYAMLUnmarshal = unmarshaler } func EnableYAMLWithMarshal(marshaler func(any) ([]byte, error)) { enableYAMLMarshal = marshaler } // Unmarshal is a wrapper to some external library to unmarshal YAML documents. func Unmarshal(in []byte, out any) error { if enableYAMLUnmarshal == nil { // fail early and loud panic(` YAML is not enabled yet! You should enable a YAML library before running this test, e.g. by adding the following to your imports: import ( _ "github.com/go-openapi/testify/enable/yaml/v2" ) `, ) } return enableYAMLUnmarshal(in, out) } // Marshal is a wrapper to some external library to marshal YAML documents. func Marshal(in any) ([]byte, error) { if enableYAMLMarshal == nil { // fail early and loud panic(` YAML is not enabled yet! You should enable a YAML library before running this test, e.g. by adding the following to your imports: import ( _ "github.com/go-openapi/testify/enable/yaml/v2" ) `, ) } return enableYAMLMarshal(in) } go-openapi-testify-c10ca71/internal/assertions/equal.go000066400000000000000000000274751520301377500233240ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "fmt" "reflect" "time" "github.com/go-openapi/testify/v2/internal/assertions/enable/colors" ) // Equal asserts that two objects are equal. // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). // // Function equality cannot be determined and will always fail. // // # Usage // // assertions.Equal(t, 123, 123) // // # Examples // // success: 123, 123 // failure: 123, 456 func Equal(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotEqual if h, ok := t.(H); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } if !ObjectsAreEqual(expected, actual) { return failWithDiff(t, expected, actual, msgAndArgs...) } return true } // EqualT asserts that two objects of the same comparable type are equal. // // Pointer variable equality is determined based on the equality of the memory addresses (unlike [Equal], but like [Same]). // // Functions, slices and maps are not comparable. See also [ComparisonOperators]. // // If you need to compare values of non-comparable types, or compare pointers by the value they point to, // use [Equal] instead. // // # Usage // // assertions.EqualT(t, 123, 123) // // # Examples // // success: 123, 123 // failure: 123, 456 // // [ComparisonOperators]: https://go.dev/ref/spec#Comparison_operators. func EqualT[V comparable](t T, expected, actual V, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotEqualT if expected != actual { return failWithDiff(t, expected, actual, msgAndArgs...) } return true } // NotEqual asserts that the specified values are NOT equal. // // # Usage // // assertions.NotEqual(t, obj1, obj2) // // Pointer variable equality is determined based on the equality of the // referenced values (as opposed to the memory addresses). // // Function equality cannot be determined and will always fail. // // # Examples // // success: 123, 456 // failure: 123, 123 func NotEqual(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v != %#v (%s)", expected, actual, err), msgAndArgs...) } if ObjectsAreEqual(expected, actual) { return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true } // NotEqualT asserts that the specified values of the same comparable type are NOT equal. // // See [EqualT]. // // # Usage // // assertions.NotEqualT(t, obj1, obj2) // // # Examples // // success: 123, 456 // failure: 123, 123 func NotEqualT[V comparable](t T, expected, actual V, msgAndArgs ...any) bool { // Domain: equality if expected == actual { return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true } // EqualValues asserts that two objects are equal or convertible to the larger // type and equal. // // Function equality cannot be determined and will always fail. // // # Usage // // assertions.EqualValues(t, uint32(123), int32(123)) // // # Examples // // success: uint32(123), int32(123) // failure: uint32(123), int32(456) func EqualValues(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotEqualValues if h, ok := t.(H); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } if !ObjectsAreEqualValues(expected, actual) { diff := diff(expected, actual) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal: \n"+ "expected: %s\n"+ "actual : %s%s", expected, actual, diff), msgAndArgs...) } return true } // NotEqualValues asserts that two objects are not equal even when converted to the same type. // // Function equality cannot be determined and will always fail. // // # Usage // // assertions.NotEqualValues(t, obj1, obj2) // // # Examples // // success: uint32(123), int32(456) // failure: uint32(123), int32(123) func NotEqualValues(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } if ObjectsAreEqualValues(expected, actual) { return Fail(t, fmt.Sprintf("Should not be: %s\n", truncatingFormat("%#v", actual)), msgAndArgs...) } return true } // EqualExportedValues asserts that the types of two objects are equal and their public // fields are also equal. // // This is useful for comparing structs that have private fields that could potentially differ. // // Function equality cannot be determined and will always fail. // // # Usage // // type S struct { // Exported int // notExported int // } // assertions.EqualExportedValues(t, S{1, 2}, S{1, 3}) => true // assertions.EqualExportedValues(t, S{1, 2}, S{2, 3}) => false // // # Examples // // success: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "a", b: 2} // failure: &dummyStruct{A: "a", b: 1}, &dummyStruct{A: "b", b: 1} func EqualExportedValues(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } if err := validateEqualArgs(expected, actual); err != nil { return Fail(t, fmt.Sprintf("Invalid operation: %#v == %#v (%s)", expected, actual, err), msgAndArgs...) } aType := reflect.TypeOf(expected) bType := reflect.TypeOf(actual) if aType != bType { return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } expected = copyExportedFields(expected) actual = copyExportedFields(actual) if !ObjectsAreEqualValues(expected, actual) { diff := diff(expected, actual) expected, actual = formatUnequalValues(expected, actual) return Fail(t, fmt.Sprintf("Not equal (comparing only exported fields): \n"+ "expected: %s\n"+ "actual : %s%s", expected, actual, diff), msgAndArgs...) } return true } // Exactly asserts that two objects are equal in value and type. // // # Usage // // assertions.Exactly(t, int32(123), int64(123)) // // # Examples // // success: int32(123), int32(123) // failure: int32(123), int64(123) func Exactly(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } aType := reflect.TypeOf(expected) bType := reflect.TypeOf(actual) if aType != bType { return Fail(t, fmt.Sprintf("Types expected to match exactly\n\t%v != %v", aType, bType), msgAndArgs...) } return Equal(t, expected, actual, msgAndArgs...) } func failWithDiff(t T, expected, actual any, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } diff := diff(expected, actual) expectedStr, actualStr := formatUnequalValues(expected, actual) if colors.Enabled() { expectedStr = colors.ExpectedColorizer()(expectedStr) actualStr = colors.ActualColorizer()(actualStr) } return Fail(t, fmt.Sprintf("Not equal: \n"+ "expected: %s\n"+ "actual : %s%s", expectedStr, actualStr, diff), msgAndArgs..., ) } // validateEqualArgs checks whether provided arguments can be safely used in the // Equal/NotEqual/EqualValues/NotEqualValues functions. func validateEqualArgs(expected, actual any) error { if expected == nil && actual == nil { return nil } if isFunction(expected) || isFunction(actual) { return errors.New("cannot take func type as argument") } return nil } // samePointers checks if two arbitrary interface objects are pointers of the same // type pointing to the same object. // // It returns two values: same indicating if they are the same type and point to the same object, // and ok indicating that both inputs are pointers. func samePointers(first, second any) (same bool, ok bool) { firstPtr, secondPtr := reflect.ValueOf(first), reflect.ValueOf(second) if firstPtr.Kind() != reflect.Pointer || secondPtr.Kind() != reflect.Pointer { return false, false // not both are pointers } firstType, secondType := reflect.TypeOf(first), reflect.TypeOf(second) if firstType != secondType { return false, true // both are pointers, but of different types } // compare pointer addresses return first == second, true } // formatUnequalValues takes two values of arbitrary types and returns string // representations appropriate to be presented to the user. // // If the values are not of like type, the returned strings will be prefixed // with the type name, and the value will be enclosed in parentheses similar // to a type conversion in the Go grammar. func formatUnequalValues(expected, actual any) (e string, a string) { if reflect.TypeOf(expected) != reflect.TypeOf(actual) { return fmt.Sprintf("%T(%s)", expected, truncatingFormat("%#v", expected)), fmt.Sprintf("%T(%s)", actual, truncatingFormat("%#v", actual)) } switch expected.(type) { case time.Duration, uint, uint8, uint16, uint32, uint64: return fmt.Sprint(expected), fmt.Sprint(actual) default: return truncatingFormat("%#v", expected), truncatingFormat("%#v", actual) } } // copyExportedFields iterates downward through nested data structures and creates a copy // that only contains the exported struct fields. func copyExportedFields(expected any) any { if isNil(expected) { return expected } expectedType := reflect.TypeOf(expected) expectedKind := expectedType.Kind() expectedValue := reflect.ValueOf(expected) switch expectedKind { case reflect.Struct: result := reflect.New(expectedType).Elem() for i := range expectedType.NumField() { field := expectedType.Field(i) isExported := field.IsExported() if isExported { fieldValue := expectedValue.Field(i) if isNil(fieldValue) || isNil(fieldValue.Interface()) { continue } newValue := copyExportedFields(fieldValue.Interface()) result.Field(i).Set(reflect.ValueOf(newValue)) } } return result.Interface() case reflect.Pointer: result := reflect.New(expectedType.Elem()) unexportedRemoved := copyExportedFields(expectedValue.Elem().Interface()) result.Elem().Set(reflect.ValueOf(unexportedRemoved)) return result.Interface() case reflect.Array, reflect.Slice: var result reflect.Value if expectedKind == reflect.Array { result = reflect.New(reflect.ArrayOf(expectedValue.Len(), expectedType.Elem())).Elem() } else { result = reflect.MakeSlice(expectedType, expectedValue.Len(), expectedValue.Len()) } for i := range expectedValue.Len() { index := expectedValue.Index(i) if !index.CanInterface() { // this should not be possible with current reflect, since values are retrieved from an array or slice, not a struct panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", index)) } unexportedRemoved := copyExportedFields(index.Interface()) result.Index(i).Set(reflect.ValueOf(unexportedRemoved)) } return result.Interface() case reflect.Map: result := reflect.MakeMap(expectedType) for _, k := range expectedValue.MapKeys() { index := expectedValue.MapIndex(k) if !index.CanInterface() { // this should not be possible with current reflect, since values are retrieved from a map, not a struct panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", index)) } unexportedRemoved := copyExportedFields(index.Interface()) result.SetMapIndex(k, reflect.ValueOf(unexportedRemoved)) } return result.Interface() default: return expected } } func isFunction(arg any) bool { if arg == nil { return false } return reflect.TypeOf(arg).Kind() == reflect.Func } go-openapi-testify-c10ca71/internal/assertions/equal_impl_test.go000066400000000000000000000051441520301377500253710ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "iter" "slices" "testing" ) const shortpkg = "assertions" func TestEqualUnexportedImplementationDetails(t *testing.T) { t.Parallel() t.Run("formatUnequalValue", testFormatUnequalValues()) t.Run("validateEqualArgs", testValidateEqualArgs()) } func testFormatUnequalValues() func(*testing.T) { return func(t *testing.T) { t.Parallel() for tt := range formatUnequalCases() { t.Run(tt.testName, func(t *testing.T) { t.Parallel() expected, actual := formatUnequalValues(tt.unequalExpected, tt.unequalActual) if tt.expectedExpected != expected { t.Errorf("%s: expected formatted expected %q, got %q", tt.testName, tt.expectedExpected, expected) } if tt.expectedActual != actual { t.Errorf("%s: expected formatted actual %q, got %q", tt.testName, tt.expectedActual, actual) } }) } } } func testValidateEqualArgs() func(*testing.T) { return func(t *testing.T) { t.Parallel() if validateEqualArgs(func() {}, func() {}) == nil { t.Error("non-nil functions should error") } if validateEqualArgs(func() {}, func() {}) == nil { t.Error("non-nil functions should error") } if validateEqualArgs(nil, nil) != nil { t.Error("nil functions are equal") } } } type formatUnequalCase struct { unequalExpected any unequalActual any expectedExpected string expectedActual string testName string } func formatUnequalCases() iter.Seq[formatUnequalCase] { type testStructType struct { Val string } return slices.Values([]formatUnequalCase{ {"foo", "bar", `"foo"`, `"bar"`, "value should not include type"}, {123, 123, `123`, `123`, "value should not include type"}, {int64(123), int32(123), `int64(123)`, `int32(123)`, "value should include type"}, {int64(123), nil, `int64(123)`, `()`, "value should include type"}, { unequalExpected: &testStructType{Val: "test"}, unequalActual: &testStructType{Val: "test"}, expectedExpected: fmt.Sprintf(`&%s.testStructType{Val:"test"}`, shortpkg), expectedActual: fmt.Sprintf(`&%s.testStructType{Val:"test"}`, shortpkg), testName: "value should not include type annotation", }, {uint(123), uint(124), `123`, `124`, "uint should print clean"}, {uint8(123), uint8(124), `123`, `124`, "uint8 should print clean"}, {uint16(123), uint16(124), `123`, `124`, "uint16 should print clean"}, {uint32(123), uint32(124), `123`, `124`, "uint32 should print clean"}, {uint64(123), uint64(124), `123`, `124`, "uint64 should print clean"}, }) } go-openapi-testify-c10ca71/internal/assertions/equal_pointer.go000066400000000000000000000061311520301377500250460ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import "fmt" // Same asserts that two pointers reference the same object. // // Both arguments must be pointer variables. // // Pointer variable sameness is determined based on the equality of both type and value. // // Unlike [Equal] pointers, [Same] pointers point to the same memory address. // // # Usage // // assertions.Same(t, ptr1, ptr2) // // # Examples // // success: &staticVar, staticVarPtr // failure: &staticVar, ptr("static string") func Same(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotSame if h, ok := t.(H); ok { h.Helper() } same, ok := samePointers(expected, actual) if !ok { return Fail(t, "Both arguments must be pointers", msgAndArgs...) } if !same { // both are pointers but not the same type & pointing to the same address return Fail(t, fmt.Sprintf("Not same: \n"+ "expected: %[2]s (%[1]T)(%[1]p)\n"+ "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) } return true } // SameT asserts that two pointers of the same type reference the same object. // // See [Same]. // // # Usage // // assertions.SameT(t, ptr1, ptr2) // // # Examples // // success: &staticVar, staticVarPtr // failure: &staticVar, ptr("static string") func SameT[P any](t T, expected, actual *P, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotSameT if h, ok := t.(H); ok { h.Helper() } if expected != actual { return Fail(t, fmt.Sprintf("Not same: \n"+ "expected: %[2]s (%[1]T)(%[1]p)\n"+ "actual : %[4]s (%[3]T)(%[3]p)", expected, truncatingFormat("%#v", expected), actual, truncatingFormat("%#v", actual)), msgAndArgs...) } return true } // NotSame asserts that two pointers do not reference the same object. // // See [Same]. // // # Usage // // assertions.NotSame(t, ptr1, ptr2) // // # Examples // // success: &staticVar, ptr("static string") // failure: &staticVar, staticVarPtr func NotSame(t T, expected, actual any, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } same, ok := samePointers(expected, actual) if !ok { // fails when the arguments are not pointers return Fail(t, "Both arguments must be pointers", msgAndArgs...) } if same { return Fail(t, fmt.Sprintf( "Expected and actual point to the same object: %p %s", expected, truncatingFormat("%#v", expected)), msgAndArgs...) } return true } // NotSameT asserts that two pointers do not reference the same object. // // See [SameT]. // // # Usage // // assertions.NotSameT(t, ptr1, ptr2) // // # Examples // // success: &staticVar, ptr("static string") // failure: &staticVar, staticVarPtr func NotSameT[P any](t T, expected, actual *P, msgAndArgs ...any) bool { // Domain: equality if h, ok := t.(H); ok { h.Helper() } if expected == actual { return Fail(t, fmt.Sprintf( "Expected and actual point to the same object: %p %s", expected, truncatingFormat("%#v", expected)), msgAndArgs...) } return true } go-openapi-testify-c10ca71/internal/assertions/equal_pointer_test.go000066400000000000000000000160321520301377500261060ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "iter" "slices" "testing" ) // Pointer identity tests (Same, SameT, NotSame, NotSameT). func TestEqualPointers(t *testing.T) { t.Parallel() for tc := range unifiedPointerPairCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() expected, actual := tc.makeValues() if !tc.reflectionOnly { t.Run("with Same", testPointerAssertion(tc, sameKind, Same, expected, actual)) t.Run("with NotSame", testPointerAssertion(tc, notSameKind, NotSame, expected, actual)) // Generic variants - type dispatch t.Run("with SameT", testPointerAssertionT(tc, sameTKind, expected, actual)) t.Run("with NotSameT", testPointerAssertionT(tc, notSameTKind, expected, actual)) return } // Reflection-only cases (non-pointer args, different types, one nil) t.Run("with Same (reflection)", testPointerAssertion(tc, sameKind, Same, expected, actual)) t.Run("with NotSame (reflection)", testPointerAssertion(tc, notSameKind, NotSame, expected, actual)) }) } } func TestEqualPointerErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, equalPointerFailCases()) } // ============================================================================ // TestEqualPointers // ============================================================================ type pointerPairTestCase struct { name string makeValues func() (expected, actual any) relationship pairRelationship reflectionOnly bool } func unifiedPointerPairCases() iter.Seq[pointerPairTestCase] { const hello = "hello" return slices.Values([]pointerPairTestCase{ // Both nil {"both-nil/ptr", func() (any, any) { return (*int)(nil), (*int)(nil) }, bothNil, false}, {"both-nil/different-types", func() (any, any) { return (*int)(nil), (*string)(nil) }, differentTypes, true}, // One nil {"one-nil/first", func() (any, any) { v := 42; return (*int)(nil), &v }, oneNil, true}, {"one-nil/second", func() (any, any) { v := 42; return &v, (*int)(nil) }, oneNil, true}, // Same identity - both point to same address {"same-identity/int", func() (any, any) { v := 42; return &v, &v }, sameIdentity, false}, {"same-identity/string", func() (any, any) { s := hello; return &s, &s }, sameIdentity, false}, {"same-identity/float64", func() (any, any) { f := 3.14; return &f, &f }, sameIdentity, false}, // Different identity - point to different adresses {"different-identity/equal-values", func() (any, any) { v1, v2 := 42, 42; return &v1, &v2 }, differentIdentity, false}, {"different-identity/different-values", func() (any, any) { v1, v2 := 42, 43; return &v1, &v2 }, differentIdentity, false}, // Different types (reflection-only) {"different-types/int-string", func() (any, any) { i, s := 1, hello return &i, &s }, differentTypes, true}, // Edge cases (always false) {"not-pointer/right", func() (any, any) { v1 := 12; return &v1, 1 }, notPointer, true}, {"not-pointer/left", func() (any, any) { v1 := 12; return 1, &v1 }, notPointer, true}, {"not-pointer/both", func() (any, any) { return 1, 1 }, notPointer, true}, }) } func testPointerAssertion( tc pointerPairTestCase, kind pointerAssertionKind, pointerAssertion func(T, any, any, ...any) bool, expected, actual any, ) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := pointerAssertion(mock, expected, actual) shouldPass := expectedStatusForPointerAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } type pointerAssertionKind int const ( sameKind pointerAssertionKind = iota sameTKind notSameKind notSameTKind ) type pairRelationship int const ( bothNil pairRelationship = iota oneNil sameIdentity differentIdentity differentTypes notPointer ) func expectedStatusForPointerAssertion(kind pointerAssertionKind, relationship pairRelationship) bool { positive := kind == sameKind || kind == sameTKind switch relationship { case notPointer: return false case sameIdentity, bothNil: // Two nil pointers of the same type are considered "same" in Go return positive case oneNil, differentIdentity, differentTypes: return !positive default: panic(fmt.Errorf("test case configuration error: invalid pairRelationship: %d", relationship)) } } func testPointerAssertionT(tc pointerPairTestCase, kind pointerAssertionKind, expected, actual any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) stop := func(expected string, actual any) { t.Fatalf("test case error: expected=%s, actual=%T", expected, actual) } // Type switch with safety check // // Add more supported types to the switch with new test cases var result bool switch exp := expected.(type) { case *int: act, ok := actual.(*int) if !ok { stop("*int", actual) } result = testPointerGenericAssertion(mock, kind, exp, act) case *string: act, ok := actual.(*string) if !ok { stop("*string", actual) } result = testPointerGenericAssertion(mock, kind, exp, act) case *float64: act, ok := actual.(*float64) if !ok { stop("*float64", actual) } result = testPointerGenericAssertion(mock, kind, exp, act) default: t.Fatalf("unsupported type: %T", expected) } shouldPass := expectedStatusForPointerAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } func testPointerGenericAssertion[P any](mock T, kind pointerAssertionKind, expected, actual *P) bool { switch kind { case sameTKind: return SameT(mock, expected, actual) case notSameTKind: return NotSameT(mock, expected, actual) default: panic(fmt.Errorf("test case configuration error: invalid pointerAssertionKind: %d", kind)) } } // ============================================================================ // TestEqualPointerErrorMessages // ============================================================================ func equalPointerFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "Same/different-pointers", assertion: func(t T) bool { v1, v2 := 42, 42 return Same(t, &v1, &v2) }, wantContains: []string{"Not same"}, }, { name: "Same/not-pointers", assertion: func(t T) bool { return Same(t, 1, 2) }, wantError: "Both arguments must be pointers", }, { name: "NotSame/same-pointer", assertion: func(t T) bool { v := 42 return NotSame(t, &v, &v) }, wantContains: []string{"Expected and actual point to the same object"}, }, { name: "NotSame/not-pointers", assertion: func(t T) bool { return NotSame(t, 1, 2) }, wantError: "Both arguments must be pointers", }, { name: "SameT/different-pointers", assertion: func(t T) bool { v1, v2 := 42, 42 return SameT(t, &v1, &v2) }, wantContains: []string{"Not same"}, }, { name: "NotSameT/same-pointer", assertion: func(t T) bool { v := 42 return NotSameT(t, &v, &v) }, wantContains: []string{"Expected and actual point to the same object"}, }, }) } go-openapi-testify-c10ca71/internal/assertions/equal_test.go000066400000000000000000000652151520301377500243550ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "iter" "slices" "testing" ) // Test EqualValues and NotEqualValues. func TestEqualValues(t *testing.T) { t.Parallel() for tc := range equalValuesCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() mock := new(mockT) res := NotEqualValues(mock, tc.expected, tc.actual) if res != tc.notEqualValue { t.Errorf("NotEqualValues(%#v, %#v) should return %t", tc.expected, tc.actual, tc.notEqualValue) } }) // Test EqualValues (inverse of NotEqualValues) t.Run(tc.name, func(t *testing.T) { t.Parallel() mock := new(mockT) res := EqualValues(mock, tc.expected, tc.actual) if res != tc.equalValue { t.Errorf("EqualValues(%#v, %#v) should return %t", tc.expected, tc.actual, tc.equalValue) } }) } } func TestEqualErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, equalErrorMessageCases()) } // Test EqualExportedValues. func TestEqualExportedValues(t *testing.T) { t.Parallel() for tc := range objectEqualExportedValuesCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() mockT := new(mockT) actual := EqualExportedValues(mockT, tc.expected, tc.actual) if actual != tc.expectedEqual { t.Errorf("Expected EqualExportedValues to be %t, but was %t", tc.expectedEqual, actual) } }) } } // TestEqualExportedValuesErrorMessages tests the semantic content of error messages // produced by EqualExportedValues for failing cases. func TestEqualExportedValuesErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, equalExportedValuesFailCases()) } // Deep equality tests (Equal, EqualT, NotEqual, NotEqualT, Exactly). func TestEqualDeepEqual(t *testing.T) { t.Parallel() for tc := range unifiedEqualityCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() expected, actual := tc.makeValues() if !tc.reflectionOnly { t.Run("with Equal", testEqualityAssertion(tc, equalKind, Equal, expected, actual)) t.Run("with NotEqual", testEqualityAssertion(tc, notEqualKind, NotEqual, expected, actual)) t.Run("with Exactly", testEqualityAssertion(tc, exactlyKind, Exactly, expected, actual)) // Generic variants - type dispatch t.Run("with EqualT", testEqualityAssertionT(tc, equalTKind, expected, actual)) t.Run("with NotEqualT", testEqualityAssertionT(tc, notEqualTKind, expected, actual)) } else { // Reflection-only cases t.Run("with Equal (reflection)", testEqualityAssertion(tc, equalKind, Equal, expected, actual)) t.Run("with NotEqual (reflection)", testEqualityAssertion(tc, notEqualKind, NotEqual, expected, actual)) t.Run("with Exactly (reflection)", testEqualityAssertion(tc, exactlyKind, Exactly, expected, actual)) } }) } } // ============================================================================ // TestEqualDeepEqual // ============================================================================ type equalityRelationship int const ( eqBothNil equalityRelationship = iota eqOneNil eqSameIdentity eqEqualValueComparable eqEqualValueNonComparable eqDifferentValueSameType eqDifferentType eqFunction ) func testEqualityAssertion(tc equalityTestCase, kind equalityAssertionKind, equalityAssertion func(T, any, any, ...any) bool, expected, actual any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := equalityAssertion(mock, expected, actual) shouldPass := expectedStatusForEqualityAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } //nolint:gocognit,gocyclo,cyclop // no other way here than a big type switch func testEqualityAssertionT(tc equalityTestCase, kind equalityAssertionKind, expected, actual any) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) stop := func(expected, actual any) { t.Fatalf("test case error: expected=%s, actual=%T", expected, actual) } // Type switch with safety check // // Add more (comparable) types when new test cases require it. var result bool switch exp := expected.(type) { case int: act, ok := actual.(int) if !ok { stop("int", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case int8: act, ok := actual.(int8) if !ok { stop("int8", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case int16: act, ok := actual.(int16) if !ok { stop("int16", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case int32: act, ok := actual.(int32) if !ok { stop("int32", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case int64: act, ok := actual.(int64) if !ok { stop("int32", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case uint: act, ok := actual.(uint) if !ok { stop("uint", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case uint8: act, ok := actual.(uint8) if !ok { stop("uint8", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case uint16: act, ok := actual.(uint16) if !ok { stop("uint16", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case uint32: act, ok := actual.(uint32) if !ok { stop("uint32", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case uint64: act, ok := actual.(uint64) if !ok { stop("uint64", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case string: act, ok := actual.(string) if !ok { stop("string", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case bool: act, ok := actual.(bool) if !ok { stop("bool", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case float32: act, ok := actual.(float32) if !ok { stop("float32", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case float64: act, ok := actual.(float64) if !ok { stop("float64", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case *int: act, ok := actual.(*int) if !ok { stop("*int", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case *string: act, ok := actual.(*string) if !ok { stop("*string", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case myType: act, ok := actual.(myType) if !ok { stop("myType", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case struct{}: act, ok := actual.(struct{}) if !ok { stop("struct{}", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) case *struct{}: act, ok := actual.(*struct{}) if !ok { stop("*struct{}", actual) } result = testEqualityGenericAssertion(mock, kind, exp, act) default: t.Fatalf("unsupported type: %T", expected) } shouldPass := expectedStatusForEqualityAssertion(kind, tc.relationship) shouldPassOrFail(t, mock, result, shouldPass) } } type equalityTestCase struct { name string makeValues func() (expected, actual any) relationship equalityRelationship reflectionOnly bool } type ( structWithUnexportedMapWithArrayKey struct { m any } s struct { f map[[1]byte]int } myType string myMap map[string]any ) func unifiedEqualityCases() iter.Seq[equalityTestCase] { const hello = "hello" s1 := struct{}{} p1 := &s1 p2 := &s1 f1 := func() bool { return true } return slices.Values([]equalityTestCase{ // Both nil {"both-nil/ptr", func() (any, any) { return (*int)(nil), (*int)(nil) }, eqBothNil, false}, {"both-nil/interface", func() (any, any) { return (any)(nil), (any)(nil) }, eqBothNil, true}, // One nil (reflection only - type mismatch) {"one-nil/first", func() (any, any) { v := 42; return nil, &v }, eqOneNil, true}, {"one-nil/second", func() (any, any) { v := 42; return &v, nil }, eqOneNil, true}, {"one-nil/bytes", func() (any, any) { return nil, make([]byte, 0) }, eqOneNil, true}, {"one-nil/struct", func() (any, any) { return nil, new(AssertionTesterConformingObject) }, eqOneNil, true}, // Same identity (pointers to same object) {"same-identity/int-ptr", func() (any, any) { v := 42; return &v, &v }, eqSameIdentity, false}, {"same-identity/string-ptr", func() (any, any) { s := hello; return &s, &s }, eqSameIdentity, false}, { "same-identity/pointer-to-struct", func() (any, any) { return p1, p2 }, eqSameIdentity, false, }, // Equal value, comparable (core types - start with 5) {"equal-comparable/int", func() (any, any) { return 42, 42 }, eqEqualValueComparable, false}, {"equal-comparable/string", func() (any, any) { return hello, hello }, eqEqualValueComparable, false}, {"equal-comparable/bool-true", func() (any, any) { return true, true }, eqEqualValueComparable, false}, {"equal-comparable/bool-false", func() (any, any) { return false, false }, eqEqualValueComparable, false}, {"equal-comparable/float64", func() (any, any) { return 3.14, 3.14 }, eqEqualValueComparable, false}, {"equal-comparable/float32", func() (any, any) { return float32(3.14), float32(3.14) }, eqEqualValueComparable, false}, {"equal-comparable/int8", func() (any, any) { return int8(10), int8(10) }, eqEqualValueComparable, false}, {"equal-comparable/int16", func() (any, any) { return int16(100), int16(100) }, eqEqualValueComparable, false}, {"equal-comparable/int32", func() (any, any) { return int32(1000), int32(1000) }, eqEqualValueComparable, false}, {"equal-comparable/int64", func() (any, any) { return int64(10000), int64(10000) }, eqEqualValueComparable, false}, {"equal-comparable/uint", func() (any, any) { return uint(42), uint(42) }, eqEqualValueComparable, false}, {"equal-comparable/uint8", func() (any, any) { return uint8(10), uint8(10) }, eqEqualValueComparable, false}, {"equal-comparable/uint16", func() (any, any) { return uint16(100), uint16(100) }, eqEqualValueComparable, false}, {"equal-comparable/uint32", func() (any, any) { return uint32(1000), uint32(1000) }, eqEqualValueComparable, false}, {"equal-comparable/uint64", func() (any, any) { return uint64(10000), uint64(10000) }, eqEqualValueComparable, false}, {"equal-comparable/~string", func() (any, any) { return myType("1"), myType("1") }, eqEqualValueComparable, false}, { "equal-comparable/anonymous-struct", func() (any, any) { return struct{}{}, struct{}{} }, eqEqualValueComparable, false, }, { "equal-comparable/pointer-to-anonymous-struct", func() (any, any) { return &struct{}{}, &struct{}{} // this a special case in go, as the pointer to this empty type is not allocated: values are equal }, eqEqualValueComparable, false, }, // Equal value, non-comparable (reflection only) {"equal-non-comparable/slice", func() (any, any) { return []int{1, 2, 3}, []int{1, 2, 3} }, eqEqualValueNonComparable, true}, {"equal-non-comparable/struct-ptr", func() (any, any) { return &struct{}{}, &struct{}{} }, eqEqualValueNonComparable, true}, {"equal-non-comparable/bytes", func() (any, any) { return []byte(hello), []byte(hello) }, eqEqualValueNonComparable, true}, {"equal-non-comparable/map", func() (any, any) { return myMap{"bar": 1}, myMap{"bar": 1} }, eqEqualValueNonComparable, true}, { "equal-non-comparable/bytes-zero-same-len", func() (any, any) { return make([]byte, 2), make([]byte, 2) }, eqEqualValueNonComparable, true, }, { "equal-non-comparable/bytes-zero-same-len-diff-cap", func() (any, any) { return make([]byte, 2), make([]byte, 2, 3) }, eqEqualValueNonComparable, true, }, { "equal-non-comparable/bytes-zero-same-len-diff-cap", func() (any, any) { return new(AssertionTesterConformingObject), new(AssertionTesterConformingObject) }, eqEqualValueNonComparable, true, }, { "equal-non-comparable/map-unexported-struct", func() (any, any) { return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: nil, {2}: nil}}, structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{2}: nil, {1}: nil}} }, eqEqualValueNonComparable, true, }, { "equal-non-comparable/map-unexported-struct-non-nil", func() (any, any) { return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: {}, {2}: nil}}, structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: {}, {2}: nil}} }, eqEqualValueNonComparable, true, }, // Different value, same type {"diff-value/int", func() (any, any) { return 42, 43 }, eqDifferentValueSameType, false}, {"diff-value/string", func() (any, any) { return hello, "world" }, eqDifferentValueSameType, false}, {"diff-value/bool", func() (any, any) { return true, false }, eqDifferentValueSameType, false}, {"diff-value/float64", func() (any, any) { return 3.14, 2.71 }, eqDifferentValueSameType, false}, {"diff-value/float32", func() (any, any) { return float32(3.15), float32(3.14) }, eqDifferentValueSameType, false}, {"diff-value/int8", func() (any, any) { return int8(10), int8(11) }, eqDifferentValueSameType, false}, {"diff-value/int16", func() (any, any) { return int16(110), int16(100) }, eqDifferentValueSameType, false}, {"diff-value/int32", func() (any, any) { return int32(1003), int32(1000) }, eqDifferentValueSameType, false}, {"diff-value/int64", func() (any, any) { return int64(10400), int64(10000) }, eqDifferentValueSameType, false}, {"diff-value/uint", func() (any, any) { return uint(43), uint(42) }, eqDifferentValueSameType, false}, {"diff-value/uint8", func() (any, any) { return uint8(10), uint8(11) }, eqDifferentValueSameType, false}, {"diff-value/uint16", func() (any, any) { return uint16(101), uint16(100) }, eqDifferentValueSameType, false}, {"diff-value/uint32", func() (any, any) { return uint32(1040), uint32(1000) }, eqDifferentValueSameType, false}, {"diff-value/uint64", func() (any, any) { return uint64(10000), uint64(14000) }, eqDifferentValueSameType, false}, {"diff-value/~string", func() (any, any) { return myType("1"), myType("2") }, eqDifferentValueSameType, false}, {"diff-value/slice", func() (any, any) { return []int{1, 2}, []int{1, 3} }, eqDifferentValueSameType, true}, {"diff-value/map", func() (any, any) { return myMap{"bar": 1}, myMap{"bar": 2} }, eqDifferentValueSameType, true}, { "diff-value/map-unexported-struct", func() (any, any) { return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: nil, {2}: nil}}, structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{}} }, eqDifferentValueSameType, true, }, { "diff-value/map-unexported-struct-non-nil", func() (any, any) { return structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: {}, {2}: nil}}, structWithUnexportedMapWithArrayKey{map[[1]byte]*struct{}{{1}: nil, {2}: {}}} }, eqDifferentValueSameType, true, }, { "diff-value/func", func() (any, any) { return func() int { return 23 }, func() int { return 24 } }, eqFunction, true, }, { "same-value/func", func() (any, any) { return f1, f1 }, eqFunction, true, }, // Different type (reflection only - can't use with generics) {"diff-type/int-uint", func() (any, any) { return 42, uint(42) }, eqDifferentType, true}, {"diff-type/int-int64", func() (any, any) { return 42, int64(42) }, eqDifferentType, true}, {"diff-type/int-float64", func() (any, any) { return 10, 10.0 }, eqDifferentType, true}, {"diff-type/float32-float64", func() (any, any) { return float32(10), float64(10) }, eqDifferentType, true}, {"diff-value/edge-case-map", func() (any, any) { // this case used to panic return s{ f: map[[1]byte]int{ {0x1}: 0, {0x2}: 0, }, }, s{} }, eqDifferentValueSameType, true}, }) } type equalityAssertionKind int const ( equalKind equalityAssertionKind = iota equalTKind notEqualKind notEqualTKind exactlyKind ) func expectedStatusForEqualityAssertion(kind equalityAssertionKind, relationship equalityRelationship) bool { positive := kind == equalKind || kind == equalTKind || kind == exactlyKind switch relationship { case eqFunction: // A special validation is carried out to reject function types with an error return false case eqBothNil, eqSameIdentity, eqEqualValueComparable, eqEqualValueNonComparable: return positive case eqOneNil, eqDifferentValueSameType: return !positive case eqDifferentType: // Exactly requires exact type match (fails on different types) // Equal uses reflection (can handle different types) // EqualT requires same type (won't compile with different types) if kind == exactlyKind { return false } // Equal might handle some type coercion, but generally fails return !positive default: panic(fmt.Errorf("test case configuration error: invalid equalityRelationship: %d", relationship)) } } func testEqualityGenericAssertion[V comparable](mock T, kind equalityAssertionKind, expected, actual V) bool { switch kind { case equalTKind: return EqualT(mock, expected, actual) case notEqualTKind: return NotEqualT(mock, expected, actual) default: panic(fmt.Errorf("test case configuration error: invalid equalityAssertionKind for generic: %d", kind)) } } // ============================================================================ // equalErrorMessageCases: error message tests for Equal-family assertions. // ============================================================================ func equalErrorMessageCases() iter.Seq[failCase] { return slices.Values([]failCase{ // Truncation cases (values too long to print) truncationCase("truncation/Same", func(t T) bool { longSlice := make([]int, 1_000_000) return Same(t, &[]int{}, &longSlice) }), truncationCase("truncation/NotSame", func(t T) bool { longSlice := make([]int, 1_000_000) return NotSame(t, &longSlice, &longSlice) }), truncationCase("truncation/NotEqual", func(t T) bool { longSlice := make([]int, 1_000_000) return NotEqual(t, longSlice, longSlice) }), truncationCase("truncation/NotEqualValues", func(t T) bool { longSlice := make([]int, 1_000_000) return NotEqualValues(t, longSlice, longSlice) }), // Semantic content of Equal diff messages { name: "Equal/multiline-diff", assertion: func(t T) bool { return Equal(t, "hi, \nmy name is", "what,\nmy name is") }, wantContains: []string{ "Not equal:", `expected: "hi, \nmy name is"`, `actual : "what,\nmy name is"`, "--- Expected", "+++ Actual", "-hi,", "+what,", "my name is", }, }, { name: "Equal/single-line-diff", assertion: func(t T) bool { return Equal(t, "want", "got") }, wantContains: []string{ "Not equal:", `expected: "want"`, `actual : "got"`, "-want", "+got", }, }, }) } // ============================================================================ // TestEqualValues // ============================================================================ type equalValuesCase struct { name string expected any actual any equalValue bool notEqualValue bool // notEqualValue = !equalValue, except for invalid types (e.g. functions) } func equalValuesCases() iter.Seq[equalValuesCase] { return slices.Values([]equalValuesCase{ // cases that are expected not to match {"not-equal/string", "Hello World", "Hello World!", false, true}, {"not-equal/int", 123, 1234, false, true}, {"not-equal/float64", 123.5, 123.55, false, true}, {"not-equal/[]byte", []byte("Hello World"), []byte("Hello World!"), false, true}, {"not-equal/nil-not-nil", nil, new(AssertionTesterConformingObject), false, true}, {"not-equal/converted", uint(10), int(11), false, true}, // cases that are expected to match {"equal/nil-nil", nil, nil, true, false}, {"equal/string", "Hello World", "Hello World", true, false}, {"equal/int", 123, 123, true, false}, {"equal/float64", 123.5, 123.5, true, false}, {"equal/[]byte", []byte("Hello World"), []byte("Hello World"), true, false}, {"equal/pointer-to-struct", new(AssertionTesterConformingObject), new(AssertionTesterConformingObject), true, false}, {"equal/pointer-to-anonymous-struct", &struct{}{}, &struct{}{}, true, false}, {"equal/converted", int(10), uint(10), true, false}, {"equal/anonymous-struct", struct{}{}, struct{}{}, true, false}, // always fail {"always-fail/func", func() int { return 23 }, func() int { return 24 }, false, false}, }) } // ============================================================================ // TestEqualExportedValues // ============================================================================ type objectEqualExportedValuesCase struct { name string expected any actual any expectedEqual bool } func objectEqualExportedValuesCases() iter.Seq[objectEqualExportedValuesCase] { type specialKey struct { a string } return slices.Values([]objectEqualExportedValuesCase{ { name: "edge-case/func", expected: func() {}, actual: func() {}, expectedEqual: false, }, { name: "edge-case/expect-nil", expected: nil, actual: nil, expectedEqual: true, }, { name: "edge-case/expect-nil-actual-not-nil", expected: nil, actual: 1, expectedEqual: false, }, { name: "edge-case/map-with-struct-key", expected: map[specialKey]S{{a: "a"}: {}}, actual: map[specialKey]S{{a: "a"}: {}}, expectedEqual: true, }, { name: "equal-values/map", expected: map[string]S{ "key": {1, Nested{2, 3}, 4, Nested{5, 6}}, }, actual: map[string]S{ "key": {1, Nested{2, nil}, nil, Nested{}}, }, expectedEqual: true, }, { name: "diff-values/map", expected: map[string]S{ "key": {1, Nested{2, 3}, 4, Nested{5, 6}}, }, actual: map[string]S{ "x": {1, Nested{2, nil}, nil, Nested{}}, }, expectedEqual: false, }, { name: "equal-values/nested-struct", expected: S{1, Nested{2, 3}, 4, Nested{5, 6}}, actual: S{1, Nested{2, nil}, nil, Nested{}}, expectedEqual: true, }, { name: "diff-values/nested-struct(1)", expected: S{1, Nested{2, 3}, 4, Nested{5, 6}}, actual: S{1, Nested{1, nil}, nil, Nested{}}, expectedEqual: false, }, { name: "diff-values/nested-struct(2)", expected: S3{&Nested{1, 2}, &Nested{3, 4}}, actual: S3{&Nested{"a", 2}, &Nested{3, 4}}, expectedEqual: false, }, { name: "diff-values/inner-slice", expected: S4{[]*Nested{ {1, 2}, {3, 4}, }}, actual: S4{[]*Nested{ {1, "a"}, {2, "b"}, }}, expectedEqual: false, }, { name: "equal-values/inner-array-unexported-diff", expected: S{[2]int{1, 2}, Nested{2, 3}, 4, Nested{5, 6}}, actual: S{[2]int{1, 2}, Nested{2, nil}, nil, Nested{}}, expectedEqual: true, }, { name: "equal-values/inner-array", expected: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, actual: &S{1, Nested{2, nil}, nil, Nested{}}, expectedEqual: true, }, { name: "diff-values/inner-slice-exported-diff", expected: &S{1, Nested{2, 3}, 4, Nested{5, 6}}, actual: &S{1, Nested{1, nil}, nil, Nested{}}, expectedEqual: false, }, { name: "equal-values/slice", expected: []int{1, 2}, actual: []int{1, 2}, expectedEqual: true, }, { name: "diff-values/slice", expected: []int{1, 2}, actual: []int{1, 3}, expectedEqual: false, }, { name: "equal-values/slice-of-pointers", expected: []*int{ptr(1), nil, ptr(2)}, actual: []*int{ptr(1), nil, ptr(2)}, expectedEqual: true, }, { name: "equal-values/slice-of-struct", expected: []*Nested{ {1, 2}, {3, 4}, }, actual: []*Nested{ {1, "a"}, {3, "b"}, }, expectedEqual: true, }, { name: "diff-values/slice-of-struct", expected: []*Nested{ {1, 2}, {3, 4}, }, actual: []*Nested{ {1, "a"}, {2, "b"}, }, expectedEqual: false, }, }) } // ============================================================================ // TestEqualExportedValuesErrorMessages // ============================================================================ func equalExportedValuesFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "nested-struct(1)/diff-in-exported-field", assertion: func(t T) bool { return EqualExportedValues(t, S{1, Nested{2, 3}, 4, Nested{5, 6}}, S{1, Nested{1, nil}, nil, Nested{}}, ) }, wantContains: []string{ "Not equal (comparing only exported fields):", "--- Expected", "+++ Actual", fmt.Sprintf("Exported2: (%s.Nested) {", shortpkg), "- Exported: (int) 2,", "+ Exported: (int) 1,", }, }, { name: "nested-struct(2)/int-vs-string", assertion: func(t T) bool { return EqualExportedValues(t, S3{&Nested{1, 2}, &Nested{3, 4}}, S3{&Nested{"a", 2}, &Nested{3, 4}}, ) }, wantContains: []string{ "Not equal (comparing only exported fields):", "--- Expected", "+++ Actual", fmt.Sprintf("Exported1: (*%s.Nested)({", shortpkg), "- Exported: (int) 1,", `+ Exported: (string) (len=1) "a",`, }, }, { name: "inner-slice/diff-in-nested-exported", assertion: func(t T) bool { return EqualExportedValues(t, S4{[]*Nested{{1, 2}, {3, 4}}}, S4{[]*Nested{{1, "a"}, {2, "b"}}}, ) }, wantContains: []string{ "Not equal (comparing only exported fields):", "--- Expected", "+++ Actual", fmt.Sprintf("(*%s.Nested)({", shortpkg), "- Exported: (int) 3,", "+ Exported: (int) 2,", }, }, { name: "inner-slice-exported-diff/ptr-receiver", assertion: func(t T) bool { return EqualExportedValues(t, &S{1, Nested{2, 3}, 4, Nested{5, 6}}, &S{1, Nested{1, nil}, nil, Nested{}}, ) }, wantContains: []string{ "Not equal (comparing only exported fields):", "--- Expected", "+++ Actual", fmt.Sprintf("Exported2: (%s.Nested) {", shortpkg), "- Exported: (int) 2,", "+ Exported: (int) 1,", }, }, { name: "slice/int-diff", assertion: func(t T) bool { return EqualExportedValues(t, []int{1, 2}, []int{1, 3}) }, wantContains: []string{ "Not equal (comparing only exported fields):", "--- Expected", "+++ Actual", "(int) 1,", "- (int) 2", "+ (int) 3", }, }, { name: "slice-of-struct/diff-in-exported", assertion: func(t T) bool { return EqualExportedValues(t, []*Nested{{1, 2}, {3, 4}}, []*Nested{{1, "a"}, {2, "b"}}, ) }, wantContains: []string{ "Not equal (comparing only exported fields):", "--- Expected", "+++ Actual", fmt.Sprintf("(*%s.Nested)({", shortpkg), "- Exported: (int) 3,", "+ Exported: (int) 2,", }, }, }) } go-openapi-testify-c10ca71/internal/assertions/equal_unary.go000066400000000000000000000065571520301377500245400ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "reflect" ) // Nil asserts that the specified object is nil. // // # Usage // // assertions.Nil(t, err) // // # Examples // // success: nil // failure: "not nil" func Nil(t T, object any, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotNil if isNil(object) { return true } if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Expected nil, but got: "+truncatingFormat("%#v", object), msgAndArgs...) } // NotNil asserts that the specified object is not nil. // // # Usage // // assertions.NotNil(t, err) // // # Examples // // success: "not nil" // failure: nil func NotNil(t T, object any, msgAndArgs ...any) bool { // Domain: equality if !isNil(object) { return true } if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Expected value not to be nil.", msgAndArgs...) } // Empty asserts that the given value is "empty". // // Zero values are "empty". // // Arrays are "empty" if every element is the zero value of the type (stricter than "empty"). // // Slices, maps and channels with zero length are "empty". // // Pointer values are "empty" if the pointer is nil or if the pointed value is "empty". // // # Usage // // assertions.Empty(t, obj) // // # Examples // // success: "" // failure: "not empty" // // [Zero values]: https://go.dev/ref/spec#The_zero_value func Empty(t T, object any, msgAndArgs ...any) bool { // Domain: equality // Opposite: NotEmpty pass := isEmpty(object) if !pass { if h, ok := t.(H); ok { h.Helper() } Fail(t, "Should be empty, but was "+truncatingFormat("%v", object), msgAndArgs...) } return pass } // NotEmpty asserts that the specified object is NOT [Empty]. // // # Usage // // if assert.NotEmpty(t, obj) { // assertions.Equal(t, "two", obj[1]) // } // // # Examples // // success: "not empty" // failure: "" func NotEmpty(t T, object any, msgAndArgs ...any) bool { // Domain: equality pass := !isEmpty(object) if !pass { if h, ok := t.(H); ok { h.Helper() } Fail(t, fmt.Sprintf("Should NOT be empty, but was %v", object), msgAndArgs...) } return pass } // isNil checks if a specified object is nil or not, without Failing. func isNil(object any) bool { if object == nil { return true } value := reflect.ValueOf(object) switch value.Kind() { case reflect.Chan, reflect.Func, reflect.Interface, reflect.Map, reflect.Pointer, reflect.Slice, reflect.UnsafePointer: return value.IsNil() default: return false } } // isEmpty gets whether the specified object is considered empty or not. func isEmpty(object any) bool { // get nil case out of the way if object == nil { return true } return isEmptyValue(reflect.ValueOf(object)) } // isEmptyValue gets whether the specified reflect.Value is considered empty or not. func isEmptyValue(objValue reflect.Value) bool { if objValue.IsZero() { return true } // Special cases of non-zero values that we consider empty switch objValue.Kind() { // collection types are empty when they have no element // Note: array types are empty when they match their zero-initialized state. case reflect.Chan, reflect.Map, reflect.Slice: return objValue.Len() == 0 // non-nil pointers are empty if the value they point to is empty case reflect.Pointer: return isEmptyValue(objValue.Elem()) default: return false } } go-openapi-testify-c10ca71/internal/assertions/equal_unary_test.go000066400000000000000000000156771520301377500256020ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "fmt" "iter" "slices" "testing" ) // Unary assertion tests (Nil, NotNil, Empty, NotEmpty). func TestEqualUnaryAssertions(t *testing.T) { t.Parallel() for tc := range unifiedUnaryCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() t.Run("with Nil", testUnaryAssertion(tc, nilKind, Nil)) t.Run("with NotNil", testUnaryAssertion(tc, notNilKind, NotNil)) t.Run("with Empty", testUnaryAssertion(tc, emptyKind, Empty)) t.Run("with NotEmpty", testUnaryAssertion(tc, notEmptyKind, NotEmpty)) }) } } func TestEqualUnaryErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, equalUnaryFailCases()) } // ============================================================================ // TestEqualUnaryAssertions // ============================================================================ type unaryTestCase struct { name string object any category objectCategory } func unifiedUnaryCases() iter.Seq[unaryTestCase] { chWithValue := make(chan struct{}, 1) chWithValue <- struct{}{} x := 1 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 {"nil/nil-ptr", (*int)(nil), nilCategory}, {"nil/nil-slice", []int(nil), nilCategory}, {"nil/nil-interface", (any)(nil), nilCategory}, {"nil/nil-struct-ptr", (*struct{})(nil), nilCategory}, // Empty non-nil category {"empty/slice", []int{}, emptyNonNil}, {"empty/string", "", emptyNonNil}, {"empty/zero-int", 0, emptyNonNil}, {"empty/zero-bool", false, emptyNonNil}, {"empty/channel", make(chan struct{}), emptyNonNil}, {"empty/zero-struct", TStruct{}, emptyNonNil}, {"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}, {"non-empty/channel", chWithValue, nonEmptyComparable}, {"non-empty/struct", TStruct{x: 1}, nonEmptyComparable}, {"non-empty/aliased-string", TString("abc"), nonEmptyComparable}, {"non-empty/ptr", xP, nonEmptyComparable}, {"non-empty/array", [1]int{42}, nonEmptyComparable}, // 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}, }) } type unaryAssertionKind int const ( nilKind unaryAssertionKind = iota notNilKind emptyKind notEmptyKind ) type objectCategory int const ( nilCategory objectCategory = iota emptyNonNil nonEmptyComparable nonEmptyNonComparable ) // expectedStatusForUnaryAssertion returns the expected semantics for a given assertion (Nil, Empty, ...) // and a given category of input. func expectedStatusForUnaryAssertion(kind unaryAssertionKind, category objectCategory) bool { switch kind { case nilKind: return category == nilCategory case notNilKind: return category != nilCategory case emptyKind: return category == nilCategory || category == emptyNonNil case notEmptyKind: return category == nonEmptyComparable || category == nonEmptyNonComparable default: panic(fmt.Errorf("test case configuration error: invalid unaryAssertionKind: %d", kind)) } } func testUnaryAssertion(tc unaryTestCase, kind unaryAssertionKind, unaryAssertion func(T, any, ...any) bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := unaryAssertion(mock, tc.object) shouldPass := expectedStatusForUnaryAssertion(kind, tc.category) shouldPassOrFail(t, mock, result, shouldPass) } } // ============================================================================ // TestEqualUnaryErrorMessages // ============================================================================ func equalUnaryFailCases() iter.Seq[failCase] { chWithValue := make(chan struct{}, 1) chWithValue <- struct{}{} x := 1 xP := &x type TString string type TStruct struct { x int } return slices.Values([]failCase{ { name: "Empty/non-empty-string", assertion: func(t T) bool { return Empty(t, "something") }, wantError: "Should be empty, but was something", }, { name: "Empty/non-nil-error", assertion: func(t T) bool { return Empty(t, errors.New("something")) }, wantError: "Should be empty, but was something", }, { name: "Empty/non-empty-string-array", assertion: func(t T) bool { return Empty(t, []string{"something"}) }, wantError: "Should be empty, but was [something]", }, { name: "Empty/non-zero-int", assertion: func(t T) bool { return Empty(t, 1) }, wantError: "Should be empty, but was 1", }, { name: "Empty/true-value", assertion: func(t T) bool { return Empty(t, true) }, wantError: "Should be empty, but was true", }, { name: "Empty/channel-with-values", assertion: func(t T) bool { return Empty(t, chWithValue) }, wantContains: []string{"Should be empty, but was"}, }, { name: "Empty/struct-with-values", assertion: func(t T) bool { return Empty(t, TStruct{x: 1}) }, wantError: "Should be empty, but was {1}", }, { name: "Empty/aliased-string", assertion: func(t T) bool { return Empty(t, TString("abc")) }, wantError: "Should be empty, but was abc", }, { name: "Empty/ptr-to-non-nil", assertion: func(t T) bool { return Empty(t, xP) }, wantContains: []string{"Should be empty, but was"}, }, { name: "Empty/non-zero-array", assertion: func(t T) bool { return Empty(t, [1]int{42}) }, wantError: "Should be empty, but was [42]", }, { name: "Empty/whitespace-string", assertion: func(t T) bool { return Empty(t, " ") }, wantContains: []string{"Should be empty, but was"}, }, { name: "Empty/newline-string", assertion: func(t T) bool { return Empty(t, "\n") }, wantContains: []string{"Should be empty, but was"}, }, { name: "Empty/non-printable-char", assertion: func(t T) bool { return Empty(t, "\u00a0") }, wantContains: []string{"Should be empty, but was"}, }, }) } go-openapi-testify-c10ca71/internal/assertions/error.go000066400000000000000000000164051520301377500233350ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "fmt" "reflect" "strings" ) var _ error = TestExampleError("") // 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 as an example, this error may be used to make // the test code more readable. const ErrTest TestExampleError = "assert.ErrTest general error for testing" // NoError asserts that a function returned a nil error (i.e. no error). // // # Usage // // actualObj, err := SomeFunction() // if assert.NoError(t, err) { // assertions.Equal(t, expectedObj, actualObj) // } // // # Examples // // success: nil // failure: ErrTest func NoError(t T, err error, msgAndArgs ...any) bool { // Domain: error if err != nil { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "Received unexpected error:\n"+truncatingFormat("%+v", err), msgAndArgs...) } return true } // Error asserts that a function returned a non-nil error (i.e. an error). // // # Usage // // actualObj, err := SomeFunction() // assertions.Error(t, err) // // # Examples // // success: ErrTest // failure: nil func Error(t T, err error, msgAndArgs ...any) bool { // Domain: error // Opposite: NoError if err == nil { if h, ok := t.(H); ok { h.Helper() } return Fail(t, "An error is expected but got nil.", msgAndArgs...) } return true } // EqualError asserts that a function returned a non-nil error (i.e. an error) // and that it is equal to the provided error. // // # Usage // // actualObj, err := SomeFunction() // assertions.EqualError(t, err, expectedErrorString) // // # Examples // // success: ErrTest, "assert.ErrTest general error for testing" // failure: ErrTest, "wrong error message" func EqualError(t T, err error, errString string, msgAndArgs ...any) bool { // Domain: error if h, ok := t.(H); ok { h.Helper() } if !Error(t, err, msgAndArgs...) { return false } expected := errString actual := err.Error() // don't need to use deep equals here, we know they are both strings if expected != actual { return Fail(t, fmt.Sprintf("Error message not equal:\n"+ "expected: %q\n"+ "actual : %s", expected, truncatingFormat("%q", actual)), msgAndArgs...) } return true } // ErrorContains asserts that a function returned a non-nil error (i.e. an // error) and that the error contains the specified substring. // // # Usage // // actualObj, err := SomeFunction() // assertions.ErrorContains(t, err, expectedErrorSubString) // // # Examples // // success: ErrTest, "general error" // failure: ErrTest, "not in message" func ErrorContains(t T, err error, contains string, msgAndArgs ...any) bool { // Domain: error if h, ok := t.(H); ok { h.Helper() } if !Error(t, err, msgAndArgs...) { return false } actual := err.Error() if !strings.Contains(actual, contains) { return Fail(t, fmt.Sprintf("Error %s does not contain %#v", truncatingFormat("%#v", actual), contains), msgAndArgs...) } return true } // ErrorIs asserts that at least one of the errors in err's chain matches target. // // This is a wrapper for [errors.Is]. // // # Usage // // assertions.ErrorIs(t, err, io.EOF) // // # Examples // // success: fmt.Errorf("wrap: %w", io.EOF), io.EOF // failure: ErrTest, io.EOF func ErrorIs(t T, err, target error, msgAndArgs ...any) bool { // Domain: error // Opposite: NotErrorIs if h, ok := t.(H); ok { h.Helper() } if errors.Is(err, target) { return true } var expectedText string if target != nil { expectedText = target.Error() if err == nil { return Fail(t, fmt.Sprintf("Expected error with %q in chain but got nil.", expectedText), msgAndArgs...) } } chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should be in err chain:\n"+ "expected: %s\n"+ "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } // NotErrorIs asserts that none of the errors in err's chain matches target. // // This is a wrapper for [errors.Is]. // // # Usage // // assertions.NotErrorIs(t, err, io.EOF) // // # Examples // // success: ErrTest, io.EOF // failure: fmt.Errorf("wrap: %w", io.EOF), io.EOF func NotErrorIs(t T, err, target error, msgAndArgs ...any) bool { // Domain: error if h, ok := t.(H); ok { h.Helper() } if !errors.Is(err, target) { return true } var expectedText string if target != nil { expectedText = target.Error() } chain := buildErrorChainString(err, false) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %s\n"+ "in chain: %s", truncatingFormat("%q", expectedText), truncatingFormat("%s", chain), ), msgAndArgs...) } // ErrorAs asserts that at least one of the errors in err's chain matches target, and if so, sets target to that error value. // // This is a wrapper for [errors.As]. // // # Usage // // assertions.ErrorAs(t, err, &target) // // # Examples // // success: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) // failure: ErrTest, new(*dummyError) func ErrorAs(t T, err error, target any, msgAndArgs ...any) bool { // Domain: error // Opposite: NotErrorAs if h, ok := t.(H); ok { h.Helper() } if errors.As(err, target) { return true } expectedType := reflect.TypeOf(target).Elem().String() if err == nil { return Fail(t, fmt.Sprintf("An error is expected but got nil.\n"+ "expected: %s", expectedType), msgAndArgs...) } chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Should be in error chain:\n"+ "expected: %s\n"+ "in chain: %s", expectedType, truncatingFormat("%s", chain), ), msgAndArgs...) } // NotErrorAs asserts that none of the errors in err's chain matches target, // but if so, sets target to that error value. // // # Usage // // assertions.NotErrorAs(t, err, &target) // // # Examples // // success: ErrTest, new(*dummyError) // failure: fmt.Errorf("wrap: %w", &dummyError{}), new(*dummyError) func NotErrorAs(t T, err error, target any, msgAndArgs ...any) bool { // Domain: error if h, ok := t.(H); ok { h.Helper() } if !errors.As(err, target) { return true } chain := buildErrorChainString(err, true) return Fail(t, fmt.Sprintf("Target error should not be in err chain:\n"+ "found: %s\n"+ "in chain: %s", reflect.TypeOf(target).Elem().String(), truncatingFormat("%s", chain), ), msgAndArgs...) } func unwrapAll(err error) (errs []error) { errs = append(errs, err) switch x := err.(type) { //nolint:errorlint // false positive: this type switch is checking for interfaces case interface{ Unwrap() error }: err = x.Unwrap() if err == nil { return } errs = append(errs, unwrapAll(err)...) case interface{ Unwrap() []error }: for _, err := range x.Unwrap() { errs = append(errs, unwrapAll(err)...) } } return } func buildErrorChainString(err error, withType bool) string { if err == nil { return "" } var chain strings.Builder errs := unwrapAll(err) for i := range errs { if i != 0 { chain.WriteString("\n\t") } fmt.Fprintf(&chain, "%q", errs[i].Error()) if withType { fmt.Fprintf(&chain, " (%T)", errs[i]) } } return chain.String() } go-openapi-testify-c10ca71/internal/assertions/error_test.go000066400000000000000000000260121520301377500243670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "fmt" "io" "iter" "slices" "testing" ) func TestErrorNoError(t *testing.T) { t.Parallel() mock := new(mockT) // start with a nil error var err error if !NoError(mock, err) { t.Error("NoError should return true for nil arg") } // now set an error err = errors.New("some error") if NoError(mock, err) { t.Error("NoError with error should return false") } // returning an empty error interface err = func() error { var err *customError return err }() if err == nil { // err is not nil here! t.Errorf("Error should be nil due to empty interface: %s", err) } if NoError(mock, err) { t.Error("NoError should fail with empty error interface") } } func TestError(t *testing.T) { t.Parallel() mock := new(mockT) // start with a nil error var err error if Error(mock, err) { t.Error("Error should return false for nil arg") } // now set an error err = errors.New("some error") if !Error(mock, err) { t.Error("Error with error should return true") } // returning an empty error interface err = func() error { var err *customError return err }() if err == nil { // err is not nil here! t.Errorf("Error should be nil due to empty interface: %s", err) } if !Error(mock, err) { t.Error("Error should pass with empty error interface") } } func TestErrorEqualError(t *testing.T) { t.Parallel() mock := new(mockT) // start with a nil error var err error if EqualError(mock, err, "") { t.Error("EqualError should return false for nil arg") } // now set an error err = errors.New("some error") if EqualError(mock, err, "Not some error") { t.Error("EqualError should return false for different error string") } if !EqualError(mock, err, "some error") { t.Error("EqualError should return true") } } func TestErrorContains(t *testing.T) { t.Parallel() mock := new(mockT) // start with a nil error var err error if ErrorContains(mock, err, "") { t.Error("ErrorContains should return false for nil arg") } // now set an error err = errors.New("some error: another error") if ErrorContains(mock, err, "bad error") { t.Error("ErrorContains should return false for different error string") } if !ErrorContains(mock, err, "some error") { t.Error("ErrorContains should return true for 'some error'") } if !ErrorContains(mock, err, "another error") { t.Error("ErrorContains should return true for 'another error'") } } func TestErrorNotErrorAs(t *testing.T) { t.Parallel() for tt := range errorNotErrorAsCases() { t.Run(fmt.Sprintf("NotErrorAs(%#v,%#v)", tt.err, &customError{}), func(t *testing.T) { t.Parallel() mock := new(mockT) var target *customError res := NotErrorAs(mock, tt.err, &target) shouldPassOrFail(t, mock, res, tt.result) }) } } func TestErrorErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, errorFailCases()) } func TestErrorIs(t *testing.T) { t.Parallel() for tt := range errorIsCases() { t.Run(fmt.Sprintf("ErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { t.Parallel() mock := new(mockT) res := ErrorIs(mock, tt.err, tt.target) shouldPassOrFail(t, mock, res, tt.result) }) } } func TestErrorNotErrorIs(t *testing.T) { t.Parallel() for tt := range errorNotErrorIsCases() { t.Run(fmt.Sprintf("NotErrorIs(%#v,%#v)", tt.err, tt.target), func(t *testing.T) { t.Parallel() mock := new(mockT) res := NotErrorIs(mock, tt.err, tt.target) shouldPassOrFail(t, mock, res, tt.result) }) } } func TestErrorAs(t *testing.T) { t.Parallel() for tt := range errorAsCases() { t.Run(fmt.Sprintf("ErrorAs(%#v,%#v)", tt.err, &customError{}), func(t *testing.T) { t.Parallel() mock := new(mockT) var target *customError res := ErrorAs(mock, tt.err, &target) shouldPassOrFail(t, mock, res, tt.result) }) } } // ============================================================================ // TestNotErrorAs // ============================================================================ type errorNotErrorAsCase struct { err error result bool } func errorNotErrorAsCases() iter.Seq[errorNotErrorAsCase] { return slices.Values([]errorNotErrorAsCase{ { err: fmt.Errorf("wrap: %w", &customError{}), result: false, }, { err: io.EOF, result: true, }, { err: nil, result: true, }, }) } type errorIsCase struct { err error target error result bool } func errorIsCases() iter.Seq[errorIsCase] { return slices.Values([]errorIsCase{ {err: io.EOF, target: io.EOF, result: true}, {err: fmt.Errorf("wrap: %w", io.EOF), target: io.EOF, result: true}, {err: io.EOF, target: io.ErrClosedPipe, result: false}, {err: nil, target: io.EOF, result: false}, {err: io.EOF, target: nil, result: false}, {err: nil, target: nil, result: true}, {err: fmt.Errorf("abc: %w", errors.New("def")), target: io.EOF, result: false}, }) } type errorNotErrorIsCase struct { err error target error result bool } func errorNotErrorIsCases() iter.Seq[errorNotErrorIsCase] { return slices.Values([]errorNotErrorIsCase{ {err: io.EOF, target: io.EOF, result: false}, {err: fmt.Errorf("wrap: %w", io.EOF), target: io.EOF, result: false}, {err: io.EOF, target: io.ErrClosedPipe, result: true}, {err: nil, target: io.EOF, result: true}, {err: io.EOF, target: nil, result: true}, {err: nil, target: nil, result: false}, {err: fmt.Errorf("abc: %w", errors.New("def")), target: io.EOF, result: true}, }) } type errorAsCase struct { err error result bool } func errorAsCases() iter.Seq[errorAsCase] { return slices.Values([]errorAsCase{ {err: fmt.Errorf("wrap: %w", &customError{}), result: true}, {err: io.EOF, result: false}, {err: nil, result: false}, {err: fmt.Errorf("abc: %w", errors.New("def")), result: false}, {err: &wrapsNilError{msg: "dead end"}, result: false}, // covers unwrapAll: Unwrap() error returning nil {err: errors.Join(errors.New("err1"), errors.New("err2")), result: false}, // covers unwrapAll: Unwrap() []error }) } type customError struct{} func (*customError) Error() string { return "fail" } type wrapsNilError struct{ msg string } func (e *wrapsNilError) Error() string { return e.msg } func (e *wrapsNilError) Unwrap() error { return nil } // ============================================================================ // TestErrorErrorMessages // ============================================================================ func errorFailCases() iter.Seq[failCase] { longSlice := make([]int, 1_000_000) return slices.Values([]failCase{ // --- truncation cases --- truncationCase("NoError/truncation", func(t T) bool { return NoError(t, fmt.Errorf("long: %v", longSlice)) }), truncationCase("EqualError/truncation", func(t T) bool { return EqualError(t, fmt.Errorf("long: %v", longSlice), "EOF") }), truncationCase("ErrorContains/truncation", func(t T) bool { return ErrorContains(t, fmt.Errorf("long: %v", longSlice), "EOF") }), truncationCase("ErrorIs/truncation", func(t T) bool { return ErrorIs(t, fmt.Errorf("long: %v", longSlice), fmt.Errorf("also: %v", longSlice)) }), truncationCase("NotErrorIs/truncation", func(t T) bool { err := fmt.Errorf("long: %v", longSlice) return NotErrorIs(t, err, err) }), truncationCase("ErrorAs/truncation", func(t T) bool { var target *customError return ErrorAs(t, fmt.Errorf("long: %v", longSlice), &target) }), truncationCase("NotErrorAs/truncation", func(t T) bool { var target *customError return NotErrorAs(t, fmt.Errorf("long: %v %w", longSlice, &customError{}), &target) }), // --- ErrorIs message cases --- { name: "ErrorIs/not_in_chain", assertion: func(t T) bool { return ErrorIs(t, io.EOF, io.ErrClosedPipe) }, wantError: "" + "Target error should be in err chain:\n" + "expected: \"io: read/write on closed pipe\"\n" + "in chain: \"EOF\"", }, { name: "ErrorIs/nil_err", assertion: func(t T) bool { return ErrorIs(t, nil, io.EOF) }, wantError: "Expected error with \"EOF\" in chain but got nil.", }, { name: "ErrorIs/nil_target", assertion: func(t T) bool { return ErrorIs(t, io.EOF, nil) }, wantError: "" + "Target error should be in err chain:\n" + "expected: \"\"\n" + "in chain: \"EOF\"", }, { name: "ErrorIs/wrapped_not_in_chain", assertion: func(t T) bool { return ErrorIs(t, fmt.Errorf("abc: %w", errors.New("def")), io.EOF) }, wantError: "" + "Target error should be in err chain:\n" + "expected: \"EOF\"\n" + "in chain: \"abc: def\"\n" + "\t\"def\"", }, // --- NotErrorIs message cases --- { name: "NotErrorIs/same_error", assertion: func(t T) bool { return NotErrorIs(t, io.EOF, io.EOF) }, wantError: "" + "Target error should not be in err chain:\n" + "found: \"EOF\"\n" + "in chain: \"EOF\"", }, { name: "NotErrorIs/wrapped_in_chain", assertion: func(t T) bool { return NotErrorIs(t, fmt.Errorf("wrap: %w", io.EOF), io.EOF) }, wantError: "" + "Target error should not be in err chain:\n" + "found: \"EOF\"\n" + "in chain: \"wrap: EOF\"\n" + "\t\"EOF\"", }, { name: "NotErrorIs/both_nil", assertion: func(t T) bool { return NotErrorIs(t, nil, nil) }, wantError: "" + "Target error should not be in err chain:\n" + "found: \"\"\n" + "in chain: ", }, // --- ErrorAs message cases --- { name: "ErrorAs/not_in_chain", assertion: func(t T) bool { var target *customError return ErrorAs(t, io.EOF, &target) }, wantError: "" + "Should be in error chain:\n" + fmt.Sprintf("expected: *%s.customError\n", shortpkg) + "in chain: \"EOF\" (*errors.errorString)", }, { name: "ErrorAs/nil_err", assertion: func(t T) bool { var target *customError return ErrorAs(t, nil, &target) }, wantError: "" + "An error is expected but got nil.\n" + fmt.Sprintf("expected: *%s.customError", shortpkg), }, { name: "ErrorAs/wrapped_not_in_chain", assertion: func(t T) bool { var target *customError return ErrorAs(t, fmt.Errorf("abc: %w", errors.New("def")), &target) }, wantError: "" + "Should be in error chain:\n" + fmt.Sprintf("expected: *%s.customError\n", shortpkg) + "in chain: \"abc: def\" (*fmt.wrapError)\n" + "\t\"def\" (*errors.errorString)", }, // --- NotErrorAs message cases --- { name: "NotErrorAs/found_in_chain", assertion: func(t T) bool { var target *customError return NotErrorAs(t, fmt.Errorf("wrap: %w", &customError{}), &target) }, wantError: "" + "Target error should not be in err chain:\n" + fmt.Sprintf("found: *%s.customError\n", shortpkg) + "in chain: \"wrap: fail\" (*fmt.wrapError)\n" + fmt.Sprintf("\t\"fail\" (*%s.customError)", shortpkg), }, // -- TestExample error { name: "NotError/TestExampleError", assertion: func(t T) bool { return NoError(t, ErrTest) }, wantError: "Received unexpected error:\n" + "assert.ErrTest general error for testing", }, }) } go-openapi-testify-c10ca71/internal/assertions/file.go000066400000000000000000000127721520301377500231260ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "io/fs" "os" ) // FileExists checks whether a file exists in the given path. It also fails if // the path points to a directory or there is an error when trying to check the file. // // # Usage // // assertions.FileExists(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"existing_file") // failure: filepath.Join(testDataPath(),"non_existing_file") func FileExists(t T, path string, msgAndArgs ...any) bool { // Domain: file // Opposite: FileNotExists if h, ok := t.(H); ok { h.Helper() } info, err := lstat(path, "file") if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } return true } // FileNotExists checks whether a file does not exist in a given path. It fails // if the path points to an existing _file_ only. // // # Usage // // assertions.FileNotExists(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"non_existing_file") // failure: filepath.Join(testDataPath(),"existing_file") func FileNotExists(t T, path string, msgAndArgs ...any) bool { // Domain: file if h, ok := t.(H); ok { h.Helper() } info, err := os.Lstat(path) if err != nil { return true } if info.IsDir() { return true } return Fail(t, fmt.Sprintf("file %q exists", path), msgAndArgs...) } // DirExists checks whether a directory exists in the given path. It also fails // if the path is a file rather a directory or there is an error checking whether it exists. // // # Usage // // assertions.DirExists(t, "path/to/directory") // // # Examples // // success: filepath.Join(testDataPath(),"existing_dir") // failure: filepath.Join(testDataPath(),"non_existing_dir") func DirExists(t T, path string, msgAndArgs ...any) bool { // Domain: file // Opposite: DirNotExists if h, ok := t.(H); ok { h.Helper() } info, err := lstat(path, "directory") if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if !info.IsDir() { return Fail(t, fmt.Sprintf("%q is a file", path), msgAndArgs...) } return true } // DirNotExists checks whether a directory does not exist in the given path. // It fails if the path points to an existing _directory_ only. // // # Usage // // assertions.DirNotExists(t, "path/to/directory") // // # Examples // // success: filepath.Join(testDataPath(),"non_existing_dir") // failure: filepath.Join(testDataPath(),"existing_dir") func DirNotExists(t T, path string, msgAndArgs ...any) bool { // Domain: file if h, ok := t.(H); ok { h.Helper() } info, err := os.Lstat(path) if err != nil { return true } if !info.IsDir() { return true } return Fail(t, fmt.Sprintf("directory %q exists", path), msgAndArgs...) } // FileEmpty checks whether a file exists in the given path and is empty. // It fails if the file is not empty, if the path points to a directory or there is an error when trying to check the file. // // # Usage // // assertions.FileEmpty(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"empty_file") // failure: filepath.Join(testDataPath(),"existing_file") func FileEmpty(t T, path string, msgAndArgs ...any) bool { // Domain: file // Opposite: FileNotEmpty if h, ok := t.(H); ok { h.Helper() } info, err := lstat(path, "file") if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } target, isSymlink, err := isSymlink(path, info) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if isSymlink { return FileEmpty(t, target, msgAndArgs...) } if info.Size() > 0 { return Fail(t, fmt.Sprintf("%q is not empty", path), msgAndArgs...) } return true } // FileNotEmpty checks whether a file exists in the given path and is not empty. // It fails if the file is empty, if the path points to a directory or there is an error when trying to check the file. // // # Usage // // assertions.FileNotEmpty(t, "path/to/file") // // # Examples // // success: filepath.Join(testDataPath(),"existing_file") // failure: filepath.Join(testDataPath(),"empty_file") func FileNotEmpty(t T, path string, msgAndArgs ...any) bool { // Domain: file if h, ok := t.(H); ok { h.Helper() } info, err := lstat(path, "file") if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if info.IsDir() { return Fail(t, fmt.Sprintf("%q is a directory", path), msgAndArgs...) } target, isSymlink, err := isSymlink(path, info) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if isSymlink { return FileNotEmpty(t, target, msgAndArgs...) } if info.Size() == 0 { return Fail(t, fmt.Sprintf("%q is empty", path), msgAndArgs...) } return true } func lstat(path, kind string) (info os.FileInfo, err error) { info, err = os.Lstat(path) if err != nil { if os.IsNotExist(err) { return info, fmt.Errorf("unable to find %s %q: %w", kind, path, err) } return info, fmt.Errorf("error when running os.Lstat(%q): %w", path, err) } return info, nil } func isSymlink(path string, info os.FileInfo) (target string, isSymlink bool, err error) { if info.Mode()&fs.ModeSymlink == 0 { return path, false, nil } target, err = os.Readlink(path) if err != nil { // This is not reachable on linux, but windows has different semantics for symlinks return target, true, fmt.Errorf("could not resolve symlink %q", path) } return target, true, nil } go-openapi-testify-c10ca71/internal/assertions/file_test.go000066400000000000000000000225511520301377500241610ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "os" "path/filepath" "runtime" "slices" "testing" ) func TestFileExists(t *testing.T) { t.Parallel() for c := range fileExistsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() path := resolvePath(t, c) mock := new(mockT) res := FileExists(mock, path) shouldPassOrFail(t, mock, res, c.result) }) } } func TestFileNotExists(t *testing.T) { t.Parallel() for c := range fileNotExistsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() path := resolvePath(t, c) mock := new(mockT) res := FileNotExists(mock, path) shouldPassOrFail(t, mock, res, c.result) }) } } func TestDirExists(t *testing.T) { t.Parallel() for c := range dirExistsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() path := resolvePath(t, c) mock := new(mockT) res := DirExists(mock, path) shouldPassOrFail(t, mock, res, c.result) }) } } func TestDirNotExists(t *testing.T) { t.Parallel() for c := range dirNotExistsCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() path := resolvePath(t, c) mock := new(mockT) res := DirNotExists(mock, path) shouldPassOrFail(t, mock, res, c.result) }) } } func TestFileEmpty(t *testing.T) { t.Parallel() for c := range fileEmptyCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() path := resolvePath(t, c) mock := new(mockT) res := FileEmpty(mock, path) shouldPassOrFail(t, mock, res, c.result) }) } } func TestFileNotEmpty(t *testing.T) { t.Parallel() for c := range fileNotEmptyCases() { t.Run(c.name, func(t *testing.T) { t.Parallel() path := resolvePath(t, c) mock := new(mockT) res := FileNotEmpty(mock, path) shouldPassOrFail(t, mock, res, c.result) }) } } func TestFileLstatPermissionError(t *testing.T) { t.Parallel() if runtime.GOOS == "windows" { t.Skip("skipping on windows OS: altering file permissions don't work the same") } if os.Getuid() == 0 { t.Skip("skipping permission test when running as root") } // Create a directory with a file inside, then remove execute permission // from the directory. os.Lstat on the file will fail with EACCES, not ENOENT. dir := t.TempDir() filePath := filepath.Join(dir, "secret") if err := os.WriteFile(filePath, []byte("data"), 0o600); err != nil { t.Fatal(err) } if err := os.Chmod(dir, 0o000); err != nil { t.Fatal(err) } t.Cleanup(func() { os.Chmod(dir, 0o755) }) //nolint:errcheck // best-effort restore for cleanup mock := new(mockT) if FileExists(mock, filePath) { t.Error("expected FileExists to return false for inaccessible file") } if !mock.failed { t.Error("expected FileExists to fail") } } func TestFileErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, fileFailCases()) } // ============================================================================ // Table-driven tests for file assertions // ============================================================================ type fileTestCase struct { name string path string // direct path to test symlinkTo string // if set, creates a symlink to this target instead result bool } func resolvePath(t *testing.T, c fileTestCase) string { t.Helper() if c.symlinkTo != "" { return getTempSymlinkPath(t, c.symlinkTo) } return c.path } func getTempSymlinkPath(t *testing.T, file string) string { t.Helper() tempDir := t.TempDir() link := filepath.Join(tempDir, filepath.Base(file)+"_symlink") if err := os.Symlink(file, link); err != nil { t.Fatalf("could not create temp symlink %q pointing to %q: %v", link, file, err) } return link } // ============================================================================ // Test cases // ============================================================================ func fileExistsCases() iter.Seq[fileTestCase] { return slices.Values([]fileTestCase{ {name: "existing-file", path: filepath.Join("testdata", "existing_file"), result: true}, {name: "non-existent", path: "random_file", result: false}, {name: "directory", path: filepath.Join("testdata", "existing_dir"), result: false}, {name: "symlink/existing-file", symlinkTo: filepath.Join("testdata", "existing_file"), result: true}, {name: "symlink/broken", symlinkTo: "non_existent_file", result: true}, }) } func fileNotExistsCases() iter.Seq[fileTestCase] { return slices.Values([]fileTestCase{ {name: "existing-file", path: filepath.Join("testdata", "existing_file"), result: false}, {name: "non-existent", path: "non_existent_file", result: true}, {name: "directory", path: filepath.Join("testdata", "existing_dir"), result: true}, {name: "symlink/existing-file", symlinkTo: filepath.Join("testdata", "existing_file"), result: false}, {name: "symlink/broken", symlinkTo: "non_existent_file", result: false}, }) } func dirExistsCases() iter.Seq[fileTestCase] { return slices.Values([]fileTestCase{ {name: "is-file", path: filepath.Join("testdata", "existing_file"), result: false}, {name: "non-existent", path: "non_existent_dir", result: false}, {name: "existing-dir", path: filepath.Join("testdata", "existing_dir"), result: true}, {name: "symlink/to-file", symlinkTo: filepath.Join("testdata", "existing_file"), result: false}, {name: "symlink/broken", symlinkTo: "non_existent_dir", result: false}, }) } func dirNotExistsCases() iter.Seq[fileTestCase] { return slices.Values([]fileTestCase{ {name: "is-file", path: filepath.Join("testdata", "existing_file"), result: true}, {name: "non-existent", path: "non_existent_dir", result: true}, {name: "existing-dir", path: filepath.Join("testdata", "existing_dir"), result: false}, {name: "symlink/to-file", symlinkTo: filepath.Join("testdata", "existing_file"), result: true}, {name: "symlink/broken", symlinkTo: "non_existent_dir", result: true}, }) } func fileEmptyCases() iter.Seq[fileTestCase] { return slices.Values([]fileTestCase{ {name: "empty-file", path: filepath.Join("testdata", "empty_file"), result: true}, {name: "non-empty-file", path: filepath.Join("testdata", "existing_file"), result: false}, {name: "non-existent", path: "random_file", result: false}, {name: "directory", path: filepath.Join("testdata", "existing_dir"), result: false}, {name: "symlink/empty-file", symlinkTo: filepath.Join("testdata", "empty_file"), result: true}, {name: "symlink/non-empty-file", symlinkTo: filepath.Join("testdata", "existing_file"), result: false}, {name: "symlink/broken", symlinkTo: "non_existent_file", result: false}, }) } func fileNotEmptyCases() iter.Seq[fileTestCase] { return slices.Values([]fileTestCase{ {name: "non-empty-file", path: filepath.Join("testdata", "existing_file"), result: true}, {name: "empty-file", path: filepath.Join("testdata", "empty_file"), result: false}, {name: "non-existent", path: "non_existent_file", result: false}, {name: "directory", path: filepath.Join("testdata", "existing_dir"), result: false}, {name: "symlink/empty-file", symlinkTo: filepath.Join("testdata", "empty_file"), result: false}, {name: "symlink/non-empty-file", symlinkTo: filepath.Join("testdata", "existing_file"), result: true}, {name: "symlink/broken", symlinkTo: "non_existent_file", result: false}, }) } // ============================================================================ // TestFileErrorMessages // ============================================================================ func fileFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "FileExists/nonexistent", assertion: func(t T) bool { return FileExists(t, "nonexistent_file") }, wantContains: []string{"unable to find file"}, }, { name: "FileExists/is-directory", assertion: func(t T) bool { return FileExists(t, filepath.Join("testdata", "existing_dir")) }, wantContains: []string{"is a directory"}, }, { name: "FileNotExists/existing-file", assertion: func(t T) bool { return FileNotExists(t, filepath.Join("testdata", "existing_file")) }, wantContains: []string{"file", "exists"}, }, { name: "DirExists/nonexistent", assertion: func(t T) bool { return DirExists(t, "nonexistent_dir") }, wantContains: []string{"unable to find directory"}, }, { name: "DirExists/is-file", assertion: func(t T) bool { return DirExists(t, filepath.Join("testdata", "existing_file")) }, wantContains: []string{"is a file"}, }, { name: "DirNotExists/existing-dir", assertion: func(t T) bool { return DirNotExists(t, filepath.Join("testdata", "existing_dir")) }, wantContains: []string{"directory", "exists"}, }, { name: "FileEmpty/non-empty-file", assertion: func(t T) bool { return FileEmpty(t, filepath.Join("testdata", "existing_file")) }, wantContains: []string{"is not empty"}, }, { name: "FileEmpty/nonexistent", assertion: func(t T) bool { return FileEmpty(t, "nonexistent_file") }, wantContains: []string{"unable to find file"}, }, { name: "FileNotEmpty/empty-file", assertion: func(t T) bool { return FileNotEmpty(t, filepath.Join("testdata", "empty_file")) }, wantContains: []string{"is empty"}, }, { name: "FileNotEmpty/nonexistent", assertion: func(t T) bool { return FileNotEmpty(t, "nonexistent_file") }, wantContains: []string{"unable to find file"}, }, }) } go-openapi-testify-c10ca71/internal/assertions/format.go000066400000000000000000000061101520301377500234640ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "bufio" "bytes" "fmt" "strings" ) // ========================================== // Utility helpers to format error messages. // ========================================== const maxMessageSize = 1024 // truncatingFormat formats the data and truncates it if it's too long. // // This helps keep formatted error messages lines from exceeding maxMessageSize for readability's sake. func truncatingFormat(format string, data any) string { value := fmt.Sprintf(format, data) // Give us space for two truncated objects and the surrounding sentence. if len(value) > maxMessageSize { value = value[0:maxMessageSize] + "<... truncated>" } return value } // Aligns the provided message so that all lines after the first line start at the same location as the first line. // // Assumes that the first line starts at the correct location (after carriage return, tab, label, spacer and tab). // // The longestLabelLen parameter specifies the length of the longest label in the output (required because this is the // basis on which the alignment occurs). func indentMessageLines(message string, longestLabelLen int) string { outBuf := new(bytes.Buffer) scanner := bufio.NewScanner(strings.NewReader(message)) for firstLine := true; scanner.Scan(); firstLine = false { if !firstLine { fmt.Fprint(outBuf, "\n\t"+strings.Repeat(" ", longestLabelLen+1)+"\t") } fmt.Fprint(outBuf, scanner.Text()) } return outBuf.String() } func messageFromMsgAndArgs(msgAndArgs ...any) string { if len(msgAndArgs) == 0 || msgAndArgs == nil { return "" } if len(msgAndArgs) == 1 { msg := msgAndArgs[0] if msgAsStr, ok := msg.(string); ok { return msgAsStr } return fmt.Sprintf("%+v", msg) } if len(msgAndArgs) > 1 { format, ok := msgAndArgs[0].(string) if ok { return fmt.Sprintf(format, msgAndArgs[1:]...) } } return "" } type labeledContent struct { label string content string } // labeledOutput returns a string consisting of the provided labeledContent. Each labeled output is appended in the following manner: // // \t{{label}}:{{align_spaces}}\t{{content}}\n // // The initial carriage return is required to undo/erase any padding added by testing.T.Errorf. The "\t{{label}}:" is for the label. // If a label is shorter than the longest label provided, padding spaces are added to make all the labels match in length. Once this // alignment is achieved, "\t{{content}}\n" is added for the output. // // If the content of the labeledOutput contains line breaks, the subsequent lines are aligned so that they start at the same location as the first line. func labeledOutput(content ...labeledContent) string { longestLabel := 0 for _, v := range content { if len(v.label) > longestLabel { longestLabel = len(v.label) } } var output strings.Builder for _, v := range content { output.WriteString("\t" + v.label + ":" + strings.Repeat(" ", longestLabel-len(v.label)) + "\t" + indentMessageLines(v.content, longestLabel) + "\n") } return output.String() } go-openapi-testify-c10ca71/internal/assertions/format_impl_test.go000066400000000000000000000222111520301377500255440ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "iter" "regexp" "slices" "strings" "testing" ) func TestFormatUnexportedImplementationDetails(t *testing.T) { t.Parallel() t.Run("truncatingFormat", testTruncatingFormat) t.Run("indent message lines", testIndentMessageLines) t.Run("message from MsgAndArgs", testMessageFromMsgAndArgs) t.Run("labeled output", testLabeledOutput) // sanity-check testing infrastructure (used by the failCase test harness) t.Run("check testing utility labeled output parsing", testParseLabeledOutput) t.Run("check testing utility error envelope parsing", testErrorEnvelopeIntegration) } func testTruncatingFormat(t *testing.T) { t.Parallel() original := strings.Repeat("a", maxMessageSize-100) t.Run("should not truncate rendered value", func(t *testing.T) { result := truncatingFormat("%#v", original) expected := fmt.Sprintf("%#v", original) if expected != result { t.Errorf("string should not be truncated: expected %q, got %q", expected, result) } }) t.Run("should truncate rendered value", func(t *testing.T) { original += strings.Repeat("x", 100) result := truncatingFormat("%#v", original) full := fmt.Sprintf("%#v", original) if full == result { t.Error("string should have been truncated") } if !strings.HasSuffix(result, "<... truncated>") { t.Error("truncated string should have <... truncated> suffix") } }) } func testIndentMessageLines(t *testing.T) { t.Parallel() for tc := range indentMessageLinesCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() result := indentMessageLines(tc.message, tc.longestLabel) if tc.expected != result { t.Errorf("expected %q, got %q", tc.expected, result) } }) } } func testParseLabeledOutput(t *testing.T) { t.Parallel() t.Run("round-trip single label", func(t *testing.T) { t.Parallel() input := []labeledContent{{errString, "test message"}} output := labeledOutput(input...) parsed := parseLabeledOutput(output) if parsed == nil { t.Fatal("expected non-nil parsed output") } if len(parsed) != 1 { t.Fatalf("expected 1 label, got %d", len(parsed)) } if parsed[0].label != errString { t.Errorf("expected label %q, got %q", errString, parsed[0].label) } if parsed[0].content != "test message\n" { t.Errorf("expected content %q, got %q", "test message\n", parsed[0].content) } }) t.Run("round-trip multiple labels", func(t *testing.T) { t.Parallel() input := []labeledContent{ {"Error Trace", "file.go:42"}, {errString, "not equal"}, {"Test", "TestFoo"}, {"Messages", "extra info"}, } output := labeledOutput(input...) parsed := parseLabeledOutput(output) if parsed == nil { t.Fatal("expected non-nil parsed output") } if len(parsed) != 4 { t.Fatalf("expected 4 labels, got %d", len(parsed)) } if parsed[0].label != "Error Trace" { t.Errorf("expected label %q, got %q", "Error Trace", parsed[0].label) } if parsed[1].label != errString { t.Errorf("expected label %q, got %q", errString, parsed[1].label) } if parsed[2].label != "Test" { t.Errorf("expected label %q, got %q", "Test", parsed[2].label) } if parsed[3].label != "Messages" { t.Errorf("expected label %q, got %q", "Messages", parsed[3].label) } }) t.Run("blank line skipping", func(t *testing.T) { t.Parallel() // Build output with blank lines injected output := "\n\tError:\ttest message\n\n" parsed := parseLabeledOutput(output) if parsed == nil { t.Fatal("expected non-nil parsed output") } if len(parsed) != 1 { t.Fatalf("expected 1 label, got %d", len(parsed)) } if parsed[0].label != errString { t.Errorf("expected label %q, got %q", errString, parsed[0].label) } }) t.Run("malformed input returns nil", func(t *testing.T) { t.Parallel() parsed := parseLabeledOutput("this is not labeled output") if parsed != nil { t.Errorf("expected nil, got %v", parsed) } }) } func testMessageFromMsgAndArgs(t *testing.T) { t.Parallel() for tc := range messageFromMsgAndArgsCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() result := messageFromMsgAndArgs(tc.args...) if tc.expected != result { t.Errorf("expected %q, got %q", tc.expected, result) } }) } } // Tests for envelope infrastructure functions. func testLabeledOutput(t *testing.T) { t.Parallel() for tc := range labeledOutputCases() { t.Run(tc.name, func(t *testing.T) { t.Parallel() result := labeledOutput(tc.input...) if tc.expected != result { t.Errorf("expected %q, got %q", tc.expected, result) } }) } } func testErrorEnvelopeIntegration(t *testing.T) { t.Parallel() mock := new(captureT) Fail(mock, "test message") if !mock.failed { t.Fatal("Fail should mark test as failed") } parsed := parseLabeledOutput(mock.msg) if parsed == nil { t.Fatal("Fail output should be parseable by parseLabeledOutput") } var hasErrorTrace, hasError bool var errorContent string for _, lc := range parsed { switch lc.label { case "Error Trace": hasErrorTrace = true case errString: hasError = true errorContent = strings.TrimRight(lc.content, "\n") } } if !hasErrorTrace { t.Error("envelope should contain Error Trace label") } if !hasError { t.Error("envelope should contain Error label") } if errorContent != "test message" { t.Errorf("expected error content %q, got %q", "test message", errorContent) } } // ======================================= // TestLabeledOutput // ======================================= type labeledOutputCase struct { name string input []labeledContent expected string } func labeledOutputCases() iter.Seq[labeledOutputCase] { return slices.Values([]labeledOutputCase{ { name: "single label", input: []labeledContent{{errString, "something failed"}}, expected: "\tError:\tsomething failed\n", }, { name: "multiple labels aligned", input: []labeledContent{ {"Error Trace", "file.go:42"}, {errString, "not equal"}, {"Test", "TestFoo"}, }, expected: "\tError Trace:\tfile.go:42\n" + "\tError: \tnot equal\n" + "\tTest: \tTestFoo\n", }, { name: "multi-line content indented", input: []labeledContent{{errString, "line1\nline2\nline3"}}, expected: "\tError:\tline1\n" + "\t \tline2\n" + "\t \tline3\n", }, { name: "empty content", input: []labeledContent{{"Label", ""}}, expected: "\tLabel:\t\n", }, }) } // ======================================= // TestIndentMessageLines // ======================================= type indentMessageLinesCase struct { name string message string longestLabel int expected string } func indentMessageLinesCases() iter.Seq[indentMessageLinesCase] { return slices.Values([]indentMessageLinesCase{ { name: "single line unchanged", message: "hello world", longestLabel: 5, expected: "hello world", }, { name: "multi-line indented", message: "line1\nline2\nline3", longestLabel: 5, expected: "line1\n\t \tline2\n\t \tline3", }, { name: "empty string", message: "", longestLabel: 5, expected: "", }, { name: "trailing newline", message: "hello\n", longestLabel: 3, expected: "hello", }, }) } // ======================================= // TestMessageFromMsgAndArgs // ======================================= type messageFromMsgAndArgsCase struct { name string args []any expected string } func messageFromMsgAndArgsCases() iter.Seq[messageFromMsgAndArgsCase] { return slices.Values([]messageFromMsgAndArgsCase{ { name: "no args", args: nil, expected: "", }, { name: "single string", args: []any{"hello"}, expected: "hello", }, { name: "single non-string", args: []any{42}, expected: "42", }, { name: "format string with args", args: []any{"value is %d", 42}, expected: "value is 42", }, { name: "format string with multiple args", args: []any{"%s=%d", "x", 5}, expected: "x=5", }, { name: "multiple args with non-string first", args: []any{42, "extra"}, expected: "", }, }) } // parseLabeledOutput does the inverse of labeledOutput - it takes a formatted // output string and turns it back into a slice of labeledContent. func parseLabeledOutput(output string) []labeledContent { labelPattern := regexp.MustCompile(`^\t([^\t]*): *\t(.*)$`) contentPattern := regexp.MustCompile(`^\t *\t(.*)$`) var contents []labeledContent lines := strings.Split(output, "\n") i := -1 for _, line := range lines { if line == "" { // skip blank lines continue } matches := labelPattern.FindStringSubmatch(line) if len(matches) == 3 { // a label contents = append(contents, labeledContent{ label: matches[1], content: matches[2] + "\n", }) i++ continue } matches = contentPattern.FindStringSubmatch(line) if len(matches) == 2 { // just content if i >= 0 { contents[i].content += matches[1] + "\n" continue } } // Couldn't parse output return nil } return contents } go-openapi-testify-c10ca71/internal/assertions/generics.go000066400000000000000000000125711520301377500240030ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "cmp" "context" "regexp" "time" ) // Type constraint definitions for generic variants of assertions. type ( // Boolean is a bool or any type that can be converted to a bool. Boolean interface { ~bool } // Text is any type of underlying type string or []byte. // // This is used by [RegexpT], [NotRegexpT], [JSONEqT], and [YAMLEqT]. // // NOTE: unfortunately, []rune is not supported. Text interface { ~string | ~[]byte } // RText extends [Text] by supporting dynamic construction of the // expected or actual value, e.g. "redact" functions. RText interface { Text | Redactor } // Redactor allows dynamic construction of expected or actual values, e.g. "redacting" values dynamically. // // This is used by json and yaml assertions. Redactor interface { func() string | func() []byte } // Ordered is a standard ordered type (i.e. types that support "<": [cmp.Ordered]) plus []byte and [time.Time]. // // This is used by [GreaterT], [GreaterOrEqualT], [LessT], [LessOrEqualT], [IsIncreasingT], [IsDecreasingT]. // // NOTE: since [time.Time] is a struct, custom types which redeclare [time.Time] are not supported. Ordered interface { cmp.Ordered | []byte | time.Time } // SignedNumeric is a signed integer or a floating point number or any type that can be converted to one of these. SignedNumeric interface { ~int | ~int8 | ~int16 | ~int32 | ~int64 | ~float32 | ~float64 } // UnsignedNumeric is an unsigned integer. // // NOTE: there are no unsigned floating point numbers. UnsignedNumeric interface { ~uint | ~uint8 | ~uint16 | ~uint32 | ~uint64 } // Measurable is any number for which we can compute a delta (floats or integers). // // This is used by [InDeltaT] and [InEpsilonT]. // // NOTE: unfortunately complex64 and complex128 are not supported. Measurable interface { SignedNumeric | UnsignedNumeric | ~float32 | ~float64 } // RegExp is either a text containing a regular expression to compile (string or []byte), or directly the compiled regexp. // // This is used by [RegexpT] and [NotRegexpT]. RegExp interface { Text | *regexp.Regexp } // Conditioner is a function used in asynchronous condition assertions. // // This type constraint allows for "overloaded" versions of the condition assertions ([Eventually], [Consistently]). // // The [WithSynctest] and [WithSynctestContext] wrappers opt a call into // fake-time polling via [testing/synctest]. See [WithSynctest] for details. Conditioner interface { func() bool | func(context.Context) error | WithSynctest | WithSynctestContext } // NeverConditioner is a function used by [Never]. // // Unlike [Conditioner], [Never] does not accept the context-returning-error // form to avoid the double-negation confusion ("never returns no error"). // // The [WithSynctest] wrapper opts a call into fake-time polling. NeverConditioner interface { func() bool | WithSynctest } // CollectibleConditioner is a function used in asynchronous condition assertions that use [CollectT]. // // This type constraint allows for "overloaded" versions of the condition assertions ([EventuallyWith]). // // The [WithSynctestCollect] and [WithSynctestCollectContext] wrappers opt a // call into fake-time polling. See [WithSynctest] for details. CollectibleConditioner interface { func(*CollectT) | func(context.Context, *CollectT) | WithSynctestCollect | WithSynctestCollectContext } // WithSynctest wraps a [func() bool] condition to run [Eventually] / // [Never] / [Consistently] polling inside a [testing/synctest] bubble, // so `time.Ticker`, `time.After`, and `context.WithTimeout` use a fake // clock. Activation requires the caller to pass a real `*testing.T`; // with mocks or other [T] implementations, the wrapper falls back to // real-time polling. // // # When to use // // Use when the condition is pure compute, relies on `time.Sleep`, or // coordinates via channels created inside the condition. Fake time // eliminates timing-induced flakiness and enables deterministic tick // counts. // // # When not to use // // Do NOT use when the condition performs real I/O (network, filesystem, // syscalls): those block goroutines non-durably, so the fake clock // stalls and the timeout may not fire. Also do NOT use inside a test // that is already running in a [synctest.Test] bubble — nested bubbles // are forbidden and will panic. // // # Shared state // // The condition may read and write variables captured from the enclosing // scope; condition execution is serialized by design (see [Eventually]'s // Concurrency section). Avoid sharing channels or mutexes with goroutines // outside the bubble, as this will stall the fake clock. WithSynctest func() bool // WithSynctestContext is the [func(context.Context) error] counterpart // of [WithSynctest]. See [WithSynctest] for details. WithSynctestContext func(context.Context) error // WithSynctestCollect is the [func(*CollectT)] counterpart of // [WithSynctest] for use with [EventuallyWith]. See [WithSynctest] for details. WithSynctestCollect func(*CollectT) // WithSynctestCollectContext is the [func(context.Context, *CollectT)] // counterpart of [WithSynctest] for use with [EventuallyWith]. See // [WithSynctest] for details. WithSynctestCollectContext func(context.Context, *CollectT) ) go-openapi-testify-c10ca71/internal/assertions/http.go000066400000000000000000000146751520301377500231720ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "context" "fmt" "net/http" "net/http/httptest" "net/url" "strings" ) // HTTPSuccess asserts that a specified handler returns a success status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPSuccess(t, myHandler, "POST", "http://www.google.com", nil) // // # Examples // // success: httpOK, "GET", "/", nil // failure: httpError, "GET", "/", nil func HTTPSuccess(t T, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...any) bool { // Domain: http if h, ok := t.(H); ok { h.Helper() } err := httpCodeInRange(handler, http.StatusOK, http.StatusPartialContent, "HTTP success", method, url, values) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } return true } // HTTPRedirect asserts that a specified handler returns a redirect status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPRedirect(t, myHandler, "GET", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // # Examples // // success: httpRedirect, "GET", "/", nil // failure: httpError, "GET", "/", nil func HTTPRedirect(t T, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...any) bool { // Domain: http if h, ok := t.(H); ok { h.Helper() } err := httpCodeInRange(handler, http.StatusMultipleChoices, http.StatusTemporaryRedirect, "HTTP redirect", method, url, values) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } return true } // HTTPError asserts that a specified handler returns an error status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPError(t, myHandler, "POST", "/a/b/c", url.Values{"a": []string{"b", "c"}} // // # Examples // // success: httpError, "GET", "/", nil // failure: httpOK, "GET", "/", nil func HTTPError(t T, handler http.HandlerFunc, method, url string, values url.Values, msgAndArgs ...any) bool { // Domain: http if h, ok := t.(H); ok { h.Helper() } err := httpCodeInRange(handler, http.StatusBadRequest, -1, "HTTP error", method, url, values) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } return true } // HTTPStatusCode asserts that a specified handler returns a specified status code. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPStatusCode(t, myHandler, "GET", "/notImplemented", nil, 501) // // # Examples // // success: httpOK, "GET", "/", nil, http.StatusOK // failure: httpError, "GET", "/", nil, http.StatusOK func HTTPStatusCode(t T, handler http.HandlerFunc, method, url string, values url.Values, statuscode int, msgAndArgs ...any) bool { // Domain: http if h, ok := t.(H); ok { h.Helper() } err := httpCodeInRange(handler, statuscode, statuscode, fmt.Sprintf("HTTP status code %d", statuscode), method, url, values) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } return true } // HTTPBody is a helper that returns the HTTP body of the response. // It returns the empty string if building a new request fails. func HTTPBody(handler http.HandlerFunc, method, url string, values url.Values) string { // Domain: http w := httptest.NewRecorder() if len(values) > 0 { url += "?" + values.Encode() } req, err := http.NewRequestWithContext(context.Background(), method, url, http.NoBody) if err != nil { return "" } handler(w, req) return w.Body.String() } // HTTPBodyContains asserts that a specified handler returns a body that contains a string. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPBodyContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // # Examples // // success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, World!" // failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, World!" func HTTPBodyContains(t T, handler http.HandlerFunc, method, url string, values url.Values, str any, msgAndArgs ...any) bool { // Domain: http // Opposite: HTTPBodyNotContains if h, ok := t.(H); ok { h.Helper() } body := HTTPBody(handler, method, url, values) contains := strings.Contains(body, fmt.Sprint(str)) if !contains { Fail(t, fmt.Sprintf("Expected response body for %q to contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return contains } // HTTPBodyNotContains asserts that a specified handler returns a // body that does not contain a string. // // Returns whether the assertion was successful (true) or not (false). // // # Usage // // assertions.HTTPBodyNotContains(t, myHandler, "GET", "www.google.com", nil, "I'm Feeling Lucky") // // # Examples // // success: httpBody, "GET", "/", url.Values{"name": []string{"World"}}, "Hello, Bob!" // failure: httpBody, "GET", "/", url.Values{"name": []string{"Bob"}}, "Hello, Bob!" func HTTPBodyNotContains(t T, handler http.HandlerFunc, method, url string, values url.Values, str any, msgAndArgs ...any) bool { // Domain: http if h, ok := t.(H); ok { h.Helper() } body := HTTPBody(handler, method, url, values) contains := strings.Contains(body, fmt.Sprint(str)) if contains { Fail(t, fmt.Sprintf("Expected response body for %q to NOT contain %q but found %q", url+"?"+values.Encode(), str, body), msgAndArgs...) } return !contains } func httpCodeInRange(handler http.HandlerFunc, minStatusCode, maxStatusCode int, expectedStatus, method, url string, values url.Values) error { code, err := httpCode(handler, method, url, values) if err != nil { return fmt.Errorf("failed to build test request, got error: %w", err) } if (code < minStatusCode && minStatusCode != -1) || (code > maxStatusCode && maxStatusCode != -1) { return fmt.Errorf("expected %s status code for %q but received %d", expectedStatus, url+"?"+values.Encode(), code) } return nil } // httpCode is a helper that returns the HTTP code of the response. // // It returns -1 and an error if building a new request fails. func httpCode(handler http.HandlerFunc, method, url string, values url.Values) (int, error) { // maintainer: should inject t.Context() w := httptest.NewRecorder() req, err := http.NewRequestWithContext(context.Background(), method, url, http.NoBody) if err != nil { return -1, err } req.URL.RawQuery = values.Encode() handler(w, req) return w.Code, nil } go-openapi-testify-c10ca71/internal/assertions/http_test.go000066400000000000000000000213671520301377500242250ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "io" "iter" "net/http" "net/url" "slices" "testing" ) // TestHTTPStatus runs all HTTP status assertions against a unified set of handlers. // // The expected pass/fail is determined by [expectedHTTPStatus]. func TestHTTPStatus(t *testing.T) { t.Parallel() for tc := range httpStatusCases() { t.Run(tc.name, testAllHTTPStatusAssertions(tc)) } } // TestHTTPBody runs all HTTP body assertions against a unified set of cases. // // The expected pass/fail is determined by [expectedHTTPBody]. func TestHTTPBody(t *testing.T) { t.Parallel() for tc := range httpBodyCases() { t.Run(tc.name, testAllHTTPBodyAssertions(tc)) } } func TestHTTPErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, httpFailCases()) } // ============================================================================ // Unified HTTP status assertion tests (truth matrix pattern) // ============================================================================ // handlerKind represents the HTTP response category of a handler. type handlerKind int const ( successHandler handlerKind = iota // 2xx redirectHandler // 3xx errorHandler // 4xx/5xx otherHandler // 1xx or other non-standard badRequestHandler // request construction fails (e.g. invalid method) ) type httpAssertionKind int const ( httpSuccessAssertion httpAssertionKind = iota httpRedirectAssertion httpErrorAssertion ) func testAllHTTPStatusAssertions(tc httpStatusTestCase) func(*testing.T) { return func(t *testing.T) { t.Run("HTTPSuccess", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPSuccess(mock, tc.handler, tc.method, "/", tc.params) shouldPassOrFail(t, mock, res, expectedHTTPStatus(httpSuccessAssertion, tc.kind)) }) t.Run("HTTPRedirect", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPRedirect(mock, tc.handler, tc.method, "/", tc.params) shouldPassOrFail(t, mock, res, expectedHTTPStatus(httpRedirectAssertion, tc.kind)) }) t.Run("HTTPError", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPError(mock, tc.handler, tc.method, "/", tc.params) shouldPassOrFail(t, mock, res, expectedHTTPStatus(httpErrorAssertion, tc.kind)) }) t.Run("HTTPStatusCode/match", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPStatusCode(mock, tc.handler, tc.method, "/", tc.params, tc.code) shouldPassOrFail(t, mock, res, tc.kind != badRequestHandler) }) t.Run("HTTPStatusCode/mismatch", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPStatusCode(mock, tc.handler, tc.method, "/", tc.params, 0) shouldPassOrFail(t, mock, res, false) }) } } // ============================================================================ // Unified HTTP body assertion tests (truth matrix pattern) // ============================================================================ // bodyMatchKind represents whether a string is found in the response body. type bodyMatchKind int const ( bodyMatches bodyMatchKind = iota // string is found in response body bodyMisses // string is not found in response body ) type httpBodyAssertionKind int const ( httpBodyContainsAssertion httpBodyAssertionKind = iota httpBodyNotContainsAssertion ) func testAllHTTPBodyAssertions(tc httpBodyTestCase) func(*testing.T) { return func(t *testing.T) { t.Run("HTTPBodyContains", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPBodyContains(mock, tc.handler, tc.method, "/", tc.params, tc.str) shouldPassOrFail(t, mock, res, expectedHTTPBody(httpBodyContainsAssertion, tc.kind)) }) t.Run("HTTPBodyNotContains", func(t *testing.T) { t.Parallel() mock := new(mockT) res := HTTPBodyNotContains(mock, tc.handler, tc.method, "/", tc.params, tc.str) shouldPassOrFail(t, mock, res, expectedHTTPBody(httpBodyNotContainsAssertion, tc.kind)) }) } } // ============================================================================ // HTTP status test cases // ============================================================================ type httpStatusTestCase struct { name string handler http.HandlerFunc kind handlerKind code int // actual HTTP status code returned by the handler method string // HTTP method for the request params url.Values // optional query params (nil for none) } func httpStatusCases() iter.Seq[httpStatusTestCase] { return slices.Values([]httpStatusTestCase{ {"ok", httpOK, successHandler, http.StatusOK, "GET", nil}, {"ok/with-params", httpOK, successHandler, http.StatusOK, "GET", url.Values{"id": {"12345"}}}, {"redirect", httpRedirect, redirectHandler, http.StatusTemporaryRedirect, "GET", nil}, {"error", httpError, errorHandler, http.StatusInternalServerError, "GET", nil}, {"status-code-1xx", httpStatusCode, otherHandler, http.StatusSwitchingProtocols, "GET", nil}, {"read-body", httpReadBody, successHandler, http.StatusOK, "GET", nil}, {"bad-method", httpOK, badRequestHandler, http.StatusOK, "BAD METHOD", nil}, }) } // expectedHTTPStatus determines the expected pass/fail for each assertion based on handler kind. func expectedHTTPStatus(assertion httpAssertionKind, kind handlerKind) bool { switch assertion { case httpSuccessAssertion: return kind == successHandler case httpRedirectAssertion: return kind == redirectHandler case httpErrorAssertion: return kind == errorHandler default: panic(fmt.Errorf("test case configuration error: invalid httpAssertionKind: %d", assertion)) } } // ============================================================================ // HTTP Body tests // ============================================================================ type httpBodyTestCase struct { name string handler http.HandlerFunc method string params url.Values str string kind bodyMatchKind } func httpBodyCases() iter.Seq[httpBodyTestCase] { p := url.Values{"name": []string{"World"}} return slices.Values([]httpBodyTestCase{ {"full-match", httpHelloName, "GET", p, "Hello, World!", bodyMatches}, {"partial-match", httpHelloName, "GET", p, "World", bodyMatches}, {"case-sensitive-miss", httpHelloName, "GET", p, "world", bodyMisses}, {"read-body-handler", httpReadBody, "GET", nil, "hello", bodyMatches}, {"bad-method", httpHelloName, "BAD METHOD", nil, "Hello", bodyMisses}, }) } // expectedHTTPBody determines the expected pass/fail for each assertion based on body match kind. func expectedHTTPBody(assertion httpBodyAssertionKind, kind bodyMatchKind) bool { switch assertion { case httpBodyContainsAssertion: return kind == bodyMatches case httpBodyNotContainsAssertion: return kind == bodyMisses default: panic(fmt.Errorf("test case configuration error: invalid httpBodyAssertionKind: %d", assertion)) } } // ============================================================================ // Error message tests // ============================================================================ func httpFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "HTTPSuccess/error-handler", assertion: func(t T) bool { return HTTPSuccess(t, httpError, "GET", "/", nil) }, wantContains: []string{"expected HTTP success status code"}, }, { name: "HTTPRedirect/ok-handler", assertion: func(t T) bool { return HTTPRedirect(t, httpOK, "GET", "/", nil) }, wantContains: []string{"expected HTTP redirect status code"}, }, { name: "HTTPError/redirect-handler", assertion: func(t T) bool { return HTTPError(t, httpRedirect, "GET", "/", nil) }, wantContains: []string{"expected HTTP error status code"}, }, }) } // ============================================================================ // Test HTTP handlers // ============================================================================ func httpHelloName(w http.ResponseWriter, r *http.Request) { r.Body = http.MaxBytesReader(w, r.Body, 100) name := r.FormValue("name") _, _ = fmt.Fprintf(w, "Hello, %s!", name) //nolint:gosec // G705: XSS via taint analysis but okay in tests } func httpOK(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusOK) } func httpReadBody(w http.ResponseWriter, r *http.Request) { _, _ = io.Copy(io.Discard, r.Body) w.WriteHeader(http.StatusOK) _, _ = w.Write([]byte("hello")) } func httpRedirect(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusTemporaryRedirect) } func httpError(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusInternalServerError) } func httpStatusCode(w http.ResponseWriter, _ *http.Request) { w.WriteHeader(http.StatusSwitchingProtocols) } go-openapi-testify-c10ca71/internal/assertions/ifaces.go000066400000000000000000000024661520301377500234400ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import "context" // T is an interface wrapper around [testing.T]. type T interface { Errorf(format string, args ...any) } // H is an interface for types that implement the Helper method. // This allows marking functions as test helpers, e.g. [testing.T.Helper]. type H interface { Helper() } type ( // ComparisonAssertionFunc is a common function prototype when comparing two values. Can be useful // for table driven tests. ComparisonAssertionFunc func(T, any, any, ...any) bool // ValueAssertionFunc is a common function prototype when validating a single value. Can be useful // for table driven tests. ValueAssertionFunc func(T, any, ...any) bool // BoolAssertionFunc is a common function prototype when validating a bool value. Can be useful // for table driven tests. BoolAssertionFunc func(T, bool, ...any) bool // ErrorAssertionFunc is a common function prototype when validating an error value. Can be useful // for table driven tests. ErrorAssertionFunc func(T, error, ...any) bool ) type failNower interface { FailNow() } type namer interface { Name() string } type contextualizer interface { Context() context.Context } type skipper interface { Skip(args ...any) } go-openapi-testify-c10ca71/internal/assertions/ifaces_test.go000066400000000000000000000012141520301377500244650ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions // AssertionTesterInterface defines an interface to be used for testing assertion methods. type AssertionTesterInterface interface { TestMethod() } // AssertionTesterConformingObject is an object that conforms to the AssertionTesterInterface interface. type AssertionTesterConformingObject struct{} func (a *AssertionTesterConformingObject) TestMethod() { } // AssertionTesterNonConformingObject is an object that does not conform to the AssertionTesterInterface interface. type AssertionTesterNonConformingObject struct{} go-openapi-testify-c10ca71/internal/assertions/json.go000066400000000000000000000132631520301377500231540ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "bytes" "encoding/json" "fmt" "reflect" ) // JSONEqBytes asserts that two JSON slices of bytes are semantically equivalent. // // Expected and actual must be valid JSON. // // # Usage // // assertions.JSONEqBytes(t, []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`)) // // # Examples // // success: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`{"foo": "bar", "hello": "world"}`) // failure: []byte(`{"hello": "world", "foo": "bar"}`), []byte(`[{"foo": "bar"}, {"hello": "world"}]`) func JSONEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // Domain: json // Maintainers: Proposal for enhancement. // We could use and indirection for users to inject their favorite JSON // library like we do for YAML. if h, ok := t.(H); ok { h.Helper() } var expectedJSONAsInterface, actualJSONAsInterface any if err := json.Unmarshal(expected, &expectedJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Expected value (%q) is not valid json.\nJSON parsing error: %v", expected, err), msgAndArgs...) } // Shortcut if same bytes if bytes.Equal(actual, expected) { return true } if err := json.Unmarshal(actual, &actualJSONAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input (%q) needs to be valid json.\nJSON parsing error: %v", actual, err), msgAndArgs...) } return Equal(t, expectedJSONAsInterface, actualJSONAsInterface, msgAndArgs...) } // JSONEq asserts that two JSON strings are semantically equivalent. // // Expected and actual must be valid JSON. // // For dynamic redaction of the input text via a callback, use [JSONEqT]. // // # Usage // // assertions.JSONEq(t, `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`) // // # Examples // // success: `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}` // failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` func JSONEq(t T, expected, actual string, msgAndArgs ...any) bool { // Domain: json if h, ok := t.(H); ok { h.Helper() } return JSONEqBytes(t, []byte(expected), []byte(actual), msgAndArgs...) } // JSONEqT asserts that two JSON documents are semantically equivalent. // // The expected and actual arguments may be string or []byte. They do not need to be of the same type. // // Expected and actual must be valid JSON. // // NOTE: passed values (expected, actual) may be wrapped as functions to redact the input text dynamically. // // # Usage // // assertions.JSONEqT(t, `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`)) // // # Examples // // success: `{"hello": "world", "foo": "bar"}`, []byte(`{"foo": "bar", "hello": "world"}`) // failure: `{"hello": "world", "foo": "bar"}`, `[{"foo": "bar"}, {"hello": "world"}]` func JSONEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { // Domain: json if h, ok := t.(H); ok { h.Helper() } return JSONEqBytes(t, asBytes(expected), asBytes(actual), msgAndArgs...) } // JSONUnmarshalAsT wraps [Equal] after [json.Unmarshal]. // // The input JSON may be a string or []byte. // // It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. // // Be careful not to wrap the expected object into an "any" interface if this is not what you expected: // the unmarshaling would take this type to unmarshal as a map[string]any. // // NOTE: passed jazon value may be wrapped as a function to redact the input JSON dynamically. // // # Usage // // expected := struct { // A int `json:"a"` // }{ // A: 10, // } // // assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // success: dummyStruct{A: "a"} , []byte(`{"A": "a"}`) // failure: 1, `[{"foo": "bar"}, {"hello": "world"}]` func JSONUnmarshalAsT[Object any, ADoc RText](t T, expected Object, jazon ADoc, msgAndArgs ...any) bool { // Domain: json if h, ok := t.(H); ok { h.Helper() } var actual Object if err := json.Unmarshal(asBytes(jazon), &actual); err != nil { return Fail(t, fmt.Sprintf("JSON unmarshal failed: %v", err), msgAndArgs...) } return Equal(t, expected, actual, msgAndArgs...) } // JSONMarshalAsT wraps [JSONEqT] after [json.Marshal]. // // The input JSON may be a string or []byte. // // It fails if the marshaling returns an error or if the expected JSON bytes differ semantically // from the expected ones. // // NOTE: passed expected value may be wrapped as a function to redact the input text dynamically. // // # Usage // // actual := struct { // A int `json:"a"` // }{ // A: 10, // } // // assertions.JSONUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // success: []byte(`{"A": "a"}`), dummyStruct{A: "a"} // failure: `[{"foo": "bar"}, {"hello": "world"}]`, 1 func JSONMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool { // Domain: json if h, ok := t.(H); ok { h.Helper() } actual, err := json.Marshal(object) if err != nil { return Fail(t, fmt.Sprintf("JSON marshal failed: %v", err), msgAndArgs...) } return JSONEqBytes(t, asBytes(expected), actual, msgAndArgs...) } func asBytes[EDoc RText](e EDoc) []byte { ie := any(e) switch typed := ie.(type) { case func() string: if typed == nil { panic("passed Redactor cannot be nil") } return []byte(typed()) case func() []byte: if typed == nil { panic("passed Redactor cannot be nil") } return typed() case string: return []byte(typed) case []byte: return typed default: // this edge case (redefined type) requires the input to be converted: the type constraint warrants it to work return convertReflectValue[[]byte](e, reflect.ValueOf(e)) } } go-openapi-testify-c10ca71/internal/assertions/json_test.go000066400000000000000000000340621520301377500242130ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "slices" "strings" "sync/atomic" "testing" ) func TestJSONEq(t *testing.T) { t.Parallel() for tc := range jsonCases() { t.Run(tc.name, tc.test) } } func TestJSONMarshalUnmarshalAs(t *testing.T) { t.Parallel() for tc := range jsonMarshalCases() { t.Run(tc.name, tc.test) } } func TestJSONErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, jsonFailCases()) } // ======================================= // Test JSONEq variants // ======================================= // test all JSONEq variants with the same input (possibly converted). func testAllJSONEq(expected, actual string, success bool) func(*testing.T) { return func(t *testing.T) { t.Run("with JSONEq", testJSONEq(expected, actual, success)) t.Run("with JSONEqBytes", testJSONEqBytes(expected, actual, success)) t.Run("with JSONEqT[[]byte,string]", testJSONEqT[[]byte, string](expected, actual, success)) t.Run("with JSONEqT[string,[]byte]", testJSONEqT[string, []byte](expected, actual, success)) t.Run("with JSONEqT[byte,byte]", testJSONEqT[[]byte, []byte](expected, actual, success)) t.Run("with JSONEqT[string,string]", testJSONEqT[string, string](expected, actual, success)) } } func testJSONEq(expected, actual string, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONEq(mock, expected, actual) if res != success { if success { croakWantEquiv(t, expected, actual) return } croakWantNotEquiv(t, expected, actual) } } } func testJSONEqBytes(expected, actual string, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONEqBytes(mock, []byte(expected), []byte(actual)) if res != success { if success { croakWantEquiv(t, expected, actual) return } croakWantNotEquiv(t, expected, actual) } } } func testJSONEqT[EDoc, ADoc Text](expected, actual string, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONEqT(mock, EDoc(expected), ADoc(actual)) if res != success { if success { croakWantEquiv(t, expected, actual) return } croakWantNotEquiv(t, expected, actual) } } } func croakWantEquiv(t *testing.T, expected, actual string) { t.Helper() t.Errorf("expected %q to be equivalent to %q", expected, actual) } func croakWantNotEquiv(t *testing.T, expected, actual string) { t.Helper() t.Errorf("expected %q NOT to be equivalent to %q", expected, actual) } func jsonCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ {"should be equivalent JSON", testAllJSONEq( `{"hello": "world", "foo": "bar"}`, `{"hello": "world", "foo": "bar"}`, true, )}, {"should be equivalent but not equal JSON", testAllJSONEq( `{"hello": "world", "foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, true, )}, {"should be equivalent object with array and nested objects", testAllJSONEq( "{\r\n\t\"numeric\": 1.5,\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]],"+ "\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", \"nested\"]},\r\n\t\"string\": \"foo\"\r\n}", "{\r\n\t\"numeric\": 1.5,\r\n\t\"hash\": {\"nested\": \"hash\", \"nested_slice\": [\"this\", \"is\", "+ "\"nested\"]},\r\n\t\"string\": \"foo\",\r\n\t\"array\": [{\"foo\": \"bar\"}, 1, \"string\", [\"nested\", \"array\", 5.5]]\r\n}", true, )}, {"should be equivalent array (ordered)", testAllJSONEq( `["foo", {"hello": "world", "nested": "hash"}]`, `["foo", {"nested": "hash", "hello": "world"}]`, true, )}, {"should NOT be equivalent array (elements are different)", testAllJSONEq( `["foo", {"hello": "world", "nested": "hash"}]`, `{"foo": "bar", {"nested": "hash", "hello": "world"}}`, false, )}, {"should NOT be equivalent array (order is different)", testAllJSONEq( `["foo", {"hello": "world", "nested": "hash"}]`, `[{ "hello": "world", "nested": "hash"}, "foo"]`, false, )}, {"should NOT be equivalent objects (keys are different)", testAllJSONEq( `{"foo": "bar"}`, `{"foo": "bar", "hello": "world"}`, false, )}, {"should fail with actual invalid JSON", testAllJSONEq( `{"foo": "bar"}`, "Not JSON", false, )}, {"should fail with expected invalid JSON", testAllJSONEq( "Not JSON", `{"foo": "bar", "hello": "world"}`, false, )}, {"should fail with expected and actual invalid JSON", testAllJSONEq( "Not JSON", "Not JSON", false, )}, }) } // ======================================= // Test JSONMarshalAsT / JSONUnmarshalAsT // ======================================= func jsonMarshalCases() iter.Seq[genericTestCase] { type canMarshalJSON struct { A string `json:"a"` B int `json:"b"` C int `json:"c,omitempty"` } type cantMarshalJSON struct { A string `json:"a"` B int `json:"b"` C func() `json:"c,omitempty"` // this fails when marshaling } const jazon = `{"a":"x","b":1}` valueCanDo := canMarshalJSON{A: "x", B: 1} valueCantDo := cantMarshalJSON{A: "x", B: 1, C: func() {}} return slices.Values([]genericTestCase{ {"can JSON", testAllMarshalAs(valueCanDo, jazon, true)}, {"can't JSON/marshal-fails", testAllMarshalAs(valueCantDo, jazon, false)}, {"can JSON/different-values", testAllMarshalAs(valueCanDo, `{"a": 1,"b":"x"}`, false)}, }) } func testAllMarshalAs[Doc Text, Object any](value Object, jazon Doc, success bool) func(*testing.T) { return func(t *testing.T) { t.Run("with JSONMarshalAsT", testJSONMarshalAsT(value, jazon, success)) t.Run("with JSONUnmarshalAsT", testJSONUnmarshalAsT(value, jazon, success)) } } func testJSONMarshalAsT[Doc Text, Object any](value Object, jazon Doc, success bool) func(*testing.T) { if success { return func(t *testing.T) { t.Run("should marshal JSON", func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONMarshalAsT(mock, jazon, value) if !res { t.Errorf("expected struct to marshal correctly as JSON: %#v <=> %s.Got %s", value, jazon, mock.errorString()) } }) } } return func(t *testing.T) { t.Run("should not marshal JSON", func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONMarshalAsT(mock, jazon, value) if res { t.Errorf("expected struct NOT to marshal correctly as JSON: %#v != %s", value, jazon) } }) } } func testJSONUnmarshalAsT[Doc Text, Object any](value Object, jazon Doc, success bool) func(*testing.T) { if success { return func(t *testing.T) { t.Run("should unmarshal JSON", func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONUnmarshalAsT(mock, value, jazon) if !res { t.Errorf("expected json string to unmarshal correctly from JSON: %#v <=> %s. Got %s", value, jazon, mock.errorString()) } }) } } return func(t *testing.T) { t.Run("should not unmarshal JSON", func(t *testing.T) { t.Parallel() mock := new(mockT) res := JSONUnmarshalAsT(mock, value, jazon) if res { t.Errorf("expected json string NOT to unmarshal correctly from JSON: %#v != %s", value, jazon) } }) } } // ======================================= // Test JSONErrorMessages // ======================================= func jsonFailCases() iter.Seq[failCase] { const ( part1 = `{"a":1}` part2 = `{"a":2}` ) return slices.Values([]failCase{ { name: "JSONEq/not-equal", assertion: func(t T) bool { return JSONEq(t, part1, part2) }, wantContains: []string{"Not equal"}, }, { name: "JSONEq/invalid-expected", assertion: func(t T) bool { return JSONEq(t, "not json", part1) }, wantContains: []string{"is not valid json"}, }, { name: "JSONEq/invalid-actual", assertion: func(t T) bool { return JSONEq(t, part1, "not json") }, wantContains: []string{"needs to be valid json"}, }, { name: "JSONEqT/redactor-mismatch", assertion: func(t T) bool { return JSONEqT(t, func() string { return part1 }, part2) }, wantContains: []string{"Not equal"}, }, }) } // ======================================= // Test JSONEqT / JSONUnmarshalAsT / JSONMarshalAsT with Redactor inputs // // These tests exercise the Redactor (`func() string` / `func() []byte`) // arms of the [RText] type set, the named-string/named-bytes default // branch in asBytes, and the nil-redactor panic guard. // ======================================= func TestJSONRedact(t *testing.T) { t.Parallel() t.Run("JSONEqT/redactor-string-on-expected", func(t *testing.T) { t.Parallel() mock := new(mockT) red := func() string { return `{"a":1}` } if !JSONEqT(mock, red, `{"a":1}`) { t.Errorf("expected redactor output to match literal; mock: %s", mock.errorString()) } }) t.Run("JSONEqT/redactor-bytes-on-actual", func(t *testing.T) { t.Parallel() mock := new(mockT) red := func() []byte { return []byte(`{"a":1}`) } if !JSONEqT(mock, []byte(`{"a":1}`), red) { t.Errorf("expected literal to match redactor output; mock: %s", mock.errorString()) } }) t.Run("JSONEqT/redactor-on-both-sides", func(t *testing.T) { t.Parallel() mock := new(mockT) left := func() string { return `{"a":1,"b":2}` } right := func() []byte { return []byte(`{"b":2,"a":1}`) } if !JSONEqT(mock, left, right) { t.Errorf("expected both redactor outputs to be JSON-equivalent; mock: %s", mock.errorString()) } }) // Real-world scenario: a redactor normalises a non-deterministic field // (timestamp) before comparison. t.Run("JSONEqT/redactor-normalises-timestamp", func(t *testing.T) { t.Parallel() mock := new(mockT) raw := `{"id":42,"timestamp":"2026-04-26T15:30:45Z"}` red := func() string { return strings.Replace(raw, `"2026-04-26T15:30:45Z"`, `"REDACTED"`, 1) } expected := `{"id":42,"timestamp":"REDACTED"}` if !JSONEqT(mock, expected, red) { t.Errorf("expected redactor to normalise timestamp; mock: %s", mock.errorString()) } }) // Side-effect proof: the redactor IS invoked (asBytes runs before the // comparison) and is invoked exactly once per call, regardless of which // position it's in. t.Run("JSONEqT/redactor-invoked-once", func(t *testing.T) { t.Parallel() mock := new(mockT) var calls atomic.Int32 red := func() string { calls.Add(1) return `{"a":1}` } if !JSONEqT(mock, red, `{"a":1}`) { t.Errorf("expected match; mock: %s", mock.errorString()) } if got := calls.Load(); got != 1 { t.Errorf("expected redactor to be called exactly once, got %d", got) } }) t.Run("JSONUnmarshalAsT/with-redactor-string", func(t *testing.T) { t.Parallel() mock := new(mockT) type doc struct { A int `json:"a"` } red := func() string { return `{"a":42}` } if !JSONUnmarshalAsT(mock, doc{A: 42}, red) { t.Errorf("expected unmarshal of redactor output to match expected struct; mock: %s", mock.errorString()) } }) t.Run("JSONUnmarshalAsT/with-redactor-bytes", func(t *testing.T) { t.Parallel() mock := new(mockT) type doc struct { A int `json:"a"` } red := func() []byte { return []byte(`{"a":42}`) } if !JSONUnmarshalAsT(mock, doc{A: 42}, red) { t.Errorf("expected unmarshal of redactor output to match expected struct; mock: %s", mock.errorString()) } }) t.Run("JSONMarshalAsT/with-redactor-string", func(t *testing.T) { t.Parallel() mock := new(mockT) type doc struct { A int `json:"a"` } red := func() string { return `{"a":42}` } if !JSONMarshalAsT(mock, red, doc{A: 42}) { t.Errorf("expected marshal of struct to match redactor output; mock: %s", mock.errorString()) } }) t.Run("JSONMarshalAsT/with-redactor-bytes", func(t *testing.T) { t.Parallel() mock := new(mockT) type doc struct { A int `json:"a"` } red := func() []byte { return []byte(`{"a":42}`) } if !JSONMarshalAsT(mock, red, doc{A: 42}) { t.Errorf("expected marshal of struct to match redactor output; mock: %s", mock.errorString()) } }) // Named string/[]byte types reach the default reflect branch in asBytes. t.Run("JSONEqT/named-string-type", func(t *testing.T) { t.Parallel() type myJSON string mock := new(mockT) var v myJSON = `{"a":1}` if !JSONEqT(mock, v, `{"a":1}`) { t.Errorf("expected named string type to convert to []byte via reflect; mock: %s", mock.errorString()) } }) t.Run("JSONEqT/named-bytes-type", func(t *testing.T) { t.Parallel() type myRaw []byte mock := new(mockT) v := myRaw(`{"a":1}`) if !JSONEqT(mock, v, []byte(`{"a":1}`)) { t.Errorf("expected named []byte type to convert via reflect; mock: %s", mock.errorString()) } }) // Nil redactors are a programming error and panic with a clear message // rather than the cryptic nil-pointer dereference users would otherwise see. t.Run("JSONEqT/nil-func-string-panics", func(t *testing.T) { t.Parallel() mustPanicWithRedactor(t, "JSONEqT", func() { var nilFn func() string _ = JSONEqT(new(mockT), nilFn, `{"a":1}`) }) }) t.Run("JSONEqT/nil-func-bytes-panics", func(t *testing.T) { t.Parallel() mustPanicWithRedactor(t, "JSONEqT", func() { var nilFn func() []byte _ = JSONEqT(new(mockT), nilFn, []byte(`{"a":1}`)) }) }) t.Run("JSONUnmarshalAsT/nil-func-string-panics", func(t *testing.T) { t.Parallel() mustPanicWithRedactor(t, "JSONUnmarshalAsT", func() { var nilFn func() string _ = JSONUnmarshalAsT(new(mockT), struct{}{}, nilFn) }) }) t.Run("JSONMarshalAsT/nil-func-bytes-panics", func(t *testing.T) { t.Parallel() mustPanicWithRedactor(t, "JSONMarshalAsT", func() { var nilFn func() []byte _ = JSONMarshalAsT(new(mockT), nilFn, struct{}{}) }) }) } // mustPanicWithRedactor asserts that fn panics with the standard // nil-Redactor message. Used by both JSON and YAML redactor tests. func mustPanicWithRedactor(t *testing.T, fn string, body func()) { t.Helper() const wantMsg = "passed Redactor cannot be nil" defer func() { rec := recover() if rec == nil { t.Errorf("expected %s to panic with nil Redactor, got no panic", fn) return } s, _ := rec.(string) if !strings.Contains(s, wantMsg) { t.Errorf("expected %s panic message to contain %q, got: %v", fn, wantMsg, rec) } }() body() } go-openapi-testify-c10ca71/internal/assertions/mock_test.go000066400000000000000000000116001520301377500241640ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "context" "fmt" "iter" "regexp" "strings" "testing" ) var ( _ T = &mockT{} _ T = &mockFailNowT{} _ failNower = &mockFailNowT{} _ T = &captureT{} _ T = &errorsCapturingT{} ) type mockT struct { errorFmt string args []any failed bool } const ( errString = "Error" errTrace = "Error Trace" ) // Helper is like [testing.T.Helper] but does nothing. func (mockT) Helper() {} func (m *mockT) Errorf(format string, args ...any) { m.errorFmt = format m.args = args m.failed = true } func (m *mockT) Failed() bool { return m.errorFmt != "" || m.failed } func (m *mockT) errorString() string { return fmt.Sprintf(m.errorFmt, m.args...) } type mockFailNowT struct { mockT } func (m *mockFailNowT) FailNow() { m.failed = true } // errorsCapturingT is a mock implementation of TestingT that captures multiple errors reported with Errorf. // // It may be equipped with a [context.Context] for tests that check on the [testing.T.Context]. type errorsCapturingT struct { errors []error ctx context.Context //nolint:containedctx // this is ok to support context injection tests } // Helper is like [testing.T.Helper] but does nothing. func (errorsCapturingT) Helper() {} func (t errorsCapturingT) Context() context.Context { if t.ctx == nil { return context.Background() } return t.ctx } func (t *errorsCapturingT) WithContext(ctx context.Context) *errorsCapturingT { t.ctx = ctx return t } func (t *errorsCapturingT) Errorf(format string, args ...any) { t.errors = append(t.errors, fmt.Errorf(format, args...)) } type captureT struct { failed bool msg string } // Helper is like [testing.T.Helper] but does nothing. func (captureT) Helper() {} func (ctt *captureT) Errorf(format string, args ...any) { ctt.msg = fmt.Sprintf(format, args...) ctt.failed = true } func shouldPassOrFail(t *testing.T, mock *mockT, result, shouldPass bool) { t.Helper() if shouldPass { if !result || mock.Failed() { t.Error("expected to pass") } return } if result || !mock.Failed() { t.Error("expected to fail") } } func ptr(i int) *int { return &i } // failCase defines a test case for verifying assertion error messages. // // Only one of wantError, wantMatch, or wantContains should be set per case. type failCase struct { name string assertion func(t T) bool // assertion call with bad inputs baked in wantError string // exact match on errString label content wantMatch string // regexp match on errString label content wantContains []string // substring checks on errString label content } // runFailCases runs a set of failCase tests using the standard harness. func runFailCases(t *testing.T, cases iter.Seq[failCase]) { t.Helper() for tc := range cases { t.Run(tc.name, runFailCase(tc)) } } func runFailCase(tc failCase) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(captureT) result := tc.assertion(mock) // 1. Verify assertion returned false if result { t.Error("expected assertion to return false") return } // 2. Verify mock recorded a failure if !mock.failed { t.Error("expected mock to record a failure") return } // 3. Parse envelope parsed := parseLabeledOutput(mock.msg) if parsed == nil { t.Errorf("could not parse labeled output: %q", mock.msg) return } // 4. Validate envelope structure var hasErrorTrace, hasError bool var errorContent string for _, lc := range parsed { switch lc.label { case errTrace: hasErrorTrace = true case errString: hasError = true errorContent = strings.TrimRight(lc.content, "\n") } } if !hasErrorTrace { t.Error("envelope missing Error Trace label") } if !hasError { t.Error("envelope missing Error label") return } // 5. Match based on which want* field is set switch { case tc.wantError != "": if errorContent != tc.wantError { t.Errorf("error content mismatch:\n want: %q\n got: %q", tc.wantError, errorContent) } case tc.wantMatch != "": matched, err := regexp.MatchString(tc.wantMatch, errorContent) if err != nil { t.Errorf("invalid regexp %q: %v", tc.wantMatch, err) return } if !matched { t.Errorf("error content does not match pattern %q:\n got: %q", tc.wantMatch, errorContent) } case len(tc.wantContains) > 0: for _, sub := range tc.wantContains { if !strings.Contains(errorContent, sub) { t.Errorf("error content missing substring %q:\n got: %q", sub, errorContent) } } } } } // truncationCase is a convenience constructor for a failCase that checks for truncation. func truncationCase(name string, assertion func(t T) bool) failCase { return failCase{ name: name, assertion: assertion, wantContains: []string{"<... truncated>"}, } } go-openapi-testify-c10ca71/internal/assertions/number.go000066400000000000000000000363461520301377500235020ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "math" "reflect" "time" ) // InDelta asserts that the two numerals are within delta of each other. // // Delta must be greater than or equal to zero. // // Expected and actual values should convert to float64. // To compare large integers that can't be represented accurately as float64 (e.g. uint64), // prefer [InDeltaT] to preserve the original type. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.Sqrt](-1), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // # Usage // // assertions.InDelta(t, math.Pi, 22/7.0, 0.01) // // # Examples // // success: 1.0, 1.01, 0.02 // failure: 1.0, 1.1, 0.05 func InDelta(t T, expected, actual any, delta float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } af, aok := toFloat(expected) bf, bok := toFloat(actual) if !aok || !bok { return Fail(t, "Parameters must be numerical", msgAndArgs...) } msg, skip, ok := checkDeltaEdgeCases(af, bf, delta) if !ok { return Fail(t, msg, msgAndArgs...) } if skip { return true } dt := af - bf if dt < -delta || dt > delta { return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) } return true } // InDeltaT asserts that the two numerals of the same type numerical type are within delta of each other. // // [InDeltaT] accepts any go numeric type, including integer types. // // The main difference with [InDelta] is that the delta is expressed with the same type as the values, not necessarily a float64. // // Delta must be greater than or equal to zero. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: InDeltaT([math.NaN](), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // # Usage // // assertions.InDeltaT(t, math.Pi, 22/7.0, 0.01) // // # Examples // // success: 1.0, 1.01, 0.02 // failure: 1.0, 1.1, 0.05 func InDeltaT[Number Measurable](t T, expected, actual, delta Number, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } msg, skip, ok := checkDeltaEdgeCases(expected, actual, delta) if !ok { return Fail(t, msg, msgAndArgs...) } if skip { return true } var ( dt Number failed bool ) // check is a little slower than straight delta, but it can handle unsigned numbers without errors if expected > actual { dt = expected - actual } else { dt = actual - expected } failed = dt > delta if failed { return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) } return true } // InEpsilon asserts that expected and actual have a relative error less than epsilon. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.NaN](), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. // // Formula: // // - If expected == 0: fail if |actual - expected| > epsilon // - If expected != 0: fail if |actual - expected| > epsilon * |expected| // // This allows [InEpsilonT] to work naturally across the full numeric range including zero. // // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 func InEpsilon(t T, expected, actual any, epsilon float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } af, aok := toFloat(expected) bf, bok := toFloat(actual) if !aok || !bok { return Fail(t, "Parameters must be numerical", msgAndArgs...) } msg, skip, ok := checkDeltaEdgeCases(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } if skip { return true } msg, ok = compareRelativeError(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } return true } // InEpsilonT asserts that expected and actual have a relative error less than epsilon. // // When expected is zero, epsilon is interpreted as an absolute error threshold, // since relative error is mathematically undefined for zero values. // // Unlike [InDeltaT], which preserves the original type, [InEpsilonT] converts the expected and actual // numbers to float64, since the relative error doesn't make sense as an integer. // // # Behavior with IEEE floating point arithmetic // // - expected NaN is matched only by a NaN, e.g. this works: [InDeltaT]([math.NaN](), [math.Sqrt](-1), 0.0) // - expected +Inf is matched only by a +Inf // - expected -Inf is matched only by a -Inf // // Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDeltaT]. // // Formula: // // - If expected == 0: fail if |actual - expected| > epsilon // - If expected != 0: fail if |actual - expected| > epsilon * |expected| // // This allows [InEpsilonT] to work naturally across the full numeric range including zero. // // # Usage // // assertions.InEpsilon(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 func InEpsilonT[Number Measurable](t T, expected, actual Number, epsilon float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } af := float64(expected) bf := float64(actual) msg, skip, ok := checkDeltaEdgeCases(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } if skip { return true } msg, ok = compareRelativeError(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } return true } // InEpsilonSymmetric asserts that 2 numbers are close, with a symmetric relative error. // // Unlike with [InEpsilon], both numbers play a symmetric role and the relative error is // computed relative to the number with greatest amplitude. This mirrors the behavior of // Python's [math.isclose] (with the relative-tolerance term only). // // See also [InEpsilon]. // // # Behavior with IEEE floating point arithmetic // // - NaN is matched only by a NaN, e.g. this works: [InEpsilonSymmetric]([math.NaN](), [math.Sqrt](-1), 0.0) // - +Inf is matched only by a +Inf // - -Inf is matched only by a -Inf // // Edge case: for very large integers that do not convert accurately to a float64 (e.g. uint64), prefer [InDelta]. // // Formula: // // - If x == 0 and y == 0: success // - Otherwise fail if |x - y| > epsilon * max(|x|,|y|) // // # Usage // // assertions.InEpsilonSymmetric(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 // // [math.isclose]: https://docs.python.org/3/library/math.html#math.isclose func InEpsilonSymmetric(t T, x, y any, epsilon float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } af, aok := toFloat(x) bf, bok := toFloat(y) if !aok || !bok { return Fail(t, "Parameters must be numerical", msgAndArgs...) } msg, skip, ok := checkDeltaEdgeCases(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } if skip { return true } msg, ok = compareSymmetricRelativeError(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } return true } // InEpsilonSymmetricT is the type-safe version of [InEpsilonSymmetric], comparing numbers of the same numerical type. // // See [InEpsilonSymmetric]. // // # Usage // // assertions.InEpsilonSymmetricT(t, 100.0, 101.0, 0.02) // // # Examples // // success: 100.0, 101.0, 0.02 // failure: 100.0, 110.0, 0.05 func InEpsilonSymmetricT[Number Measurable](t T, x, y Number, epsilon float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } af := float64(x) bf := float64(y) msg, skip, ok := checkDeltaEdgeCases(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } if skip { return true } msg, ok = compareSymmetricRelativeError(af, bf, epsilon) if !ok { return Fail(t, msg, msgAndArgs...) } return true } // InDeltaSlice is the same as [InDelta], except it compares two slices. // // See [InDelta]. // // # Usage // // assertions.InDeltaSlice(t, []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02) // // # Examples // // success: []float64{1.0, 2.0}, []float64{1.01, 2.01}, 0.02 // failure: []float64{1.0, 2.0}, []float64{1.1, 2.1}, 0.05 func InDeltaSlice(t T, expected, actual any, delta float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Slice || reflect.TypeOf(expected).Kind() != reflect.Slice { return Fail(t, "Parameters must be slice", msgAndArgs...) } actualSlice := reflect.ValueOf(actual) expectedSlice := reflect.ValueOf(expected) for i := range actualSlice.Len() { result := InDelta(t, actualSlice.Index(i).Interface(), expectedSlice.Index(i).Interface(), delta, msgAndArgs...) if !result { return result } } return true } // InDeltaMapValues is the same as [InDelta], but it compares all values between two maps. Both maps must have exactly the same keys. // // See [InDelta]. // // # Usage // // assertions.InDeltaMapValues(t, map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02) // // # Examples // // success: map[string]float64{"a": 1.0}, map[string]float64{"a": 1.01}, 0.02 // failure: map[string]float64{"a": 1.0}, map[string]float64{"a": 1.1}, 0.05 func InDeltaMapValues(t T, expected, actual any, delta float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } if expected == nil || actual == nil || reflect.TypeOf(actual).Kind() != reflect.Map || reflect.TypeOf(expected).Kind() != reflect.Map { return Fail(t, "Arguments must be maps", msgAndArgs...) } expectedMap := reflect.ValueOf(expected) actualMap := reflect.ValueOf(actual) if expectedMap.Len() != actualMap.Len() { return Fail(t, "Arguments must have the same number of keys", msgAndArgs...) } for _, k := range expectedMap.MapKeys() { ev := expectedMap.MapIndex(k) av := actualMap.MapIndex(k) // from [reflect.MapIndex] contract, ev is always a valid [reflect.Value] here // because we know that the key has been found. // On the other hand, av may not be there. if !av.IsValid() { return Fail(t, fmt.Sprintf("missing key %q in actual map", k), msgAndArgs...) } if !InDelta( t, ev.Interface(), av.Interface(), delta, msgAndArgs..., ) { return false } } return true } // InEpsilonSlice is the same as [InEpsilon], except it compares each value from two slices. // // See [InEpsilon]. // // # Usage // // assertions.InEpsilonSlice(t, []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02) // // # Examples // // success: []float64{100.0, 200.0}, []float64{101.0, 202.0}, 0.02 // failure: []float64{100.0, 200.0}, []float64{110.0, 220.0}, 0.05 func InEpsilonSlice(t T, expected, actual any, epsilon float64, msgAndArgs ...any) bool { // Domain: number if h, ok := t.(H); ok { h.Helper() } if expected == nil || actual == nil { return Fail(t, "Parameters must be slice", msgAndArgs...) } expectedSlice := reflect.ValueOf(expected) actualSlice := reflect.ValueOf(actual) if expectedSlice.Type().Kind() != reflect.Slice { return Fail(t, "Expected value must be slice", msgAndArgs...) } expectedLen := expectedSlice.Len() if !IsType(t, expected, actual) || !Len(t, actual, expectedLen) { return false } for i := range expectedLen { if !InEpsilon(t, expectedSlice.Index(i).Interface(), actualSlice.Index(i).Interface(), epsilon, "at index %d", i) { return false } } return true } func checkDeltaEdgeCases[Number Measurable](expected, actual, delta Number) (msg string, skip bool, ok bool) { if delta < 0 { return "Delta must not be negative", true, false } // IEEE float edge cases: NaN, +Inf/-Inf if isNaN(delta) || isInf(delta, 0) { return "Delta must not be NaN or Inf", true, false } expectedInf := isInf(expected, 0) actualInf := isInf(actual, 0) if expectedInf { // expected -Inf/+Inf if !actualInf { return "Expected an Inf value", true, false } if isInf(expected, 1) && !isInf(actual, 1) { return "Expected a +Inf value but got -Inf", true, false } if isInf(expected, -1) && !isInf(actual, -1) { return "Expected a -Inf value but got +Inf", true, false } // Both are Inf and match - success return "", true, true } if actualInf { return "Actual is Inf", true, false } expectedNaN := isNaN(expected) actualNaN := isNaN(actual) if expectedNaN && actualNaN { // expected NaN return "", true, true } if expectedNaN { return "Expected a NaN value but actual is finite", true, false } if actualNaN { return fmt.Sprintf("Expected %v with delta %v, but was NaN", expected, delta), true, false } return "", false, true } func compareRelativeError(expected, actual, epsilon float64) (msg string, ok bool) { delta := math.Abs(expected - actual) if delta == 0 { return "", true } if expected == 0 { if delta > epsilon { return fmt.Sprintf( "Expected value is zero, using absolute error comparison.\n"+ "Absolute difference is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, delta), false } return "", true } if delta > epsilon*math.Abs(expected) { return fmt.Sprintf("Relative error is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, delta/math.Abs(expected)), false } return "", true } func compareSymmetricRelativeError(a, b, epsilon float64) (msg string, ok bool) { delta := math.Abs(a - b) if delta == 0 { return "", true } amplitude := max(math.Abs(a), math.Abs(b)) if delta > epsilon*amplitude { return fmt.Sprintf("Symmetric relative error is too high: %#v (expected)\n"+ " < %#v (actual)", epsilon, delta/amplitude), false } return "", true } func toFloat(x any) (float64, bool) { var xf float64 xok := true switch xn := x.(type) { case uint: xf = float64(xn) case uint8: xf = float64(xn) case uint16: xf = float64(xn) case uint32: xf = float64(xn) case uint64: xf = float64(xn) case int: xf = float64(xn) case int8: xf = float64(xn) case int16: xf = float64(xn) case int32: xf = float64(xn) case int64: xf = float64(xn) case float32: xf = float64(xn) case float64: xf = xn case time.Duration: xf = float64(xn) default: // try reflect conversion val := reflect.ValueOf(xn) typ := reflect.TypeFor[float64]() if val.IsValid() && val.CanConvert(typ) { rxf := val.Convert(typ) xf = rxf.Float() break } xok = false } return xf, xok } func isNaN[Number Measurable](f Number) bool { return f != f //nolint:gocritic // yes this weird property is held by NaN only } func isInf[Number Measurable](f Number, sign int) bool { v := any(f) switch ff := v.(type) { case float32: return isInf32(ff, sign) case float64: return math.IsInf(ff, sign) default: return false } } func isInf32(f float32, sign int) bool { return (sign >= 0 && f > math.MaxFloat32) || (sign <= 0 && f < -math.MaxFloat32) } go-openapi-testify-c10ca71/internal/assertions/number_test.go000066400000000000000000001110701520301377500245250ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "math" "slices" "testing" "time" ) type customFloat float64 func TestNumberEdgeCases(t *testing.T) { t.Parallel() t.Run("InDelta reflect-specific (type conversion)", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDelta(mock, "", nil, 1) if result { t.Errorf("Expected non numerals to fail") } }) t.Run("InEpsilon reflect-specific (type conversion)", func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilon(mock, "", nil, 1) if result { t.Errorf("Expected non numerals to fail") } }) } func TestNumberInDelta(t *testing.T) { t.Parallel() // run all test cases with both InDelta and InDeltaT // // NOTE: testing pattern, focused on the expected result (true/false) and _NOT_ the content of the returned message. // - deltaCases: loop over generic test cases AND type combinations (reason: not all types are compatible, e.g. uint64 may overflow float64) // - testAllDelta: dispatch over the assertion variants (reflection-based, generic, X vs NotX semantics) // Single assertion test functions: // - testDelta // - testDeltaT for tc := range deltaCases() { t.Run(tc.name, tc.test) } } func TestNumberInDeltaSlice(t *testing.T) { t.Parallel() // only have a reflection-based assertion here for tc := range deltaSliceCases() { t.Run(tc.name, tc.test) } } func TestNumberInDeltaMapValues(t *testing.T) { t.Parallel() mock := new(mockT) // only have a reflection-based assertion here for tc := range numberInDeltaMapCases() { result := InDeltaMapValues(mock, tc.expect, tc.actual, tc.delta) if result != tc.shouldPass { t.Errorf("%s: expected result=%v, got %v\n%s", tc.name, tc.shouldPass, result, diff(tc.expect, tc.actual)) } } } func TestNumberInEpsilon(t *testing.T) { t.Parallel() // run all test cases with both InEpsilon and InEpsilonT for tc := range epsilonCases() { t.Run(tc.name, tc.test) } } func TestNumberInEpsilonSlice(t *testing.T) { t.Parallel() for tc := range epsilonSliceCases() { t.Run(tc.name, tc.test) } } func TestNumberInEpsilonSymmetric(t *testing.T) { t.Parallel() // run all test cases with both InEpsilonSymmetric and InEpsilonSymmetricT for tc := range inEpsilonSymmetricCases() { t.Run(tc.name, tc.test) } } func TestNumberErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, numberFailCases()) } // ======================================= // Test NumberInDelta variants // ======================================= func deltaCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Simple input cases {"simple/within-delta-1.001-1-0.01", testAllDelta(1.001, 1.0, 0.01, true)}, {"simple/within-delta-1-1.001-0.01", testAllDelta(1.0, 1.001, 0.01, true)}, {"simple/within-delta-1-2-1", testAllDelta(1.0, 2.0, 1.0, true)}, {"simple/exceeds-delta-1-2-0.5", testAllDelta(1.0, 2.0, 0.5, false)}, {"simple/exceeds-delta-2-1-0.5", testAllDelta(2.0, 1.0, 0.5, false)}, // Edge cases - NaN {"edge/nan-for-actual", testAllDelta(42.0, math.NaN(), 0.01, false)}, {"edge/nan-for-expected", testAllDelta(math.NaN(), 42.0, 0.01, false)}, {"edge/nan-for-both", testAllDelta(math.NaN(), math.NaN(), 0.01, true)}, // All integer types - basic success cases {"int/success", testAllDelta(int(2), int(1), int(1), true)}, {"int8/success", testAllDelta(int8(2), int8(1), int8(1), true)}, {"int16/success", testAllDelta(int16(2), int16(1), int16(1), true)}, {"int32/success", testAllDelta(int32(2), int32(1), int32(1), true)}, {"int64/success", testAllDelta(int64(2), int64(1), int64(1), true)}, {"uint/success", testAllDelta(uint(2), uint(1), uint(1), true)}, {"uint8/success", testAllDelta(uint8(2), uint8(1), uint8(1), true)}, {"uint16/success", testAllDelta(uint16(2), uint16(1), uint16(1), true)}, {"uint32/success", testAllDelta(uint32(2), uint32(1), uint32(1), true)}, {"uint64/success", testAllDelta(uint64(2), uint64(1), uint64(1), true)}, {"float32/success", testAllDelta(float32(2.0), float32(1.0), float32(1.0), true)}, {"float64/success", testAllDelta(2.0, 1.0, 1.0, true)}, // Basic failure cases {"int/failure", testAllDelta(int(10), int(1), int(5), false)}, {"uint/failure", testAllDelta(uint(10), uint(1), uint(5), false)}, {"float64/failure", testAllDelta(10.0, 1.0, 5.0, false)}, // Exact match (zero delta) {"int/exact", testAllDelta(int(5), int(5), int(0), true)}, {"uint/exact", testAllDelta(uint(5), uint(5), uint(0), true)}, {"float64/exact", testAllDelta(5.0, 5.0, 0.0, true)}, // Zero values {"int/zero", testAllDelta(int(0), int(0), int(0), true)}, {"uint/zero", testAllDelta(uint(0), uint(0), uint(0), true)}, {"float64/zero", testAllDelta(0.0, 0.0, 0.0, true)}, {"int/near-zero", testAllDelta(int(1), int(0), int(1), true)}, {"float64/near-zero", testAllDelta(0.01, 0.0, 0.02, true)}, // Negative numbers {"int/negative", testAllDelta(int(-5), int(-4), int(2), true)}, {"int/negative-fail", testAllDelta(int(-10), int(-1), int(5), false)}, {"float64/negative", testAllDelta(-5.0, -4.0, 2.0, true)}, // Mixed positive/negative {"int/mixed", testAllDelta(int(5), int(-5), int(10), true)}, {"int/mixed-fail", testAllDelta(int(5), int(-5), int(9), false)}, {"float64/mixed", testAllDelta(5.0, -5.0, 10.0, true)}, // Unsigned integer edge cases (overflow protection) {"uint/expected-greater", testAllDelta(uint(100), uint(50), uint(60), true)}, {"uint/actual-greater", testAllDelta(uint(50), uint(100), uint(60), true)}, {"uint8/max-value", testAllDelta(uint8(255), uint8(250), uint8(10), true)}, {"uint8/max-value-fail", testAllDelta(uint8(255), uint8(250), uint8(4), false)}, {"uint16/large-diff", testAllDelta(uint16(60000), uint16(40000), uint16(25000), true)}, {"uint32/large-diff", testAllDelta(uint32(4000000000), uint32(3000000000), uint32(1000000001), true)}, {"uint64/large-diff", testAllDelta(uint64(10000000000), uint64(5000000000), uint64(5000000001), true)}, // Boundary testing for unsigned (expected > actual path) {"uint8/boundary-expected-gt-actual", testAllDelta(uint8(200), uint8(100), uint8(100), true)}, {"uint8/boundary-expected-gt-actual-fail", testAllDelta(uint8(200), uint8(100), uint8(99), false)}, // Boundary testing for unsigned (actual > expected path) {"uint8/boundary-actual-gt-expected", testAllDelta(uint8(100), uint8(200), uint8(100), true)}, {"uint8/boundary-actual-gt-expected-fail", testAllDelta(uint8(100), uint8(200), uint8(99), false)}, // Float32 NaN cases {"float32/both-nan", testAllDelta(float32(math.NaN()), float32(math.NaN()), float32(1.0), true)}, {"float32/expected-nan", testAllDelta(float32(math.NaN()), float32(1.0), float32(1.0), false)}, {"float32/actual-nan", testAllDelta(float32(1.0), float32(math.NaN()), float32(1.0), false)}, // Float64 NaN cases {"float64/both-nan", testAllDelta(math.NaN(), math.NaN(), 1.0, true)}, {"float64/expected-nan", testAllDelta(math.NaN(), 1.0, 1.0, false)}, {"float64/actual-nan", testAllDelta(1.0, math.NaN(), 1.0, false)}, // Float32 +Inf cases {"float32/both-plus-inf", testAllDelta(float32(math.Inf(1)), float32(math.Inf(1)), float32(1.0), true)}, {"float32/expected-plus-inf-actual-minus-inf", testAllDelta(float32(math.Inf(1)), float32(math.Inf(-1)), float32(1.0), false)}, {"float32/expected-plus-inf-actual-finite", testAllDelta(float32(math.Inf(1)), float32(1.0), float32(1.0), false)}, {"float32/expected-finite-actual-plus-inf", testAllDelta(float32(1.0), float32(math.Inf(1)), float32(1.0), false)}, // Float64 +Inf cases {"float64/both-plus-inf", testAllDelta(math.Inf(1), math.Inf(1), 1.0, true)}, {"float64/expected-plus-inf-actual-minus-inf", testAllDelta(math.Inf(1), math.Inf(-1), 1.0, false)}, {"float64/expected-plus-inf-actual-finite", testAllDelta(math.Inf(1), 1.0, 1.0, false)}, {"float64/expected-finite-actual-plus-inf", testAllDelta(1.0, math.Inf(1), 1.0, false)}, // Float32 -Inf cases {"float32/both-minus-inf", testAllDelta(float32(math.Inf(-1)), float32(math.Inf(-1)), float32(1.0), true)}, {"float32/expected-minus-inf-actual-plus-inf", testAllDelta(float32(math.Inf(-1)), float32(math.Inf(1)), float32(1.0), false)}, {"float32/expected-minus-inf-actual-finite", testAllDelta(float32(math.Inf(-1)), float32(1.0), float32(1.0), false)}, {"float32/expected-finite-actual-minus-inf", testAllDelta(float32(1.0), float32(math.Inf(-1)), float32(1.0), false)}, // Float64 -Inf cases {"float64/both-minus-inf", testAllDelta(math.Inf(-1), math.Inf(-1), 1.0, true)}, {"float64/expected-minus-inf-actual-plus-inf", testAllDelta(math.Inf(-1), math.Inf(1), 1.0, false)}, {"float64/expected-minus-inf-actual-finite", testAllDelta(math.Inf(-1), 1.0, 1.0, false)}, {"float64/expected-finite-actual-minus-inf", testAllDelta(1.0, math.Inf(-1), 1.0, false)}, // Delta validation - NaN delta {"float64/delta-nan", testAllDelta(1.0, 1.0, math.NaN(), false)}, {"float32/delta-nan", testAllDelta(float32(1.0), float32(1.0), float32(math.NaN()), false)}, // Delta validation - Inf delta {"float64/delta-plus-inf", testAllDelta(1.0, 1.0, math.Inf(1), false)}, {"float64/delta-minus-inf", testAllDelta(1.0, 1.0, math.Inf(-1), false)}, {"float32/delta-plus-inf", testAllDelta(float32(1.0), float32(1.0), float32(math.Inf(1)), false)}, {"float32/delta-minus-inf", testAllDelta(float32(1.0), float32(1.0), float32(math.Inf(-1)), false)}, // Very small deltas (precision testing) {"float64/small-delta", testAllDelta(1.0, 1.0000001, 0.000001, true)}, {"float64/small-delta-fail", testAllDelta(1.0, 1.0000001, 0.00000001, false)}, {"float32/small-delta", testAllDelta(float32(1.0), float32(1.00001), float32(0.0001), true)}, {"float32/small-delta-fail", testAllDelta(float32(1.0), float32(1.00001), float32(0.00001), false)}, // Large values {"int64/large-values", testAllDelta(int64(9223372036854775800), int64(9223372036854775700), int64(200), true)}, {"uint64/large-values", testAllDelta(uint64(18446744073709551600), uint64(18446744073709551500), uint64(200), true)}, {"float64/large-values", testAllDelta(1e308, 1e308-1e307, 2e307, true)}, // Edge case: delta is zero with different values {"int/zero-delta-different", testAllDelta(int(5), int(6), int(0), false)}, {"float64/zero-delta-different", testAllDelta(5.0, 5.00001, 0.0, false)}, // Commutative property (order shouldn't matter) {"int/commutative-1", testAllDelta(int(10), int(5), int(6), true)}, {"int/commutative-2", testAllDelta(int(5), int(10), int(6), true)}, {"float64/commutative-1", testAllDelta(10.0, 5.0, 6.0, true)}, {"float64/commutative-2", testAllDelta(5.0, 10.0, 6.0, true)}, // time.Duration (covers toFloat time.Duration case) {"duration/success", testAllDelta(time.Second, time.Second+time.Millisecond, time.Millisecond, true)}, {"duration/failure", testAllDelta(time.Second, time.Second+10*time.Millisecond, time.Millisecond, false)}, // custom float type (covers toFloat reflect conversion) {"custom-float/success", testAllDelta(customFloat(2.0), customFloat(1.0), customFloat(1.0), true)}, {"custom-float/failure", testAllDelta(customFloat(10.0), customFloat(1.0), customFloat(5.0), false)}, }) } // testAllDelta tests both InDelta and InDeltaT with the same input. func testAllDelta[Number Measurable](expected, actual, delta Number, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() if shouldPass { t.Run("should pass", func(t *testing.T) { t.Run("with InDelta", testDelta(expected, actual, delta, true)) t.Run("with InDeltaT", testDeltaT(expected, actual, delta, true)) }) } else { t.Run("should fail", func(t *testing.T) { t.Run("with InDelta", testDelta(expected, actual, delta, false)) t.Run("with InDeltaT", testDeltaT(expected, actual, delta, false)) }) } } } func testDelta[Number Measurable](expected, actual, delta Number, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) // InDelta requires delta as float64, so convert it result := InDelta(mock, expected, actual, float64(delta)) shouldPassOrFail(t, mock, result, shouldPass) } } func testDeltaT[Number Measurable](expected, actual, delta Number, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaT(mock, expected, actual, delta) shouldPassOrFail(t, mock, result, shouldPass) } } // Helper functions and test data for InDeltaMapValues type numberInDeltaMapCase struct { name string expect any actual any shouldPass bool delta float64 } func numberInDeltaMapCases() iter.Seq[numberInDeltaMapCase] { keyA := "a" var iface any return slices.Values([]numberInDeltaMapCase{ { name: "Within delta", expect: map[string]float64{ "foo": 1.0, "bar": 2.0, "baz": math.NaN(), }, actual: map[string]float64{ "foo": 1.01, "bar": 1.99, "baz": math.NaN(), }, delta: 0.1, shouldPass: true, }, { name: "Within delta", expect: map[int]float64{ 1: 1.0, 2: 2.0, }, actual: map[int]float64{ 1: 1.0, 2: 1.99, }, delta: 0.1, shouldPass: true, }, { name: "Different number of keys", expect: map[int]float64{ 1: 1.0, 2: 2.0, }, actual: map[int]float64{ 1: 1.0, }, delta: 0.1, shouldPass: false, }, { name: "Within delta with zero value", expect: map[string]float64{ "zero": 0, }, actual: map[string]float64{ "zero": 0, }, delta: 0.1, shouldPass: true, }, { name: "With missing key with zero value", expect: map[string]float64{ "zero": 0, "foo": 0, }, actual: map[string]float64{ "zero": 0, "bar": 0, }, shouldPass: false, }, { name: "With nil maps", expect: map[string]float64(nil), actual: map[string]float64(nil), shouldPass: true, }, { name: "With nil values (not a map)", expect: map[string]float64(nil), actual: []float64(nil), shouldPass: false, }, { name: "With nil values (not a map)", expect: []float64(nil), actual: map[string]float64(nil), shouldPass: false, }, { name: "With expected nil keys", expect: map[*string]float64{ &keyA: 1.00, (*string)(nil): 2.00, }, actual: map[*string]float64{ &keyA: 1.00, (*string)(nil): 2.00, }, shouldPass: true, }, { name: "With expected invalid value", expect: map[string]any{ keyA: &iface, }, actual: map[string]any{ keyA: &iface, }, shouldPass: false, }, }) } // ======================================= // Test NumberInEpsilon variants // ======================================= func epsilonCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Simple input cases {"simple/1pct-error-within-2pct-epsilon", testAllEpsilon(100.0, 101.0, 0.02, true)}, {"simple/5pct-error-exceeds-2pct-epsilon", testAllEpsilon(100.0, 105.0, 0.02, false)}, {"simple/exact-match-zero-epsilon", testAllEpsilon(100.0, 100.0, 0.0, true)}, // Edge cases - NaN {"edge/nan-for-actual", testAllEpsilon(42.0, math.NaN(), 0.01, false)}, {"edge/nan-for-expected", testAllEpsilon(math.NaN(), 42.0, 0.01, false)}, {"edge/nan-for-both", testAllEpsilon(math.NaN(), math.NaN(), 0.01, true)}, // Edge cases - zero expected (uses absolute error) {"edge/zero-expected-within", testAllEpsilon(0.0, 0.009, 0.01, true)}, {"edge/zero-expected-exceeds", testAllEpsilon(0.0, 0.011, 0.01, false)}, // All numeric types - basic success cases (12 cases) {"int/success", testAllEpsilon(int(100), int(101), 0.02, true)}, // 1% error < 2% epsilon {"int8/success", testAllEpsilon(int8(100), int8(101), 0.02, true)}, // 1% error < 2% epsilon {"int16/success", testAllEpsilon(int16(100), int16(101), 0.02, true)}, // 1% error < 2% epsilon {"int32/success", testAllEpsilon(int32(100), int32(101), 0.02, true)}, // 1% error < 2% epsilon {"int64/success", testAllEpsilon(int64(100), int64(101), 0.02, true)}, // 1% error < 2% epsilon {"uint/success", testAllEpsilon(uint(100), uint(101), 0.02, true)}, // 1% error < 2% epsilon {"uint8/success", testAllEpsilon(uint8(100), uint8(101), 0.02, true)}, // 1% error < 2% epsilon {"uint16/success", testAllEpsilon(uint16(100), uint16(101), 0.02, true)}, // 1% error < 2% epsilon {"uint32/success", testAllEpsilon(uint32(100), uint32(101), 0.02, true)}, // 1% error < 2% epsilon {"uint64/success", testAllEpsilon(uint64(100), uint64(101), 0.02, true)}, // 1% error < 2% epsilon {"float32/success", testAllEpsilon(float32(100.0), float32(101.0), 0.02, true)}, // 1% error < 2% epsilon {"float64/success", testAllEpsilon(100.0, 101.0, 0.02, true)}, // 1% error < 2% epsilon // Basic failure cases (3 cases) {"int/failure", testAllEpsilon(int(100), int(110), 0.05, false)}, // 10% error > 5% epsilon {"uint/failure", testAllEpsilon(uint(100), uint(110), 0.05, false)}, // 10% error > 5% epsilon {"float64/failure", testAllEpsilon(100.0, 110.0, 0.05, false)}, // 10% error > 5% epsilon // Exact match (3 cases) {"int/exact", testAllEpsilon(int(100), int(100), 0.0, true)}, // Exact match {"uint/exact", testAllEpsilon(uint(100), uint(100), 0.0, true)}, // Exact match {"float64/exact", testAllEpsilon(100.0, 100.0, 0.0, true)}, // Exact match // Zero expected value - uses absolute error (8 cases) {"int/both-zero", testAllEpsilon(int(0), int(0), 0.01, true)}, // Both zero {"uint/both-zero", testAllEpsilon(uint(0), uint(0), 0.01, true)}, // Both zero {"float64/both-zero", testAllEpsilon(0.0, 0.0, 0.01, true)}, // Both zero {"float64/zero-expected-within", testAllEpsilon(0.0, 0.009, 0.01, true)}, // |0.009| <= 0.01 {"float64/zero-expected-at-boundary", testAllEpsilon(0.0, 0.01, 0.01, true)}, // |0.01| <= 0.01 {"float64/zero-expected-exceed", testAllEpsilon(0.0, 0.011, 0.01, false)}, // |0.011| > 0.01 {"float64/zero-expected-large", testAllEpsilon(0.0, 100.0, 0.01, false)}, // |100| > 0.01 {"int/zero-expected-negative", testAllEpsilon(int(0), int(-5), 10.0, true)}, // |-5| <= 10 (absolute) // Near-zero values (2 cases) {"float64/near-zero-success", testAllEpsilon(0.001, 0.00101, 0.02, true)}, // 1% error < 2% {"float64/near-zero-failure", testAllEpsilon(0.001, 0.00110, 0.05, false)}, // 10% error > 5% // Negative numbers (3 cases) {"int/negative", testAllEpsilon(int(-100), int(-101), 0.02, true)}, // 1% error < 2% {"int/negative-fail", testAllEpsilon(int(-100), int(-110), 0.05, false)}, // 10% error > 5% {"float64/negative", testAllEpsilon(-100.0, -101.0, 0.02, true)}, // 1% error < 2% // Mixed positive/negative (3 cases) {"int/mixed-small", testAllEpsilon(int(100), int(-100), 2.1, true)}, // 200% error <= 210% epsilon {"int/mixed-fail", testAllEpsilon(int(100), int(-100), 1.9, false)}, // 200% error > 190% epsilon {"float64/mixed", testAllEpsilon(100.0, -100.0, 2.1, true)}, // 200% error <= 210% epsilon // Float32 NaN cases (3 cases) {"float32/both-nan", testAllEpsilon(float32(math.NaN()), float32(math.NaN()), 0.01, true)}, {"float32/expected-nan", testAllEpsilon(float32(math.NaN()), float32(42.0), 0.01, false)}, {"float32/actual-nan", testAllEpsilon(float32(42.0), float32(math.NaN()), 0.01, false)}, // Float64 NaN cases (3 cases) {"float64/both-nan", testAllEpsilon(math.NaN(), math.NaN(), 0.01, true)}, {"float64/expected-nan", testAllEpsilon(math.NaN(), 42.0, 0.01, false)}, {"float64/actual-nan", testAllEpsilon(42.0, math.NaN(), 0.01, false)}, // Float32 +Inf cases (4 cases) {"float32/both-plus-inf", testAllEpsilon(float32(math.Inf(1)), float32(math.Inf(1)), 0.01, true)}, {"float32/expected-plus-inf-actual-minus-inf", testAllEpsilon(float32(math.Inf(1)), float32(math.Inf(-1)), 0.01, false)}, {"float32/expected-plus-inf-actual-finite", testAllEpsilon(float32(math.Inf(1)), float32(100.0), 0.01, false)}, {"float32/expected-finite-actual-plus-inf", testAllEpsilon(float32(100.0), float32(math.Inf(1)), 0.01, false)}, // Float64 +Inf cases (4 cases) {"float64/both-plus-inf", testAllEpsilon(math.Inf(1), math.Inf(1), 0.01, true)}, {"float64/expected-plus-inf-actual-minus-inf", testAllEpsilon(math.Inf(1), math.Inf(-1), 0.01, false)}, {"float64/expected-plus-inf-actual-finite", testAllEpsilon(math.Inf(1), 100.0, 0.01, false)}, {"float64/expected-finite-actual-plus-inf", testAllEpsilon(100.0, math.Inf(1), 0.01, false)}, // Float32 -Inf cases (4 cases) {"float32/both-minus-inf", testAllEpsilon(float32(math.Inf(-1)), float32(math.Inf(-1)), 0.01, true)}, {"float32/expected-minus-inf-actual-plus-inf", testAllEpsilon(float32(math.Inf(-1)), float32(math.Inf(1)), 0.01, false)}, {"float32/expected-minus-inf-actual-finite", testAllEpsilon(float32(math.Inf(-1)), float32(100.0), 0.01, false)}, {"float32/expected-finite-actual-minus-inf", testAllEpsilon(float32(100.0), float32(math.Inf(-1)), 0.01, false)}, // Float64 -Inf cases (4 cases) {"float64/both-minus-inf", testAllEpsilon(math.Inf(-1), math.Inf(-1), 0.01, true)}, {"float64/expected-minus-inf-actual-plus-inf", testAllEpsilon(math.Inf(-1), math.Inf(1), 0.01, false)}, {"float64/expected-minus-inf-actual-finite", testAllEpsilon(math.Inf(-1), 100.0, 0.01, false)}, {"float64/expected-finite-actual-minus-inf", testAllEpsilon(100.0, math.Inf(-1), 0.01, false)}, // Epsilon validation (6 cases) {"float64/epsilon-negative", testAllEpsilon(100.0, 100.0, -0.01, false)}, // Negative epsilon {"float64/epsilon-nan", testAllEpsilon(100.0, 100.0, math.NaN(), false)}, // NaN epsilon {"float32/epsilon-nan", testAllEpsilon(float32(100.0), float32(100.0), math.NaN(), false)}, // NaN epsilon {"float64/epsilon-plus-inf", testAllEpsilon(100.0, 100.0, math.Inf(1), false)}, // +Inf epsilon {"float64/epsilon-minus-inf", testAllEpsilon(100.0, 100.0, math.Inf(-1), false)}, // -Inf epsilon {"float32/epsilon-plus-inf", testAllEpsilon(float32(100.0), float32(100.0), math.Inf(1), false)}, // +Inf epsilon // Precision testing (4 cases) {"float64/small-epsilon-pass", testAllEpsilon(1.0, 1.000001, 0.00001, true)}, // Very small error {"float64/small-epsilon-fail", testAllEpsilon(1.0, 1.000011, 0.00001, false)}, // Exceeds small epsilon {"float32/small-epsilon-pass", testAllEpsilon(float32(1.0), float32(1.000001), 0.00001, true)}, // Very small error {"float32/small-epsilon-fail", testAllEpsilon(float32(1.0), float32(1.000011), 0.00001, false)}, // Exceeds small epsilon // Large values (3 cases) {"int64/large-values", testAllEpsilon(int64(1000000000), int64(1010000000), 0.02, true)}, // 1% error < 2% {"uint64/large-values", testAllEpsilon(uint64(1000000000), uint64(1010000000), 0.02, true)}, // 1% error < 2% {"float64/large-values", testAllEpsilon(1e15, 1.01e15, 0.02, true)}, // 1% error < 2% // Edge cases (4 cases) {"int/zero-epsilon-same", testAllEpsilon(int(100), int(100), 0.0, true)}, // Zero epsilon, exact match {"float64/zero-epsilon-different", testAllEpsilon(100.0, 100.1, 0.0, false)}, // Zero epsilon, different {"int/large-epsilon", testAllEpsilon(int(100), int(200), 1.5, true)}, // 100% error < 150% epsilon {"float64/boundary", testAllEpsilon(100.0, 102.0, 0.02, true)}, // Exactly 2% error with 2% epsilon // time.Duration (covers toFloat time.Duration case) {"duration/success", testAllEpsilon(100*time.Millisecond, 101*time.Millisecond, 0.02, true)}, // 1% error < 2% epsilon {"duration/failure", testAllEpsilon(100*time.Millisecond, 110*time.Millisecond, 0.05, false)}, // 10% error > 5% epsilon // custom float type (covers toFloat reflect conversion) {"custom-float/success", testAllEpsilon(customFloat(100.0), customFloat(101.0), 0.02, true)}, // 1% error < 2% epsilon {"custom-float/failure", testAllEpsilon(customFloat(100.0), customFloat(110.0), 0.05, false)}, // 10% error > 5% epsilon }) } // testAllEpsilon tests both InEpsilon and InEpsilonT with the same input. func testAllEpsilon[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() if shouldPass { t.Run("should pass", func(t *testing.T) { t.Run("with InEpsilon", testEpsilon(expected, actual, epsilon, true)) t.Run("with InEpsilonT", testEpsilonT(expected, actual, epsilon, true)) }) } else { t.Run("should fail", func(t *testing.T) { t.Run("with InEpsilon", testEpsilon(expected, actual, epsilon, false)) t.Run("with InEpsilonT", testEpsilonT(expected, actual, epsilon, false)) }) } } } func testEpsilon[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilon(mock, expected, actual, epsilon) shouldPassOrFail(t, mock, result, shouldPass) } } func testEpsilonT[Number Measurable](expected, actual Number, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonT(mock, expected, actual, epsilon) shouldPassOrFail(t, mock, result, shouldPass) } } // ======================================= // Test NumberInEpsilonSymmetric variants // ======================================= func inEpsilonSymmetricCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Asymmetric-vs-symmetric — these are the cases that justify InEpsilonSymmetric. // With InEpsilon the threshold is epsilon * |expected|; with InEpsilonSymmetric // it is epsilon * max(|x|, |y|). When |y| > |x|, InEpsilonSymmetric is more lenient. // // (10, 14, 0.3): InEpsilon fails (4 > 3), InEpsilonSymmetric passes (4 <= 4.2) // (0.1, 0.14, 0.3): same ratio, scaled down {"asymmetric/larger-second-arg", testAllSymmetric(10.0, 14.0, 0.3, true)}, {"asymmetric/larger-second-arg-small", testAllSymmetric(0.1, 0.14, 0.3, true)}, // And the symmetric counterpart: swapping arguments doesn't change the result. {"asymmetric/larger-first-arg", testAllSymmetric(14.0, 10.0, 0.3, true)}, {"asymmetric/larger-first-arg-small", testAllSymmetric(0.14, 0.1, 0.3, true)}, // Just below the boundary — both must still fail. {"asymmetric/just-below-boundary", testAllSymmetric(10.0, 14.0, 0.28, false)}, // Simple input cases {"simple/1pct-error-within-2pct-epsilon", testAllSymmetric(100.0, 101.0, 0.02, true)}, {"simple/5pct-error-exceeds-2pct-epsilon", testAllSymmetric(100.0, 105.0, 0.02, false)}, {"simple/exact-match-zero-epsilon", testAllSymmetric(100.0, 100.0, 0.0, true)}, // Edge cases - NaN {"edge/nan-for-actual", testAllSymmetric(42.0, math.NaN(), 0.01, false)}, {"edge/nan-for-expected", testAllSymmetric(math.NaN(), 42.0, 0.01, false)}, {"edge/nan-for-both", testAllSymmetric(math.NaN(), math.NaN(), 0.01, true)}, // Edge cases - both zero (passes whatever epsilon) {"edge/both-zero", testAllSymmetric(0.0, 0.0, 0.01, true)}, {"edge/both-zero-zero-epsilon", testAllSymmetric(0.0, 0.0, 0.0, true)}, // One side is zero: amplitude == |non-zero side|, so 1 > epsilon must hold. {"edge/zero-vs-nonzero-fails", testAllSymmetric(0.0, 0.5, 0.5, false)}, // 0.5 > 0.5*0.5 = 0.25 {"edge/zero-vs-nonzero-large-epsilon", testAllSymmetric(0.0, 0.5, 1.0, true)}, // All numeric types - basic success cases {"int/success", testAllSymmetric(int(100), int(101), 0.02, true)}, {"int8/success", testAllSymmetric(int8(100), int8(101), 0.02, true)}, {"int16/success", testAllSymmetric(int16(100), int16(101), 0.02, true)}, {"int32/success", testAllSymmetric(int32(100), int32(101), 0.02, true)}, {"int64/success", testAllSymmetric(int64(100), int64(101), 0.02, true)}, {"uint/success", testAllSymmetric(uint(100), uint(101), 0.02, true)}, {"uint8/success", testAllSymmetric(uint8(100), uint8(101), 0.02, true)}, {"uint16/success", testAllSymmetric(uint16(100), uint16(101), 0.02, true)}, {"uint32/success", testAllSymmetric(uint32(100), uint32(101), 0.02, true)}, {"uint64/success", testAllSymmetric(uint64(100), uint64(101), 0.02, true)}, {"float32/success", testAllSymmetric(float32(100.0), float32(101.0), 0.02, true)}, {"float64/success", testAllSymmetric(100.0, 101.0, 0.02, true)}, // Basic failure cases {"int/failure", testAllSymmetric(int(100), int(110), 0.05, false)}, {"uint/failure", testAllSymmetric(uint(100), uint(110), 0.05, false)}, {"float64/failure", testAllSymmetric(100.0, 110.0, 0.05, false)}, // Negative numbers {"int/negative", testAllSymmetric(int(-100), int(-101), 0.02, true)}, {"int/negative-fail", testAllSymmetric(int(-100), int(-110), 0.05, false)}, {"float64/negative", testAllSymmetric(-100.0, -101.0, 0.02, true)}, // Mixed positive/negative — amplitude is max(|x|, |y|), so for (100, -100) // delta = 200 and threshold = 100 * epsilon. Boundary is epsilon == 2.0. {"mixed/at-boundary", testAllSymmetric(100.0, -100.0, 2.0, true)}, {"mixed/just-below", testAllSymmetric(100.0, -100.0, 1.99, false)}, // Float32 NaN cases {"float32/both-nan", testAllSymmetric(float32(math.NaN()), float32(math.NaN()), 0.01, true)}, {"float32/expected-nan", testAllSymmetric(float32(math.NaN()), float32(42.0), 0.01, false)}, {"float32/actual-nan", testAllSymmetric(float32(42.0), float32(math.NaN()), 0.01, false)}, // Float32 +Inf cases {"float32/both-plus-inf", testAllSymmetric(float32(math.Inf(1)), float32(math.Inf(1)), 0.01, true)}, {"float32/plus-inf-vs-minus-inf", testAllSymmetric(float32(math.Inf(1)), float32(math.Inf(-1)), 0.01, false)}, {"float32/plus-inf-vs-finite", testAllSymmetric(float32(math.Inf(1)), float32(100.0), 0.01, false)}, {"float32/finite-vs-plus-inf", testAllSymmetric(float32(100.0), float32(math.Inf(1)), 0.01, false)}, // Float64 +Inf cases {"float64/both-plus-inf", testAllSymmetric(math.Inf(1), math.Inf(1), 0.01, true)}, {"float64/plus-inf-vs-minus-inf", testAllSymmetric(math.Inf(1), math.Inf(-1), 0.01, false)}, {"float64/plus-inf-vs-finite", testAllSymmetric(math.Inf(1), 100.0, 0.01, false)}, // Float64 -Inf cases {"float64/both-minus-inf", testAllSymmetric(math.Inf(-1), math.Inf(-1), 0.01, true)}, {"float64/minus-inf-vs-finite", testAllSymmetric(math.Inf(-1), 100.0, 0.01, false)}, // Epsilon validation {"epsilon-negative", testAllSymmetric(100.0, 100.0, -0.01, false)}, {"epsilon-nan", testAllSymmetric(100.0, 100.0, math.NaN(), false)}, {"epsilon-plus-inf", testAllSymmetric(100.0, 100.0, math.Inf(1), false)}, {"epsilon-minus-inf", testAllSymmetric(100.0, 100.0, math.Inf(-1), false)}, // Large values {"int64/large-values", testAllSymmetric(int64(1000000000), int64(1010000000), 0.02, true)}, {"uint64/large-values", testAllSymmetric(uint64(1000000000), uint64(1010000000), 0.02, true)}, {"float64/large-values", testAllSymmetric(1e15, 1.01e15, 0.02, true)}, // time.Duration (covers toFloat time.Duration case in InEpsilonSymmetric) {"duration/success", testAllSymmetric(100*time.Millisecond, 101*time.Millisecond, 0.02, true)}, {"duration/failure", testAllSymmetric(100*time.Millisecond, 110*time.Millisecond, 0.05, false)}, // custom float type (covers toFloat reflect conversion in InEpsilonSymmetric) {"custom-float/success", testAllSymmetric(customFloat(100.0), customFloat(101.0), 0.02, true)}, {"custom-float/failure", testAllSymmetric(customFloat(100.0), customFloat(110.0), 0.05, false)}, }) } // testAllSymmetric tests both InEpsilonSymmetric and InEpsilonSymmetricT with the same input. func testAllSymmetric[Number Measurable](x, y Number, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() if shouldPass { t.Run("should pass", func(t *testing.T) { t.Run("with InEpsilonSymmetric", testSymmetric(x, y, epsilon, true)) t.Run("with InEpsilonSymmetricT", testSymmetricT(x, y, epsilon, true)) }) } else { t.Run("should fail", func(t *testing.T) { t.Run("with InEpsilonSymmetric", testSymmetric(x, y, epsilon, false)) t.Run("with InEpsilonSymmetricT", testSymmetricT(x, y, epsilon, false)) }) } } } func testSymmetric[Number Measurable](x, y Number, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetric(mock, x, y, epsilon) shouldPassOrFail(t, mock, result, shouldPass) } } func testSymmetricT[Number Measurable](x, y Number, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSymmetricT(mock, x, y, epsilon) shouldPassOrFail(t, mock, result, shouldPass) } } // Helper functions and test data for InDeltaSlice func deltaSliceCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Success cases - slices are element-wise within delta { "within-delta-with-nan", testDeltaSlice( []float64{1.001, math.NaN(), 0.999}, []float64{1, math.NaN(), 1}, 0.1, true, ), }, { "within-delta-1.0", testDeltaSlice( []float64{1, math.NaN(), 2}, []float64{0, math.NaN(), 3}, 1, true, ), }, // Failure cases - slices are not element-wise within delta { "not-within-delta", testDeltaSlice( []float64{1, math.NaN(), 2}, []float64{0, math.NaN(), 3}, 0.1, false, ), }, // Edge cases - invalid inputs { "invalid-non-slice-inputs", testDeltaSlice("", nil, 1, false), }, }) } func testDeltaSlice(expected, actual any, delta float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InDeltaSlice(mock, expected, actual, delta) shouldPassOrFail(t, mock, result, shouldPass) } } // Helper functions and test data for InEpsilonSlice func epsilonSliceCases() iter.Seq[genericTestCase] { return slices.Values([]genericTestCase{ // Success cases - slices are element-wise within epsilon { "within-epsilon-with-nan", testEpsilonSlice( []float64{2.2, math.NaN(), 2.0}, []float64{2.1, math.NaN(), 2.1}, 0.06, true, ), }, // Failure cases - slices are not element-wise within epsilon { "not-within-epsilon", testEpsilonSlice( []float64{2.2, 2.0}, []float64{2.1, 2.1}, 0.04, false, ), }, // Edge cases - invalid inputs { "invalid-expected-nil", testEpsilonSlice("", nil, 1, false), }, { "invalid-actual-nil", testEpsilonSlice(nil, "", 1, false), }, { "invalid-expected-not-slice", testEpsilonSlice(1, []int{}, 1, false), }, { "invalid-actual-not-slice", testEpsilonSlice([]int{}, 1, 1, false), }, { "invalid-expected-non-numeric-slice", testEpsilonSlice([]string{}, []int{}, 1, false), }, { "invalid-actual-non-numeric-slice", testEpsilonSlice([]int{}, []string{}, 1, false), }, }) } func testEpsilonSlice(expected, actual any, epsilon float64, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := InEpsilonSlice(mock, expected, actual, epsilon) shouldPassOrFail(t, mock, result, shouldPass) } } // ======================================= // Test NumberErrorMessages // ======================================= func numberFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "InDeltaT/shows-difference", assertion: func(t T) bool { return InDeltaT(t, 10, 1, 5) }, wantContains: []string{"difference was 9"}, }, { name: "InEpsilonT/relative-error", assertion: func(t T) bool { return InEpsilonT(t, 100.0, 110.0, 0.05) }, wantContains: []string{"Relative error is too high", "0.1"}, }, { name: "InEpsilonT/absolute-error-for-zero", assertion: func(t T) bool { return InEpsilonT(t, 0.0, 0.5, 0.1) }, wantContains: []string{"Expected value is zero, using absolute error comparison", "0.5"}, }, { name: "InDelta/non-numeric-types", assertion: func(t T) bool { return InDelta(t, "", 0.5, 0.1) }, wantContains: []string{"Parameters must be numerical"}, }, { name: "InEpsilon/non-numeric-types", assertion: func(t T) bool { return InEpsilon(t, "", 0.5, 0.1) }, wantContains: []string{"Parameters must be numerical"}, }, { name: "InEpsilonSymmetric/non-numeric-types", assertion: func(t T) bool { return InEpsilonSymmetric(t, "", 0.5, 0.1) }, wantContains: []string{"Parameters must be numerical"}, }, { name: "InEpsilonSymmetricT/relative-error", assertion: func(t T) bool { return InEpsilonSymmetricT(t, 100.0, 110.0, 0.05) }, wantContains: []string{"Symmetric relative error is too high"}, }, }) } go-openapi-testify-c10ca71/internal/assertions/object.go000066400000000000000000000063531520301377500234530ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "bytes" "reflect" ) // ObjectsAreEqual determines if two objects are considered equal. // // This function does no assertion of any kind. func ObjectsAreEqual(expected, actual any) bool { if expected == nil || actual == nil { return expected == actual } exp, ok := expected.([]byte) if !ok { return reflect.DeepEqual(expected, actual) } act, ok := actual.([]byte) if !ok { return false } if exp == nil || act == nil { return exp == nil && act == nil } return bytes.Equal(exp, act) } // ObjectsAreEqualValues gets whether two objects are equal, or if their // values are equal. func ObjectsAreEqualValues(expected, actual any) bool { if ObjectsAreEqual(expected, actual) { return true } expectedValue := reflect.ValueOf(expected) actualValue := reflect.ValueOf(actual) if !expectedValue.IsValid() || !actualValue.IsValid() { return false } expectedType := expectedValue.Type() actualType := actualValue.Type() if !expectedType.ConvertibleTo(actualType) { return false } // Attempt conversion of expected to actual type. // This handles more cases than just the ConvertibleTo check above. if !expectedValue.CanConvert(actualType) { // Types are not convertible, so they cannot be equal // This prevents panics when calling [reflect.Value.Convert] return false } expectedConverted := expectedValue.Convert(actualType) if !expectedConverted.CanInterface() { // Unreachable with current Go reflection: values from reflect.ValueOf() // are always interfaceable, and Convert() preserves that property. // CanInterface() is only false for values obtained via unexported struct fields. panic("reflect: converted value is not interfaceable") } if !isNumericType(expectedType) || !isNumericType(actualType) { // Attempt comparison after type conversion. return reflect.DeepEqual( expectedConverted.Interface(), actual, ) } // If BOTH values are numeric, there are chances of false positives due // to overflow or underflow. So, we need to make sure to always convert // the smaller type to a larger type before comparing. if expectedType.Size() >= actualType.Size() { if !actualValue.CanConvert(expectedType) { // Unreachable with current Go reflection: all numeric kinds (Int through Complex128) // are convertible to all other numeric kinds. panic("reflect: numeric value is not convertible to numeric type") } actualConverted := actualValue.Convert(expectedType) if !actualConverted.CanInterface() { // Unreachable with current Go reflection: values from reflect.ValueOf() // are always interfaceable, and Convert() preserves that property. // CanInterface() is only false for values obtained via unexported struct fields. panic("reflect: converted value is not interfaceable") } return actualConverted.Interface() == expected } return expectedConverted.Interface() == actual } // isNumericType returns true if the type is one of: // int, int8, int16, int32, int64, uint, uint8, uint16, uint32, uint64, // float32, float64, complex64, complex128. func isNumericType(t reflect.Type) bool { return t.Kind() >= reflect.Int && t.Kind() <= reflect.Complex128 } go-openapi-testify-c10ca71/internal/assertions/object_test.go000066400000000000000000000115001520301377500245000ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "iter" "math" "slices" "testing" "time" ) func TestObjectsAreEqual(t *testing.T) { t.Parallel() for c := range objectEqualCases() { t.Run(fmt.Sprintf("ObjectsAreEqual(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { res := ObjectsAreEqual(c.expected, c.actual) if res != c.result { t.Errorf("ObjectsAreEqual(%#v, %#v) should return %#v", c.expected, c.actual, c.result) } }) } } func TestObjectsAreEqualValues(t *testing.T) { t.Parallel() for c := range objectEqualValuesCases() { t.Run(fmt.Sprintf("ObjectsAreEqualValues(%#v, %#v)", c.expected, c.actual), func(t *testing.T) { res := ObjectsAreEqualValues(c.expected, c.actual) if res != c.result { t.Errorf("ObjectsAreEqualValues(%#v, %#v) should return %#v", c.expected, c.actual, c.result) } }) } } func TestObjectsCopyExportedFields(t *testing.T) { t.Parallel() for c := range objectCopyExportedFieldsCases() { t.Run("", func(t *testing.T) { output := copyExportedFields(c.input) if !ObjectsAreEqualValues(c.expected, output) { t.Errorf("%#v, %#v should be equal", c.expected, output) } }) } } type Nested struct { Exported any notExported any } type S struct { Exported1 any Exported2 Nested notExported1 any notExported2 Nested } type S3 struct { Exported1 *Nested Exported2 *Nested } type S4 struct { Exported1 []*Nested } type S5 struct { Exported Nested } type S6 struct { Exported string unexported string } type objectEqualCase struct { expected any actual any result bool } func objectEqualCases() iter.Seq[objectEqualCase] { return slices.Values([]objectEqualCase{ // cases that are expected to be equal {"Hello World", "Hello World", true}, {123, 123, true}, {123.5, 123.5, true}, {[]byte("Hello World"), []byte("Hello World"), true}, {nil, nil, true}, // byte slice: expected is []byte but actual is not {[]byte("hello"), "hello", false}, // byte slice: nil cases {[]byte(nil), []byte(nil), true}, {[]byte(nil), []byte("hello"), false}, // cases that are expected not to be equal {map[int]int{5: 10}, map[int]int{10: 20}, false}, {'x', "x", false}, {"x", 'x', false}, {0, 0.1, false}, {0.1, 0, false}, {time.Now, time.Now, false}, {func() {}, func() {}, false}, {uint32(10), int32(10), false}, }) } func objectEqualValuesCases() iter.Seq[objectEqualCase] { now := time.Now() return slices.Values([]objectEqualCase{ {uint32(10), int32(10), true}, {0, nil, false}, {nil, 0, false}, // should not be time zone independent {now, now.In(time.Local), false}, //nolint:gosmopolitan // ok in this context: this is precisely the goal of this test {int(270), int8(14), false}, // should handle overflow/underflow {int8(14), int(270), false}, {[]int{270, 270}, []int8{14, 14}, false}, {complex128(1e+100 + 1e+100i), complex64(complex(math.Inf(0), math.Inf(0))), false}, {complex64(complex(math.Inf(0), math.Inf(0))), complex128(1e+100 + 1e+100i), false}, {complex128(1e+100 + 1e+100i), 270, false}, {270, complex128(1e+100 + 1e+100i), false}, {complex128(1e+100 + 1e+100i), 3.14, false}, {3.14, complex128(1e+100 + 1e+100i), false}, {complex128(1e+10 + 1e+10i), complex64(1e+10 + 1e+10i), true}, {complex64(1e+10 + 1e+10i), complex128(1e+10 + 1e+10i), true}, {[]int{1, 2}, (*[3]int)(nil), false}, // panics should be caught and treated as inequality (https://github.com/stretchr/testify/issues/1699) }) } type objectCopyFieldsCase struct { input any expected any } func objectCopyExportedFieldsCases() iter.Seq[objectCopyFieldsCase] { intValue := 1 return slices.Values([]objectCopyFieldsCase{ { input: Nested{"a", "b"}, expected: Nested{"a", nil}, }, { input: Nested{&intValue, 2}, expected: Nested{&intValue, nil}, }, { input: Nested{nil, 3}, expected: Nested{nil, nil}, }, { input: S{1, Nested{2, 3}, 4, Nested{5, 6}}, expected: S{1, Nested{2, nil}, nil, Nested{}}, }, { input: S3{}, expected: S3{}, }, { input: S3{&Nested{1, 2}, &Nested{3, 4}}, expected: S3{&Nested{1, nil}, &Nested{3, nil}}, }, { input: S3{Exported1: &Nested{"a", "b"}}, expected: S3{Exported1: &Nested{"a", nil}}, }, { input: S4{[]*Nested{ nil, {1, 2}, }}, expected: S4{[]*Nested{ nil, {1, nil}, }}, }, { input: S4{ []*Nested{ {1, 2}, }, }, expected: S4{ []*Nested{ {1, nil}, }, }, }, { input: S4{[]*Nested{ {1, 2}, {3, 4}, }}, expected: S4{[]*Nested{ {1, nil}, {3, nil}, }}, }, { input: S5{Exported: Nested{"a", "b"}}, expected: S5{Exported: Nested{"a", nil}}, }, { input: S6{"a", "b"}, expected: S6{"a", ""}, }, }) } go-openapi-testify-c10ca71/internal/assertions/order.go000066400000000000000000000235711520301377500233210ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "reflect" "slices" ) // IsIncreasing asserts that the collection is strictly increasing. // // # Usage // // assertions.IsIncreasing(t, []int{1, 2, 3}) // assertions.IsIncreasing(t, []float{1, 2}) // assertions.IsIncreasing(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 2, 3} // failure: []int{1, 1, 2} func IsIncreasing(t T, collection any, msgAndArgs ...any) bool { // Domain: ordering // Opposite: IsNonIncreasing if h, ok := t.(H); ok { h.Helper() } values, ok, err := isStrictlyOrdered(collection, false) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if !ok { return Fail(t, fmt.Sprintf("\"%v\" is not less than \"%v\"", values...), msgAndArgs...) } return true } // IsIncreasingT asserts that a slice of [Ordered] is strictly increasing. // // # Usage // // assertions.IsIncreasingT(t, []int{1, 2, 3}) // assertions.IsIncreasingT(t, []float{1, 2}) // assertions.IsIncreasingT(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 2, 3} // failure: []int{1, 1, 2} func IsIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering // Opposite: IsNonIncreasingT if h, ok := t.(H); ok { h.Helper() } isIncreasing := slices.IsSortedFunc(collection, compareStrictOrdered) if !isIncreasing { return Fail(t, "should be increasing", msgAndArgs...) } return true } // SortedT asserts that the slice of [Ordered] is sorted (i.e. non-strictly increasing). // // Unlike [IsIncreasingT], it accepts elements to be equal. // // # Usage // // assertions.SortedT(t, []int{1, 2, 3}) // assertions.SortedT(t, []float{1, 2}) // assertions.SortedT(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 1, 3} // failure: []int{1, 4, 2} func SortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering // Opposite: NotSortedT if h, ok := t.(H); ok { h.Helper() } isSorted := slices.IsSortedFunc(collection, compareOrdered) if !isSorted { return Fail(t, "should be sorted", msgAndArgs...) } return true } // NotSortedT asserts that the slice of [Ordered] is NOT sorted (i.e. non-strictly increasing). // // Unlike [IsDecreasingT], it accepts slices that are neither increasing nor decreasing. // // # Usage // // assertions.NotSortedT(t, []int{3, 2, 3}) // assertions.NotSortedT(t, []float{2, 1}) // assertions.NotSortedT(t, []string{"b", "a"}) // // # Examples // // success: []int{3, 1, 3} // failure: []int{1, 4, 8} func NotSortedT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } isSorted := slices.IsSortedFunc(collection, compareOrdered) if isSorted { return Fail(t, "should not be sorted", msgAndArgs...) } return true } // IsNonIncreasing asserts that the collection is not strictly increasing. // // This is the logical negation of [IsIncreasing]: it succeeds whenever // [IsIncreasing] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [1, 3, 2]). // // It is NOT the same as "weakly decreasing" (a[i] >= a[i+1] for all i). // // # Usage // // assertions.IsNonIncreasing(t, []int{2, 1, 1}) // assertions.IsNonIncreasing(t, []float{2, 1}) // assertions.IsNonIncreasing(t, []string{"b", "a"}) // // # Examples // // success: []int{2, 1, 1} // failure: []int{1, 2, 3} func IsNonIncreasing(t T, collection any, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } _, ok, err := isStrictlyOrdered(collection, false) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if !ok { return true } return Fail(t, "should not be increasing", msgAndArgs...) } // IsNonIncreasingT asserts that a slice of [Ordered] is NOT strictly increasing. // // This is the logical negation of [IsIncreasingT]: it succeeds whenever // [IsIncreasingT] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [1, 3, 2]). // // It is NOT the same as "weakly decreasing" (a[i] >= a[i+1] for all i). // // # Usage // // assertions.IsNonIncreasingT(t, []int{2, 1, 1}) // assertions.IsNonIncreasingT(t, []float{2, 1}) // assertions.IsNonIncreasingT(t, []string{"b", "a"}) // // # Examples // // success: []int{2, 1, 1} // failure: []int{1, 2, 3} func IsNonIncreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } isIncreasing := slices.IsSortedFunc(collection, compareStrictOrdered) if isIncreasing { return Fail(t, "should not be increasing", msgAndArgs...) } return true } // IsDecreasing asserts that the collection is strictly decreasing. // // # Usage // // assertions.IsDecreasing(t, []int{2, 1, 0}) // assertions.IsDecreasing(t, []float{2, 1}) // assertions.IsDecreasing(t, []string{"b", "a"}) // // # Examples // // success: []int{3, 2, 1} // failure: []int{1, 2, 3} func IsDecreasing(t T, collection any, msgAndArgs ...any) bool { // Domain: ordering // Opposite: IsNonDecreasing if h, ok := t.(H); ok { h.Helper() } values, ok, err := isStrictlyOrdered(collection, true) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if !ok { values = append(values, msgAndArgs...) return Fail(t, fmt.Sprintf("\"%v\" is not greater than \"%v\"", values...), msgAndArgs...) } return true } // IsDecreasingT asserts that a slice of [Ordered] is strictly decreasing. // // # Usage // // assertions.IsDecreasingT(t, []int{2, 1, 0}) // assertions.IsDecreasingT(t, []float{2, 1}) // assertions.IsDecreasingT(t, []string{"b", "a"}) // // # Examples // // success: []int{3, 2, 1} // failure: []int{1, 2, 3} func IsDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering // Opposite: IsNonDecreasingT if h, ok := t.(H); ok { h.Helper() } isDecreasing := slices.IsSortedFunc(collection, reverseCompareStrictOrdered) if !isDecreasing { return Fail(t, "should be decreasing", msgAndArgs...) } return true } // IsNonDecreasing asserts that the collection is not strictly decreasing. // // This is the logical negation of [IsDecreasing]: it succeeds whenever // [IsDecreasing] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [3, 1, 2]). // // It is NOT the same as "weakly increasing" (a[i] <= a[i+1] for all i). // // # Usage // // assertions.IsNonDecreasing(t, []int{1, 1, 2}) // assertions.IsNonDecreasing(t, []float{1, 2}) // assertions.IsNonDecreasing(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 1, 2} // failure: []int{2, 1, 0} func IsNonDecreasing(t T, collection any, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } _, ok, err := isStrictlyOrdered(collection, true) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } if !ok { return true } return Fail(t, "should not be decreasing", msgAndArgs...) } // IsNonDecreasingT asserts that a slice of [Ordered] is NOT strictly decreasing. // // This is the logical negation of [IsDecreasingT]: it succeeds whenever // [IsDecreasingT] would fail, including for sequences that are neither // increasing nor decreasing (e.g. [3, 1, 2]). // // It is NOT the same as "weakly increasing" (a[i] <= a[i+1] for all i). // // # Usage // // assertions.IsNonDecreasingT(t, []int{1, 1, 2}) // assertions.IsNonDecreasingT(t, []float{1, 2}) // assertions.IsNonDecreasingT(t, []string{"a", "b"}) // // # Examples // // success: []int{1, 1, 2} // failure: []int{2, 1, 0} func IsNonDecreasingT[OrderedSlice ~[]E, E Ordered](t T, collection OrderedSlice, msgAndArgs ...any) bool { // Domain: ordering if h, ok := t.(H); ok { h.Helper() } isDecreasing := slices.IsSortedFunc(collection, reverseCompareStrictOrdered) if isDecreasing { return Fail(t, "should not be decreasing", msgAndArgs...) } return true } // isStrictlyOrdered checks that collection contains orderable elements, which are strictly ordered. // // It returns an error if the object can't be ordered. // When not strictly ordered, it returns the first 2 offending values found. func isStrictlyOrdered(object any, reverseOrder bool) ([]any, bool, error) { objKind := reflect.TypeOf(object).Kind() if objKind != reflect.Slice && objKind != reflect.Array { return nil, false, fmt.Errorf("object %T is not an ordered collection", object) } objValue := reflect.ValueOf(object) objLen := objValue.Len() if objLen <= 1 { return nil, true, nil } value := objValue.Index(0) if !value.CanInterface() { // this should not be possible with current reflect, since values are retrieved from an array or slice, not a struct panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", value)) } valueInterface := value.Interface() firstValueKind := value.Kind() for i := 1; i < objLen; i++ { prevValue := value prevValueInterface := valueInterface value = objValue.Index(i) if !value.CanInterface() { panic(fmt.Errorf("internal error: can't resolve Interface() for value %v", value)) } valueInterface = value.Interface() compareResult, isComparable := compare(prevValueInterface, valueInterface, firstValueKind) if !isComparable { return nil, false, fmt.Errorf(`cannot compare type "%T" and "%T"`, value, prevValue) } if (!reverseOrder && compareResult != -1) || (reverseOrder && compareResult != 1) { return []any{prevValueInterface, valueInterface}, false, nil } } return nil, true, nil } func compareStrictOrdered[E Ordered](a, b E) int { v := compareOrderedWithAny[E](a, b) if v == 0 { return -1 } return v } func compareOrdered[E Ordered](a, b E) int { v := compareOrderedWithAny[E](a, b) return v } func reverseCompareStrictOrdered[E Ordered](a, b E) int { v := compareOrderedWithAny[E](b, a) if v == 0 { return -1 } return v } go-openapi-testify-c10ca71/internal/assertions/order_impl_test.go000066400000000000000000000023031520301377500253670ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import "testing" func TestOrderUnexportedImplementationDetails(t *testing.T) { t.Parallel() t.Run("compareStrictOrdered", testCompareStrictOrdered) t.Run("reverseCompareStrictOrdered", testReverseCompareStrictOrdered) } func testCompareStrictOrdered(t *testing.T) { t.Parallel() // Expectations: // a < b : -1 // a == b : -1 (attention standard cmp.Compare yield 0) // a > b : 1 res := compareStrictOrdered(1, 0) if res != 1 { t.Fatalf("expected 1 > 0") } res = compareStrictOrdered(0, 0) if res != -1 { t.Fatalf("expected !(0 > 0)") } res = compareStrictOrdered(0, 1) if res != -1 { t.Fatalf("expected !(0 > 1)") } } func testReverseCompareStrictOrdered(t *testing.T) { t.Parallel() // Expectations: // a < b : 1 // a == b : -1 (attention standard cmp.Compare yield 0) // a > b : -1 res := reverseCompareStrictOrdered(1, 0) if res != -1 { t.Fatalf("expected !(1 < 0)") } res = reverseCompareStrictOrdered(0, 0) if res != -1 { t.Fatalf("expected !(0 < 0)") } res = reverseCompareStrictOrdered(0, 1) if res != 1 { t.Fatalf("expected 0 < 1)") } } go-openapi-testify-c10ca71/internal/assertions/order_test.go000066400000000000000000000426341520301377500243610ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "iter" "slices" "testing" "time" ) // Test functions for reflection-based and generic assertions // Unified test for all order assertions, with different input types // // NOTE: Unified testing pattern for ordering assertions. // Test cases are defined with their intrinsic ordering property (kind). // Expected pass/fail is determined from the kind + assertion semantics. // // Unlike the pattern used in string_test.go, we can't easily use a conversion for slices. // Therefore, we have to resort to a type switch with a fixed known list of tested slice types. // // The matrix of expected assertion semantics is defined by [expectedStatusForAssertion]. func TestOrder(t *testing.T) { t.Parallel() for tc := range unifiedOrderCases() { t.Run(tc.name, testAllOrdersWithTypes(tc)) } } func TestOrderOppositePairs(t *testing.T) { t.Parallel() pairs := [][2]orderAssertionKind{ {increasingKind, notIncreasingKind}, {decreasingKind, notDecreasingKind}, {sortedKind, notSortedKind}, } for tc := range unifiedOrderCases() { if tc.kind == errorCase { continue } for _, pair := range pairs { a := expectedStatusForAssertion(pair[0], tc.kind) b := expectedStatusForAssertion(pair[1], tc.kind) if a == b { t.Errorf("%s: opposite pair (%d, %d) both returned %v for kind %d", tc.name, pair[0], pair[1], a, tc.kind) } } } } func TestOrderErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, orderFailCases()) } // ======================================= // TestOrder // ======================================= func testAllOrdersWithTypes(tc orderTestCase) func(*testing.T) { return func(t *testing.T) { t.Run("with IsIncreasing", func(t *testing.T) { t.Parallel() shouldPass := expectedStatusForAssertion(increasingKind, tc.kind) t.Run("with reflection", testOrderReflectBased(IsIncreasing, tc.collection, shouldPass)) if !tc.reflectionOnly { t.Run("with generic", testOrderGeneric(increasingKind, tc.collection, shouldPass)) } }) t.Run("with IsNonIncreasing", func(t *testing.T) { t.Parallel() shouldPass := expectedStatusForAssertion(notIncreasingKind, tc.kind) t.Run("with reflection", testOrderReflectBased(IsNonIncreasing, tc.collection, shouldPass)) if !tc.reflectionOnly { t.Run("with generic", testOrderGeneric(notIncreasingKind, tc.collection, shouldPass)) } }) t.Run("with IsDecreasing", func(t *testing.T) { t.Parallel() shouldPass := expectedStatusForAssertion(decreasingKind, tc.kind) t.Run("with reflection", testOrderReflectBased(IsDecreasing, tc.collection, shouldPass)) if !tc.reflectionOnly { t.Run("with generic", testOrderGeneric(decreasingKind, tc.collection, shouldPass)) } }) t.Run("with IsNonDecreasing", func(t *testing.T) { t.Parallel() shouldPass := expectedStatusForAssertion(notDecreasingKind, tc.kind) t.Run("with reflection", testOrderReflectBased(IsNonDecreasing, tc.collection, shouldPass)) if !tc.reflectionOnly { t.Run("with generic", testOrderGeneric(notDecreasingKind, tc.collection, shouldPass)) } }) if tc.reflectionOnly { return } t.Run("with SortedT", func(t *testing.T) { t.Parallel() shouldPass := expectedStatusForAssertion(sortedKind, tc.kind) t.Run("with generic only", testOrderGeneric(sortedKind, tc.collection, shouldPass)) }) t.Run("with NotSortedT", func(t *testing.T) { t.Parallel() shouldPass := expectedStatusForAssertion(notSortedKind, tc.kind) t.Run("with generic only", testOrderGeneric(notSortedKind, tc.collection, shouldPass)) }) } } // collectionKind represents the ordering property of a collection. type collectionKind int const ( allEqual collectionKind = iota // all values equal (sorted but not strictly) strictlyAsc // strictly ascending (each < next) strictlyDesc // strictly descending (each > next) nonStrictlyAsc // non-strictly ascending (each <= next, some equal) nonStrictlyDesc // non-strictly descending (each >= next, some equal) unsorted // no ordering passAll // empty or single element collection errorCase // should fail with error (not panic) ) type orderAssertionKind int const ( increasingKind orderAssertionKind = iota notIncreasingKind decreasingKind notDecreasingKind sortedKind notSortedKind ) // orderTestCase represents a test case that can be used for all ordering assertions. type orderTestCase struct { name string collection any kind collectionKind reflectionOnly bool } // Unified test cases for all ordering assertions. type ( myFloat float64 myCollection []myFloat ) func unifiedOrderCases() iter.Seq[orderTestCase] { t0 := time.Now() t1 := t0.Add(time.Second) t2 := t1.Add(time.Second) // Test types for reflection-only edge cases. type nonComparableStruct struct { Value int Data []int // slices make structs non-comparable } type structWithUnexportedField struct { unexported int } return slices.Values([]orderTestCase{ // Edge cases: nil, empty, single element collections {"empty/int", []int{}, passAll, false}, {"nil/int", []int(nil), passAll, false}, {"single/int", []int{1}, passAll, false}, // All equal - both non-strict pass, both strict fail, sorted {"all-equal/int", []int{2, 2, 2}, allEqual, false}, {"all-equal/float64", []float64{1.5, 1.5, 1.5}, allEqual, false}, {"all-equal/~float64", []myFloat{1.5, 1.5, 1.5}, allEqual, false}, {"all-equal/~[]~float64", myCollection{1.5, 1.5, 1.5}, allEqual, false}, {"all-equal/string", []string{"a", "a", "a"}, allEqual, false}, {"all-equal/time.Time", []time.Time{t0, t0, t0}, allEqual, false}, {"all-equal/[]byte", [][]byte{[]byte("a"), []byte("a"), []byte("a")}, allEqual, false}, // Strictly ascending - IsIncreasing passes, IsNonDecreasing passes, sorted {"strictly-asc/int-short", []int{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/int-long", []int{1, 2, 3, 4, 5}, strictlyAsc, false}, {"strictly-asc/int8", []int8{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/int16", []int16{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/int32", []int32{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/int64", []int64{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/uint", []uint{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/uint8", []uint8{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/uint16", []uint16{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/uint32", []uint32{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/uint64", []uint64{1, 2, 3}, strictlyAsc, false}, {"strictly-asc/float32", []float32{1.1, 2.2, 3.3}, strictlyAsc, false}, {"strictly-asc/float64", []float64{1.1, 2.2, 3.3}, strictlyAsc, false}, {"strictly-asc/~float64", []myFloat{1.1, 2.2, 3.3}, strictlyAsc, false}, {"strictly-asc/~[]~float64", myCollection{1.1, 2.2, 3.3}, strictlyAsc, false}, {"strictly-asc/string", []string{"a", "b", "c"}, strictlyAsc, false}, {"strictly-asc/time.Time", []time.Time{t0, t1, t2}, strictlyAsc, false}, {"strictly-asc/[]byte", [][]byte{[]byte("a"), []byte("b"), []byte("c")}, strictlyAsc, false}, // Strictly descending - IsDecreasing passes, IsNonIncreasing passes, not sorted {"strictly-desc/int-short", []int{3, 2, 1}, strictlyDesc, false}, {"strictly-desc/int-long", []int{5, 4, 3, 2, 1}, strictlyDesc, false}, {"strictly-desc/float64", []float64{3.3, 2.2, 1.1}, strictlyDesc, false}, {"strictly-desc/~float64", []myFloat{3.3, 2.2, 1.1}, strictlyDesc, false}, {"strictly-desc/~[]~float64", myCollection{3.3, 2.2, 1.1}, strictlyDesc, false}, {"strictly-desc/string", []string{"c", "b", "a"}, strictlyDesc, false}, {"strictly-desc/time.Time", []time.Time{t2, t1, t0}, strictlyDesc, false}, {"strictly-desc/[]byte", [][]byte{[]byte("c"), []byte("b"), []byte("a")}, strictlyDesc, false}, // Non-strictly ascending - sorted, but not strictly (has equal adjacent) {"non-strictly-asc/int-with-equal", []int{1, 1, 2, 3}, nonStrictlyAsc, false}, {"non-strictly-asc/int-with-equal-middle", []int{1, 2, 2, 3}, nonStrictlyAsc, false}, {"non-strictly-asc/float64", []float64{1.1, 2.2, 2.2, 3.3}, nonStrictlyAsc, false}, {"non-strictly-asc/~float64", []myFloat{1.1, 1.1, 2.2, 3.3}, nonStrictlyAsc, false}, {"non-strictly-asc/~[]~float64", myCollection{1.1, 1.1, 2.2, 3.3}, nonStrictlyAsc, false}, {"non-strictly-asc/string", []string{"a", "a", "b", "c"}, nonStrictlyAsc, false}, {"non-strictly-asc/time.Time", []time.Time{t0, t0, t1, t2}, nonStrictlyAsc, false}, {"non-strictly-asc/[]byte", [][]byte{[]byte("a"), []byte("a"), []byte("b"), []byte("c")}, nonStrictlyAsc, false}, // Non-strictly descending - not sorted, but consistently >= (has equal adjacent) {"non-strictly-desc/int-with-equal", []int{3, 2, 2, 1}, nonStrictlyDesc, false}, {"non-strictly-desc/int-with-equal-start", []int{3, 3, 2, 1}, nonStrictlyDesc, false}, {"non-strictly-desc/float64", []float64{3.3, 2.2, 2.2, 1.1}, nonStrictlyDesc, false}, {"non-strictly-desc/~float64", []myFloat{3.3, 2.2, 2.2, 1.1}, nonStrictlyDesc, false}, {"non-strictly-desc/~[]~float64", myCollection{3.3, 2.2, 2.2, 1.1}, nonStrictlyDesc, false}, {"non-strictly-desc/string", []string{"c", "b", "b", "a"}, nonStrictlyDesc, false}, {"non-strictly-desc/time.Time", []time.Time{t2, t1, t1, t0}, nonStrictlyDesc, false}, {"non-strictly-desc/[]byte", [][]byte{[]byte("c"), []byte("b"), []byte("b"), []byte("a")}, nonStrictlyDesc, false}, // Unsorted - no ordering pattern {"unsorted/int-mixed", []int{1, 4, 2}, unsorted, false}, {"unsorted/int-up-down-up", []int{1, 3, 2, 4}, unsorted, false}, {"unsorted/float64", []float64{1.1, 3.3, 2.2}, unsorted, false}, {"unsorted/~float64", []myFloat{1.1, 3.3, 2.2}, unsorted, false}, {"unsorted/~[]~float64", myCollection{1.1, 3.3, 2.2}, unsorted, false}, {"unsorted/string", []string{"b", "a", "c"}, unsorted, false}, {"unsorted/time.Time", []time.Time{t1, t0, t2}, unsorted, false}, {"unsorted/[]byte", [][]byte{[]byte("b"), []byte("a"), []byte("c")}, unsorted, false}, // Reflection-only edge cases // Case 1: Object is not a slice or array (should error) {"error/not-a-collection", nonComparableStruct{Value: 1, Data: []int{1}}, errorCase, true}, // Case 2: Slice of non-comparable elements (should error) {"error/non-comparable-elements", []nonComparableStruct{ {Value: 1, Data: []int{1}}, {Value: 2, Data: []int{2}}, }, errorCase, true}, // Case 3: Slice with unexported fields (triggers panic bug in isStrictlyOrdered) {"panic/unexported-fields", []structWithUnexportedField{ {unexported: 1}, {unexported: 2}, }, errorCase, true}, }) } // Determine the expected pass/fail status for each assertion based on ordering kind. func expectedStatusForAssertion(assertionKind orderAssertionKind, kind collectionKind) bool { // Error cases always fail (return false) if kind == errorCase { return false } switch assertionKind { case increasingKind: // IsIncreasing: strictly ascending only return kind == strictlyAsc || kind == passAll case notIncreasingKind: return kind != strictlyAsc && kind != passAll case decreasingKind: // IsDecreasing: strictly descending only return kind == strictlyDesc || kind == passAll case notDecreasingKind: return kind != strictlyDesc && kind != passAll case sortedKind: // SortedT: passes for sorted (non-strictly ascending, allows equal) return kind == allEqual || kind == strictlyAsc || kind == nonStrictlyAsc || kind == passAll case notSortedKind: // NotSortedT: inverse of SortedT return kind != allEqual && kind != strictlyAsc && kind != nonStrictlyAsc && kind != passAll default: panic(fmt.Errorf("test case configuration error: invalid orderAssertionKind: %d", assertionKind)) } } func testOrderReflectBased(orderAssertion func(T, any, ...any) bool, collection any, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := orderAssertion(mock, collection) shouldPassOrFail(t, mock, result, shouldPass) } } func testOrderGeneric(assertionKind orderAssertionKind, collection any, shouldPass bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) result := testOrderAssertionResult(mock, assertionKind, collection) shouldPassOrFail(t, mock, result, shouldPass) } } func testOrderAssertionResult(mock T, assertionKind orderAssertionKind, collection any) bool { // Type switch to call the appropriate generic function. // // This switch doesn't cover ALL variants of ~[]Ordered but is deemed sufficient for the purpose // of testing ordering. switch coll := collection.(type) { case []int: return testGenericAssertion(mock, assertionKind, coll) case []int8: return testGenericAssertion(mock, assertionKind, coll) case []int16: return testGenericAssertion(mock, assertionKind, coll) case []int32: return testGenericAssertion(mock, assertionKind, coll) case []int64: return testGenericAssertion(mock, assertionKind, coll) case []uint: return testGenericAssertion(mock, assertionKind, coll) case []uint8: return testGenericAssertion(mock, assertionKind, coll) case []uint16: return testGenericAssertion(mock, assertionKind, coll) case []uint32: return testGenericAssertion(mock, assertionKind, coll) case []uint64: return testGenericAssertion(mock, assertionKind, coll) case []uintptr: return testGenericAssertion(mock, assertionKind, coll) case []float32: return testGenericAssertion(mock, assertionKind, coll) case []float64: return testGenericAssertion(mock, assertionKind, coll) case []myFloat: return testGenericAssertion(mock, assertionKind, coll) case myCollection: return testGenericAssertion(mock, assertionKind, coll) case [][]byte: return testGenericAssertion(mock, assertionKind, coll) case []string: return testGenericAssertion(mock, assertionKind, coll) case []time.Time: return testGenericAssertion(mock, assertionKind, coll) default: panic(fmt.Errorf("internal test error: unsupported collection type in test suite: %T", coll)) } } func testGenericAssertion[Collection ~[]E, E Ordered](mock T, assertionKind orderAssertionKind, collection Collection) bool { switch assertionKind { case increasingKind: return IsIncreasingT(mock, collection) case notIncreasingKind: return IsNonIncreasingT(mock, collection) case decreasingKind: return IsDecreasingT(mock, collection) case notDecreasingKind: return IsNonDecreasingT(mock, collection) case sortedKind: return SortedT(mock, collection) case notSortedKind: return NotSortedT(mock, collection) default: panic(fmt.Errorf("test case configuration error: invalid orderAssertionKind: %d", assertionKind)) } } // ======================================= // TestOrderErrorMessages // ======================================= func orderFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "IsIncreasing/string", assertion: func(t T) bool { return IsIncreasing(t, []string{"b", "a"}) }, wantContains: []string{`"b" is not less than "a"`}, }, { name: "IsIncreasing/int", assertion: func(t T) bool { return IsIncreasing(t, []int{2, 1}) }, wantContains: []string{`"2" is not less than "1"`}, }, { name: "IsIncreasing/int8", assertion: func(t T) bool { return IsIncreasing(t, []int8{2, 1}) }, wantContains: []string{`"2" is not less than "1"`}, }, { name: "IsIncreasing/float32", assertion: func(t T) bool { return IsIncreasing(t, []float32{2.34, 1.23}) }, wantContains: []string{`"2.34" is not less than "1.23"`}, }, { name: "IsIncreasing/invalid-type", assertion: func(t T) bool { return IsIncreasing(t, struct{}{}) }, wantContains: []string{`object struct {} is not an ordered collection`}, }, { name: "IsNonIncreasing/string", assertion: func(t T) bool { return IsNonIncreasing(t, []string{"a", "b"}) }, wantContains: []string{`should not be increasing`}, }, { name: "IsNonIncreasing/int", assertion: func(t T) bool { return IsNonIncreasing(t, []int{1, 2}) }, wantContains: []string{`should not be increasing`}, }, { name: "IsNonIncreasing/float64", assertion: func(t T) bool { return IsNonIncreasing(t, []float64{1.23, 2.34}) }, wantContains: []string{`should not be increasing`}, }, { name: "IsDecreasing/string", assertion: func(t T) bool { return IsDecreasing(t, []string{"a", "b"}) }, wantContains: []string{`"a" is not greater than "b"`}, }, { name: "IsDecreasing/int", assertion: func(t T) bool { return IsDecreasing(t, []int{1, 2}) }, wantContains: []string{`"1" is not greater than "2"`}, }, { name: "IsDecreasing/uint64", assertion: func(t T) bool { return IsDecreasing(t, []uint64{1, 2}) }, wantContains: []string{`"1" is not greater than "2"`}, }, { name: "IsNonDecreasing/string", assertion: func(t T) bool { return IsNonDecreasing(t, []string{"b", "a"}) }, wantContains: []string{`should not be decreasing`}, }, { name: "IsNonDecreasing/int", assertion: func(t T) bool { return IsNonDecreasing(t, []int{2, 1}) }, wantContains: []string{`should not be decreasing`}, }, { name: "IsNonDecreasing/float32", assertion: func(t T) bool { return IsNonDecreasing(t, []float32{2.34, 1.23}) }, wantContains: []string{`should not be decreasing`}, }, }) } go-openapi-testify-c10ca71/internal/assertions/panic.go000066400000000000000000000077571520301377500233100ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "runtime/debug" ) // PanicAssertionFunc is a common function prototype when validating a panic value. Can be useful // for table driven tests. type PanicAssertionFunc func(t T, f func(), msgAndArgs ...any) bool // Panics asserts that the code inside the specified function panics. // // # Usage // // assertions.Panics(t, func(){ GoCrazy() }) // // # Examples // // success: func() { panic("panicking") } // failure: func() { } func Panics(t T, f func(), msgAndArgs ...any) bool { // Domain: panic // Opposite: NotPanics if h, ok := t.(H); ok { h.Helper() } if funcDidPanic, panicValue, _ := didPanic(f); !funcDidPanic { return Fail(t, fmt.Sprintf("func should panic\n\tPanic value:\t%#v", panicValue), msgAndArgs...) } return true } // PanicsWithValue asserts that the code inside the specified function panics, // and that the recovered panic value equals the expected panic value. // // # Usage // // assertions.PanicsWithValue(t, "crazy error", func(){ GoCrazy() }) // // # Examples // // success: "panicking", func() { panic("panicking") } // failure: "panicking", func() { } func PanicsWithValue(t T, expected any, f func(), msgAndArgs ...any) bool { // Domain: panic if h, ok := t.(H); ok { h.Helper() } funcDidPanic, panicValue, panickedStack := didPanic(f) if !funcDidPanic { return Fail(t, fmt.Sprintf("func should panic\n\tPanic value:\t%#v", panicValue), msgAndArgs...) } if panicValue != expected { return Fail(t, fmt.Sprintf("func should panic with value:\t%#v\n\tPanic value:\t%#v\n\tPanic stack:\t%s", expected, panicValue, panickedStack), msgAndArgs...) } return true } // PanicsWithError asserts that the code inside the specified function panics, // and that the recovered panic value is an error that satisfies the [EqualError] comparison. // // # Usage // // assertions.PanicsWithError(t, "crazy error", func(){ GoCrazy() }) // // # Examples // // success: ErrTest.Error(), func() { panic(ErrTest) } // failure: ErrTest.Error(), func() { } func PanicsWithError(t T, errString string, f func(), msgAndArgs ...any) bool { // Domain: panic if h, ok := t.(H); ok { h.Helper() } funcDidPanic, panicValue, panickedStack := didPanic(f) if !funcDidPanic { return Fail(t, fmt.Sprintf("func should panic\n\tPanic value:\t%#v", panicValue), msgAndArgs...) } panicErr, isError := panicValue.(error) if !isError || panicErr.Error() != errString { msg := fmt.Sprintf("func should panic with error message:\t%#v\n", errString) if isError { msg += fmt.Sprintf("\tError message:\t%#v\n", panicErr.Error()) } msg += fmt.Sprintf("\tPanic value:\t%#v\n", panicValue) msg += fmt.Sprintf("\tPanic stack:\t%s\n", panickedStack) return Fail(t, msg, msgAndArgs...) } return true } // NotPanics asserts that the code inside the specified function does NOT panic. // // # Usage // // assertions.NotPanics(t, func(){ RemainCalm() }) // // # Examples // // success: func() { } // failure: func() { panic("panicking") } func NotPanics(t T, f func(), msgAndArgs ...any) bool { // Domain: panic if h, ok := t.(H); ok { h.Helper() } if funcDidPanic, panicValue, panickedStack := didPanic(f); funcDidPanic { return Fail(t, fmt.Sprintf("func should not panic\n\tPanic value:\t%v\n\tPanic stack:\t%s", panicValue, panickedStack), msgAndArgs...) } return true } // didPanic returns true if the function passed to it panics. Otherwise, it returns false. func didPanic(f func()) (didPanic bool, message any, stack string) { didPanic = true defer func() { message = recover() if didPanic { stack = string(debug.Stack()) } // Go 1.21 introduces runtime.PanicNilError on panic(nil), // so maintain the same logic going forward (https://github.com/golang/go/issues/25448). if err, ok := message.(error); ok { if err.Error() == "panic called with nil argument" { message = nil } } }() // call the target function f() didPanic = false return } go-openapi-testify-c10ca71/internal/assertions/panic_test.go000066400000000000000000000114021520301377500243250ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "io" "iter" "slices" "strings" "testing" ) func TestPanicDidPanic(t *testing.T) { t.Parallel() const panicMsg = "Panic!" if funcDidPanic, msg, _ := didPanic(func() { panic(panicMsg) }); !funcDidPanic || msg != panicMsg { t.Error("didPanic should return true, panicMsg") } { funcDidPanic, msg, _ := didPanic(func() { err := errors.New("test") panic(err) }) if !funcDidPanic { t.Error("didPanic should have panicked") } if msg == nil { t.Errorf("didPanic should have returned something, but got nil") } } { // Go 1.21+ wraps panic(nil) as runtime.PanicNilError; // didPanic normalizes the message back to nil. funcDidPanic, msg, _ := didPanic(func() { panic(nil) //nolint:nilness // deliberate: testing the panic(nil) edge case }) if !funcDidPanic { t.Error("didPanic should have panicked on panic(nil)") } if msg != nil { t.Errorf("didPanic should have normalized panic(nil) message to nil, got %v", msg) } } if funcDidPanic, _, _ := didPanic(func() { }); funcDidPanic { t.Error("didPanic should return false") } } func TestPanics(t *testing.T) { t.Parallel() mock := new(mockT) if !Panics(mock, func() { panic("Panic!") }) { t.Error("Panics should return true") } if Panics(mock, func() { }) { t.Error("Panics should return false") } } func TestPanicsWithValue(t *testing.T) { t.Parallel() mock := new(mockT) if !PanicsWithValue(mock, "Panic!", func() { panic("Panic!") }) { t.Error("PanicsWithValue should return true") } { err := errors.New("test") if !PanicsWithValue(mock, err, func() { panic(err) // panic no longer supports a nil argument }) { t.Error("PanicsWithValue should return true") } } if PanicsWithValue(mock, "Panic!", func() { }) { t.Error("PanicsWithValue should return false") } if PanicsWithValue(mock, "at the disco", func() { panic("Panic!") }) { t.Error("PanicsWithValue should return false") } } func TestPanicsWithError(t *testing.T) { t.Parallel() mock := new(mockT) succeeded := PanicsWithError(mock, "panic", func() { panic(errors.New("panic")) }) shouldPassOrFail(t, mock, succeeded, true) } func TestPanicNotPanics(t *testing.T) { t.Parallel() mock := new(mockT) if !NotPanics(mock, func() { }) { t.Error("NotPanics should return true") } if NotPanics(mock, func() { panic("Panic!") }) { t.Error("NotPanics should return false") } } func TestPanicCallerInfoWithAutogeneratedFunctions(t *testing.T) { t.Parallel() func() { defer func() { if r := recover(); r != nil { t.Errorf("testAutogeneratedFunction should not panic: %v", r) } }() testAutogeneratedFunction() }() } func TestPanicsWithErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, panicsWithErrorFailCases()) t.Run("string-panic-has-no-error-message-label", func(t *testing.T) { t.Parallel() mock := new(mockT) PanicsWithError(mock, "expected panic msg", func() { panic("actual panic msg") }) if strings.Contains(mock.errorString(), "Error message:") { t.Error("PanicsWithError should not report error message if not due an error") } }) } // ======================================= // TestPanicsWithErrorMessages // ======================================= func panicsWithErrorFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "PanicsWithError/no-panic", assertion: func(t T) bool { return PanicsWithError(t, "Panic!", func() {}) }, wantContains: []string{"Panic value:"}, }, { name: "PanicsWithError/wrong-error", assertion: func(t T) bool { return PanicsWithError(t, "expected panic err msg", func() { panic(errors.New("actual panic err msg")) }) }, wantContains: []string{"Error message:", "actual panic err msg"}, }, { name: "PanicsWithError/wrapped-error", assertion: func(t T) bool { return PanicsWithError(t, "expected panic err msg", func() { panic(&PanicsWrapperError{"wrapped", errors.New("actual panic err msg")}) }) }, wantContains: []string{"Error message:", "wrapped: actual panic err msg"}, }, { name: "PanicsWithError/string-panic", assertion: func(t T) bool { return PanicsWithError(t, "expected panic msg", func() { panic("actual panic msg") }) }, wantContains: []string{"Panic value:", "actual panic msg"}, }, }) } type PanicsWrapperError struct { Prefix string Err error } func (e PanicsWrapperError) Error() string { return e.Prefix + ": " + e.Err.Error() } func testAutogeneratedFunction() { defer func() { if err := recover(); err == nil { panic("did not panic") } CallerInfo() }() t := struct { io.Closer }{} c := t c.Close() } go-openapi-testify-c10ca71/internal/assertions/safety.go000066400000000000000000000067261520301377500235040ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "context" "runtime" "github.com/go-openapi/testify/v2/internal/fdleak" "github.com/go-openapi/testify/v2/internal/leak" ) // fdLeakSupported reports whether the current platform has an fdleak // implementation. Mirrors the build tags in internal/fdleak. func fdLeakSupported() bool { switch runtime.GOOS { case "linux", "darwin": return true default: return false } } // NoGoRoutineLeak ensures that no goroutine did leak from inside the tested function. // // NOTE: only the go routines spawned from inside the tested function are checked for leaks. // No filter or configuration is needed to exclude "known go routines". // // Resource cleanup should be done inside the tested function, and not using [testing.T.Cleanup], // as t.Cleanup is called after the leak check. // // # Edge cases // // - if the tested function panics leaving behind leaked goroutines, these are detected. // - if the tested function calls [runtime.Goexit] (e.g. from [testing.T.FailNow]) leaving behind leaked goroutines, // these are detected. // - if a panic occurs in one of the leaked go routines, it cannot be recovered with certainty and // the calling program will usually panic. // // # Concurrency // // [NoGoRoutineLeak] may be used safely in parallel tests. // // # Usage // // NoGoRoutineLeak(t, func() { // ... // }, // "should not leak any go routine", // ) // // # Examples // // success: func() {} func NoGoRoutineLeak(t T, tested func(), msgAndArgs ...any) bool { // Domain: safety if h, ok := t.(H); ok { h.Helper() } var ctx context.Context c, ok := t.(contextualizer) if ok { ctx = c.Context() } if ctx == nil { ctx = context.Background() } signature := leak.Leaked(ctx, tested) if signature == "" { return true } return Fail(t, "found leaked go routines: "+signature, msgAndArgs...) } // NoFileDescriptorLeak ensures that no file descriptor leaks from inside the tested function. // // This assertion works on Linux (via /proc/self/fd) and macOS (via fstat probing). // On other platforms, the test is skipped. // // NOTE: this assertion is not compatible with parallel tests. // File descriptors are a process-wide resource; concurrent tests // opening files would cause false positives. // // Sockets, pipes, and other kernel-internal descriptors (Linux anon_inode, // darwin kqueue) are filtered out by default, as these are typically // managed by the Go runtime. // // # Concurrency // // [NoFileDescriptorLeak] is not compatible with parallel tests. // File descriptors are a process-wide resource; any concurrent I/O // from other goroutines may cause false positives. // // Calls to [NoFileDescriptorLeak] are serialized with a mutex // to prevent multiple leak checks from interfering with each other. // // # Usage // // NoFileDescriptorLeak(t, func() { // // code that should not leak file descriptors // }) // // # Examples // // success: func() {} func NoFileDescriptorLeak(t T, tested func(), msgAndArgs ...any) bool { // Domain: safety if h, ok := t.(H); ok { h.Helper() } if !fdLeakSupported() { if s, ok := t.(skipper); ok { s.Skip("NoFileDescriptorLeak is not supported on " + runtime.GOOS) } return true } msg, err := fdleak.Leaked(tested) if err != nil { return Fail(t, "file descriptor snapshot failed: "+err.Error(), msgAndArgs...) } if msg == "" { return true } return Fail(t, msg, msgAndArgs...) } go-openapi-testify-c10ca71/internal/assertions/safety_test.go000066400000000000000000000054771520301377500245450ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "context" "net" "os" "runtime" "sync" "testing" ) func TestNoGoRoutineLeak_Success(t *testing.T) { mockT := new(mockT) result := NoGoRoutineLeak(mockT, func() { // Clean function — no goroutines spawned. }) if !result { t.Error("expected NoGoRoutineLeak to return true for clean function") } if mockT.failed { t.Error("expected no failure for clean function") } } func TestNoGoRoutineLeak_Failure(t *testing.T) { blocker := make(chan struct{}) var wg sync.WaitGroup t.Cleanup(func() { close(blocker) wg.Wait() }) mockT := new(mockT) wg.Add(1) result := NoGoRoutineLeak(mockT, func() { go func() { defer wg.Done() <-blocker // leaked: blocks until cleanup }() }) if result { t.Error("expected NoGoRoutineLeak to return false for leaking function") } if !mockT.failed { t.Error("expected failure to be reported for leaking function") } } func TestNoFileDescriptorLeak_Success(t *testing.T) { mockT := new(mockT) result := NoFileDescriptorLeak(mockT, func() { // Clean function — no file descriptors opened. }) if !result { t.Error("expected NoFileDescriptorLeak to return true for clean function") } if mockT.failed { t.Error("expected no failure for clean function") } } func TestNoFileDescriptorLeak_Failure(t *testing.T) { if !fdLeakSupported() { t.Skipf("file descriptor leak detection is not supported on %s", runtime.GOOS) } mockT := new(mockT) var leakedFile *os.File result := NoFileDescriptorLeak(mockT, func() { f, err := os.CreateTemp(t.TempDir(), "fdleak-test-*") if err != nil { t.Fatalf("CreateTemp: %v", err) } leakedFile = f // intentionally not closed }) t.Cleanup(func() { if leakedFile != nil { leakedFile.Close() os.Remove(leakedFile.Name()) } }) if result { t.Error("expected NoFileDescriptorLeak to return false for leaking function") } if !mockT.failed { t.Error("expected failure to be reported for leaking function") } } func TestNoFileDescriptorLeak_SocketFiltered(t *testing.T) { if !fdLeakSupported() { t.Skipf("file descriptor leak detection is not supported on %s", runtime.GOOS) } mockT := new(mockT) var leakedListener net.Listener result := NoFileDescriptorLeak(mockT, func() { var lc net.ListenConfig ln, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen: %v", err) } leakedListener = ln // intentionally not closed — socket FD should be filtered }) t.Cleanup(func() { if leakedListener != nil { leakedListener.Close() } }) if !result { t.Error("expected socket FD to be filtered, but assertion reported a leak") } if mockT.failed { t.Error("expected no failure when socket FD is filtered") } } go-openapi-testify-c10ca71/internal/assertions/spew.go000066400000000000000000000011031520301377500231470ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import "github.com/go-openapi/testify/v2/internal/spew" 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, } return spewConfig.Sdump(a...) } go-openapi-testify-c10ca71/internal/assertions/string.go000066400000000000000000000135641520301377500235150ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "reflect" "regexp" ) // Regexp asserts that a specified regular expression matches a string. // // The regular expression may be passed as a [regexp.Regexp], a string or a []byte and will be compiled. // // The actual argument to be matched may be a string, []byte or anything that prints as a string with [fmt.Sprint]. // // # Usage // // assertions.Regexp(t, regexp.MustCompile("start"), "it's starting") // assertions.Regexp(t, "start...$", "it's not starting") // // # Examples // // success: "^start", "starting" // failure: "^start", "not starting" func Regexp(t T, rx any, actual any, msgAndArgs ...any) bool { // Domain: string // Opposite: NotRegexp if h, ok := t.(H); ok { h.Helper() } re, err := buildRegex(rx) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } switch v := actual.(type) { case []byte: return matchRegex(t, re, v, true, msgAndArgs...) case string: return matchRegex(t, re, v, true, msgAndArgs...) default: // reflection-based check for uncommon usage str, ok := asString(actual) if !ok { return matchRegex(t, re, fmt.Sprint(actual), true, msgAndArgs...) } return matchRegex(t, re, str, true, msgAndArgs...) } } // RegexpT asserts that a specified regular expression matches a string. // // The actual argument to be matched may be a string or []byte. // // See [Regexp]. // // # Examples // // success: "^start", "starting" // failure: "^start", "not starting" func RegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { // Domain: string // Opposite: NotRegexpT if h, ok := t.(H); ok { h.Helper() } re, err := buildRegex(rx) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } return matchRegex(t, re, actual, true, msgAndArgs...) } // NotRegexp asserts that a specified regular expression does not match a string. // // See [Regexp]. // // # Usage // // assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assertions.NotRegexp(t, "^start", "it's not starting") // // # Examples // // success: "^start", "not starting" // failure: "^start", "starting" func NotRegexp(t T, rx any, actual any, msgAndArgs ...any) bool { // Domain: string if h, ok := t.(H); ok { h.Helper() } re, err := buildRegex(rx) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } switch v := actual.(type) { case []byte: return matchRegex(t, re, v, false, msgAndArgs...) case string: return matchRegex(t, re, v, false, msgAndArgs...) default: // reflection-based check for uncommon usage str, ok := asString(actual) if !ok { return matchRegex(t, re, fmt.Sprint(actual), false, msgAndArgs...) } // handle ~string, ~[]byte return matchRegex(t, re, str, false, msgAndArgs...) } } // NotRegexpT asserts that a specified regular expression does not match a string. // // See [RegexpT]. // // # Usage // // assertions.NotRegexp(t, regexp.MustCompile("starts"), "it's starting") // assertions.NotRegexp(t, "^start", "it's not starting") // // # Examples // // success: "^start", "not starting" // failure: "^start", "starting" func NotRegexpT[Rex RegExp, ADoc Text](t T, rx Rex, actual ADoc, msgAndArgs ...any) bool { // Domain: string if h, ok := t.(H); ok { h.Helper() } re, err := buildRegex(rx) if err != nil { return Fail(t, err.Error(), msgAndArgs...) } return matchRegex(t, re, actual, false, msgAndArgs...) } func buildRegex(re any) (*regexp.Regexp, error) { // 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 { return nil, fmt.Errorf("regexp must not be nil: %w", errAssertions) } return v, nil case string: return compileRegex(v) case []byte: if v == nil { return nil, fmt.Errorf("regexp must not be nil: %w", errAssertions) } return compileRegex(string(v)) default: // reflection-based check for uncommon usage str, ok := asString(re) if !ok { return nil, fmt.Errorf( "type for regexp is not supported. Want string, []byte or anything that converts to those, but got %T", re, ) } // handle ~string, ~[]byte return compileRegex(str) } } func compileRegex(rx string) (*regexp.Regexp, error) { const errMsg = "invalid error expression %q: %w" rex, err := regexp.Compile(rx) if err != nil { return nil, fmt.Errorf(errMsg, rx, err) } return rex, nil } func matchRegex[ADoc Text](t T, rx *regexp.Regexp, actual ADoc, wantMatch bool, msgAndArgs ...any) bool { if h, ok := t.(H); ok { h.Helper() } var matched bool str := any(actual) switch v := str.(type) { case []byte: matched = rx.Match(v) case string: matched = rx.MatchString(v) default: // safeguard: should never get there matched = rx.MatchString(string(actual)) } switch { case wantMatch && !matched: return Fail(t, fmt.Sprintf(`Expect %q to match %q"`, string(actual), rx), msgAndArgs...) case !wantMatch && matched: return Fail(t, fmt.Sprintf("Expect %q to NOT match %q", string(actual), rx), msgAndArgs...) default: return true } } func asString(v any) (string, bool) { typeString := reflect.TypeFor[string]() val := reflect.ValueOf(v) kind := val.Kind() if !val.CanConvert(typeString) { return "", false } // weird reflection: numbers CanConvert but their string rep is wrong. Need to check further. typ := val.Type() 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 return val.Convert(typeString).String(), true } // assertionsError reports an error from a bad usage of an assertion. type assertionsError string func (e assertionsError) Error() string { return string(e) } const errAssertions assertionsError = "error from assertions" go-openapi-testify-c10ca71/internal/assertions/string_test.go000066400000000000000000000241211520301377500245430ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "regexp" "slices" "testing" ) func TestStringRegexp(t *testing.T) { t.Parallel() // run test cases with all combinations of supported types // // NOTE: testing pattern, focused on the expected result (true/false) and _NOT_ the content of the returned message. // - stringRegexpCases: loop over generic test cases // - testAllRegexpWithTypes: dispatch over type combinations of values // - testAllRegexp: dispatch over the assertion variants (reflection-based, generic, X vs NotX semantics) // Single assertion test functions: // - testRegexp // - testRegexpT // - testNotRegexp // - testNotRegexpT for tc := range stringRegexpCases() { t.Run(tc.name, tc.test) } } func TestStringErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, stringFailCases()) } // ======================================= // TestStringRegexp // ======================================= // Values to populate the test harness: // // - 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( "^start", "start of the line", true, true, )}, {"end$ (match)", testAllRegexpWithTypes( "end$", "in the end", true, true, )}, {"end$ (match)", testAllRegexpWithTypes( "end$", "in the end", true, true, )}, {"phone number (match)", testAllRegexpWithTypes( "[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12.34", true, true, )}, // failed matches {"start (no match)", testAllRegexpWithTypes( "^asdfastart", "Not the start of the line", false, true, )}, {"end$ (no match)", testAllRegexpWithTypes( "end$", "in the end.", false, true, )}, {"phone number (no match)", testAllRegexpWithTypes( "[0-9]{3}[.-]?[0-9]{2}[.-]?[0-9]{2}", "My phone number is 650.12a.34", false, true, )}, // invalid pattern {"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, )}, }) } // test all Regexp variants with the same input (possibly converted). func testAllRegexpWithTypes(regString, str string, success, valid bool) func(*testing.T) { type ( // redefined types to check for ~string and ~[]byte type constraints MyString string MyBytes []byte ) return func(t *testing.T) { t.Run("with all type combinations", func(t *testing.T) { // generic version : 5 x 4 combinations of input types t.Run("with [string,string]", testAllRegexp[string, string](regString, str, success, valid)) t.Run("with [string,[]byte]", testAllRegexp[string, []byte](regString, []byte(str), success, valid)) t.Run("with [string,~string]", testAllRegexp[string, MyString](regString, MyString(str), success, valid)) t.Run("with [string,~[]byte]", testAllRegexp[string, MyBytes](regString, MyBytes(str), success, valid)) // t.Run("with [[]byte,string]", testAllRegexp[[]byte, string]([]byte(regString), str, success, valid)) t.Run("with [[]byte,[]byte]", testAllRegexp[[]byte, []byte]([]byte(regString), []byte(str), success, valid)) t.Run("with [[]byte,~string]", testAllRegexp[[]byte, MyString]([]byte(regString), MyString(str), success, valid)) t.Run("with [[]byte,~[]byte]", testAllRegexp[[]byte, MyBytes]([]byte(regString), MyBytes(str), success, valid)) // t.Run("with [~string,string]", testAllRegexp[MyString, string](MyString(regString), str, success, valid)) t.Run("with [~string,[]byte]", testAllRegexp[MyString, []byte](MyString(regString), []byte(str), success, valid)) t.Run("with [~string,~string]", testAllRegexp[MyString, MyString](MyString(regString), MyString(str), success, valid)) t.Run("with [~string,~[]byte]", testAllRegexp[MyString, MyBytes](MyString(regString), MyBytes(str), success, valid)) // t.Run("with [~[]byte,string]", testAllRegexp[MyBytes, string](MyBytes(regString), str, success, valid)) t.Run("with [~[]byte,[]byte]", testAllRegexp[MyBytes, []byte](MyBytes(regString), []byte(str), success, valid)) t.Run("with [~[]byte,~string]", testAllRegexp[MyBytes, MyString](MyBytes(regString), MyString(str), success, valid)) t.Run("with [~[]byte,~[]byte]", testAllRegexp[MyBytes, MyBytes](MyBytes(regString), MyBytes(str), success, valid)) // t.Run("with [*regexp.Regexp,string]", testAllRegexp[*regexp.Regexp, string](testRex(regString), str, success, valid)) t.Run("with [*regexp.Regexp,[]byte]", testAllRegexp[*regexp.Regexp, []byte](testRex(regString), []byte(str), success, valid)) t.Run("with [*regexp.Regexp,~string]", testAllRegexp[*regexp.Regexp, MyString](testRex(regString), MyString(str), success, valid)) t.Run("with [*regexp.Regexp,~[]byte]", testAllRegexp[*regexp.Regexp, MyBytes](testRex(regString), MyBytes(str), success, valid)) }) } } // testRegexpWithAny tests edge cases that are specific to the reflection-based variants. 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)) }) } } func testAllRegexp[Rex RegExp, ADoc Text](rx Rex, actual ADoc, success, valid bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() if !valid { // 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 NoRegexp", testNotRegexp(rx, actual, false)) t.Run("with RegexpT", testRegexpT(rx, actual, false)) t.Run("with NoRegexpT", testNotRegexpT(rx, actual, false)) }) return } if success { t.Run("should match", func(t *testing.T) { t.Run("with Regexp", testRegexp(rx, actual, true)) t.Run("with RegexpT", testRegexpT(rx, actual, true)) }) t.Run("should fail", func(t *testing.T) { t.Run("with NoRegexp", testNotRegexp(rx, actual, false)) t.Run("with NoRegexpT", testNotRegexpT(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(rx any, str any, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := Regexp(mock, rx, str) if res != success { if success { croakWantMatch(t, rx, str) return } croakWantNotMatch(t, rx, str) } } } func testNotRegexp(rx any, str any, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := NotRegexp(mock, rx, str) if res != success { if success { croakWantMatch(t, rx, str) return } croakWantNotMatch(t, rx, str) } } } func testRegexpT[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := RegexpT(mock, rx, str) if res != success { if success { croakWantMatch(t, rx, str) return } croakWantNotMatch(t, rx, str) } } } func testNotRegexpT[Rex RegExp, ADoc Text](rx Rex, str ADoc, success bool) func(*testing.T) { return func(t *testing.T) { t.Parallel() mock := new(mockT) res := NotRegexpT(mock, rx, str) if res != success { if success { croakWantMatch(t, rx, str) return } croakWantNotMatch(t, rx, str) } } } func croakWantMatch(t *testing.T, rx any, str any) { t.Helper() t.Errorf("expected %q to match %q", str, rx) } func croakWantNotMatch(t *testing.T, rx, str any) { t.Helper() t.Errorf("expected %q NOT to match %q", str, rx) } func testRex(rex string) *regexp.Regexp { rx, _ := compileRegex(rex) return rx } // ======================================= // TestStringErrorMessages // ======================================= func stringFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "Regexp/no-match", assertion: func(t T) bool { return Regexp(t, "^start", "no match") }, wantContains: []string{"Expect", "to match"}, }, { name: "NotRegexp/unexpected-match", assertion: func(t T) bool { return NotRegexp(t, "^start", "starting") }, wantContains: []string{"Expect", "to NOT match"}, }, { name: "Regexp/invalid-regexp", assertion: func(t T) bool { return Regexp(t, "\\C", "whatever") }, wantContains: []string{"invalid error expression"}, }, { name: "Regexp/invalid-type", assertion: func(t T) bool { return Regexp(t, struct{ a string }{a: "x"}, "whatever") }, wantContains: []string{"type for regexp is not supported"}, }, }) } go-openapi-testify-c10ca71/internal/assertions/testdata/000077500000000000000000000000001520301377500234605ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/testdata/empty_file000066400000000000000000000000001520301377500255260ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/testdata/existing_dir/000077500000000000000000000000001520301377500261505ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/testdata/existing_dir/.gitkeep000066400000000000000000000000001520301377500275670ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/testdata/existing_file000066400000000000000000000000121520301377500262250ustar00rootroot00000000000000NOT EMPTY go-openapi-testify-c10ca71/internal/assertions/testing.go000066400000000000000000000105101520301377500236500ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "runtime" "strings" "unicode" "unicode/utf8" ) // Fail reports a failure through. // // # Usage // // assertions.Fail(t, "failed") // // # Examples // // failure: "failed" func Fail(t T, failureMessage string, msgAndArgs ...any) bool { // Domain: testing if h, ok := t.(H); ok { h.Helper() } if failureMessage != "" || len(msgAndArgs) > 0 { errorWithCallerInfo(t, 1, failureMessage, msgAndArgs...) } return false } // FailNow fails test. // // # Usage // // assertions.FailNow(t, "failed") // // # Examples // // failure: "failed" func FailNow(t T, failureMessage string, msgAndArgs ...any) bool { // Domain: testing if h, ok := t.(H); ok { h.Helper() } Fail(t, failureMessage, msgAndArgs...) // We cannot extend T with FailNow() and // maintain backwards compatibility, so we fallback // to panicking when FailNow is not available in T. // See issue #263 if t, ok := t.(failNower); ok { t.FailNow() } else { panic("test failed and t is missing `FailNow()`") } return false } // CallerInfo returns an array of strings containing the file and line number // of each stack frame leading from the current test to the assert call that // failed. func CallerInfo() []string { // CallerInfo is necessary because the assert functions use the testing object // internally, causing it to print the file:line of the assert method, rather than where // the problem actually occurred in calling code.*/ // // Maintainer: it is not necessary to export CallerInfo. This should remain an internal implementation detail. return callerInfo(1) } // Stolen from the `go test` tool. // isTest tells whether name looks like a test (or benchmark, according to prefix). // It is a Test (say) if there is a character after Test that is not a lower-case letter. // We don't want TesticularCancer. func isTest(name, prefix string) bool { if !strings.HasPrefix(name, prefix) { return false } if len(name) == len(prefix) { // "Test" is ok return true } r, _ := utf8.DecodeRuneInString(name[len(prefix):]) return !unicode.IsLower(r) } func errorWithCallerInfo(t T, offset int, failureMessage string, msgAndArgs ...any) { if h, ok := t.(H); ok { h.Helper() } content := []labeledContent{ {"Error Trace", strings.Join(callerInfo(offset), "\n\t\t\t")}, {"Error", failureMessage}, } // Add test name if the Go version supports it if n, ok := t.(namer); ok { content = append(content, labeledContent{"Test", n.Name()}) } message := messageFromMsgAndArgs(msgAndArgs...) if len(message) > 0 { content = append(content, labeledContent{"Messages", message}) } t.Errorf("\n%s", ""+labeledOutput(content...)) } func callerInfo(offset int) []string { var pc uintptr var file string var line int var name string const stackFrameBufferSize = 10 pcs := make([]uintptr, stackFrameBufferSize) callers := []string{} for { n := runtime.Callers(offset, pcs) if n == 0 { break } frames := runtime.CallersFrames(pcs[:n]) for { frame, more := frames.Next() pc = frame.PC file = frame.File line = frame.Line // This is a huge edge case, but it will panic if this is the case, see #180 if file == "" { break } f := runtime.FuncForPC(pc) if f == nil { break } name = f.Name() // testing.tRunner is the standard library function that calls // tests. Subtests are called directly by tRunner, without going through // the Test/Benchmark/Example function that contains the t.Run calls, so // with subtests we should break when we hit tRunner, without adding it // to the list of callers. if name == "testing.tRunner" { break } parts := strings.Split(file, "/") if len(parts) > 1 { filename := parts[len(parts)-1] dir := parts[len(parts)-2] if (dir != "assert" && dir != "mock" && dir != "require" && dir != "assertions") || filename == "mock_test.go" { callers = append(callers, fmt.Sprintf("%s:%d", file, line)) } } // Drop the package dotPos := strings.LastIndexByte(name, '.') name = name[dotPos+1:] if isTest(name, "Test") || isTest(name, "Benchmark") || isTest(name, "Example") { break } if !more { break } } // Next batch offset += cap(pcs) } return callers } go-openapi-testify-c10ca71/internal/assertions/testing_test.go000066400000000000000000000017731520301377500247220ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "testing" ) const failedStr = "failed" func TestTestingFail(t *testing.T) { t.Parallel() mock := new(mockT) if Fail(mock, failedStr) { t.Error("Fail is expected to return false") } } func TestTestingFailNow(t *testing.T) { t.Parallel() t.Run("with plain T (no Failnow support)", func(t *testing.T) { t.Parallel() mock := new(mockT) t.Run("should panic", func(t *testing.T) { defer func() { if recover() == nil { t.Error("should panic since mockT is missing FailNow()") } }() FailNow(mock, failedStr) }) }) t.Run("with full T (Failnow support)", func(t *testing.T) { t.Parallel() mock := new(mockFailNowT) t.Run("should not panic", func(t *testing.T) { defer func() { if r := recover(); r != nil { t.Errorf("should call mockT.FailNow() rather than panicking: %v", r) } }() FailNow(mock, failedStr) }) }) } go-openapi-testify-c10ca71/internal/assertions/time.go000066400000000000000000000037751520301377500231500ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "time" ) // WithinDuration asserts that the two times are within duration delta of each other. // // # Usage // // assertions.WithinDuration(t, time.Now(), 10*time.Second) // // # Examples // // success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 1, 0, time.UTC), 2*time.Second // failure: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 12, 0, 10, 0, time.UTC), 1*time.Second func WithinDuration(t T, expected, actual time.Time, delta time.Duration, msgAndArgs ...any) bool { // Domain: time if h, ok := t.(H); ok { h.Helper() } dt := expected.Sub(actual) if dt < -delta || dt > delta { return Fail(t, fmt.Sprintf("Max difference between %v and %v allowed is %v, but difference was %v", expected, actual, delta, dt), msgAndArgs...) } return true } // WithinRange asserts that a time is within a time range (inclusive). // // # Usage // // assertions.WithinRange(t, time.Now(), time.Now().Add(-time.Second), time.Now().Add(time.Second)) // // # Examples // // success: time.Date(2024, 1, 1, 12, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) // failure: time.Date(2024, 1, 1, 14, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 11, 0, 0, 0, time.UTC), time.Date(2024, 1, 1, 13, 0, 0, 0, time.UTC) func WithinRange(t T, actual, start, end time.Time, msgAndArgs ...any) bool { // Domain: time if h, ok := t.(H); ok { h.Helper() } if end.Before(start) { return Fail(t, "Start should be before end", msgAndArgs...) } if actual.Before(start) { return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is before the range", actual, start, end), msgAndArgs...) } else if actual.After(end) { return Fail(t, fmt.Sprintf("Time %v expected to be in time range %v to %v, but is after the range", actual, start, end), msgAndArgs...) } return true } go-openapi-testify-c10ca71/internal/assertions/time_test.go000066400000000000000000000051701520301377500241760ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "iter" "slices" "testing" "time" ) func TestTimeWithinDuration(t *testing.T) { t.Parallel() mock := new(mockT) a := time.Now() b := a.Add(10 * time.Second) if !WithinDuration(mock, a, b, 10*time.Second) { t.Error("A 10s difference is within a 10s time difference") } if !WithinDuration(mock, b, a, 10*time.Second) { t.Error("A 10s difference is within a 10s time difference (reversed)") } if WithinDuration(mock, a, b, 9*time.Second) { t.Error("A 10s difference is not within a 9s time difference") } if WithinDuration(mock, b, a, 9*time.Second) { t.Error("A 10s difference is not within a 9s time difference (reversed)") } if WithinDuration(mock, a, b, -9*time.Second) { t.Error("A 10s difference is not within a -9s time difference") } if WithinDuration(mock, b, a, -9*time.Second) { t.Error("A 10s difference is not within a -9s time difference (reversed)") } if WithinDuration(mock, a, b, -11*time.Second) { t.Error("A 10s difference is not within a -11s time difference") } if WithinDuration(mock, b, a, -11*time.Second) { t.Error("A 10s difference is not within a -11s time difference (reversed)") } } func TestTimeWithinRange(t *testing.T) { t.Parallel() mock := new(mockT) n := time.Now() s := n.Add(-time.Second) e := n.Add(time.Second) if !WithinRange(mock, n, n, n) { t.Error("Exact same actual, start, and end values should return true") } if !WithinRange(mock, n, s, e) { t.Error("Time in range should be within the time range") } if !WithinRange(mock, s, s, e) { t.Error("The start time should be within the time range") } if !WithinRange(mock, e, s, e) { t.Error("The end time should be within the time range") } if WithinRange(mock, s.Add(-time.Nanosecond), s, e) { t.Error("Just before the start time should not be within the time range") } if WithinRange(mock, e.Add(time.Nanosecond), s, e) { t.Error("Just after the end time should not be within the time range") } if WithinRange(mock, n, e, s) { t.Error("Reversed range (start > end) should return false") } } func TestTimeErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, timeFailCases()) } // ======================================= // TestTimeErrorMessages // ======================================= func timeFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "Equal/time-formatting", assertion: func(t T) bool { return Equal(t, time.Second*2, time.Millisecond) }, wantContains: []string{"Not equal:", "2s", "1ms"}, }, }) } go-openapi-testify-c10ca71/internal/assertions/type.go000066400000000000000000000147571520301377500231750ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "fmt" "reflect" ) // Implements asserts that an object is implemented by the specified interface. // // # Usage // // assertions.Implements(t, (*MyInterface)(nil), new(MyObject)) // // # Examples // // success: ptr(dummyInterface), new(testing.T) // failure: (*error)(nil), new(testing.T) func Implements(t T, interfaceObject any, object any, msgAndArgs ...any) bool { // Domain: type // Opposite: NotImplements if h, ok := t.(H); ok { h.Helper() } interfaceType := reflect.TypeOf(interfaceObject).Elem() if object == nil { return Fail(t, fmt.Sprintf("Cannot check if nil implements %v", interfaceType), msgAndArgs...) } if !reflect.TypeOf(object).Implements(interfaceType) { return Fail(t, fmt.Sprintf("%T must implement %v", object, interfaceType), msgAndArgs...) } return true } // NotImplements asserts that an object does not implement the specified interface. // // # Usage // // assertions.NotImplements(t, (*MyInterface)(nil), new(MyObject)) // // # Examples // // success: (*error)(nil), new(testing.T) // failure: ptr(dummyInterface), new(testing.T) func NotImplements(t T, interfaceObject any, object any, msgAndArgs ...any) bool { // Domain: type if h, ok := t.(H); ok { h.Helper() } interfaceType := reflect.TypeOf(interfaceObject).Elem() if object == nil { return Fail(t, fmt.Sprintf("Cannot check if nil does not implement %v", interfaceType), msgAndArgs...) } if reflect.TypeOf(object).Implements(interfaceType) { return Fail(t, fmt.Sprintf("%T implements %v", object, interfaceType), msgAndArgs...) } return true } // IsType asserts that the specified objects are of the same type. // // # Usage // // assertions.IsType(t, &MyStruct{}, &MyStruct{}) // // # Examples // // success: 123, 456 // failure: int32(123), int64(456) func IsType(t T, expectedType, object any, msgAndArgs ...any) bool { // Domain: type // Opposite: IsNotType if isType(expectedType, object) { return true } if h, ok := t.(H); ok { h.Helper() } return Fail(t, fmt.Sprintf("Object expected to be of type %T, but was %T", expectedType, object), msgAndArgs...) } // IsOfTypeT asserts that an object is of a given type. // // # Usage // // assertions.IsOfTypeT[MyType](t,myVar) // // # Examples // // success: myType(123.123) // failure: 123.123 func IsOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { // Domain: type // Opposite: IsNotOfTypeT if h, ok := t.(H); ok { h.Helper() } _, ok := object.(EType) if ok { return true } return Fail(t, fmt.Sprintf("Object expected to be of type %v, but was %T", reflect.TypeFor[EType](), object), msgAndArgs...) } // IsNotType asserts that the specified objects are not of the same type. // // # Usage // // assertions.IsNotType(t, &NotMyStruct{}, &MyStruct{}) // // # Examples // // success: int32(123), int64(456) // failure: 123, 456 func IsNotType(t T, theType, object any, msgAndArgs ...any) bool { // Domain: type if !isType(theType, object) { return true } if h, ok := t.(H); ok { h.Helper() } return Fail(t, fmt.Sprintf("Object type expected to be different than %T", theType), msgAndArgs...) } // IsNotOfTypeT asserts that an object is not of a given type. // // # Usage // // assertions.IsOfType[MyType](t,myVar) // // # Examples // // success: 123.123 // failure: myType(123.123) func IsNotOfTypeT[EType any](t T, object any, msgAndArgs ...any) bool { // Domain: type if h, ok := t.(H); ok { h.Helper() } _, ok := object.(EType) if !ok { return true } return Fail(t, fmt.Sprintf("Object type expected to be different than %T", reflect.TypeFor[EType]()), msgAndArgs...) } // Zero asserts that i is the zero value for its type. // // # Usage // // assertions.Zero(t, obj) // // # Examples // // success: 0 // failure: 1 func Zero(t T, i any, msgAndArgs ...any) bool { // Domain: type // Opposite: NotZero if h, ok := t.(H); ok { h.Helper() } if i != nil && !reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { return Fail(t, "Should be zero, but was "+truncatingFormat("%v", i), msgAndArgs...) } return true } // NotZero asserts that i is not the zero value for its type. // // # Usage // // assertions.NotZero(t, obj) // // # Examples // // success: 1 // failure: 0 func NotZero(t T, i any, msgAndArgs ...any) bool { // Domain: type if h, ok := t.(H); ok { h.Helper() } if i == nil || reflect.DeepEqual(i, reflect.Zero(reflect.TypeOf(i)).Interface()) { return Fail(t, fmt.Sprintf("Should not be zero, but was %v", i), msgAndArgs...) } return true } // Kind asserts that the [reflect.Kind] of a given object matches the expected [reflect.Kind]. // // Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) // are comparable to [reflect.Invalid]. See also [reflect.Value.Kind]. // // # Usage // // assertions.Kind(t, reflect.String, "Hello World") // // # Examples // // success: reflect.String, "hello" // failure: reflect.String, 0 func Kind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { // Domain: type // Opposite: NotKind if h, ok := t.(H); ok { h.Helper() } val := reflect.ValueOf(object) kind := val.Kind() if kind != expectedKind { if kind == reflect.Invalid { // add some explanation when reflect.Invalid does not match the expectation (common gotcha with reflect) return Fail(t, "object has reflect.Invalid kind: this is nil or an interface with nil value", msgAndArgs...) } return Fail(t, fmt.Sprintf("object expected to be of kind %v, but was %v", expectedKind, kind), msgAndArgs...) } return true } // NotKind asserts that the [reflect.Kind] of a given object does not match the expected [reflect.Kind]. // // Kind reflects the concrete value stored in the object. The nil value (or interface with nil value) // are comparable to [reflect.Invalid]. See also [reflect.Value.Kind]. // // # Usage // // assertions.NotKind(t, reflect.Int, "Hello World") // // # Examples // // success: reflect.String, 0 // failure: reflect.String, "hello" func NotKind(t T, expectedKind reflect.Kind, object any, msgAndArgs ...any) bool { // Domain: type if h, ok := t.(H); ok { h.Helper() } val := reflect.ValueOf(object) kind := val.Kind() if kind != expectedKind { return true } return Fail(t, fmt.Sprintf("object expected not to be of kind %v, but was %v", expectedKind, kind), msgAndArgs...) } func isType(expectedType, object any) bool { return ObjectsAreEqual(reflect.TypeOf(object), reflect.TypeOf(expectedType)) } go-openapi-testify-c10ca71/internal/assertions/type_test.go000066400000000000000000000202101520301377500242110ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "errors" "iter" "reflect" "slices" "testing" ) func TestTypeImplements(t *testing.T) { t.Parallel() mock := new(mockT) if !Implements(mock, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { t.Error("Implements method should return true: AssertionTesterConformingObject implements AssertionTesterInterface") } if Implements(mock, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { t.Error("Implements method should return false: AssertionTesterNonConformingObject does not implements AssertionTesterInterface") } if Implements(mock, (*AssertionTesterInterface)(nil), nil) { t.Error("Implements method should return false: nil does not implement AssertionTesterInterface") } } func TestTypeNotImplements(t *testing.T) { t.Parallel() mock := new(mockT) if !NotImplements(mock, (*AssertionTesterInterface)(nil), new(AssertionTesterNonConformingObject)) { t.Error("NotImplements method should return true: AssertionTesterNonConformingObject does not implement AssertionTesterInterface") } if NotImplements(mock, (*AssertionTesterInterface)(nil), new(AssertionTesterConformingObject)) { t.Error("NotImplements method should return false: AssertionTesterConformingObject implements AssertionTesterInterface") } if NotImplements(mock, (*AssertionTesterInterface)(nil), nil) { t.Error("NotImplements method should return false: nil can't be checked to be implementing AssertionTesterInterface or not") } } func TestTypeIsType(t *testing.T) { t.Parallel() mock := new(mockT) if !IsType(mock, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { t.Error("IsType should return true: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") } if IsType(mock, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { t.Error("IsType should return false: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") } } func TestTypeNotIsType(t *testing.T) { t.Parallel() mock := new(mockT) if !IsNotType(mock, new(AssertionTesterConformingObject), new(AssertionTesterNonConformingObject)) { t.Error("NotIsType should return true: AssertionTesterConformingObject is not the same type as AssertionTesterNonConformingObject") } if IsNotType(mock, new(AssertionTesterConformingObject), new(AssertionTesterConformingObject)) { t.Error("NotIsType should return false: AssertionTesterConformingObject is the same type as AssertionTesterConformingObject") } } func TestTypeIsOfTypeT(t *testing.T) { t.Parallel() mock := new(mockT) type myType float64 var myVar myType = 1.2 f := 1.2 if !IsOfTypeT[myType](mock, myVar) { t.Errorf("expected myVar to be of type %T", myVar) } if IsNotOfTypeT[myType](mock, myVar) { t.Errorf("expected IsNotOfTypeT to return false for myVar of type %T", myVar) } if IsOfTypeT[myType](mock, f) { t.Errorf("expected f (%T) not to be of type %T", f, myVar) } if !IsNotOfTypeT[myType](mock, f) { t.Errorf("expected IsNotOfTypeT to return true for f (%T) vs %T", f, myVar) } } func TestTypeZero(t *testing.T) { t.Parallel() mock := new(mockT) for test := range typeZeros() { if !Zero(mock, test) { t.Errorf("expected %#v to be the zero value for %T", test, test) } } for test := range typeNonZeros() { if Zero(mock, test) { t.Errorf("expected %#v to NOT be the zero value for %T", test, test) } } } func TestTypeNotZero(t *testing.T) { t.Parallel() mock := new(mockT) for test := range typeZeros() { if NotZero(mock, test) { t.Errorf("expected NotZero to return false for zero value %#v (%T)", test, test) } } for test := range typeNonZeros() { if !NotZero(mock, test) { t.Errorf("expected NotZero to return true for non-zero value %#v (%T)", test, test) } } } func TestTypeKind(t *testing.T) { t.Parallel() for tt := range kindCases() { t.Run(tt.name, func(t *testing.T) { t.Parallel() mock := new(mockT) result := Kind(mock, tt.expectedKind, tt.value) resultNot := NotKind(mock, tt.expectedKind, tt.value) if tt.result { if !result { t.Errorf("expected kind of %T to be %q, but Kind reported %t", tt.value, tt.expectedKind, result) } if resultNot { t.Errorf("expected kind of %T to be %q, but NotKind reported %t", tt.value, tt.expectedKind, resultNot) } return } // expected: false if result { t.Errorf("expected kind of %T NOT to be %q, but Kind reported %t", tt.value, tt.expectedKind, result) } if !resultNot { t.Errorf("expected kind of %T NOT to be %q, but NotKind reported %t", tt.value, tt.expectedKind, resultNot) } }) } } func TestTypeDiffEmptyCases(t *testing.T) { t.Parallel() cases := []struct { a, b any }{ {nil, nil}, {struct{ foo string }{}, nil}, {nil, struct{ foo string }{}}, {1, 2}, {1, 2}, {[]int{1}, []bool{true}}, } for _, tc := range cases { if result := diff(tc.a, tc.b); result != "" { t.Errorf("expected empty diff for (%v, %v), got %q", tc.a, tc.b, result) } } } func TestTypeErrorMessages(t *testing.T) { t.Parallel() runFailCases(t, typeFailCases()) } // ======================================= // TestTypeErrorMessages // ======================================= func typeFailCases() iter.Seq[failCase] { return slices.Values([]failCase{ { name: "Zero/large-slice-truncated", assertion: func(t T) bool { return Zero(t, make([]int, 1_000_000)) }, wantContains: []string{"Should be zero, but was", "<... truncated>"}, }, }) } // ======================================= // TestTypeIsZero // ======================================= func typeZeros() iter.Seq[any] { return slices.Values([]any{ false, byte(0), complex64(0), complex128(0), float32(0), float64(0), int(0), int8(0), int16(0), int32(0), int64(0), rune(0), uint(0), uint8(0), uint16(0), uint32(0), uint64(0), uintptr(0), "", [0]any{}, []any(nil), struct{ x int }{}, (*any)(nil), (func())(nil), nil, any(nil), map[any]any(nil), (chan any)(nil), (<-chan any)(nil), (chan<- any)(nil), }) } func typeNonZeros() iter.Seq[any] { var i int return slices.Values([]any{ true, byte(1), complex64(1), complex128(1), float32(1), float64(1), int(1), int8(1), int16(1), int32(1), int64(1), rune(1), uint(1), uint8(1), uint16(1), uint32(1), uint64(1), uintptr(1), "s", [1]any{1}, []any{}, struct{ x int }{1}, (&i), (func() {}), any(1), map[any]any{}, (make(chan any)), (<-chan any)(make(chan any)), (chan<- any)(make(chan any)), }) } type kindCase struct { expectedKind reflect.Kind value any result bool name string } func kindCases() iter.Seq[kindCase] { var iface any = "string" return slices.Values([]kindCase{ // True cases {reflect.String, "Hello World", true, "is string"}, {reflect.Int, 123, true, "is int"}, {reflect.Array, [6]int{2, 3, 5, 7, 11, 13}, true, "is array"}, {reflect.Func, Kind, true, "is func"}, {reflect.Float64, 0.0345, true, "is float64"}, {reflect.Map, make(map[string]int), true, "is map"}, {reflect.Bool, true, true, "is bool"}, {reflect.Pointer, new(int), true, "is pointer"}, // False cases {reflect.String, 13, false, "not string"}, {reflect.Int, [6]int{2, 3, 5, 7, 11, 13}, false, "not int"}, {reflect.Float64, 12, false, "not float64"}, {reflect.Bool, make(map[string]int), false, "not bool"}, // Edge cases // True {reflect.Invalid, any(nil), true, "legitimate expectation of reflect.Invalid (any)"}, {reflect.Pointer, (*any)(nil), true, "legitimate expectation of reflect.Pointer (*any)"}, {reflect.Invalid, (error)(nil), true, "legitimate expectation of reflect.Invalid (error)"}, {reflect.Invalid, nil, true, "legitimate nil input"}, // False {reflect.Interface, iface, false, "interface returns concrete type (any)"}, {reflect.Interface, errors.New("stuff"), false, "interface returns concrete type (error)"}, {reflect.Invalid, "string", false, "wrong expectation of reflect.Invalid"}, {reflect.Pointer, nil, false, "nil input"}, }) } go-openapi-testify-c10ca71/internal/assertions/unsafetests/000077500000000000000000000000001520301377500242135ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/assertions/unsafetests/doc.go000066400000000000000000000004101520301377500253020ustar00rootroot00000000000000// 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. package unsafetests go-openapi-testify-c10ca71/internal/assertions/unsafetests/unsafetests_test.go000066400000000000000000000022021520301377500301410ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package unsafetests_test import ( "fmt" "testing" "unsafe" assert "github.com/go-openapi/testify/v2/internal/assertions" ) // safeguard: ignoreT implements [assert.T] var _ assert.T = ignoreT{} func TestEqualUnsafePointers(t *testing.T) { var ignore ignoreT assert.True(t, assert.Nil(t, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) assert.False(t, assert.NotNil(ignore, unsafe.Pointer(nil), "unsafe.Pointer(nil) is nil")) assert.True(t, assert.Nil(t, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) assert.False(t, assert.NotNil(ignore, unsafe.Pointer((*int)(nil)), "unsafe.Pointer((*int)(nil)) is nil")) assert.False(t, assert.Nil(ignore, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) assert.True(t, assert.NotNil(t, unsafe.Pointer(new(int)), "unsafe.Pointer(new(int)) is NOT nil")) } type ignoreT struct{} func (ignoreT) Helper() {} func (ignoreT) Errorf(format string, args ...any) { // Run the formatting, but ignore the result msg := fmt.Sprintf(format, args...) _ = msg } go-openapi-testify-c10ca71/internal/assertions/yaml.go000066400000000000000000000113601520301377500231410ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "bytes" "fmt" "github.com/go-openapi/testify/v2/internal/assertions/enable/yaml" ) // YAMLEqBytes asserts that two YAML slices of bytes are equivalent. // // Expected and actual must be valid YAML. // // # Important // // By default, this function is disabled and will panic. // // To enable it, you should add a blank import like so: // // import( // "github.com/go-openapi/testify/enable/yaml/v2" // ) // // For dynamic redaction of the input text via a callback, use [YAMLEqT]. // // # Usage // // expected := `--- // key: value // --- // key: this is a second document, it is not evaluated // ` // actual := `--- // key: value // --- // key: this is a subsequent document, it is not evaluated // ` // assertions.YAMLEq(t, expected, actual) // // # Examples // // panic: []byte("key: value"), []byte("key: value") // should panic without the yaml feature enabled. func YAMLEqBytes(t T, expected, actual []byte, msgAndArgs ...any) bool { // Domain: yaml if h, ok := t.(H); ok { h.Helper() } var expectedYAMLAsInterface, actualYAMLAsInterface any if err := yaml.Unmarshal(expected, &expectedYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Expected value (%q) is not valid yaml.\nYAML parsing error: %v", expected, err), msgAndArgs...) } // Shortcut if same bytes if bytes.Equal(actual, expected) { return true } if err := yaml.Unmarshal(actual, &actualYAMLAsInterface); err != nil { return Fail(t, fmt.Sprintf("Input (%q) needs to be valid yaml.\nYAML error: %v", actual, err), msgAndArgs...) } return Equal(t, expectedYAMLAsInterface, actualYAMLAsInterface, msgAndArgs...) } // YAMLEq asserts that two YAML strings are equivalent. // // See [YAMLEqBytes]. // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. func YAMLEq(t T, expected, actual string, msgAndArgs ...any) bool { // Domain: yaml if h, ok := t.(H); ok { h.Helper() } return YAMLEqBytes(t, []byte(expected), []byte(actual), msgAndArgs...) } // YAMLEqT asserts that two YAML documents are equivalent. // // The expected and actual arguments may be string or []byte. They do not need to be of the same type. // // See [YAMLEqBytes]. // // NOTE: passed values (expected, actual) may be wrapped as functions to redact the input text dynamically. // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. func YAMLEqT[EDoc, ADoc RText](t T, expected EDoc, actual ADoc, msgAndArgs ...any) bool { // Domain: yaml if h, ok := t.(H); ok { h.Helper() } return YAMLEqBytes(t, asBytes(expected), asBytes(actual), msgAndArgs...) } // YAMLUnmarshalAsT wraps [Equal] after [yaml.Unmarshal]. // // The input YAML may be a string or []byte. // // It fails if the unmarshaling returns an error or if the resulting object is not equal to the expected one. // // Be careful not to wrap the expected object into an "any" interface if this is not what you expected: // the unmarshaling would take this type to unmarshal as a map[string]any. // // NOTE: passed yamlDoc value may be wrapped as a function to redact the input text dynamically. // // # Usage // // expected := struct { // A int `yaml:"a"` // }{ // A: 10, // } // // assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. func YAMLUnmarshalAsT[Object any, ADoc RText](t T, expected Object, yamlDoc ADoc, msgAndArgs ...any) bool { // Domain: yaml if h, ok := t.(H); ok { h.Helper() } var actual Object if err := yaml.Unmarshal(asBytes(yamlDoc), &actual); err != nil { return Fail(t, fmt.Sprintf("YAML unmarshal failed: %v", err), msgAndArgs...) } return Equal(t, expected, actual, msgAndArgs...) } // YAMLMarshalAsT wraps [YAMLEq] after [yaml.Marshal]. // // The input YAML may be a string or []byte. // // It fails if the marshaling returns an error or if the expected YAML bytes differ semantically // from the expected ones. // // NOTE: passed expected value may be wrapped as a function to redact the input text dynamically. // // # Usage // // actual := struct { // A int `yaml:"a"` // }{ // A: 10, // } // // assertions.YAMLUnmarshalAsT(t,expected, `{"a": 10}`) // // # Examples // // panic: "key: value", "key: value" // should panic without the yaml feature enabled. func YAMLMarshalAsT[EDoc RText](t T, expected EDoc, object any, msgAndArgs ...any) bool { // Domain: yaml if h, ok := t.(H); ok { h.Helper() } actual, err := yaml.Marshal(object) if err != nil { return Fail(t, fmt.Sprintf("YAML marshal failed: %v", err), msgAndArgs...) } return YAMLEqBytes(t, asBytes(expected), actual, msgAndArgs...) } go-openapi-testify-c10ca71/internal/assertions/yaml_test.go000066400000000000000000000156121520301377500242040ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package assertions import ( "sync/atomic" "testing" ) func TestYAML(t *testing.T) { t.Parallel() t.Run("should panic", testAllYAMLEq()) } // ======================================= // TestYAML: all YAML assertions // ======================================= func testAllYAMLEq() func(*testing.T) { return func(t *testing.T) { const ( actual = ` --- a: 1 ` expected = "" success = false ) a := struct { A string `json:"a"` }{ A: "x", } t.Run("with YAMLEq", testYAMLEq(expected, actual, success)) t.Run("with YAMLEqBytes", testYAMLEqBytes(expected, actual, success)) t.Run("with YAMLEqT[string,string]", testYAMLEqT[string, string](expected, actual, success)) t.Run("with YAMLEqT[[]byte,string]", testYAMLEqT[[]byte, string](expected, actual, success)) t.Run("with YAMLEqT[string,[]byte]", testYAMLEqT[string, []byte](expected, actual, success)) t.Run("with YAMLEqT[[]byte,[]byte]", testYAMLEqT[[]byte, []byte](expected, actual, success)) t.Run("with YAMLMarshalAsT[[]byte,struct{}]", testYAMLMarshalAsT(expected, a, success)) t.Run("with YAMLUnmarshalAsT[struct{},[]byte]", testYAMLUnmarshalAsT(a, actual, success)) } } func testYAMLEq(expected, actual string, success bool) func(*testing.T) { _ = success return func(t *testing.T) { t.Parallel() mock := new(mockT) panicked := func() (didPanic bool) { defer func() { if recover() != nil { didPanic = true } }() _ = YAMLEq(mock, expected, actual) return false }() if !panicked { croakWantPanic(t, "YAMLEq") } } } func testYAMLEqBytes(expected, actual string, success bool) func(*testing.T) { _ = success return func(t *testing.T) { t.Parallel() mock := new(mockT) panicked := func() (didPanic bool) { defer func() { if recover() != nil { didPanic = true } }() _ = YAMLEqBytes(mock, []byte(expected), []byte(actual)) return false }() if !panicked { croakWantPanic(t, "YAMLEqBytes") } } } func testYAMLEqT[ADoc, EDoc Text](expected, actual string, success bool) func(*testing.T) { _ = success return func(t *testing.T) { t.Parallel() mock := new(mockT) panicked := func() (didPanic bool) { defer func() { if recover() != nil { didPanic = true } }() _ = YAMLEqT(mock, EDoc(expected), ADoc(actual)) return false }() if !panicked { croakWantPanic(t, "YAMLEqT") } } } func testYAMLUnmarshalAsT[ADoc Text, Object any](expected Object, actual ADoc, success bool) func(*testing.T) { _ = success return func(t *testing.T) { t.Parallel() mock := new(mockT) panicked := func() (didPanic bool) { defer func() { if recover() != nil { didPanic = true } }() _ = YAMLUnmarshalAsT(mock, expected, actual) return false }() if !panicked { croakWantPanic(t, "YAMLUnmarshalAsT") } } } func testYAMLMarshalAsT[EDoc Text](expected EDoc, actual any, success bool) func(*testing.T) { _ = success return func(t *testing.T) { t.Parallel() mock := new(mockT) panicked := func() (didPanic bool) { defer func() { if recover() != nil { didPanic = true } }() _ = YAMLMarshalAsT(mock, expected, actual) return false }() if !panicked { croakWantPanic(t, "YAMLMarshalAsT") } } } func croakWantPanic(t *testing.T, fn string) { t.Helper() t.Errorf("expected %q to panic with default settings", fn) } // ======================================= // Test YAML T-variants accept Redactor inputs // // In this package the YAML feature is disabled, so a successful redactor // call still ends in a panic from YAMLEqBytes. These tests verify that: // // 1. The Redactor (`func() string` / `func() []byte`) arms of the [RText] // constraint compile and route through asBytes correctly. // 2. The redactor body IS invoked (proving asBytes runs before the // YAML-disabled panic). // 3. Nil redactors panic with the standard "passed Redactor cannot be nil" // message — same guard as the JSON path. // ======================================= func TestYAMLRedact(t *testing.T) { t.Parallel() t.Run("YAMLEqT/redactor-invoked-then-yaml-panics", func(t *testing.T) { t.Parallel() var calls atomic.Int32 red := func() string { calls.Add(1) return "key: value" } didPanic := func() (panicked bool) { defer func() { if recover() != nil { panicked = true } }() _ = YAMLEqT(new(mockT), red, "key: value") return false }() if !didPanic { croakWantPanic(t, "YAMLEqT (with Redactor)") } // asBytes must have run at least once (one side is the redactor). if got := calls.Load(); got != 1 { t.Errorf("expected redactor to be invoked exactly once before YAML-disabled panic, got %d", got) } }) t.Run("YAMLUnmarshalAsT/redactor-bytes-invoked-then-yaml-panics", func(t *testing.T) { t.Parallel() var calls atomic.Int32 red := func() []byte { calls.Add(1) return []byte("key: value") } didPanic := func() (panicked bool) { defer func() { if recover() != nil { panicked = true } }() _ = YAMLUnmarshalAsT(new(mockT), struct{}{}, red) return false }() if !didPanic { croakWantPanic(t, "YAMLUnmarshalAsT (with Redactor)") } if got := calls.Load(); got != 1 { t.Errorf("expected redactor to be invoked exactly once before YAML-disabled panic, got %d", got) } }) // YAMLMarshalAsT runs `yaml.Marshal(object)` BEFORE evaluating // `asBytes(expected)`, so when the YAML feature is disabled the // redactor body is never reached. We can only verify that the call // still panics; happy-path Redactor coverage for YAMLMarshalAsT // belongs in the enable/yaml integration tests. t.Run("YAMLMarshalAsT/with-redactor-still-panics", func(t *testing.T) { t.Parallel() red := func() string { return "key: value" } didPanic := func() (panicked bool) { defer func() { if recover() != nil { panicked = true } }() _ = YAMLMarshalAsT(new(mockT), red, struct{}{}) return false }() if !didPanic { croakWantPanic(t, "YAMLMarshalAsT (with Redactor)") } }) // Nil redactors panic with the standard message before reaching YAMLEqBytes, // so this asserts the asBytes guard fires regardless of the YAML feature. t.Run("YAMLEqT/nil-func-string-panics-with-redactor-message", func(t *testing.T) { t.Parallel() mustPanicWithRedactor(t, "YAMLEqT", func() { var nilFn func() string _ = YAMLEqT(new(mockT), nilFn, "key: value") }) }) t.Run("YAMLUnmarshalAsT/nil-func-bytes-panics-with-redactor-message", func(t *testing.T) { t.Parallel() mustPanicWithRedactor(t, "YAMLUnmarshalAsT", func() { var nilFn func() []byte _ = YAMLUnmarshalAsT(new(mockT), struct{}{}, nilFn) }) }) // YAMLMarshalAsT/nil-redactor case omitted: yaml.Marshal panics first // when YAML is disabled, so the nil-Redactor guard inside asBytes is // unreachable here. Covered in the enable/yaml integration tests. } go-openapi-testify-c10ca71/internal/difflib/000077500000000000000000000000001520301377500210545ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/difflib/LICENSE000066400000000000000000000261351520301377500220700ustar00rootroot00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "[]" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright [yyyy] [name of copyright owner] Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. go-openapi-testify-c10ca71/internal/difflib/NOTICE000066400000000000000000000043251520301377500217640ustar00rootroot00000000000000Copyright 2025 go-swagger maintainers // SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 This software library, github.com/go-openapi/testify, includes software developed by the go-swagger and go-openapi maintainers ("go-swagger maintainers"). Licensed under the Apache License, Version 2.0 (the "License"); you may not use this software except in compliance with the License. You may obtain a copy of the License at This software is copied from, derived from, and inspired mainly by github.com/pmezard/go-difflib. It ships with copies of other software which license terms are recalled below. github.com/pmezard/go-difflib =========================== // SPDX-FileCopyrightText: Copyright (c) 2013, Patrick Mezard // SPDX-License-Identifier: BSD-3-Clause Copyright (c) 2013, Patrick Mezard All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. go-openapi-testify-c10ca71/internal/difflib/README.md000066400000000000000000000123431520301377500223360ustar00rootroot00000000000000# internal/difflib This is an internalized and modernized copy of [github.com/pmezard/go-difflib](https://github.com/pmezard/go-difflib), a partial port of Python's difflib module for generating textual diffs. ## Original Source **Source repository:** github.com/pmezard/go-difflib **Original license:** BSD 3-Clause License (see [NOTICE](./NOTICE)) **Copyright:** 2013 Patrick Mezard **Maintenance status:** ⚠️ No longer maintained (archived by author) `difflib` provides tools to compare sequences of strings and generate textual diffs in unified or context format. It implements Python's `SequenceMatcher` class and `unified_diff()`/`context_diff()` functions. ## Why Internalized This fork of testify maintains **zero external dependencies** for the core assertion packages. By internalizing `difflib`, we: 1. Eliminate the external dependency on an **unmaintained package** (last updated 2014) 2. Gain full control to apply modernizations aligned with our go1.25 target 3. Can apply targeted fixes and optimizations specific to testify's use cases ## Modernizations Applied This internalized copy has been significantly modernized and refactored from the original go-difflib codebase: ### Go Language Features (Go 1.21+) - **Built-in functions:** Removed custom `min()` and `max()` functions in favor of Go 1.21+ built-ins - **Modern operators:** Used `--` instead of `-= 1` for decrement operations - **Efficient conversions:** Used `strconv.Itoa()` instead of `fmt.Sprintf("%d", ...)` for integer-to-string conversion - **Buffer handling:** Used `bytes.Buffer.String()` instead of `string(bytes.Buffer.Bytes())` - Used `new(bytes.Buffer)` instead of `&bytes.Buffer{}` ### Code Organization & Complexity Reduction - **Function extraction:** Refactored complex functions by extracting helper functions: - `writeGroup()` - Handles writing diff groups - `writeEqual()` - Writes unchanged lines - `writeReplaceOrDelete()` - Writes deleted/replaced lines (prefix `-`) - `writeReplaceOrInsert()` - Writes inserted/replaced lines (prefix `+`) - **Method reorganization:** Reordered methods for better logical flow (public methods first, then helpers) - **Named constants:** Added named constants for magic numbers (e.g., `hundred = 100`, `maxDisplayElements = 200`) ### Code Quality - **Godoc compliance:** Updated all function comments to start with the function name for proper godoc generation - `// Set two sequences` → `// SetSeqs sets two sequences` - `// Return list of triples` → `// GetMatchingBlocks return the list of triples` - **Linting compliance:** Removed blank identifiers in range loops where value is unused - `for s, _ := range` → `for s := range` - **Modern control flow:** Replaced if-else chains with switch statements for better readability - **Simplified logic:** Improved boolean expressions using De Morgan's laws - `!(len(group) == 1 && group[0].Tag == 'e')` → `(len(group) != 1 || group[0].Tag != 'e')` - **Struct literals:** Simplified composite literals where types are inferred - `OpCode{'e', 0, 1, 0, 1}` instead of `OpCode{Tag: 'e', I1: 0, I2: 1, J1: 0, J2: 1}` ### Documentation - **Comment punctuation:** Standardized comment formatting and added proper punctuation - **Code clarity:** Added inline comments for switch cases explaining tag meanings (`'r'`, `'d'`, `'i'`, `'e'`) ## API Compatibility The internalized copy maintains full API compatibility with the original go-difflib while incorporating the modernizations above. All public functions and types work identically to the upstream version. Key exports: - `SequenceMatcher` - Compares sequences of strings using the Ratcliff-Obershelp algorithm - `UnifiedDiff` / `WriteUnifiedDiff()` / `GetUnifiedDiffString()` - Generate unified diff format - `ContextDiff` / `WriteContextDiff()` / `GetContextDiffString()` - Generate context diff format - `SplitLines()` - Split strings on newlines while preserving them ## Use in Testify This package is used by testify's assertion functions to generate human-readable diffs when assertions fail, particularly for comparing strings, slices, and complex data structures. The diff output helps developers quickly identify what changed between expected and actual values during test failures. ## Enhancements - [x] Colorized output support ## Future Enhancements As an internalized dependency, this copy can receive targeted improvements: - **Potential:** Performance optimizations for large diffs - **Potential:** Enhanced diff algorithms for specific data types ## Maintenance This internalized copy is maintained as part of github.com/go-openapi/testify/v2 and follows the same Go version requirements (currently go1.25). It does not track upstream go-difflib releases, as the original repository is no longer maintained and this copy has diverged through modernization and refactoring. For issues or improvements specific to this internalized version, please file issues at: https://github.com/go-openapi/testify/issues ## License This library ships under the [SPDX-License-Identifier: Apache-2.0](./LICENSE) and acknowledges the original licensing terms (BSD-3-Clause-License) from the original source it was copied from: [NOTICE](./NOTICE). The original copyright and license terms are preserved in accordance with the BSD License requirements. go-openapi-testify-c10ca71/internal/difflib/difflib.go000066400000000000000000000134001520301377500230000ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package difflib import ( "bufio" "bytes" "fmt" "io" "strconv" "strings" ) // SplitLines splits a string on "\n" while preserving them. The output can be used // as input for the [UnifiedDiff] structure. func SplitLines(s string) []string { lines := strings.SplitAfter(s, "\n") lines[len(lines)-1] += "\n" return lines } // UnifiedDiff holds the unified diff parameters. type UnifiedDiff struct { *Options A []string // First sequence lines FromFile string // First file name FromDate string // First file time B []string // Second sequence lines ToFile string // Second file name ToDate string // Second file time Eol string // Headers end of line, defaults to LF Context int // Number of context lines wsE Printer wsD Printer wsU Printer wsI Printer wsO Printer wf Formatter } func (u *UnifiedDiff) applyWriter(buf *bufio.Writer) { u.wsE = u.EqualPrinter(buf) u.wsD = u.DeletePrinter(buf) u.wsU = u.UpdatePrinter(buf) u.wsI = u.InsertPrinter(buf) u.wsO = u.OtherPrinter(buf) u.wf = u.Formatter(buf) } // GetUnifiedDiffString is like [WriteUnifiedDiff] but returns the diff as a string instead of writing to an [io.Writer]. func GetUnifiedDiffString(diff UnifiedDiff) (string, error) { w := new(bytes.Buffer) err := WriteUnifiedDiff(w, diff) return w.String(), err } // WriteUnifiedDiff writes the comparison between two sequences of lines. // It generates the delta as a unified diff. // // Unified diffs are a compact way of showing line changes and a few // lines of context. The number of context lines is set by 'n' which // defaults to three. // // # Format // // By default, the diff control lines (those with ---, +++, or @@) are // created with a trailing newline. This is helpful so that inputs // created from file.readlines() result in diffs that are suitable for // file.writelines() since both the inputs and outputs have trailing // newlines. // // For inputs that do not have trailing newlines, set the lineterm // argument to "" so that the output will be uniformly newline free. // // The unidiff format normally has a header for filenames and modification // 'fromfile', 'tofile', 'fromfiledate', and 'tofiledate'. // // The modification times are normally expressed in the ISO 8601 format. func WriteUnifiedDiff(writer io.Writer, diff UnifiedDiff) error { diff.Options = optionsWithDefaults(diff.Options) buf := bufio.NewWriter(writer) defer buf.Flush() diff.applyWriter(buf) if len(diff.Eol) == 0 { diff.Eol = "\n" } m := NewMatcher(diff.A, diff.B) groups := m.GetGroupedOpCodes(diff.Context) if len(groups) == 0 { return nil } if err := writeFirstGroup(groups[0], diff); err != nil { return err } for _, g := range groups[1:] { if err := writeGroup(g, diff); err != nil { return err } } return nil } func writeFirstGroup(g []OpCode, diff UnifiedDiff) error { fromDate := "" if len(diff.FromDate) > 0 { fromDate = "\t" + diff.FromDate } toDate := "" if len(diff.ToDate) > 0 { toDate = "\t" + diff.ToDate } if diff.FromFile != "" || diff.ToFile != "" { err := diff.wf("--- %s%s%s", diff.FromFile, fromDate, diff.Eol) if err != nil { return err } err = diff.wf("+++ %s%s%s", diff.ToFile, toDate, diff.Eol) if err != nil { return err } } return writeGroup(g, diff) } func writeGroup(group []OpCode, diff UnifiedDiff) error { first, last := group[0], group[len(group)-1] range1 := formatRangeUnified(first.I1, last.I2) range2 := formatRangeUnified(first.J1, last.J2) if err := diff.wf("@@ -%s +%s @@%s", range1, range2, diff.Eol); err != nil { return err } for _, c := range group { // 'r' (replace) // 'd' (delete) // 'i' (insert) // 'e' (equal) if c.Tag != 'r' && c.Tag != 'e' && c.Tag != 'd' && c.Tag != 'i' { continue } i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 switch c.Tag { case 'e': if err := writeEqual(diff.A[i1:i2], diff.wsE); err != nil { return err } case 'd': if err := writeReplaceOrDelete(diff.A[i1:i2], diff.wsD); err != nil { return err } case 'i': if err := writeReplaceOrInsert(diff.B[j1:j2], diff.wsI); err != nil { return err } case 'r': if err := writeReplaceOrDelete(diff.A[i1:i2], diff.wsD); err != nil { return err } if err := writeReplaceOrInsert(diff.B[j1:j2], diff.wsI); err != nil { return err } } } return nil } func writeEqual(lines []string, ws Printer) error { for _, line := range lines { if err := ws(" " + line); err != nil { return err } } return nil } func writeReplaceOrDelete(lines []string, ws Printer) error { for _, line := range lines { if err := ws("-" + line); err != nil { return err } } return nil } func writeReplaceOrInsert(lines []string, ws Printer) error { for _, line := range lines { if err := ws("+" + line); err != nil { return err } } return nil } // Convert range to the "ed" format. func formatRangeContext(start, stop int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ beginning := start + 1 // lines start numbering with one length := stop - start if length == 0 { beginning-- // empty ranges begin at line just before the range } if length <= 1 { return strconv.Itoa(beginning) } return fmt.Sprintf("%d,%d", beginning, beginning+length-1) } // Convert range to the "ed" format. func formatRangeUnified(start, stop int) string { // Per the diff spec at http://www.unix.org/single_unix_specification/ beginning := start + 1 // lines start numbering with one length := stop - start if length == 1 { return strconv.Itoa(beginning) } if length == 0 { beginning-- // empty ranges begin at line just before the range } return fmt.Sprintf("%d,%d", beginning, length) } go-openapi-testify-c10ca71/internal/difflib/difflib_benchmarks_test.go000066400000000000000000000010531520301377500262350ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package difflib import ( "strings" "testing" ) func BenchmarkSplitLines100(b *testing.B) { b.Run("splitLines", benchmarkSplitLines(100)) } func BenchmarkSplitLines10000(b *testing.B) { b.Run("splitLines", benchmarkSplitLines(10000)) } func benchmarkSplitLines(count int) func(*testing.B) { return func(b *testing.B) { str := strings.Repeat("foo\n", count) b.ResetTimer() n := 0 for b.Loop() { n += len(SplitLines(str)) } } } go-openapi-testify-c10ca71/internal/difflib/difflib_test.go000066400000000000000000000372001520301377500240430ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package difflib import ( "bytes" "fmt" "reflect" "strings" "testing" ) func TestGetOptCodes(t *testing.T) { a := "qabxcd" b := "abycdf" s := NewMatcher(splitChars(a), splitChars(b)) w := &bytes.Buffer{} for _, op := range s.GetOpCodes() { fmt.Fprintf(w, "%s a[%d:%d], (%s) b[%d:%d] (%s)\n", string(op.Tag), op.I1, op.I2, a[op.I1:op.I2], op.J1, op.J2, b[op.J1:op.J2]) } result := w.String() expected := `d a[0:1], (q) b[0:0] () e a[1:3], (ab) b[0:2] (ab) r a[3:4], (x) b[2:3] (y) e a[4:6], (cd) b[3:5] (cd) i a[6:6], () b[5:6] (f) ` if expected != result { t.Errorf("unexpected op codes: \n%s", result) } } func TestGroupedOpCodes(t *testing.T) { a := []string{} for i := 0; i != 39; i++ { a = append(a, fmt.Sprintf("%02d", i)) } b := make([]string, 0, len(a)+3) b = append(b, a[:8]...) b = append(b, " i") b = append(b, a[8:19]...) b = append(b, " x") b = append(b, a[20:22]...) b = append(b, a[27:34]...) b = append(b, " y") b = append(b, a[35:]...) s := NewMatcher(a, b) w := &bytes.Buffer{} for _, g := range s.GetGroupedOpCodes(-1) { fmt.Fprintf(w, "group\n") for _, op := range g { fmt.Fprintf(w, " %s, %d, %d, %d, %d\n", string(op.Tag), op.I1, op.I2, op.J1, op.J2) } } result := w.String() expected := `group e, 5, 8, 5, 8 i, 8, 8, 8, 9 e, 8, 11, 9, 12 group e, 16, 19, 17, 20 r, 19, 20, 20, 21 e, 20, 22, 21, 23 d, 22, 27, 23, 23 e, 27, 30, 23, 26 group e, 31, 34, 27, 30 r, 34, 35, 30, 31 e, 35, 38, 31, 34 ` if expected != result { t.Errorf("unexpected op codes: \n%s", result) } } func rep(s string, count int) string { return strings.Repeat(s, count) } func TestWithAsciiOneInsert(t *testing.T) { sm := NewMatcher(splitChars(rep("b", 100)), splitChars("a"+rep("b", 100))) assertEqual(t, sm.GetOpCodes(), []OpCode{{'i', 0, 0, 0, 1}, {'e', 0, 100, 1, 101}}) assertEqual(t, len(sm.bPopular), 0) sm = NewMatcher(splitChars(rep("b", 100)), splitChars(rep("b", 50)+"a"+rep("b", 50))) assertEqual(t, sm.GetOpCodes(), []OpCode{{'e', 0, 50, 0, 50}, {'i', 50, 50, 50, 51}, {'e', 50, 100, 51, 101}}) assertEqual(t, len(sm.bPopular), 0) } func TestWithAsciiOnDelete(t *testing.T) { sm := NewMatcher(splitChars(rep("a", 40)+"c"+rep("b", 40)), splitChars(rep("a", 40)+rep("b", 40))) assertEqual(t, sm.GetOpCodes(), []OpCode{{'e', 0, 40, 0, 40}, {'d', 40, 41, 40, 40}, {'e', 41, 81, 40, 80}}) } func TestSFBugsComparingEmptyLists(t *testing.T) { groups := NewMatcher(nil, nil).GetGroupedOpCodes(-1) assertEqual(t, len(groups), 0) diff := UnifiedDiff{ FromFile: "Original", ToFile: "Current", Context: 3, } result, err := GetUnifiedDiffString(diff) assertEqual(t, err, nil) assertEqual(t, result, "") } func TestOutputFormatRangeFormatUnified(t *testing.T) { // Per the diff spec at http://www.unix.org/single_unix_specification/ // // Each field shall be of the form: // %1d", if the range contains exactly one line, // and: // "%1d,%1d", , otherwise. // If a range is empty, its beginning line number shall be the number of // the line just before the range, or 0 if the empty range starts the file. fm := formatRangeUnified assertEqual(t, fm(3, 3), "3,0") assertEqual(t, fm(3, 4), "4") assertEqual(t, fm(3, 5), "4,2") assertEqual(t, fm(3, 6), "4,3") assertEqual(t, fm(0, 0), "0,0") } func TestOutputFormatRangeFormatContext(t *testing.T) { // Per the diff spec at http://www.unix.org/single_unix_specification/ // // The range of lines in file1 shall be written in the following format // if the range contains two or more lines: // "*** %d,%d ****\n", , // and the following format otherwise: // "*** %d ****\n", // The ending line number of an empty range shall be the number of the preceding line, // or 0 if the range is at the start of the file. // // Next, the range of lines in file2 shall be written in the following format // if the range contains two or more lines: // "--- %d,%d ----\n", , // and the following format otherwise: // "--- %d ----\n", fm := formatRangeContext assertEqual(t, fm(3, 3), "3") assertEqual(t, fm(3, 4), "4") assertEqual(t, fm(3, 5), "4,5") assertEqual(t, fm(3, 6), "4,6") assertEqual(t, fm(0, 0), "0") } func TestOutputFormatTabDelimiter(t *testing.T) { diff := UnifiedDiff{ A: splitChars("one"), B: splitChars("two"), FromFile: "Original", FromDate: "2005-01-26 23:30:50", ToFile: "Current", ToDate: "2010-04-12 10:20:52", Eol: "\n", } ud, err := GetUnifiedDiffString(diff) assertEqual(t, err, nil) result := SplitLines(ud)[:2] assertEqual(t, result, []string{ "--- Original\t2005-01-26 23:30:50\n", "+++ Current\t2010-04-12 10:20:52\n", }) } func TestOutputFormatNoTrailingTabOnEmptyFiledate(t *testing.T) { diff := UnifiedDiff{ A: splitChars("one"), B: splitChars("two"), FromFile: "Original", ToFile: "Current", Eol: "\n", } ud, err := GetUnifiedDiffString(diff) assertEqual(t, err, nil) assertEqual(t, SplitLines(ud)[:2], []string{"--- Original\n", "+++ Current\n"}) } func TestOmitFilenames(t *testing.T) { diff := UnifiedDiff{ A: SplitLines("o\nn\ne\n"), B: SplitLines("t\nw\no\n"), Eol: "\n", } ud, err := GetUnifiedDiffString(diff) assertEqual(t, err, nil) assertEqual(t, SplitLines(ud), []string{ "@@ -0,0 +1,2 @@\n", "+t\n", "+w\n", "@@ -2,2 +3,0 @@\n", "-n\n", "-e\n", "\n", }) } func TestSplitLines(t *testing.T) { allTests := []struct { input string want []string }{ {"foo", []string{"foo\n"}}, {"foo\nbar", []string{"foo\n", "bar\n"}}, {"foo\nbar\n", []string{"foo\n", "bar\n", "\n"}}, } for _, test := range allTests { assertEqual(t, SplitLines(test.input), test.want) } } func assertEqual(t *testing.T, a, b any) { t.Helper() if !reflect.DeepEqual(a, b) { t.Errorf("%v != %v", a, b) } } func splitChars(s string) []string { chars := make([]string, 0, len(s)) // Assume ASCII inputs for _, r := range s { chars = append(chars, string(r)) } return chars } // TestSequenceMatcherCaching tests that GetMatchingBlocks and GetOpCodes // return cached results when called multiple times. func TestSequenceMatcherCaching(t *testing.T) { a := splitChars("abc") b := splitChars("abd") sm := NewMatcher(a, b) // Call GetMatchingBlocks twice - second call should use cache blocks1 := sm.GetMatchingBlocks() blocks2 := sm.GetMatchingBlocks() assertEqual(t, blocks1, blocks2) // Call GetOpCodes twice - second call should use cache codes1 := sm.GetOpCodes() codes2 := sm.GetOpCodes() assertEqual(t, codes1, codes2) } // TestSetSeqSamePointer tests that SetSeq1 and SetSeq2 do NOT reset caches // when the same slice pointer is passed (early return optimization). func TestSetSeqSamePointer(t *testing.T) { a := []string{"a", "b", "c"} b := []string{"x", "y", "z"} sm := NewMatcher(a, b) // Get initial blocks blocks1 := sm.GetMatchingBlocks() // Set the same sequences again using SetSeqs // Since we pass the same slice pointers, the caches should NOT be reset // (the implementation checks pointer equality for early return) sm.SetSeq1(a) sm.SetSeq2(b) // Blocks should remain cached (not nil) after setting the same sequences // so GetMatchingBlocks returns the cached result blocks2 := sm.GetMatchingBlocks() assertEqual(t, blocks1, blocks2) } // TestSequenceMatcherWithIsJunk tests the junk filtering functionality. func TestSequenceMatcherWithIsJunk(t *testing.T) { // Test with a simple IsJunk function that marks whitespace as junk a := []string{"a", " ", "b", " ", "c"} b := []string{"a", "b", "c"} sm := NewMatcher(nil, nil) sm.IsJunk = func(s string) bool { return s == " " } sm.SetSeqs(a, b) // The matcher should still find matches but handle junk elements blocks := sm.GetMatchingBlocks() if len(blocks) == 0 { t.Error("expected some matching blocks with junk filter") } } // TestAutoJunkWithLargeSequence tests the autoJunk feature with sequences >= 200 elements. func TestAutoJunkWithLargeSequence(t *testing.T) { // Create a sequence with more than 200 elements where one element appears // more than 1% of the time (which makes it "popular" and gets filtered) a := make([]string, 250) b := make([]string, 250) // Fill with unique elements for i := range 250 { a[i] = fmt.Sprintf("a%d", i) b[i] = fmt.Sprintf("a%d", i) } // Make element "common" appear more than 1% (3+ times out of 250) for i := range 10 { b[i] = "common" } sm := NewMatcher(a, b) // The popular element "common" should be filtered if len(sm.bPopular) == 0 { t.Log("bPopular might be empty if 'common' doesn't exceed threshold, which is expected") } // The matcher should still work blocks := sm.GetMatchingBlocks() if blocks == nil { t.Error("expected matching blocks") } } // TestFindLongestMatchWithJunk tests finding longest match with junk elements. func TestFindLongestMatchWithJunk(t *testing.T) { // Create sequences where junk elements are adjacent to interesting matches a := []string{"x", "a", "b", "c", "y"} b := []string{"a", "b", "c"} sm := NewMatcher(nil, nil) // Mark x and y as junk sm.IsJunk = func(s string) bool { return s == "x" || s == "y" } sm.SetSeqs(a, b) blocks := sm.GetMatchingBlocks() // Should find the "a", "b", "c" match found := false for _, block := range blocks { if block.Size == 3 { found = true break } } if !found { t.Error("expected to find a match of size 3") } } // TestFindLongestMatchExtension tests the extension of matches past popular elements. func TestFindLongestMatchExtension(t *testing.T) { // Test cases that exercise the match extension loops in findLongestMatch a := []string{"a", "b", "c", "d", "e"} b := []string{"x", "b", "c", "d", "y"} sm := NewMatcher(a, b) blocks := sm.GetMatchingBlocks() // Should find the "b", "c", "d" match found := false for _, block := range blocks { if block.Size >= 3 { found = true break } } if !found { t.Error("expected to find a match of size >= 3") } } // TestJunkFilteringInChainB tests the IsJunk function in chainB. func TestJunkFilteringInChainB(t *testing.T) { // Create a matcher with junk filtering a := []string{"line1", "junk", "line2", "junk", "line3"} b := []string{"line1", "junk", "line2", "junk", "line3", "junk"} sm := NewMatcher(nil, nil) sm.IsJunk = func(s string) bool { return s == "junk" } sm.SetSeqs(a, b) // Verify junk is correctly identified if !sm.isBJunk("junk") { t.Error("expected 'junk' to be identified as junk") } // Non-junk should not be identified as junk if sm.isBJunk("line1") { t.Error("expected 'line1' to not be junk") } // Should still be able to find matches blocks := sm.GetMatchingBlocks() if len(blocks) == 0 { t.Error("expected some matching blocks") } } // TestMatchExtensionWithJunkOnBothSides tests junk matching extension. func TestMatchExtensionWithJunkOnBothSides(t *testing.T) { // Create sequences where junk elements surround interesting matches // to exercise the junk extension loops in findLongestMatch a := []string{"junk1", "junk2", "a", "b", "c", "junk3", "junk4"} b := []string{"junk1", "junk2", "a", "b", "c", "junk3", "junk4"} sm := NewMatcher(nil, nil) sm.IsJunk = func(s string) bool { return strings.HasPrefix(s, "junk") } sm.SetSeqs(a, b) blocks := sm.GetMatchingBlocks() // Should find matches including junk elements that are identical totalSize := 0 for _, block := range blocks { totalSize += block.Size } if totalSize < 3 { t.Errorf("expected total match size >= 3, got %d", totalSize) } } // TestFindLongestMatchBreakCondition tests the j >= bhi break condition. func TestFindLongestMatchBreakCondition(t *testing.T) { // Create sequences that will trigger the j >= bhi condition // This happens when b2j has indices that exceed the search range a := []string{"x", "y", "z"} b := []string{"a", "b", "x", "y", "z"} sm := NewMatcher(a, b) blocks := sm.GetMatchingBlocks() // Should find the "x", "y", "z" match found := false for _, block := range blocks { if block.Size == 3 { found = true break } } if !found { t.Error("expected to find a match of size 3") } } // TestAutoJunkPopularElements tests the autoJunk filtering of popular elements. func TestAutoJunkPopularElements(t *testing.T) { // Create a sequence with > 200 elements where one element appears // more than 1% of the time n := 250 a := make([]string, n) b := make([]string, n) // Fill with mostly unique elements for i := range n { a[i] = fmt.Sprintf("line%d", i) b[i] = fmt.Sprintf("line%d", i) } // Make "popular" appear more than 1% (more than 2-3 times) // We need it to appear > n/100 + 1 times = 3+ times for i := range 10 { b[i*25] = "popular" } sm := NewMatcher(a, b) // The element "popular" should be filtered as popular if len(sm.bPopular) == 0 { t.Log("bPopular might be empty if threshold not exceeded") } // Matcher should still produce valid results blocks := sm.GetMatchingBlocks() if blocks == nil { t.Error("expected non-nil matching blocks") } } // TestFindLongestMatchWithJunkExtension tests the junk extension loops // at the end of findLongestMatch function. func TestFindLongestMatchWithJunkExtension(t *testing.T) { // Create sequences where junk elements are adjacent to matches // This should trigger the junk extension loops a := []string{"junk", "a", "b", "c", "junk"} b := []string{"junk", "a", "b", "c", "junk"} sm := NewMatcher(nil, nil) sm.IsJunk = func(s string) bool { return s == "junk" } sm.SetSeqs(a, b) blocks := sm.GetMatchingBlocks() // Should find matches including junk extension totalSize := 0 for _, block := range blocks { totalSize += block.Size } // The non-junk elements (a, b, c) should definitely match. // Junk elements may or may not be included depending on extension behavior. if totalSize < 3 { t.Errorf("expected total match size >= 3, got %d", totalSize) } } // TestFindLongestMatchEdgeCases tests edge cases in findLongestMatch. func TestFindLongestMatchEdgeCases(t *testing.T) { // Test case where matches are found at the end of sequences a := []string{"unique1", "unique2", "match"} b := []string{"other1", "other2", "match"} sm := NewMatcher(a, b) blocks := sm.GetMatchingBlocks() // Should find the "match" element found := false for _, block := range blocks { if block.Size == 1 && block.A == 2 && block.B == 2 { found = true break } } if !found { t.Error("expected to find a match at the end") } } // TestMatcherWithBothSequencesSame tests the matcher with identical sequences. func TestMatcherWithBothSequencesSame(t *testing.T) { a := []string{"line1", "line2", "line3"} b := []string{"line1", "line2", "line3"} sm := NewMatcher(a, b) blocks := sm.GetMatchingBlocks() // Should find all lines match if len(blocks) < 1 { t.Error("expected at least one matching block") } // The last block is always a sentinel with size 0 for _, block := range blocks[:len(blocks)-1] { if block.Size != 3 { t.Errorf("expected matching block of size 3, got %d", block.Size) } } } // TestWriteUnifiedDiffWithDefaultEol tests that default EOL is applied. func TestWriteUnifiedDiffWithDefaultEol(t *testing.T) { // Test that when Eol is empty, it defaults to "\n" diff := UnifiedDiff{ A: splitChars("abc"), B: splitChars("abd"), FromFile: "file1", ToFile: "file2", // Eol not set - should default to "\n" } result, err := GetUnifiedDiffString(diff) if err != nil { t.Fatalf("unexpected error: %v", err) } if !strings.Contains(result, "\n") { t.Error("expected newlines in output") } } go-openapi-testify-c10ca71/internal/difflib/doc.go000066400000000000000000000016751520301377500221610ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package difflib is a partial port of [Python difflib module]. // // It provides tools to compare sequences of strings and generate textual differences. // // The following class and functions have been ported: // // - [SequenceMatcher] // - [UnifiedDiff] // // Getting unified diffs was the main goal of the port. // // Keep in mind that this code is mostly suitable to output text differences in a human friendly way, // there are no guarantees that the generated diffs are consumable by [patch(1)]. // // This package was adopted from [github.com/pmezard/go-difflib] which // is no longer maintained. // // [github.com/pmezard/go-difflib]: https://github.com/pmezard/go-difflib // [Python difflib module]: https://docs.python.org/3/library/difflib.html#module-difflib // [patch(1)]: https://www.unix.com/man_page/linux/1/patch package difflib go-openapi-testify-c10ca71/internal/difflib/matcher.go000066400000000000000000000316311520301377500230320ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package difflib type Match struct { A int B int Size int } type OpCode struct { Tag byte I1 int I2 int J1 int J2 int } // SequenceMatcher compares sequence of strings. // // The basic algorithm predates, and is a little fancier than, an algorithm // published in the late 1980's by Ratcliff and Obershelp (R-O) under the // hyperbolic name "[gestalt pattern matching]". // // The basic idea is to find the longest contiguous matching subsequence that contains // no "junk" elements (R-O doesn't address junk). The same idea is then applied // recursively to the pieces of the sequences to the left and to the right // of the matching subsequence. This does not yield minimal edit // sequences, but does tend to yield matches that "look right" to people. // // The [SequenceMatcher] tries to compute a "human-friendly diff" between two // sequences. Unlike e.g. UNIX(tm) diff, the fundamental notion is the // longest *contiguous* & junk-free matching subsequence. That's what // catches peoples' eyes. The Windows(tm) windiff has another interesting // notion, pairing up elements that appear uniquely in each sequence. // // That, and the method here, appear to yield more intuitive difference // reports than does diff. This method appears to be the least vulnerable // to syncing up on blocks of "junk lines", though (like blank lines in // ordinary text files, or maybe "

" lines in HTML files). That may be // because this is the only method of the 3 that has a *concept* of // "junk" . // // Timing: Basic R-O is cubic time worst case and quadratic time expected // case. [SequenceMatcher] is quadratic time for the worst case and has // expected-case behavior dependent in a complicated way on how many // elements the sequences have in common; best case time is linear. // // [gestalt pattern matching]: https://en.wikipedia.org/wiki/Gestalt_pattern_matching type SequenceMatcher struct { a []string b []string b2j map[string][]int IsJunk func(string) bool autoJunk bool bJunk map[string]struct{} matchingBlocks []Match fullBCount map[string]int bPopular map[string]struct{} opCodes []OpCode } // NewMatcher builds a new [SequenceMatcher] comparing 2 sequences of strings. func NewMatcher(a, b []string) *SequenceMatcher { m := SequenceMatcher{autoJunk: true} m.SetSeqs(a, b) return &m } // SetSeqs sets two sequences to be compared. func (m *SequenceMatcher) SetSeqs(a, b []string) { m.SetSeq1(a) m.SetSeq2(b) } // SetSeq1 sets the first sequence to be compared. The second sequence to be compared is not changed. // // [SequenceMatcher] computes and caches detailed information about the second // sequence, so if you want to compare one sequence S against many sequences, // use .SetSeq2(s) once and call .SetSeq1(x) repeatedly for each of the other // sequences. // // See also [SequenceMatcher.SetSeqs] and [SequenceMatcher.SetSeq2]. func (m *SequenceMatcher) SetSeq1(a []string) { if &a == &m.a { return } m.a = a m.matchingBlocks = nil m.opCodes = nil } // SetSeq2 sets the second sequence to be compared. The first sequence to be compared is not changed. func (m *SequenceMatcher) SetSeq2(b []string) { if &b == &m.b { return } m.b = b m.matchingBlocks = nil m.opCodes = nil m.fullBCount = nil m.chainB() } // GetMatchingBlocks return the list of triples describing matching subsequences. // // Each triple is of the form (i, j, n), and means that // a[i:i+n] == b[j:j+n]. The triples are monotonically increasing in // i and in j. It's also guaranteed that if (i, j, n) and (i', j', n') are // adjacent triples in the list, and the second is not the last triple in the // list, then i+n != i' or j+n != j'. IOW, adjacent triples never describe // adjacent equal blocks. // // The last triple is a dummy, (len(a), len(b), 0), and is the only // triple with n==0. func (m *SequenceMatcher) GetMatchingBlocks() []Match { if m.matchingBlocks != nil { return m.matchingBlocks } var matchBlocks func(alo, ahi, blo, bhi int, matched []Match) []Match matchBlocks = func(alo, ahi, blo, bhi int, matched []Match) []Match { match := m.findLongestMatch(alo, ahi, blo, bhi) i, j, k := match.A, match.B, match.Size if match.Size > 0 { if alo < i && blo < j { matched = matchBlocks(alo, i, blo, j, matched) } matched = append(matched, match) if i+k < ahi && j+k < bhi { matched = matchBlocks(i+k, ahi, j+k, bhi, matched) } } return matched } matched := matchBlocks(0, len(m.a), 0, len(m.b), nil) // It's possible that we have adjacent equal blocks in the // matching_blocks list now. nonAdjacent := []Match{} i1, j1, k1 := 0, 0, 0 for _, b := range matched { // Is this block adjacent to i1, j1, k1? i2, j2, k2 := b.A, b.B, b.Size if i1+k1 == i2 && j1+k1 == j2 { // Yes, so collapse them -- this just increases the length of // the first block by the length of the second, and the first // block so lengthened remains the block to compare against. k1 += k2 } else { // Not adjacent. Remember the first block (k1==0 means it's // the dummy we started with), and make the second block the // new block to compare against. if k1 > 0 { nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) } i1, j1, k1 = i2, j2, k2 } } if k1 > 0 { nonAdjacent = append(nonAdjacent, Match{i1, j1, k1}) } nonAdjacent = append(nonAdjacent, Match{len(m.a), len(m.b), 0}) m.matchingBlocks = nonAdjacent return m.matchingBlocks } // GetOpCodes return the list of 5-tuples describing how to turn a into b. // // Each tuple is of the form (tag, i1, i2, j1, j2). The first tuple // has i1 == j1 == 0, and remaining tuples have i1 == the i2 from the // tuple preceding it, and likewise for j1 == the previous j2. // // The tags are characters, with these meanings: // // 'r' (replace): a[i1:i2] should be replaced by b[j1:j2] // // 'd' (delete): a[i1:i2] should be deleted, j1==j2 in this case. // // 'i' (insert): b[j1:j2] should be inserted at a[i1:i1], i1==i2 in this case. // // 'e' (equal): a[i1:i2] == b[j1:j2]. func (m *SequenceMatcher) GetOpCodes() []OpCode { if m.opCodes != nil { return m.opCodes } i, j := 0, 0 matching := m.GetMatchingBlocks() opCodes := make([]OpCode, 0, len(matching)) for _, m := range matching { // invariant: we've pumped out correct diffs to change // a[:i] into b[:j], and the next matching block is // a[ai:ai+size] == b[bj:bj+size]. So we need to pump // out a diff to change a[i:ai] into b[j:bj], pump out // the matching block, and move (i,j) beyond the match ai, bj, size := m.A, m.B, m.Size tag := byte(0) switch { case i < ai && j < bj: tag = 'r' case i < ai: tag = 'd' case j < bj: tag = 'i' } if tag > 0 { opCodes = append(opCodes, OpCode{tag, i, ai, j, bj}) } i, j = ai+size, bj+size // the list of matching blocks is terminated by a // sentinel with size 0 if size > 0 { opCodes = append(opCodes, OpCode{'e', ai, i, bj, j}) } } m.opCodes = opCodes return m.opCodes } // GetGroupedOpCodes isolates changed clusters by eliminating ranges with no changes. // // Return a generator of groups with up to n lines of context. // Each group is in the same format as returned by [SequenceMatcher.GetOpCodes]. func (m *SequenceMatcher) GetGroupedOpCodes(n int) [][]OpCode { if n < 0 { n = 3 } codes := m.GetOpCodes() if len(codes) == 0 { codes = []OpCode{{'e', 0, 1, 0, 1}} } // Fixup leading and trailing groups if they show no changes. if codes[0].Tag == 'e' { c := codes[0] i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 codes[0] = OpCode{c.Tag, max(i1, i2-n), i2, max(j1, j2-n), j2} } if codes[len(codes)-1].Tag == 'e' { c := codes[len(codes)-1] i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 codes[len(codes)-1] = OpCode{c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n)} } nn := n + n groups := [][]OpCode{} group := []OpCode{} for _, c := range codes { i1, i2, j1, j2 := c.I1, c.I2, c.J1, c.J2 // End the current group and start a new one whenever // there is a large range with no changes. if c.Tag == 'e' && i2-i1 > nn { group = append(group, OpCode{ c.Tag, i1, min(i2, i1+n), j1, min(j2, j1+n), }) groups = append(groups, group) group = []OpCode{} i1, j1 = max(i1, i2-n), max(j1, j2-n) } group = append(group, OpCode{c.Tag, i1, i2, j1, j2}) } if len(group) > 0 && (len(group) != 1 || group[0].Tag != 'e') { groups = append(groups, group) } return groups } func (m *SequenceMatcher) chainB() { // Populate line -> index mapping b2j := map[string][]int{} for i, s := range m.b { indices := b2j[s] indices = append(indices, i) b2j[s] = indices } // Purge junk elements m.bJunk = map[string]struct{}{} if m.IsJunk != nil { junk := m.bJunk for s := range b2j { if m.IsJunk(s) { junk[s] = struct{}{} } } for s := range junk { delete(b2j, s) } } // Purge remaining popular elements const ( hundred = 100 maxDisplayElements = 2 * hundred ) popular := map[string]struct{}{} n := len(m.b) if m.autoJunk && n >= maxDisplayElements { ntest := n/hundred + 1 for s, indices := range b2j { if len(indices) > ntest { popular[s] = struct{}{} } } for s := range popular { delete(b2j, s) } } m.bPopular = popular m.b2j = b2j } func (m *SequenceMatcher) isBJunk(s string) bool { _, ok := m.bJunk[s] return ok } // Find longest matching block in a[alo:ahi] and b[blo:bhi]. // // If IsJunk is not defined: // // Return (i,j,k) such that a[i:i+k] is equal to b[j:j+k], where // // alo <= i <= i+k <= ahi // blo <= j <= j+k <= bhi // // and for all (i',j',k') meeting those conditions, // // k >= k' // i <= i' // and if i == i', j <= j' // // In other words, of all maximal matching blocks, return one that // starts earliest in a, and of all those maximal matching blocks that // start earliest in a, return the one that starts earliest in b. // // If IsJunk is defined, first the longest matching block is // determined as above, but with the additional restriction that no // junk element appears in the block. Then that block is extended as // far as possible by matching (only) junk elements on both sides. So // the resulting block never matches on junk except as identical junk // happens to be adjacent to an "interesting" match. // // If no blocks match, return (alo, blo, 0). func (m *SequenceMatcher) findLongestMatch(alo, ahi, blo, bhi int) Match { // CAUTION: stripping common prefix or suffix would be incorrect. // E.g., // ab // acab // Longest matching block is "ab", but if common prefix is // stripped, it's "a" (tied with "b"). UNIX(tm) diff does so // strip, so ends up claiming that ab is changed to acab by // inserting "ca" in the middle. That's minimal but unintuitive: // "it's obvious" that someone inserted "ac" at the front. // Windiff ends up at the same place as diff, but by pairing up // the unique 'b's and then matching the first two 'a's. besti, bestj, bestsize := alo, blo, 0 // find longest junk-free match // during an iteration of the loop, j2len[j] = length of longest // junk-free match ending with a[i-1] and b[j] j2len := map[int]int{} for i := alo; i != ahi; i++ { // look at all instances of a[i] in b; note that because // b2j has no junk keys, the loop is skipped if a[i] is junk newj2len := map[int]int{} for _, j := range m.b2j[m.a[i]] { // a[i] matches b[j] if j < blo { continue } if j >= bhi { break } k := j2len[j-1] + 1 newj2len[j] = k if k > bestsize { besti, bestj, bestsize = i-k+1, j-k+1, k } } j2len = newj2len } // Extend the best by non-junk elements on each end. In particular, // "popular" non-junk elements aren't in b2j, which greatly speeds // the inner loop above, but also means "the best" match so far // doesn't contain any junk *or* popular non-junk elements. for besti > alo && bestj > blo && !m.isBJunk(m.b[bestj-1]) && m.a[besti-1] == m.b[bestj-1] { besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 } for besti+bestsize < ahi && bestj+bestsize < bhi && !m.isBJunk(m.b[bestj+bestsize]) && m.a[besti+bestsize] == m.b[bestj+bestsize] { bestsize++ } // Now that we have a wholly interesting match (albeit possibly // empty!), we may as well suck up the matching junk on each // side of it too. Can't think of a good reason not to, and it // saves post-processing the (possibly considerable) expense of // figuring out what to do with it. In the case of an empty // interesting match, this is clearly the right thing to do, // because no other kind of match is possible in the regions. for besti > alo && bestj > blo && m.isBJunk(m.b[bestj-1]) && m.a[besti-1] == m.b[bestj-1] { besti, bestj, bestsize = besti-1, bestj-1, bestsize+1 } for besti+bestsize < ahi && bestj+bestsize < bhi && m.isBJunk(m.b[bestj+bestsize]) && m.a[besti+bestsize] == m.b[bestj+bestsize] { bestsize++ } return Match{A: besti, B: bestj, Size: bestsize} } go-openapi-testify-c10ca71/internal/difflib/options.go000066400000000000000000000040421520301377500230760ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package difflib import ( "bufio" "fmt" ) type ( // Formatter is a formatting function like [fmt.Printf]. Formatter func(format string, args ...any) error // Printer outputs a formatted string, e.g. to some underlying writer. Printer func(string) error ) type ( // FormatterBuilder is a function that builds a [Formatter] given a buffered [bufio.Writer]. FormatterBuilder func(*bufio.Writer) Formatter // PrinterBuilder is a function that builds a [Printer] given a buffered [bufio.Writer]. PrinterBuilder func(*bufio.Writer) Printer ) // Options to fine-tune the rendering of the diff output. type Options struct { EqualPrinter PrinterBuilder DeletePrinter PrinterBuilder UpdatePrinter PrinterBuilder InsertPrinter PrinterBuilder OtherPrinter PrinterBuilder Formatter FormatterBuilder } func optionsWithDefaults(in *Options) *Options { o := &Options{ EqualPrinter: defaultPrinterBuilder, DeletePrinter: defaultPrinterBuilder, UpdatePrinter: defaultPrinterBuilder, InsertPrinter: defaultPrinterBuilder, OtherPrinter: defaultPrinterBuilder, Formatter: defaultFormatterBuilder, } if in == nil { return o } if in.EqualPrinter != nil { o.EqualPrinter = in.EqualPrinter } if in.DeletePrinter != nil { o.DeletePrinter = in.DeletePrinter } if in.UpdatePrinter != nil { o.UpdatePrinter = in.UpdatePrinter } if in.InsertPrinter != nil { o.InsertPrinter = in.InsertPrinter } if in.OtherPrinter != nil { o.OtherPrinter = in.OtherPrinter } if in.Formatter != nil { o.Formatter = in.Formatter } return o } func DefaultPrinterBuilder(buf *bufio.Writer) Printer { return defaultPrinterBuilder(buf) } func defaultPrinterBuilder(buf *bufio.Writer) Printer { return func(s string) error { _, err := buf.WriteString(s) return err } } func defaultFormatterBuilder(buf *bufio.Writer) Formatter { return func(format string, args ...any) error { _, err := fmt.Fprintf(buf, format, args...) return err } } go-openapi-testify-c10ca71/internal/difflib/options_test.go000066400000000000000000000137771520301377500241540ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package difflib import ( "bufio" "strings" "testing" ) // a few colors for testing. // // The complete set may be found in ../assertions/enable/colors. const ( redMark = "\033[0;31m" greenMark = "\033[0;32m" yellowMark = "\033[0;33m" cyanMark = "\033[0;36m" endMark = "\033[0m" ) func TestOptions(t *testing.T) { const ( a = "(map[spew_test.stringer]int) (len=3) {\n" + "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" + "(spew_test.stringer) (len=1) stringer 2: (int) 2,\n" + "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" + "(spew_test.stringer) (len=1) stringer 5: (int) 3\n" + "}\n" b = "(map[spew_test.stringer]int) (len=3) {\n" + "(spew_test.stringer) (len=1) stringer 1: (int) 1,\n" + "(spew_test.stringer) (len=1) stringer 2: (int) 3,\n" + "(spew_test.stringer) (len=1) stringer 3: (int) 3\n" + "(spew_test.stringer) (len=1) stringer 4: (int) 8\n" + "(spew_test.stringer) (len=1) stringer 6: (int) 9\n" + "}\n" ) greenPrinterBuilder := ansiPrinterBuilder(greenMark) cyanPrinterBuilder := ansiPrinterBuilder(cyanMark) redPrinterBuilder := ansiPrinterBuilder(redMark) yellowPrinterBuilder := ansiPrinterBuilder(yellowMark) diff, err := GetUnifiedDiffString(UnifiedDiff{ A: SplitLines(a), B: SplitLines(b), FromFile: "Expected", FromDate: "", ToFile: "Actual", ToDate: "", Context: 1, Options: &Options{ EqualPrinter: greenPrinterBuilder, DeletePrinter: redPrinterBuilder, UpdatePrinter: cyanPrinterBuilder, InsertPrinter: yellowPrinterBuilder, }, }) if err != nil { t.Fatalf("did not expect an error, but got: %v", err) } //nolint:staticcheck // ST1018: for this test we specifically want to check escape sequences if !strings.Contains(diff, ` (spew_test.stringer) (len=1) stringer 1: (int) 1, -(spew_test.stringer) (len=1) stringer 2: (int) 2, +(spew_test.stringer) (len=1) stringer 2: (int) 3,  (spew_test.stringer) (len=1) stringer 3: (int) 3 -(spew_test.stringer) (len=1) stringer 5: (int) 3`, ) { t.Errorf("expected matching ansi color sequences for diff") } // a visualization is better in this case... t.Log("\n\nDiff:\n" + diff) } func ansiPrinterBuilder(mark string) PrinterBuilder { return func(w *bufio.Writer) Printer { return func(str string) (err error) { _, err = w.WriteString(mark) if err != nil { return } _, err = w.WriteString(str) if err != nil { return } _, err = w.WriteString(endMark) if err != nil { return } return nil } } } // TestDefaultPrinterBuilder tests the DefaultPrinterBuilder function. func TestDefaultPrinterBuilder(t *testing.T) { var buf strings.Builder w := bufio.NewWriter(&buf) printer := DefaultPrinterBuilder(w) err := printer("hello world") if err != nil { t.Fatalf("unexpected error: %v", err) } w.Flush() if buf.String() != "hello world" { t.Errorf("expected 'hello world', got %q", buf.String()) } } // TestOptionsWithAllCustomPrinters tests that all custom printers are applied. func TestOptionsWithAllCustomPrinters(t *testing.T) { const ( a = "line1\nline2\nline3\n" b = "line1\nmodified\nline3\nnewline\n" ) // Create custom printers for all types including OtherPrinter and Formatter customPrinter := func(prefix string) PrinterBuilder { return func(w *bufio.Writer) Printer { return func(str string) error { _, err := w.WriteString(prefix + str) return err } } } // customFormatter is a simplified formatter for testing purposes only. // It only handles string arguments with %s placeholders. // This is sufficient for the diff header format strings used in the library. customFormatter := func(w *bufio.Writer) Formatter { return func(format string, args ...any) error { s := "[FMT]" + format for _, arg := range args { if str, ok := arg.(string); ok { s = strings.Replace(s, "%s", str, 1) } } _, err := w.WriteString(s) return err } } diff, err := GetUnifiedDiffString(UnifiedDiff{ A: SplitLines(a), B: SplitLines(b), FromFile: "Original", ToFile: "Modified", Context: 1, Options: &Options{ EqualPrinter: customPrinter("[EQ]"), DeletePrinter: customPrinter("[DEL]"), UpdatePrinter: customPrinter("[UPD]"), InsertPrinter: customPrinter("[INS]"), OtherPrinter: customPrinter("[OTH]"), Formatter: customFormatter, }, }) if err != nil { t.Fatalf("unexpected error: %v", err) } // Verify the custom formatter was used for headers if !strings.Contains(diff, "[FMT]") { t.Error("expected custom formatter to be used") } // Verify custom printers were used if !strings.Contains(diff, "[EQ]") { t.Error("expected equal printer to be used") } if !strings.Contains(diff, "[DEL]") { t.Error("expected delete printer to be used") } if !strings.Contains(diff, "[INS]") { t.Error("expected insert printer to be used") } } // TestOptionsWithDefaults tests that default options are applied correctly. func TestOptionsWithDefaults(t *testing.T) { // Test with nil options opts := optionsWithDefaults(nil) if opts == nil { t.Fatal("expected non-nil options") } if opts.EqualPrinter == nil { t.Error("expected EqualPrinter to be set") } if opts.DeletePrinter == nil { t.Error("expected DeletePrinter to be set") } if opts.UpdatePrinter == nil { t.Error("expected UpdatePrinter to be set") } if opts.InsertPrinter == nil { t.Error("expected InsertPrinter to be set") } if opts.OtherPrinter == nil { t.Error("expected OtherPrinter to be set") } if opts.Formatter == nil { t.Error("expected Formatter to be set") } // Test with partial options (only some fields set) partialOpts := &Options{ EqualPrinter: ansiPrinterBuilder(greenMark), } opts = optionsWithDefaults(partialOpts) if opts.EqualPrinter == nil { t.Error("expected EqualPrinter to be preserved") } if opts.DeletePrinter == nil { t.Error("expected DeletePrinter to have default") } } go-openapi-testify-c10ca71/internal/fdleak/000077500000000000000000000000001520301377500207035ustar00rootroot00000000000000go-openapi-testify-c10ca71/internal/fdleak/doc.go000066400000000000000000000025421520301377500220020ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 // Package fdleak provides file descriptor leak detection. // // It takes a snapshot of open file descriptors before and after running the tested function. // // Any file descriptors present in the "after" snapshot but not in the "before" snapshot // — and not of a filtered [Kind] — are considered leaks. // // # Platform support // // - Linux: enumerates /proc/self/fd and classifies FDs from the // readlink target (socket:/pipe:/anon_inode:/ path). // - darwin: enumerates /dev/fd and resolves each FD via fcntl(F_GETPATH), // falling back to fstat to classify sockets, pipes and kqueues. // - other: [Snapshot] returns an error. // // # Filtering // // Sockets, pipes and other kernel-internal descriptors (Linux anon_inode, // darwin kqueue) are excluded from leak reports by default, as these are // typically managed by the Go runtime or external libraries. // // # Concurrency // // This approach is inherently process-wide: the FD table lists all file descriptors for the process. // // Any concurrent I/O from other goroutines may cause false positives. // A mutex serializes [Leaked] calls to prevent multiple leak checks from interfering with each other, // but cannot protect against external concurrent file operations. package fdleak go-openapi-testify-c10ca71/internal/fdleak/fdleak.go000066400000000000000000000074231520301377500224660ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 package fdleak import ( "fmt" "sort" "strings" "sync" ) // Kind classifies an open file descriptor independently of platform-specific target-string conventions. type Kind int const ( // KindUnknown is used when the descriptor kind could not be determined. KindUnknown Kind = iota // KindFile denotes anything backed by a path in the file system: // regular files, directories, symlinks, block devices. KindFile // KindSocket denotes a socket (AF_UNIX, AF_INET, …). KindSocket // KindPipe denotes a pipe or FIFO. KindPipe // KindChar denotes a character device without a resolvable path // (fallback for darwin when F_GETPATH fails on a char device). KindChar // KindOther covers kernel-level descriptors that are not directly // opened by user code: Linux anon_inode (epoll, timerfd, …), darwin // kqueue, and similar. KindOther ) // FDInfo describes an open file descriptor. // // It should remain a human-readable description: a file-system path for vnode-backed FDs, // or a synthetic label such as "socket:[]" for non-vnode kinds. // // [Kind] is the authoritative classification; use it rather than parsing Target when filtering. type FDInfo struct { FD int Kind Kind Target string } // isFiltered reports whether this FD should be excluded from leak reports. // Sockets, pipes, and other kernel-internal descriptors are filtered by default, // because they are typically managed by the Go runtime or external libraries // and their lifecycles do not correlate with user-level resource management. func (f FDInfo) isFiltered() bool { switch f.Kind { case KindSocket, KindPipe, KindOther: return true default: return false } } // snapshotMu serializes Leaked calls to prevent false positives // from concurrent tests. var snapshotMu sync.Mutex //nolint:gochecknoglobals // serializes process-wide FD table access // Snapshot returns a map of currently open file descriptors for the // running process. // // The set of supported platforms is determined at build time; see the // per-platform implementations (fdleak_linux.go, fdleak_darwin.go). // // On unsupported platforms, Snapshot returns an error. // // FDs that close between enumeration and resolution are silently skipped. func Snapshot() (map[int]FDInfo, error) { return snapshot() } // Leaked takes a before/after snapshot around the tested function // and returns a formatted description of leaked file descriptors. // // Returns the empty string if no leaks are found. // On unsupported platforms, Leaked returns an error. func Leaked(tested func()) (string, error) { snapshotMu.Lock() defer snapshotMu.Unlock() before, err := snapshot() if err != nil { return "", err } tested() after, err := snapshot() if err != nil { return "", err } leaked := Diff(before, after) return FormatLeaked(leaked), nil } // Diff returns file descriptors present in after but not in before, // excluding filtered FD kinds (sockets, pipes, other kernel descriptors). func Diff(before, after map[int]FDInfo) []FDInfo { var leaked []FDInfo for fd, info := range after { if _, existed := before[fd]; existed { continue } if info.isFiltered() { continue } leaked = append(leaked, info) } sort.Slice(leaked, func(i, j int) bool { return leaked[i].FD < leaked[j].FD }) return leaked } // FormatLeaked formats leaked file descriptors into a human-readable message. // Returns the empty string if the slice is empty. func FormatLeaked(leaked []FDInfo) string { if len(leaked) == 0 { return "" } var b strings.Builder fmt.Fprintf(&b, "found %d leaked file descriptor(s):\n", len(leaked)) for _, fd := range leaked { fmt.Fprintf(&b, " fd %d: %s\n", fd.FD, fd.Target) } return b.String() } go-openapi-testify-c10ca71/internal/fdleak/fdleak_darwin.go000066400000000000000000000101651520301377500240270ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 //go:build darwin package fdleak import ( "fmt" "math" "syscall" "unsafe" ) const ( // fGetPath is the darwin fcntl command to retrieve the vnode path of a // file descriptor (see ). fGetPath = 50 // maxPathLen is darwin's MAXPATHLEN. maxPathLen = 1024 // fdProbeCap bounds the linear FD scan. Typical darwin RLIMIT_NOFILE soft // limits range from 256 to 10240; real test processes rarely hold more than // a few dozen FDs, so a 4096 ceiling caps the cost at a few thousand fstat // syscalls per snapshot (single-digit milliseconds). fdProbeCap = uint64(4096) ) // snapshot enumerates open file descriptors on darwin by probing FDs 0..N // with fstat. Unlike Linux's /proc/self/fd, darwin's /dev/fd is a devfs mount // that does not cooperate with Go's os.ReadDir fallback to fstatat, so we // bypass directory enumeration and probe FD numbers directly. // // For each live FD: // // - fcntl(F_GETPATH) resolves vnode-backed paths (regular files, devices). // - fstat classifies non-vnode kinds (sockets, pipes, kqueues, …) and // yields a synthetic target string compatible with the Linux output. // // FDs that close between fstat and fcntl are silently skipped. func snapshot() (map[int]FDInfo, error) { var rlim syscall.Rlimit if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, &rlim); err != nil { return nil, fmt.Errorf("getrlimit(RLIMIT_NOFILE): %w", err) } limit := int(min(rlim.Cur, fdProbeCap, uint64(math.MaxInt))) //nolint:gosec // the min guarantees that limit doesn't overflow int fds := make(map[int]FDInfo) for fd := range limit { info, ok := resolveFD(fd) if !ok { continue } fds[fd] = info } return fds, nil } // resolveFD builds an FDInfo for fd. It returns false when the FD is not open // (fstat returns EBADF) or the FD was closed mid-probe. func resolveFD(fd int) (FDInfo, bool) { // Classify first via fstat: this is also our liveness check (EBADF means // "not open"). Querying F_GETPATH on a closed FD returns a similar error, // but ordering fstat first keeps the closed-FD fast path to a single // syscall. var stat syscall.Stat_t if err := syscall.Fstat(fd, &stat); err != nil { return FDInfo{}, false } switch stat.Mode & syscall.S_IFMT { case syscall.S_IFSOCK: return FDInfo{FD: fd, Kind: KindSocket, Target: fmt.Sprintf("socket:[%d]", stat.Ino)}, true case syscall.S_IFIFO: return FDInfo{FD: fd, Kind: KindPipe, Target: fmt.Sprintf("pipe:[%d]", stat.Ino)}, true } // Remaining kinds are vnode-backed; try to resolve the path. path, pathErr := fcntlGetPath(fd) switch stat.Mode & syscall.S_IFMT { case syscall.S_IFCHR: if pathErr == nil { return FDInfo{FD: fd, Kind: KindChar, Target: path}, true } return FDInfo{FD: fd, Kind: KindChar, Target: fmt.Sprintf("char:[%d]", stat.Rdev)}, true case syscall.S_IFREG, syscall.S_IFDIR, syscall.S_IFLNK, syscall.S_IFBLK: if pathErr == nil { return FDInfo{FD: fd, Kind: KindFile, Target: path}, true } return FDInfo{FD: fd, Kind: KindFile, Target: fmt.Sprintf("file:[%d]", stat.Ino)}, true default: // kqueue, fsevents, netpolicy, pshm, psem, and other kernel-internal // descriptors land here. They never expose a vnode path. return FDInfo{FD: fd, Kind: KindOther, Target: fmt.Sprintf("other:[%d]", stat.Ino)}, true } } // fcntlGetPath issues fcntl(fd, F_GETPATH, buf) and returns the resolved path. // // The unsafe.Pointer → uintptr conversion happens at the syscall.Syscall call // site so Go's runtime keeps buf pinned for the duration of the call (the // documented "Case 4" rule for syscall arguments). func fcntlGetPath(fd int) (string, error) { var buf [maxPathLen]byte _, _, errno := syscall.Syscall( syscall.SYS_FCNTL, uintptr(fd), //nolint:gosec // fd is a int and it may be stored in a uintptr by definition of the int type. uintptr(fGetPath), uintptr(unsafe.Pointer(&buf[0])), // F_GETPATH requires a raw buffer pointer; lifetime is pinned across the syscall. ) if errno != 0 { return "", errno } n := 0 for n < len(buf) && buf[n] != 0 { n++ } return string(buf[:n]), nil } go-openapi-testify-c10ca71/internal/fdleak/fdleak_darwin_test.go000066400000000000000000000063641520301377500250740ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 //go:build darwin package fdleak import ( "context" "net" "os" "strings" "testing" ) func TestSnapshot(t *testing.T) { fds, err := Snapshot() if err != nil { t.Fatalf("Snapshot() error: %v", err) } // stdin, stdout, stderr should always be present. for _, fd := range []int{0, 1, 2} { info, ok := fds[fd] if !ok { t.Errorf("expected fd %d (stdin/stdout/stderr) in snapshot", fd) continue } if info.Kind == KindUnknown { t.Errorf("fd %d has KindUnknown; expected a concrete kind (got target=%q)", fd, info.Target) } } } func TestLeaked_NoLeak(t *testing.T) { leaked, err := Leaked(func() { // Clean function — no file descriptors opened. }) if err != nil { t.Fatalf("Leaked() error: %v", err) } if leaked != "" { t.Errorf("expected no leaked file descriptors, got:\n%s", leaked) } } // TestLeaked_WithLeak additionally verifies that fcntl(F_GETPATH) recovers // the file's absolute path on darwin. func TestLeaked_WithLeak(t *testing.T) { var leakedFile *os.File dir := t.TempDir() leaked, err := Leaked(func() { f, err := os.CreateTemp(dir, "fdleak-test-*") if err != nil { t.Fatalf("CreateTemp: %v", err) } leakedFile = f // intentionally not closed }) t.Cleanup(func() { if leakedFile != nil { leakedFile.Close() os.Remove(leakedFile.Name()) } }) if err != nil { t.Fatalf("Leaked() error: %v", err) } if leaked == "" { t.Fatal("expected leaked file descriptor to be detected, but found none") } // F_GETPATH should have resolved the full path of the leaked temp file. // On darwin t.TempDir() lives under /private/var/... so match on the basename prefix. if !strings.Contains(leaked, "fdleak-test-") { t.Errorf("expected leak report to include the resolved file path; got:\n%s", leaked) } else { t.Logf("detected leak:\n%s", leaked) } } // TestLeaked_SocketsFiltered exercises the fstat fallback on darwin: sockets // have no vnode path so F_GETPATH fails, and they must be classified as // KindSocket via fstat to be filtered out. func TestLeaked_SocketsFiltered(t *testing.T) { var leakedListener net.Listener leaked, err := Leaked(func() { var lc net.ListenConfig ln, err := lc.Listen(context.Background(), "tcp", "127.0.0.1:0") if err != nil { t.Fatalf("net.Listen: %v", err) } leakedListener = ln // intentionally not closed — socket FD should be filtered }) t.Cleanup(func() { if leakedListener != nil { leakedListener.Close() } }) if err != nil { t.Fatalf("Leaked() error: %v", err) } if leaked != "" { t.Errorf("expected socket FD to be filtered, but got:\n%s", leaked) } } // TestLeaked_PipesFiltered exercises the fstat fallback for pipes on darwin. func TestLeaked_PipesFiltered(t *testing.T) { var r, w *os.File leaked, err := Leaked(func() { pr, pw, err := os.Pipe() if err != nil { t.Fatalf("os.Pipe: %v", err) } r, w = pr, pw // intentionally not closed — pipe FDs should be filtered }) t.Cleanup(func() { if r != nil { r.Close() } if w != nil { w.Close() } }) if err != nil { t.Fatalf("Leaked() error: %v", err) } if leaked != "" { t.Errorf("expected pipe FDs to be filtered, but got:\n%s", leaked) } } go-openapi-testify-c10ca71/internal/fdleak/fdleak_linux.go000066400000000000000000000027061520301377500237040ustar00rootroot00000000000000// SPDX-FileCopyrightText: Copyright 2025 go-swagger maintainers // SPDX-License-Identifier: Apache-2.0 //go:build linux package fdleak import ( "fmt" "os" "strconv" "strings" ) const procSelfFD = "/proc/self/fd" // snapshot enumerates open file descriptors via /proc/self/fd. // // The readlink target carries the kind on Linux: // - "socket:[]" → KindSocket // - "pipe:[]" → KindPipe // - "anon_inode:[