[
  {
    "path": ".github/ISSUE_TEMPLATE/bug_report.md",
    "content": "---\nname: Bug report \\\nabout: Create a report to help us improve \\\ntitle: ' ' \\\nlabels: ' ' \\\nassignees: ' ' \n\n---\n\n**Describe the bug**\nA clear and concise description of what the bug is.\n\n**To Reproduce**\nSteps to reproduce the behavior:\n1. Used func: '...'\n2. What you actually wanted to do: '...'\n3. What has been expected: '...'\n4. What you got actual: '....'\n5. Error Log (if any): '...'\n6. Allure-Report screenshot (if any): '...'\n\n**Expected behavior**\nA clear and concise description of what you expected to happen.\n\n**Screenshots**\nIf applicable, add screenshots to help explain your problem.\n\n**Additional context**\nAdd any other context about the problem here."
  },
  {
    "path": ".github/ISSUE_TEMPLATE/feature_request.md",
    "content": "---\nname: Feature request \\\nabout: Suggest an idea for this project \\\ntitle: '' \\\nlabels: '' \\\nassignees: ''\n\n---\n\n**Is your feature request related to a problem? Please describe.** \\\nA clear and concise description of what the problem is. Ex. I'm always frustrated when [...]\n\n**Describe the solution you'd like** \\\nA clear and concise description of what you want to happen.\n\n**Describe alternatives you've considered** \\\nA clear and concise description of any alternative solutions or features you've considered.\n\n**Additional context** \\\nAdd any other context or screenshots about the feature request here."
  },
  {
    "path": ".github/workflows/main.yml",
    "content": "name: CI\n\non:\n  push:\n    branches: [ master ]\n  pull_request:\n    branches: [ master ]\n\njobs:\n  build_and_test:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n\n      - name: Set up Go\n        uses: actions/setup-go@v2\n        with:\n          go-version: 1.21\n\n      - name: Build\n        run: go build -v ./...\n\n      - name: Test\n        run: go test -v ./...\n\n  golangci-lint:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v3\n      - name: Run golangci-lint\n        # You may pin to the exact commit or the version.\n        # uses: golangci/golangci-lint-action@5c56cd6c9dc07901af25baab6f2b0d9f3b7c3018\n        uses: golangci/golangci-lint-action@v2.5.2\n        # for settings see https://github.com/golangci/golangci-lint-action\n        with:\n          only-new-issues: true\n          # golangci-lint command line arguments\n          args: --timeout=5m0s\n\n  examples:\n    name: examples\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions/checkout@v4\n      - name: Run examples\n        run: make examples\n      - name: Archive code coverage results\n        uses: actions/upload-artifact@v4\n        with:\n          name: allure-results\n          path: ./examples/allure-results"
  },
  {
    "path": ".gitignore",
    "content": ".idea/\ntestdata/\nallure-results/\nbin/\nvendor/\n\n# Binaries for programs and plugins\n*.exe\n*.exe~\n*.dll\n*.so\n*.dylib\n\n# Test binary, build with `go test -c`\n*.test\n\n# Output of the go coverage tool, specifically when used with LiteIDE\n*.out\n"
  },
  {
    "path": ".golangci.yaml",
    "content": "run:\n  timeout: 10m\n  issues-exit-code: 1\n  tests: true\n  skip-dirs:\n    - bin\n    - vendor\n    - var\n    - tmp\n  skip-files:\n    - \\.pb\\.go$\n    - \\.pb\\.goclay\\.go$\n\noutput:\n  format: colored-line-number\n  print-issued-lines: true\n  print-linter-name: true\n\nlinters-settings:\n  govet:\n    check-shadowing: true\n  dupl:\n    threshold: 100\n  goconst:\n    min-len:         2\n    min-occurrences: 2\n\nlinters:\n  disable-all: true\n  enable:\n    - errcheck\n    - goconst\n    - goimports\n    - gosec\n    - govet\n    - ineffassign\n    - megacheck\n    - revive\n    - typecheck\n    - unused # will be used insted of varcheck + deadcode + structcheck. More info https://github.com/golangci/golangci-lint/issues/1841\n    - prealloc\n    - wsl\n\nissues:\n  exclude-use-default: false\n  exclude:\n    # _ instead of err checks\n    - G104\n    # for \"public interface + private struct implementation\" cases only!\n    - exported func .* returns unexported type .*, which can be annoying to use\n    # can be removed in the development phase\n    # - (comment on exported (method|function|type|const)|should have( a package)? comment|comment should be of the form)\n    # not for the active development - can be removed in the stable phase\n    - should have a package comment\n    - don't use an underscore in package name\n    # EXC0001 errcheck: Almost all programs ignore errors on these functions and in most cases it's ok\n    - Error return value of .((os\\.)?std(out|err)\\..*|.*Close|.*Flush|os\\.Remove(All)?|.*print(f|ln)?|os\\.(Un)?Setenv). is not checked\n    - should check returned error before deferring\n"
  },
  {
    "path": "LICENSE",
    "content": "                                 Apache License\n                           Version 2.0, January 2004\n                        http://www.apache.org/licenses/\n\n   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION\n\n   1. Definitions.\n\n      \"License\" shall mean the terms and conditions for use, reproduction,\n      and distribution as defined by Sections 1 through 9 of this document.\n\n      \"Licensor\" shall mean the copyright owner or entity authorized by\n      the copyright owner that is granting the License.\n\n      \"Legal Entity\" shall mean the union of the acting entity and all\n      other entities that control, are controlled by, or are under common\n      control with that entity. For the purposes of this definition,\n      \"control\" means (i) the power, direct or indirect, to cause the\n      direction or management of such entity, whether by contract or\n      otherwise, or (ii) ownership of fifty percent (50%) or more of the\n      outstanding shares, or (iii) beneficial ownership of such entity.\n\n      \"You\" (or \"Your\") shall mean an individual or Legal Entity\n      exercising permissions granted by this License.\n\n      \"Source\" form shall mean the preferred form for making modifications,\n      including but not limited to software source code, documentation\n      source, and configuration files.\n\n      \"Object\" form shall mean any form resulting from mechanical\n      transformation or translation of a Source form, including but\n      not limited to compiled object code, generated documentation,\n      and conversions to other media types.\n\n      \"Work\" shall mean the work of authorship, whether in Source or\n      Object form, made available under the License, as indicated by a\n      copyright notice that is included in or attached to the work\n      (an example is provided in the Appendix below).\n\n      \"Derivative Works\" shall mean any work, whether in Source or Object\n      form, that is based on (or derived from) the Work and for which the\n      editorial revisions, annotations, elaborations, or other modifications\n      represent, as a whole, an original work of authorship. For the purposes\n      of this License, Derivative Works shall not include works that remain\n      separable from, or merely link (or bind by name) to the interfaces of,\n      the Work and Derivative Works thereof.\n\n      \"Contribution\" shall mean any work of authorship, including\n      the original version of the Work and any modifications or additions\n      to that Work or Derivative Works thereof, that is intentionally\n      submitted to Licensor for inclusion in the Work by the copyright owner\n      or by an individual or Legal Entity authorized to submit on behalf of\n      the copyright owner. For the purposes of this definition, \"submitted\"\n      means any form of electronic, verbal, or written communication sent\n      to the Licensor or its representatives, including but not limited to\n      communication on electronic mailing lists, source code control systems,\n      and issue tracking systems that are managed by, or on behalf of, the\n      Licensor for the purpose of discussing and improving the Work, but\n      excluding communication that is conspicuously marked or otherwise\n      designated in writing by the copyright owner as \"Not a Contribution.\"\n\n      \"Contributor\" shall mean Licensor and any individual or Legal Entity\n      on behalf of whom a Contribution has been received by Licensor and\n      subsequently incorporated within the Work.\n\n   2. Grant of Copyright License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      copyright license to reproduce, prepare Derivative Works of,\n      publicly display, publicly perform, sublicense, and distribute the\n      Work and such Derivative Works in Source or Object form.\n\n   3. Grant of Patent License. Subject to the terms and conditions of\n      this License, each Contributor hereby grants to You a perpetual,\n      worldwide, non-exclusive, no-charge, royalty-free, irrevocable\n      (except as stated in this section) patent license to make, have made,\n      use, offer to sell, sell, import, and otherwise transfer the Work,\n      where such license applies only to those patent claims licensable\n      by such Contributor that are necessarily infringed by their\n      Contribution(s) alone or by combination of their Contribution(s)\n      with the Work to which such Contribution(s) was submitted. If You\n      institute patent litigation against any entity (including a\n      cross-claim or counterclaim in a lawsuit) alleging that the Work\n      or a Contribution incorporated within the Work constitutes direct\n      or contributory patent infringement, then any patent licenses\n      granted to You under this License for that Work shall terminate\n      as of the date such litigation is filed.\n\n   4. Redistribution. You may reproduce and distribute copies of the\n      Work or Derivative Works thereof in any medium, with or without\n      modifications, and in Source or Object form, provided that You\n      meet the following conditions:\n\n      (a) You must give any other recipients of the Work or\n          Derivative Works a copy of this License; and\n\n      (b) You must cause any modified files to carry prominent notices\n          stating that You changed the files; and\n\n      (c) You must retain, in the Source form of any Derivative Works\n          that You distribute, all copyright, patent, trademark, and\n          attribution notices from the Source form of the Work,\n          excluding those notices that do not pertain to any part of\n          the Derivative Works; and\n\n      (d) If the Work includes a \"NOTICE\" text file as part of its\n          distribution, then any Derivative Works that You distribute must\n          include a readable copy of the attribution notices contained\n          within such NOTICE file, excluding those notices that do not\n          pertain to any part of the Derivative Works, in at least one\n          of the following places: within a NOTICE text file distributed\n          as part of the Derivative Works; within the Source form or\n          documentation, if provided along with the Derivative Works; or,\n          within a display generated by the Derivative Works, if and\n          wherever such third-party notices normally appear. The contents\n          of the NOTICE file are for informational purposes only and\n          do not modify the License. You may add Your own attribution\n          notices within Derivative Works that You distribute, alongside\n          or as an addendum to the NOTICE text from the Work, provided\n          that such additional attribution notices cannot be construed\n          as modifying the License.\n\n      You may add Your own copyright statement to Your modifications and\n      may provide additional or different license terms and conditions\n      for use, reproduction, or distribution of Your modifications, or\n      for any such Derivative Works as a whole, provided Your use,\n      reproduction, and distribution of the Work otherwise complies with\n      the conditions stated in this License.\n\n   5. Submission of Contributions. Unless You explicitly state otherwise,\n      any Contribution intentionally submitted for inclusion in the Work\n      by You to the Licensor shall be under the terms and conditions of\n      this License, without any additional terms or conditions.\n      Notwithstanding the above, nothing herein shall supersede or modify\n      the terms of any separate license agreement you may have executed\n      with Licensor regarding such Contributions.\n\n   6. Trademarks. This License does not grant permission to use the trade\n      names, trademarks, service marks, or product names of the Licensor,\n      except as required for reasonable and customary use in describing the\n      origin of the Work and reproducing the content of the NOTICE file.\n\n   7. Disclaimer of Warranty. Unless required by applicable law or\n      agreed to in writing, Licensor provides the Work (and each\n      Contributor provides its Contributions) on an \"AS IS\" BASIS,\n      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or\n      implied, including, without limitation, any warranties or conditions\n      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A\n      PARTICULAR PURPOSE. You are solely responsible for determining the\n      appropriateness of using or redistributing the Work and assume any\n      risks associated with Your exercise of permissions under this License.\n\n   8. Limitation of Liability. In no event and under no legal theory,\n      whether in tort (including negligence), contract, or otherwise,\n      unless required by applicable law (such as deliberate and grossly\n      negligent acts) or agreed to in writing, shall any Contributor be\n      liable to You for damages, including any direct, indirect, special,\n      incidental, or consequential damages of any character arising as a\n      result of this License or out of the use or inability to use the\n      Work (including but not limited to damages for loss of goodwill,\n      work stoppage, computer failure or malfunction, or any and all\n      other commercial damages or losses), even if such Contributor\n      has been advised of the possibility of such damages.\n\n   9. Accepting Warranty or Additional Liability. While redistributing\n      the Work or Derivative Works thereof, You may choose to offer,\n      and charge a fee for, acceptance of support, warranty, indemnity,\n      or other liability obligations and/or rights consistent with this\n      License. However, in accepting such obligations, You may act only\n      on Your own behalf and on Your sole responsibility, not on behalf\n      of any other Contributor, and only if You agree to indemnify,\n      defend, and hold each Contributor harmless for any liability\n      incurred by, or claims asserted against, such Contributor by reason\n      of your accepting any such warranty or additional liability.\n\n   END OF TERMS AND CONDITIONS\n\n   APPENDIX: How to apply the Apache License to your work.\n\n      To apply the Apache License to your work, attach the following\n      boilerplate notice, with the fields enclosed by brackets \"[]\"\n      replaced with your own identifying information. (Don't include\n      the brackets!)  The text should be enclosed in the appropriate\n      comment syntax for the file format. We also recommend that a\n      file or class name and description of purpose be included on the\n      same \"printed page\" as the copyright notice for easier\n      identification within third-party archives.\n\n   Copyright [yyyy] [name of copyright owner]\n\n   Licensed under the Apache License, Version 2.0 (the \"License\");\n   you may not use this file except in compliance with the License.\n   You may obtain a copy of the License at\n\n       http://www.apache.org/licenses/LICENSE-2.0\n\n   Unless required by applicable law or agreed to in writing, software\n   distributed under the License is distributed on an \"AS IS\" BASIS,\n   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n   See the License for the specific language governing permissions and\n   limitations under the License.\n"
  },
  {
    "path": "Makefile",
    "content": "export GO111MODULE=on\nexport GOSUMDB=off\nLOCAL_BIN:=$(CURDIR)/bin\n\n##################### GOLANG-CI RELATED CHECKS #####################\n# Check global GOLANGCI-LINT\nGOLANGCI_BIN:=$(LOCAL_BIN)/golangci-lint\nGOLANGCI_TAG:=1.54.2\n\n# Check local bin version\nifneq ($(wildcard $(GOLANGCI_BIN)),)\nGOLANGCI_BIN_VERSION:=$(shell $(GOLANGCI_BIN) --version)\nifneq ($(GOLANGCI_BIN_VERSION),)\nGOLANGCI_BIN_VERSION_SHORT:=$(shell echo \"$(GOLANGCI_BIN_VERSION)\" | sed -E 's/.* version (.*) built from .* on .*/\\1/g')\nelse\nGOLANGCI_BIN_VERSION_SHORT:=0\nendif\nifneq \"$(GOLANGCI_TAG)\" \"$(word 1, $(sort $(GOLANGCI_TAG) $(GOLANGCI_BIN_VERSION_SHORT)))\"\nGOLANGCI_BIN:=\nendif\nendif\n\n# Check global bin version\nifneq (, $(shell which golangci-lint))\nGOLANGCI_VERSION:=$(shell golangci-lint --version 2> /dev/null )\nifneq ($(GOLANGCI_VERSION),)\nGOLANGCI_VERSION_SHORT:=$(shell echo \"$(GOLANGCI_VERSION)\"|sed -E 's/.* version (.*) built from .* on .*/\\1/g')\nelse\nGOLANGCI_VERSION_SHORT:=0\nendif\nifeq \"$(GOLANGCI_TAG)\" \"$(word 1, $(sort $(GOLANGCI_TAG) $(GOLANGCI_VERSION_SHORT)))\"\nGOLANGCI_BIN:=$(shell which golangci-lint)\nendif\nendif\n##################### GOLANG-CI RELATED CHECKS #####################\n\n.PHONY: install\ninstall:\n\tgo mod tidy && go mod download\n\n# run full lint like in pipeline\n.PHONY: lint\nlint: install-lint\n\t$(GOLANGCI_BIN) run --config=.golangci.yaml ./... --new-from-rev=origin/master --build-tags=examples,allure_go,provider\n\n\n.PHONY: install-lint\ninstall-lint:\nifeq ($(wildcard $(GOLANGCI_BIN)),)\n\t$(info #Downloading golangci-lint v$(GOLANGCI_TAG))\n\ttmp=$$(mktemp -d) && cd $$tmp && pwd && go mod init temp && go get -d github.com/golangci/golangci-lint/cmd/golangci-lint@v$(GOLANGCI_TAG) && \\\n\t\tgo build -ldflags \"-X 'main.version=$(GOLANGCI_TAG)' -X 'main.commit=test' -X 'main.date=test'\" -o $(LOCAL_BIN)/golangci-lint github.com/golangci/golangci-lint/cmd/golangci-lint && \\\n\t\trm -rf $$tmp\nGOLANGCI_BIN:=$(LOCAL_BIN)/golangci-lint\nendif\n\n.PHONY: cover\ncover:\n\tgo test -v -coverprofile=coverage.out ./...  && go tool cover -html=coverage.out\n\n.PHONY: example\nexample:\n\tgo test ./... -tags example\n\n.PHONY: test\ntest:\n\tgo test ./...\n"
  },
  {
    "path": "README.MD",
    "content": "<p align=\"center\">\n  <img src=\".images/cute.png\" alt=\"cute\"/>\n</p>\n\n# CUTE — create your tests easily\n\nHTTP and REST API testing for Go using Allure reports.\n\n## Features\n\n- Expressive and intuitive syntax.\n- Built-in JSON support.\n- Custom asserts.\n- One step to BDD.\n- Allure reports.\n\n---\n\n## Head of contents\n\n- [Features](#features)\n- [Workflow](#workflow)\n- [Installation](#installation)\n- [Requirements](#requirements)\n- [Demo](#demo)\n- [Test examples](#test-examples)\n    - [Single test](#single-step-test)\n    - [Multi-step test](#multi-step-test)\n    - [Suite tests](#suite)\n    - [Table tests](#table-tests)\n- [Asserts](#asserts)\n    - [Ready-made asserts](#ready-made-asserts)\n        - [JSON asserts](#json-asserts)\n        - [Headers asserts](#headers-asserts)\n        - [JSON schema](#json-schema-validations)\n    - [Custom asserts](#custom-asserts)\n        - [Base](#base)\n        - [T](#t)\n        - [Errors](#assert-errors)\n- [Global Environment Keys](#global-environment-keys)\n\n\n## Workflow\n1. Create a request and write assets.\n2. Run tests.\n3. Check Allure reports.\n\n## Installation\n\n```bash\n  go get -u github.com/ozontech/cute\n```\n\n## Requirements\n\n- Go 1.17+\n\n\n## Demo\n\nRun example.\n```bash\n  make example\n```\nTo view detailed test reports, install Allure framework. It's optional.\n\n[Learn more about Allure reports](https://github.com/allure-framework)\n\n```bash\n  brew install allure\n```\n\nRun Allure.\n\n```bash\n  allure serve ./examples/allure-results\n```\n\n## Test examples\n\nSee [**Examples**](examples) directory for featured examples.\n\n### <h3><a href=\"examples/single_test.go\">Single-step test</a></h3>\n\nAllows implementing single-request tests. See full example in the [**Examples**](examples) directory.\nTo view an Allure report, use `testing.T` or `provider.T` from [allure-go](https://github.com/ozontech/allure-go/tree/master?tab=readme-ov-file).\n\n```go\nimport (\n    \"context\"\n    \"net/http\"\n    \"path\"\n    \"testing\"\n    \"time\"\n\n    \"github.com/ozontech/cute\"\n    \"github.com/ozontech/cute/asserts/json\"\n)\n\nfunc TestExample(t *testing.T) {\n    cute.NewTestBuilder().\n        Title(\"Title\").\n        Description(\"some_description\").\n        Create().\n        RequestBuilder(\n            cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n            cute.WithMethod(http.MethodGet),\n        ).\n        ExpectExecuteTimeout(10*time.Second).\n        ExpectStatus(http.StatusOK).\n        AssertBody(\n            json.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n            json.Present(\"$[1].name\"),\n        ).\n        ExecuteTest(context.Background(), t)\n}\n```\n<details>\n  <summary>Allure report</summary>\n\n![img.png](.images/simple.png)\n\n</details>\n\n\n### <h3><a href=\"examples/two_step_test.go\">Multi-step test</a></h3>\n\nAllows implementing several requests within one test.\n\n```go\nimport (\n    \"context\"\n    \"fmt\"\n    \"net/http\"\n    \"testing\"\n\n    \"github.com/ozontech/cute\"\n)\n    \nfunc Test_TwoSteps(t *testing.T) {\n    responseCode := 0\n\n    // First step\n    cute.NewTestBuilder().\n        Title(\"Test with two requests and parse body.\").\n        Tag(\"two_steps\").\n        Create().\n        RequestBuilder(\n            cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n            cute.WithMethod(http.MethodGet),\n        ).\n        ExpectStatus(http.StatusOK).\n        NextTest().\n\n        // Execute after first step and parse response code\n        AfterTestExecute(func(response *http.Response, errors []error) error { \n            responseCode = response.StatusCode\n\n            return nil\n        }).\n\n        // Second step\n        Create().\n        RequestBuilder(\n            cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/2/comments\"),\n            cute.WithMethod(http.MethodDelete),\n        ).\n        ExecuteTest(context.Background(), t)\n\n        fmt.Println(\"Response code from first request\", responseCode)\n}\n```\nSee full in the [**Examples**](examples/two_step_test.go) directory.\n\n<details>\n  <summary>Allure report</summary>\n\n![multistep_test.png](.images/multistep_test.png)\n\n</details>\n\n### <h3><a href=\"examples/suite\">Suite</a></h3>\n\nSuite provides a structure for describing tests by organizing them into test suites. It's helpful if you have a large number of different tests and find it difficult to browse through them without using additional layer nesting levels of test calls.\n\n[Learn more about suite with Allure reports](https://github.com/ozontech/allure-go#suite)\n\n\n1. Declare a structure with `suite.Suite` and `*cute.HTTPTestMaker`.\n\n```go\nimport (\n    \"github.com/ozontech/cute\"\n    \"github.com/ozontech/allure-go/pkg/framework/provider\"\n    \"github.com/ozontech/allure-go/pkg/framework/suite\"\n)\n\ntype ExampleSuite struct {\n    suite.Suite\n    host *url.URL\n\n    testMaker *cute.HTTPTestMaker\n}\n\nfunc (i *ExampleSuite) BeforeAll(t provider.T) {\n    // Prepare http test builder\n    i.testMaker = cute.NewHTTPTestMaker()\n\n    // Preparing host\n    host, err := url.Parse(\"https://jsonplaceholder.typicode.com/\")\n    if err != nil {\n        t.Fatalf(\"could not parse url, error %w\", err)\n    }\n\n    i.host = host\n}\n\n```\n2. Declare a test.\n\n```go\nimport (\n    \"github.com/ozontech/allure-go/pkg/framework/suite\"\n)\n\nfunc TestExampleTest(t *testing.T) {\n    suite.RunSuite(t, new(ExampleSuite))\n}\n```\n\n3. Describe tests.\n\n```go\nimport (\n    \"github.com/ozontech/cute\"\n    \"github.com/ozontech/cute/asserts/headers\"\n    \"github.com/ozontech/cute/asserts/json\"\n)\n\nfunc (i *ExampleSuite) TestExample_OneStep(t provider.T) {\n    var (\n        testBuilder = i.testMaker.NewTestBuilder()\n    )\n    \n    u, _ := url.Parse(i.host.String())\n    u.Path = path.Join(u.Path, \"/posts/1/comments\")\n    \n    testBuilder.\n        Title(\"TestExample_OneStep\").\n        Tags(\"one_step\", \"some_local_tag\", \"json\").\n        Create().\n        StepName(\"Example GET json request\").\n        RequestBuilder(\n            cute.WithHeaders(map[string][]string{\n                \"some_header\":       []string{\"something\"},\n                \"some_array_header\": []string{\"1\", \"2\", \"3\", \"some_thing\"},\n            }),\n            cute.WithURL(u),\n            cute.WithMethod(http.MethodGet),\n        ).\n        ExpectExecuteTimeout(10*time.Second).\n        ExpectJSONSchemaFile(\"file://./resources/example_valid_request.json\").\n        ExpectStatus(http.StatusOK).\n        AssertBody(\n            json.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n            json.Present(\"$[1].name\"),\n            json.NotPresent(\"$[1].some_not_present\"),\n            json.GreaterThan(\"$\", 3),\n            json.Length(\"$\", 5),\n            json.LessThan(\"$\", 100),\n            json.NotEqual(\"$[3].name\", \"kekekekeke\"),\n        ).\n        OptionalAssertBody(\n            json.GreaterThan(\"$\", 3),\n            json.Length(\"$\", 5),\n            json.LessThan(\"$\", 100),\n        ).\n        AssertHeaders(\n            headers.Present(\"Content-Type\"),\n        ).\n        ExecuteTest(context.Background(), t)\n}\n```\nSee full example in the [**Examples**](examples/suite) directory.\n\n<details>\n  <summary>Allure report</summary>\n\n![one_step.png](.images/one_step.png)\n\n</details>\n\n## <h2><a href=\"examples/table_test/table_test.go\">Table tests</a></h2>\n\nYou can create a table test in 2 ways. They'll have the same Allure reports.\n\n### Builder table tests\n\n```go\nimport (\n    \"context\"\n    \"fmt\"\n    \"net/http\"\n    \"testing\"\n\n    \"github.com/ozontech/cute\"\n)\n\nfunc Test_Table_Array(t *testing.T) {\n    tests := []*cute.Test{\n        {\n            Name:       \"test_1\",\n            Middleware: nil,\n            Request: &cute.Request{\n                Builders: []cute.RequestBuilder{\n                    cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n                    cute.WithMethod(http.MethodPost),\n                },\n            },\n            Expect: &cute.Expect{\n                Code: 200,\n            },\n        },\n        {\n            Name:       \"test_2\",\n            Middleware: nil,\n            Request: &cute.Request{\n                Builders: []cute.RequestBuilder{\n                    cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n                    cute.WithMethod(http.MethodGet),\n                },\n            },\n            Expect: &cute.Expect{\n                Code: 200,\n                AssertBody: []cute.AssertBody{\n                    json.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n                    json.Present(\"$[1].name\"),\n                    func(body []byte) error {\n                        return errors.NewAssertError(\"example error\", \"example message\", nil, nil)\n                    },\n                },\n            },\n        },\n    }\n\n    cute.NewTestBuilder().\n        Title(\"Example table test\").\n        Tag(\"table_test\").\n        Description(\"Execute array tests\").\n        CreateTableTest().\n        PutTests(tests...).\n        ExecuteTest(context.Background(), t)\n}\n```\n\n### Array tests\n\n```go\nfunc Test_Execute_Array(t *testing.T) {\n    tests := []*cute.Test{\n        {\n            Name:       \"test_1\",\n            Middleware: nil,\n            Request: &cute.Request{\n                Builders: []cute.RequestBuilder{\n                    cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n                    cute.WithMethod(http.MethodPost),\n                },\n            },\n            Expect: &cute.Expect{\n                Code: 200,\n            },\n        },\n        {\n            Name:       \"test_2\",\n            Middleware: nil,\n            Request: &cute.Request{\n                Builders: []cute.RequestBuilder{\n                    cute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n                    cute.WithMethod(http.MethodGet),\n                },\n            },\n            Expect: &cute.Expect{\n                Code: 200,\n                AssertBody: []cute.AssertBody{\n                    json.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n                    json.Present(\"$[1].name\"),\n                    func(body []byte) error {\n                        return errors.NewAssertError(\"example error\", \"example message\", nil, nil)\n                    },\n                },\n            },\n        },\n    }\n\n    for _, test := range tests {\n        test.Execute(context.Background(), t)\n    }\n}\n```\n\nSee full example in the [**Examples**](examples/table_test/table_test.go) directory.\n\n<details>\n  <summary>Allure report</summary>\n\nCommon report for all table tests:\n\n![table_tests_execute_array.png](.images/table_tests_execute_array.png)\n\nMain report:\n\n![table_tests_execute_array_test_1.png](.images/table_tests_execute_array_test_1.png)\n\n</details>\n\n<h2><a href=\"asserts\">Asserts</a></h2>\n\nYou can create your own asserts or use ready-made from the package asserts.\n\n### Ready-made asserts\n\n#### <h4><a href=\"asserts/json\">JSON asserts</a></h4>\n\n\n- `Equal` is a function to assert that a JSONPath expression matches the given value.\n- `NotEqual` is a function to check that a JSONPath expression value isn't equal to the given value.\n- `Length` is a function to assert that value is the expected length.\n    - `GreaterThan` is a function to assert that value is greater than the given length.\n    - `GreaterOrEqualThan` is a function to assert that value is greater or equal to the given length.\n    - `LessThan` is a function to assert that value is less than the given length.\n    - `LessOrEqualThan` is a function to assert that value is less or equal to the given length.\n- `Present` is a function to assert that value is present. Value can be 0 or null.\n- `NotEmpty` is a function to assert that value is present and not empty. Value can't be 0 or null.\n- `NotPresent` is a function to assert that value isn't present.\n- `Diff` is a function to compare two JSONs.\n- `Contains` is a function to assert that a JSONPath expression extracts a value in an array.\n- `EqualJSON` is a function to check that a JSON path expression value is equal to given JSON.\n- `NotEqualJSON` is a function to check that a JSONPath expression value isn't equal to given JSON.\n- `GetValueFromJSON` is a function for getting a value from a JSON.\n\n[Learn more about expressions](https://goessner.net/articles/JsonPath/)\n\n[Learn more about asserts implementation](https://github.com/ozontech/cute/blob/master/asserts/json/json.go)\n\n#### <h4><a href=\"asserts/headers\">Headers asserts</a></h4>\n\n- `Present` is a function to assert that header is present.\n- `NotPresent` is a function to assert that header isn't present.\n\n[Learn more about asserts implementation](asserts/headers/headers.go)\n\n#### <h4><a href=\"jsonschema.go\">JSON schema validations</a></h4>\n\nYou can validate a JSON schema in 3 ways. Choose a way depending on JSON schema location.\n\n- `ExpectJSONSchemaString(string)` is a function for validating a JSON schema from a string.\n- `ExpectJSONSchemaByte([]byte)` is a function for validating a JSON schema from an array of bytes.\n- `ExpectJSONSchemaFile(string)` is a function for validating a JSON schema from a file or remote resource.\n\n<details>\n  <summary>Allure report</summary>\n\n![img.png](.images/json_schema.png)\n\n</details>\n\n### <h3><a href=\"assert.go\">Custom asserts</a></h3>\n\nYou can implement [3 type of asserts](assert.go):\n\n#### Base\n\nTypes for creating custom assertions.\n\n```go\n    type AssertBody func(body []byte) error\n    type AssertHeaders func(headers http.Header) error\n    type AssertResponse func(response *http.Response) error\n```\n\nExample:\n\n```go\nfunc customAssertBody() cute.AssertBody {\n    return func(bytes []byte) error {\n        if len(bytes) == 0 {\n            return errors.New(\"response body is empty\")\n        }\n        \n        return nil\n    }\n}\n```\n\n#### T\n\nUsed for creating custom asserts via [Allure Actions](https://github.com/ozontech/allure-go#suite) and [testing.TB](https://pkg.go.dev/testing#TB).\nYou can:\n\n- log information to Allure,\n- log error on Allure yourself,\n- return an error.\n\n```go\n    type AssertBodyT func(t cute.T, body []byte) error\n    type AssertHeadersT func(t cute.T, headers http.Header) error\n    type AssertResponseT func(t cute.T, response *http.Response) error\n```\n\nExample with T:\n\n```go\nfunc customAssertBodyT() cute.AssertBodyT {\n    return func(t cute.T, bytes []byte) error {\n        require.GreaterOrEqual(t, len(bytes), 100)\n        return nil\n    }\n}\n```\n\nExample with creating steps:\n\n```go\nfunc customAssertBodySuite() cute.AssertBodyT {\n    return func(t cute.T, bytes []byte) error {\n        step := allure.NewSimpleStep(\"Custom assert step\")\n        defer func() {\n            t.Step(step)\n        }()\n\n        if len(bytes) == 0 {\n            step.Status = allure.Failed\n            step.Attachment(allure.NewAttachment(\"Error\", allure.Text, []byte(\"response body is empty\")))\n\n            return nil\n        }\n\n        return nil\n    }\n}\n\n```\n\n<details>\n  <summary>Allure report</summary>\n\n![custom_assert.png](.images/custom_assert.png)\n\n</details>\n\n#### <h4><a href=\"errors/error.go\">Assert errors</a></h4>\n\nYou can use `errors.NewAssertError` method from [errors](errors/error.go) package.\n\nExample:\n\n```go\nimport (\n    \"github.com/ozontech/cute\"\n    \"github.com/ozontech/cute/errors\"\n)\n\nfunc customAssertBodyWithCustomError() cute.AssertBody {\n    return func(bytes []byte) error {\n        if len(bytes) == 0 {\n            return errors.NewAssertError(\"customAssertBodyWithCustomError\", \"body must be not empty\", \"len is 0\", \"len more 0\")\n        }\n\n        return nil\n    }\n}\n```\nTo create a pretty-error in your custom assert, implement it with [interfaces](errors/error.go):\n\n- Name.\n\n```go\ntype WithNameError interface {\n    GetName() string\n    SetName(string)\n}\n```\n\n- Parameters for Allure step.\n\n```go\ntype WithFields interface {\n    GetFields() map[string]interface{}\n    PutFields(map[string]interface{})\n}\n```\n\n<details>\n  <summary>Allure report</summary>\n\n![assert_error.png](.images/assert_error.png)\n\n</details>\n\n#### Optional assert\n\nIf assert returns an optional error, step fails but the test is successful.\n\nYou can use  `errors.NewOptionalError(error)` method from [errors](errors/error.go) package.\n\n```go\nimport (\n    \"github.com/ozontech/cute\"\n    \"github.com/ozontech/cute/errors\"\n)\n\nfunc customAssertBodyWithCustomError() cute.AssertBody {\n    return func(bytes []byte) error {\n        if len(bytes) == 0 {\n            return errors.NewOptionalError(\"body is empty\")\n        }\n\n        return nil\n    }\n}\n```\n\nTo create optional error, implement error with interface:\n\n```go\ntype OptionalError interface {\n    IsOptional() bool\n    SetOptional(bool)\n}\n```\n\n<details>\n  <summary>Allure report</summary>\n\n![optional_error.png](.images/optional_error.png)\n\n</details>\n\n## <h2><a href=\"https://github.com/ozontech/allure-go?tab=readme-ov-file#wrench-configure-your-environment\">Global Environment Keys</a></h2>\n\n\n| Key | Meaning                                                       | Default                 |\n|---|---------------------------------------------------------------|-------------------------|\n|`ALLURE_OUTPUT_PATH`| Path to output allure results.                                 | `.` (Folder with tests) |\n|`ALLURE_OUTPUT_FOLDER`| Name of result folder.                                            | `/allure-results`       |\n|`ALLURE_ISSUE_PATTERN`| Url pattepn to issue. Must contain `%s`.                   |                         |\n|`ALLURE_TESTCASE_PATTERN`| URL pattern to TestCase. Must contain `%s`.               |                         |\n|`ALLURE_LAUNCH_TAGS`| Default tags for all tests. Tags must be separated by commas. |                         |\n"
  },
  {
    "path": "allure.go",
    "content": "package cute\n\nfunc (qt *cute) setAllureInformation(t allureProvider) {\n\t// Log main vars to allureProvider\n\tqt.setLabelsAllure(t)\n\tqt.setInfoAllure(t)\n\tqt.setLinksAllure(t)\n}\n\nfunc (qt *cute) setLinksAllure(t linksAllureProvider) {\n\tif qt.allureLinks.issue != \"\" {\n\t\tt.SetIssue(qt.allureLinks.issue)\n\t}\n\n\tif qt.allureLinks.testCase != \"\" {\n\t\tt.SetTestCase(qt.allureLinks.testCase)\n\t}\n\n\tif qt.allureLinks.link != nil {\n\t\tt.Link(qt.allureLinks.link)\n\t}\n\n\tif qt.allureLinks.tmsLink != \"\" {\n\t\tt.TmsLink(qt.allureLinks.tmsLink)\n\t}\n\n\tif len(qt.allureLinks.tmsLinks) > 0 {\n\t\tt.TmsLinks(qt.allureLinks.tmsLinks...)\n\t}\n}\n\nfunc (qt *cute) setLabelsAllure(t labelsAllureProvider) {\n\tif qt.allureLabels.id != \"\" {\n\t\tt.ID(qt.allureLabels.id)\n\t}\n\n\tif qt.allureLabels.suiteLabel != \"\" {\n\t\tt.AddSuiteLabel(qt.allureLabels.suiteLabel)\n\t}\n\n\tif qt.allureLabels.subSuite != \"\" {\n\t\tt.AddSubSuite(qt.allureLabels.subSuite)\n\t}\n\n\tif qt.allureLabels.parentSuite != \"\" {\n\t\tt.AddParentSuite(qt.allureLabels.parentSuite)\n\t}\n\n\tif qt.allureLabels.story != \"\" {\n\t\tt.Story(qt.allureLabels.story)\n\t}\n\n\tif qt.allureLabels.tag != \"\" {\n\t\tt.Tag(qt.allureLabels.tag)\n\t}\n\n\tif qt.allureLabels.allureID != \"\" {\n\t\tt.AllureID(qt.allureLabels.allureID)\n\t}\n\n\tif qt.allureLabels.severity != \"\" {\n\t\tt.Severity(qt.allureLabels.severity)\n\t}\n\n\tif qt.allureLabels.owner != \"\" {\n\t\tt.Owner(qt.allureLabels.owner)\n\t}\n\n\tif qt.allureLabels.lead != \"\" {\n\t\tt.Lead(qt.allureLabels.lead)\n\t}\n\n\tif qt.allureLabels.label != nil {\n\t\tt.Label(qt.allureLabels.label)\n\t}\n\n\tif len(qt.allureLabels.labels) != 0 {\n\t\tt.Labels(qt.allureLabels.labels...)\n\t}\n\n\tif qt.allureLabels.feature != \"\" {\n\t\tt.Feature(qt.allureLabels.feature)\n\t}\n\n\tif qt.allureLabels.epic != \"\" {\n\t\tt.Epic(qt.allureLabels.epic)\n\t}\n\n\tif len(qt.allureLabels.tags) != 0 {\n\t\tt.Tags(qt.allureLabels.tags...)\n\t}\n\n\tif qt.allureLabels.layer != \"\" {\n\t\tt.Layer(qt.allureLabels.layer)\n\t}\n}\n\nfunc (qt *cute) setInfoAllure(t infoAllureProvider) {\n\tif qt.allureInfo.title != \"\" {\n\t\tt.Title(qt.allureInfo.title)\n\t}\n\n\tif qt.allureInfo.description != \"\" {\n\t\tt.Description(qt.allureInfo.description)\n\t}\n\n\tif qt.allureInfo.stage != \"\" {\n\t\tt.Stage(qt.allureInfo.stage)\n\t}\n}\n"
  },
  {
    "path": "assert.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n)\n\n// This is type of asserts, for create some assert with using custom logic.\n\n// AssertBody is type for create custom assertions for body\n// Example asserts:\n// - json.LengthGreaterThan\n// - json.LengthGreaterOrEqualThan\n// - json.LengthLessThan\n// - json.LengthLessOrEqualThan\n// - json.Present\n// - json.NotEmpty\n// - json.NotPresent\ntype AssertBody func(body []byte) error\n\n// AssertHeaders is type for create custom assertions for headers\n// Example asserts:\n// - headers.Present\n// - headers.NotPresent\ntype AssertHeaders func(headers http.Header) error\n\n// AssertResponse is type for create custom assertions for response\ntype AssertResponse func(response *http.Response) error\n\n// This is type for create custom assertions with using allure and testing.allureProvider\n\n// AssertBodyT is type for create custom assertions for body with TB\n// Check example in AssertBody\n// TB is testing.T and it can be used for require ore assert from testify or another packages\ntype AssertBodyT func(t T, body []byte) error\n\n// AssertHeadersT is type for create custom assertions for headers with TB\n// Check example in AssertHeaders\n// TB is testing.T and it can be used for require ore assert from testify or another packages\ntype AssertHeadersT func(t T, headers http.Header) error\n\n// AssertResponseT is type for create custom assertions for response with TB\n// Check example in AssertResponse\n// TB is testing.T and it can be used for require ore assert from testify or another packages\ntype AssertResponseT func(t T, response *http.Response) error\n\nfunc (it *Test) assertHeaders(t internalT, headers http.Header) []error {\n\tvar (\n\t\tasserts = it.Expect.AssertHeaders\n\t\tassertT = it.Expect.AssertHeadersT\n\t)\n\n\tif len(asserts) == 0 && len(assertT) == 0 {\n\t\treturn nil\n\t}\n\n\treturn it.executeWithStep(t, \"Assert headers\", func(t T) []error {\n\t\terrs := make([]error, 0)\n\t\t// Execute assert only response\n\t\tfor _, f := range asserts {\n\t\t\terr := f(headers)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\t// Execute assert for response with TB\n\t\tfor _, f := range assertT {\n\t\t\terr := f(t, headers)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\treturn errs\n\t})\n}\n\nfunc (it *Test) assertResponse(t internalT, resp *http.Response) []error {\n\tvar (\n\t\tasserts = it.Expect.AssertResponse\n\t\tassertT = it.Expect.AssertResponseT\n\t)\n\n\tif len(asserts) == 0 && len(assertT) == 0 {\n\t\treturn nil\n\t}\n\n\treturn it.executeWithStep(t, \"Assert response\", func(t T) []error {\n\t\terrs := make([]error, 0)\n\t\t// Execute assert only response\n\t\tfor _, f := range asserts {\n\t\t\terr := f(resp)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\t// Execute assert for response with TB\n\t\tfor _, f := range assertT {\n\t\t\terr := f(t, resp)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\treturn errs\n\t})\n}\n\nfunc (it *Test) assertBody(t internalT, body []byte) []error {\n\tvar (\n\t\tasserts = it.Expect.AssertBody\n\t\tassertT = it.Expect.AssertBodyT\n\t)\n\n\tif len(asserts) == 0 && len(assertT) == 0 {\n\t\treturn nil\n\t}\n\n\treturn it.executeWithStep(t, \"Assert body\", func(t T) []error {\n\t\terrs := make([]error, 0)\n\t\t// Execute assert only response\n\t\tfor _, f := range asserts {\n\t\t\terr := f(body)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\t// Execute assert for response with TB\n\t\tfor _, f := range assertT {\n\t\t\terr := f(t, body)\n\t\t\tif err != nil {\n\t\t\t\terrs = append(errs, err)\n\t\t\t}\n\t\t}\n\n\t\treturn errs\n\t})\n}\n"
  },
  {
    "path": "assert_broken.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ozontech/cute/errors\"\n)\n\nfunc brokenAssertHeaders(assert AssertHeaders) AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\terr := assert(headers)\n\n\t\treturn wrapBrokenError(err)\n\t}\n}\n\nfunc brokenAssertBody(assert AssertBody) AssertBody {\n\treturn func(body []byte) error {\n\t\terr := assert(body)\n\n\t\treturn wrapBrokenError(err)\n\t}\n}\n\nfunc brokenAssertResponse(assert AssertResponse) AssertResponse {\n\treturn func(resp *http.Response) error {\n\t\terr := assert(resp)\n\n\t\treturn wrapBrokenError(err)\n\t}\n}\n\nfunc brokenAssertHeadersT(assert AssertHeadersT) AssertHeadersT {\n\treturn func(t T, headers http.Header) error {\n\t\terr := assert(t, headers)\n\n\t\treturn wrapBrokenError(err)\n\t}\n}\n\nfunc brokenAssertBodyT(assert AssertBodyT) AssertBodyT {\n\treturn func(t T, body []byte) error {\n\t\terr := assert(t, body)\n\n\t\treturn wrapBrokenError(err)\n\t}\n}\n\nfunc brokenAssertResponseT(assert AssertResponseT) AssertResponseT {\n\treturn func(t T, resp *http.Response) error {\n\t\terr := assert(t, resp)\n\n\t\treturn wrapBrokenError(err)\n\t}\n}\n\nfunc wrapBrokenError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif tErr, ok := err.(errors.BrokenError); ok {\n\t\ttErr.SetBroken(true)\n\n\t\treturn tErr.(error)\n\t}\n\n\treturn errors.WrapBrokenError(err)\n}\n"
  },
  {
    "path": "assert_broken_test.go",
    "content": "package cute\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBrokenAssertResponse(t *testing.T) {\n\tv := &http.Response{}\n\tf := func(_ *http.Response) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := brokenAssertResponse(f)(v)\n\n\tif BrokenError, ok := err.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n\nfunc TestBrokenAssertResponseT(t *testing.T) {\n\tv := &http.Response{}\n\tf := func(T, *http.Response) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := brokenAssertResponseT(f)(nil, v)\n\n\tif BrokenError, ok := err.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n\nfunc TestBrokenAssertHeaders(t *testing.T) {\n\th := http.Header{}\n\tf := func(_ http.Header) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := brokenAssertHeaders(f)(h)\n\n\tif BrokenError, ok := err.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n\nfunc TestBrokenAssertHeadersT(t *testing.T) {\n\th := http.Header{}\n\tf := func(T, http.Header) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := brokenAssertHeadersT(f)(nil, h)\n\n\tif BrokenError, ok := err.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n\nfunc TestBrokenAssertBody(t *testing.T) {\n\tv := []byte{}\n\tf := func(_ []byte) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := brokenAssertBody(f)(v)\n\n\tif BrokenError, ok := err.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n\nfunc TestBrokenAssertBodyT(t *testing.T) {\n\tv := []byte{}\n\tf := func(T, []byte) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := brokenAssertBodyT(f)(nil, v)\n\n\tif BrokenError, ok := err.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n\nfunc TestWrapBrokenError(t *testing.T) {\n\terr := errors.New(\"test error\")\n\n\toptError := wrapBrokenError(err)\n\tif BrokenError, ok := optError.(cuteErrors.BrokenError); assert.True(t, ok) {\n\t\trequire.True(t, BrokenError.IsBroken())\n\t}\n}\n"
  },
  {
    "path": "assert_optional.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ozontech/cute/errors\"\n)\n\nfunc optionalAssertHeaders(assert AssertHeaders) AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\terr := assert(headers)\n\n\t\treturn wrapOptionalError(err)\n\t}\n}\n\nfunc optionalAssertBody(assert AssertBody) AssertBody {\n\treturn func(body []byte) error {\n\t\terr := assert(body)\n\n\t\treturn wrapOptionalError(err)\n\t}\n}\n\nfunc optionalAssertResponse(assert AssertResponse) AssertResponse {\n\treturn func(resp *http.Response) error {\n\t\terr := assert(resp)\n\n\t\treturn wrapOptionalError(err)\n\t}\n}\n\nfunc optionalAssertHeadersT(assert AssertHeadersT) AssertHeadersT {\n\treturn func(t T, headers http.Header) error {\n\t\terr := assert(t, headers)\n\n\t\treturn wrapOptionalError(err)\n\t}\n}\n\nfunc optionalAssertBodyT(assert AssertBodyT) AssertBodyT {\n\treturn func(t T, body []byte) error {\n\t\terr := assert(t, body)\n\n\t\treturn wrapOptionalError(err)\n\t}\n}\n\nfunc optionalAssertResponseT(assert AssertResponseT) AssertResponseT {\n\treturn func(t T, resp *http.Response) error {\n\t\terr := assert(t, resp)\n\n\t\treturn wrapOptionalError(err)\n\t}\n}\n\nfunc wrapOptionalError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif tErr, ok := err.(errors.OptionalError); ok {\n\t\ttErr.SetOptional(true)\n\n\t\treturn tErr.(error)\n\t}\n\n\treturn errors.WrapOptionalError(err)\n}\n"
  },
  {
    "path": "assert_optional_test.go",
    "content": "package cute\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestOptionalAssertResponse(t *testing.T) {\n\tv := &http.Response{}\n\tf := func(*http.Response) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := optionalAssertResponse(f)(v)\n\n\tif optionalError, ok := err.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n\nfunc TestOptionalAssertResponseT(t *testing.T) {\n\tv := &http.Response{}\n\tf := func(T, *http.Response) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := optionalAssertResponseT(f)(nil, v)\n\n\tif optionalError, ok := err.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n\nfunc TestOptionalAssertHeaders(t *testing.T) {\n\th := http.Header{}\n\tf := func(http.Header) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := optionalAssertHeaders(f)(h)\n\n\tif optionalError, ok := err.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n\nfunc TestOptionalAssertHeadersT(t *testing.T) {\n\th := http.Header{}\n\tf := func(T, http.Header) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := optionalAssertHeadersT(f)(nil, h)\n\n\tif optionalError, ok := err.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n\nfunc TestOptionalAssertBody(t *testing.T) {\n\tv := []byte{}\n\tf := func([]byte) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := optionalAssertBody(f)(v)\n\n\tif optionalError, ok := err.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n\nfunc TestOptionalAssertBodyT(t *testing.T) {\n\tv := []byte{}\n\tf := func(T, []byte) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := optionalAssertBodyT(f)(nil, v)\n\n\tif optionalError, ok := err.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n\nfunc TestWrapOptionalError(t *testing.T) {\n\terr := errors.New(\"test error\")\n\n\toptError := wrapOptionalError(err)\n\tif optionalError, ok := optError.(cuteErrors.OptionalError); assert.True(t, ok) {\n\t\trequire.True(t, optionalError.IsOptional())\n\t}\n}\n"
  },
  {
    "path": "assert_require.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\n\t\"github.com/ozontech/cute/errors\"\n)\n\nfunc requireAssertHeaders(assert AssertHeaders) AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\terr := assert(headers)\n\n\t\treturn wrapRequireError(err)\n\t}\n}\n\nfunc requireAssertBody(assert AssertBody) AssertBody {\n\treturn func(body []byte) error {\n\t\terr := assert(body)\n\n\t\treturn wrapRequireError(err)\n\t}\n}\n\nfunc requireAssertResponse(assert AssertResponse) AssertResponse {\n\treturn func(resp *http.Response) error {\n\t\terr := assert(resp)\n\n\t\treturn wrapRequireError(err)\n\t}\n}\n\nfunc requireAssertHeadersT(assert AssertHeadersT) AssertHeadersT {\n\treturn func(t T, headers http.Header) error {\n\t\terr := assert(t, headers)\n\n\t\treturn wrapRequireError(err)\n\t}\n}\n\nfunc requireAssertBodyT(assert AssertBodyT) AssertBodyT {\n\treturn func(t T, body []byte) error {\n\t\terr := assert(t, body)\n\n\t\treturn wrapRequireError(err)\n\t}\n}\n\nfunc requireAssertResponseT(assert AssertResponseT) AssertResponseT {\n\treturn func(t T, resp *http.Response) error {\n\t\terr := assert(t, resp)\n\n\t\treturn wrapRequireError(err)\n\t}\n}\n\nfunc wrapRequireError(err error) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif tErr, ok := err.(errors.RequireError); ok {\n\t\ttErr.SetRequire(true)\n\n\t\treturn tErr.(error)\n\t}\n\n\treturn errors.WrapRequireError(err)\n}\n"
  },
  {
    "path": "assert_require_test.go",
    "content": "package cute\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRequireAssertResponse(t *testing.T) {\n\tv := &http.Response{}\n\tf := func(_ *http.Response) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := requireAssertResponse(f)(v)\n\n\tif RequireError, ok := err.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n\nfunc TestRequireAssertResponseT(t *testing.T) {\n\tv := &http.Response{}\n\tf := func(T, *http.Response) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := requireAssertResponseT(f)(nil, v)\n\n\tif RequireError, ok := err.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n\nfunc TestRequireAssertHeaders(t *testing.T) {\n\th := http.Header{}\n\tf := func(http.Header) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := requireAssertHeaders(f)(h)\n\n\tif RequireError, ok := err.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n\nfunc TestRequireAssertHeadersT(t *testing.T) {\n\th := http.Header{}\n\tf := func(T, http.Header) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := requireAssertHeadersT(f)(nil, h)\n\n\tif RequireError, ok := err.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n\nfunc TestRequireAssertBody(t *testing.T) {\n\tv := []byte{}\n\tf := func([]byte) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := requireAssertBody(f)(v)\n\n\tif RequireError, ok := err.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n\nfunc TestRequireAssertBodyT(t *testing.T) {\n\tv := []byte{}\n\tf := func(T, []byte) error {\n\t\treturn errors.New(\"test error\")\n\t}\n\n\terr := requireAssertBodyT(f)(nil, v)\n\n\tif RequireError, ok := err.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n\nfunc TestWrapRequireError(t *testing.T) {\n\terr := errors.New(\"test error\")\n\n\toptError := wrapRequireError(err)\n\tif RequireError, ok := optError.(cuteErrors.RequireError); assert.True(t, ok) {\n\t\trequire.True(t, RequireError.IsRequire())\n\t}\n}\n"
  },
  {
    "path": "assert_trace.go",
    "content": "package cute\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\t\"runtime\"\n\n\t\"github.com/ozontech/cute/errors\"\n)\n\n// assertHeadersWithTrace is a function to add trace inside assert headers error\nfunc assertHeadersWithTrace(assert AssertHeaders, trace string) AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\terr := assert(headers)\n\n\t\treturn wrapWithTrace(err, trace)\n\t}\n}\n\n// assertBodyWithTrace is a function to add trace inside assert body error\nfunc assertBodyWithTrace(assert AssertBody, trace string) AssertBody {\n\treturn func(body []byte) error {\n\t\terr := assert(body)\n\n\t\treturn wrapWithTrace(err, trace)\n\t}\n}\n\n// assertResponseWithTrace is a function to add trace inside assert response error\nfunc assertResponseWithTrace(assert AssertResponse, trace string) AssertResponse {\n\treturn func(resp *http.Response) error {\n\t\terr := assert(resp)\n\n\t\treturn wrapWithTrace(err, trace)\n\t}\n}\n\n// assertHeadersTWithTrace is a function to add trace inside assert headers error\nfunc assertHeadersTWithTrace(assert AssertHeadersT, trace string) AssertHeadersT {\n\treturn func(t T, headers http.Header) error {\n\t\terr := assert(t, headers)\n\n\t\treturn wrapWithTrace(err, trace)\n\t}\n}\n\n// assertBodyTWithTrace is a function to add trace inside assert body error\nfunc assertBodyTWithTrace(assert AssertBodyT, trace string) AssertBodyT {\n\treturn func(t T, body []byte) error {\n\t\terr := assert(t, body)\n\n\t\treturn wrapWithTrace(err, trace)\n\t}\n}\n\n// assertResponseTWithTrace is a function to add trace inside assert response error\nfunc assertResponseTWithTrace(assert AssertResponseT, trace string) AssertResponseT {\n\treturn func(t T, resp *http.Response) error {\n\t\terr := assert(t, resp)\n\n\t\treturn wrapWithTrace(err, trace)\n\t}\n}\n\n// wrapWithTrace is a function to add trace inside error\nfunc wrapWithTrace(err error, trace string) error {\n\tif err == nil {\n\t\treturn nil\n\t}\n\n\tif tErr, ok := err.(errors.WithTrace); ok {\n\t\ttErr.SetTrace(trace)\n\n\t\treturn tErr.(error)\n\t}\n\n\treturn errors.WrapErrorWithTrace(err, trace)\n}\n\nfunc getTrace() string {\n\tpcs := make([]uintptr, 10)\n\tdepth := runtime.Callers(3, pcs)\n\n\tif depth == 0 {\n\t\tfmt.Println(\"Couldn't get the stack information\")\n\t\treturn \"\"\n\t}\n\n\tcallers := runtime.CallersFrames(pcs[:depth])\n\tcaller, _ := callers.Next()\n\n\treturn fmt.Sprintf(\"%s:%d\", caller.File, caller.Line)\n}\n"
  },
  {
    "path": "asserts/headers/headers.go",
    "content": "package headers\n\nimport (\n\t\"fmt\"\n\t\"net/http\"\n\n\t\"github.com/ozontech/cute\"\n\t\"github.com/ozontech/cute/errors\"\n)\n\n// Present is a function to asserts that header is present\nfunc Present(key string) cute.AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\tif v := headers.Get(key); v == \"\" {\n\t\t\treturn errors.NewAssertError(\"Present\", fmt.Sprintf(\"header %s is not present\", key), nil, nil)\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// NotPresent is a function to asserts that header is not present\nfunc NotPresent(key string) cute.AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\tif v := headers.Values(key); len(v) > 0 {\n\t\t\treturn errors.NewAssertError(\"NotPresent\", fmt.Sprintf(\"header %s is present\", key), nil, nil)\n\t\t}\n\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "asserts/headers/headers_test.go",
    "content": "package headers\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestPresent(t *testing.T) {\n\theaders := http.Header{\n\t\t\"Content-Type\": []string{\"application/json\"},\n\t}\n\n\terr := Present(\"Content-Type\")(headers)\n\trequire.NoError(t, err)\n}\n\nfunc TestPresentError(t *testing.T) {\n\theaders := http.Header{\n\t\t\"Content-Type\": []string{},\n\t}\n\n\terr := Present(\"not-present\")(headers)\n\trequire.Error(t, err)\n}\n\nfunc TestNotPresent(t *testing.T) {\n\theaders := http.Header{\n\t\t\"Content-Type\": []string{\"\", \"application/json\"},\n\t}\n\n\terr := NotPresent(\"Content-Type\")(headers)\n\trequire.Error(t, err)\n}\n\nfunc TestNotPresentError(t *testing.T) {\n\theaders := http.Header{\n\t\t\"Content-Type\": []string{},\n\t}\n\n\terr := NotPresent(\"not-present\")(headers)\n\trequire.NoError(t, err)\n}\n"
  },
  {
    "path": "asserts/json/json.go",
    "content": "package json\n\nimport (\n\t\"fmt\"\n\n\tjd \"github.com/josephburnett/jd/lib\"\n\t\"github.com/ohler55/ojg/jp\"\n\t\"github.com/ohler55/ojg/oj\"\n\t\"github.com/ozontech/cute\"\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n)\n\n// Diff is a function to compare two jsons\nfunc Diff(original string) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\toriginalJSON, err := jd.ReadJsonString(original)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not parse original json in Diff error: '%s'\", err)\n\t\t}\n\n\t\tbodyJSON, err := jd.ReadJsonString(string(body))\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"could not parse body json in Diff error: '%s'\", err)\n\t\t}\n\n\t\tdiff := originalJSON.Diff(bodyJSON).Render()\n\t\tif diff != \"\" {\n\t\t\tcErr := cuteErrors.NewEmptyAssertError(\"JSON Diff\", \"JSON is not the same\")\n\t\t\tcErr.PutAttachment(&cuteErrors.Attachment{\n\t\t\t\tName:     \"JSON diff\",\n\t\t\t\tMimeType: \"text/plain\",\n\t\t\t\tContent:  []byte(diff),\n\t\t\t})\n\n\t\t\treturn cErr\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\n// Contains is a function to assert that a jsonpath expression extracts a value in an array\n// About expression - https://goessner.net/articles/JsonPath/\nfunc Contains(expression string, expect interface{}) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn contains(body, expression, expect)\n\t}\n}\n\n// Equal is a function to assert that a jsonpath expression matches the given value\n// About expression - https://goessner.net/articles/JsonPath/\nfunc Equal(expression string, expect interface{}) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn equal(body, expression, expect)\n\t}\n}\n\n// NotEqual is a function to check json path expression value is not equal to given value\n// About expression - https://goessner.net/articles/JsonPath/\nfunc NotEqual(expression string, expect interface{}) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn notEqual(body, expression, expect)\n\t}\n}\n\n// EqualJSON is a function to check json path expression value is equal to given json\n// About expression - https://goessner.net/articles/JsonPath/\nfunc EqualJSON(expression string, expect []byte) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn equalJSON(body, expression, expect)\n\t}\n}\n\n// NotEqualJSON is a function to check json path expression value is not equal to given json\n// About expression - https://goessner.net/articles/JsonPath/\nfunc NotEqualJSON(expression string, expect []byte) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn notEqualJSON(body, expression, expect)\n\t}\n}\n\n// Length is a function to asserts that value is the expected length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc Length(expression string, expectLength int) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn length(body, expression, expectLength)\n\t}\n}\n\n// LengthGreaterThan is a function to asserts that value is greater than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc LengthGreaterThan(expression string, minimumLength int) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn greaterThan(body, expression, minimumLength)\n\t}\n}\n\n// LengthGreaterOrEqualThan is a function to asserts that value is greater or equal than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc LengthGreaterOrEqualThan(expression string, minimumLength int) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn greaterOrEqualThan(body, expression, minimumLength)\n\t}\n}\n\n// LengthLessThan is a function to asserts that value is less than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc LengthLessThan(expression string, maximumLength int) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn lessThan(body, expression, maximumLength)\n\t}\n}\n\n// LengthLessOrEqualThan is a function to asserts that value is less or equal than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc LengthLessOrEqualThan(expression string, maximumLength int) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn lessOrEqualThan(body, expression, maximumLength)\n\t}\n}\n\n// Present is a function to asserts that value is present\n// value can be nil or 0\n// About expression - https://goessner.net/articles/JsonPath/\nfunc Present(expression string) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn present(body, expression)\n\t}\n}\n\n// NotEmpty is a function to asserts that value is present\n// value can't be nil or 0\n// About expression - https://goessner.net/articles/JsonPath/\nfunc NotEmpty(expression string) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn notEmpty(body, expression)\n\t}\n}\n\n// NotPresent is a function to asserts that value is not present\n// About expression - https://goessner.net/articles/JsonPath/\nfunc NotPresent(expression string) cute.AssertBody {\n\treturn func(body []byte) error {\n\t\treturn notPresent(body, expression)\n\t}\n}\n\n// GetValueFromJSON is function for get value from json\nfunc GetValueFromJSON(js []byte, expression string) ([]interface{}, error) {\n\tobj, err := oj.Parse(js)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse json in GetValueFromJSON error: '%s'\", err)\n\t}\n\n\tjsonPath, err := jp.ParseString(expression)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"could not parse path in GetValueFromJSON error: '%s'\", err)\n\t}\n\n\tres := jsonPath.Get(obj)\n\n\tif len(res) == 0 {\n\t\treturn nil, fmt.Errorf(\"could not find element by path %v in JSON\", expression)\n\t}\n\n\treturn res, nil\n}\n"
  },
  {
    "path": "asserts/json/json_test.go",
    "content": "package json\n\nimport (\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/assert\"\n\t\"github.com/stretchr/testify/require\"\n)\n\ntype jsonTest struct {\n\tcaseName   string\n\tdata       string\n\texpression string\n\texpect     interface{}\n\tIsNilErr   bool\n}\n\nfunc TestDiff(t *testing.T) {\n\ttestCases := []struct {\n\t\tname          string\n\t\toriginalJSON  string\n\t\tbodyJSON      string\n\t\texpectedError string\n\t}{\n\t\t{\n\t\t\tname:          \"SameJSON\",\n\t\t\toriginalJSON:  `{\"key1\": \"value1\", \"key2\": \"value2\"}`,\n\t\t\tbodyJSON:      `{\"key1\": \"value1\", \"key2\": \"value2\"}`,\n\t\t\texpectedError: \"\", // No error expected, JSONs are the same\n\t\t},\n\t\t{\n\t\t\tname:          \"DifferentValueJSON\",\n\t\t\toriginalJSON:  `{\"key1\": \"value1\", \"key2\": \"value2\"}`,\n\t\t\tbodyJSON:      `{\"key1\": \"value1\", \"key2\": \"value3\"}`,\n\t\t\texpectedError: \"JSON is not the same\",\n\t\t},\n\t\t{\n\t\t\tname:          \"MissingKeyJSON\",\n\t\t\toriginalJSON:  `{\"key1\": \"value1\", \"key2\": \"value2\"}`,\n\t\t\tbodyJSON:      `{\"key1\": \"value1\"}`,\n\t\t\texpectedError: \"JSON is not the same\",\n\t\t},\n\t\t{\n\t\t\tname:          \"ExtraKeyJSON\",\n\t\t\toriginalJSON:  `{\"key1\": \"value1\"}`,\n\t\t\tbodyJSON:      `{\"key1\": \"value1\", \"key2\": \"value2\"}`,\n\t\t\texpectedError: \"JSON is not the same\",\n\t\t},\n\t\t{\n\t\t\tname:          \"EmptyJSON\",\n\t\t\toriginalJSON:  `{}`,\n\t\t\tbodyJSON:      `{}`,\n\t\t\texpectedError: \"\", // No error expected, empty JSONs are the same\n\t\t},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\t// Call the Diff function with the test input\n\t\t\terr := Diff(testCase.originalJSON)([]byte(testCase.bodyJSON))\n\n\t\t\t// Check if the error message matches the expected result\n\t\t\tif testCase.expectedError == \"\" {\n\t\t\t\tassert.NoError(t, err) // No error expected\n\t\t\t} else {\n\t\t\t\tassert.Error(t, err) // Error expected\n\t\t\t\tassert.Contains(t, err.Error(), testCase.expectedError)\n\t\t\t}\n\t\t})\n\t}\n}\n\nfunc TestNotPresent(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not present check\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.b\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not present check \",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o[0]\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o[0]['1']\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := NotPresent(test.expression)([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestPresent(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not present check\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.b\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct present check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o[0]\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o[0]['1']\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not correct path\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.not_correct\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"empty integer\",\n\t\t\tdata:       `{\"o\":0}`,\n\t\t\texpression: \"$.o\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"empty object\",\n\t\t\tdata:       `{\"o\":null}`,\n\t\t\texpression: \"$.o\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"empty string\",\n\t\t\tdata:       `{\"o\":null, \"b\":\"\"}`,\n\t\t\texpression: \"$.b\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := Present(test.expression)([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestNotEmpty(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not present check\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.b\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct present check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o[0]\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o[0]['1']\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not correct path\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.not_correct\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"empty integer\",\n\t\t\tdata:       `{\"o\":0}`,\n\t\t\texpression: \"$.o\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"empty object\",\n\t\t\tdata:       `{\"o\":null}`,\n\t\t\texpression: \"$.o\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"empty string\",\n\t\t\tdata:       `{\"o\":null, \"b\":\"\"}`,\n\t\t\texpression: \"$.b\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := NotEmpty(test.expression)([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestLength(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     6,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     99,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not contain value\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     1,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := Length(test.expression, test.expect.(int))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestLengthGreaterThan(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     2,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check array when equal\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     99,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     1,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not correct path\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.not_correct\",\n\t\t\texpect:     0,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := LengthGreaterThan(test.expression, test.expect.(int))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestLengthGreaterOrEqualThan(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     2,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check array when equal\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string when equal\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     6,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     99,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     1,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map when equal\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     5,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not correct path\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.not_correct\",\n\t\t\texpect:     0,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := LengthGreaterOrEqualThan(test.expression, test.expect.(int))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestLengthLessThan(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     7,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     6,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not correct path\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.not_correct\",\n\t\t\texpect:     0,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := LengthLessThan(test.expression, test.expect.(int))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestLengthLessOrEqualThan(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check array when equal\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check array\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     2,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     7,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check string when equal\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     6,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check string\",\n\t\t\tdata:       `{\"o\":\"123456\"}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     5,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     4,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"correct check map when equal\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     3,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not correct check map\",\n\t\t\tdata:       `{\"o\":[{\"1\":\"a\"}, {\"2\":\"b\"}, {\"3\":\"c\"}]}`,\n\t\t\texpression: \"$.o\",\n\t\t\texpect:     2,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check not correct path\",\n\t\t\tdata:       `{\"o\":[\"a\", \"b\", \"c\"]}`,\n\t\t\texpression: \"$.not_correct\",\n\t\t\texpect:     0,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := LengthLessOrEqualThan(test.expression, test.expect.(int))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestEqual(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"valid json\",\n\t\t\tdata:       `{\"first\": 777, \"second\": [{\"key_1\": \"some_key\", \"value\": \"some_value\"}]}`,\n\t\t\texpression: \"$.second[0].value\",\n\t\t\texpect:     \"some_value\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName: \"not valid json\",\n\t\t\tdata:     \"{not_valid_json}\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"3rd party key\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.l\",\n\t\t\texpect:     nil,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not array\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b[bs]\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"valid array\",\n\t\t\tdata:       `{\"arr\": [\"one\",\"two\"]}`,\n\t\t\texpression: \"$.arr\",\n\t\t\texpect:     []string{\"one\", \"two\"},\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal map\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b\",\n\t\t\texpect:     map[string]interface{}{\"bs\": \"sb\"},\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"as\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal not correct string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     []byte(\"not_correct\"),\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check 186135434\",\n\t\t\tdata:       `{\"a\":186135434, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     186135434,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check float\",\n\t\t\tdata:       `{\"a\":1.0000001, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     1.0000001,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check float 2\",\n\t\t\tdata:       `{\"a\":999.0000001, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     999.0000001,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := Equal(test.expression, test.expect)([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestNotEqual(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"valid json\",\n\t\t\tdata:       `{\"first\": 777, \"second\": [{\"key_1\": \"some_key\", \"value\": \"some_value\"}]}`,\n\t\t\texpression: \"$.second[0].value\",\n\t\t\texpect:     \"some_value\",\n\t\t\tIsNilErr:   false,\n\t\t},\n\t\t{\n\t\t\tcaseName: \"not valid json\",\n\t\t\tdata:     \"{not_valid_json}\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"3rd party key\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.l\",\n\t\t\texpect:     nil,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not array\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b[bs]\",\n\t\t\texpect:     \"sb\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal map\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b\",\n\t\t\texpect:     map[string]interface{}{\"bs\": \"sb\"},\n\t\t\tIsNilErr:   false,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"as\",\n\t\t\tIsNilErr:   false,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\terr := NotEqual(test.expression, test.expect)([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestEqualJSON(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"valid json\",\n\t\t\tdata:       `{\"first\": 777, \"second\": [{\"key_1\": \"some_key\", \"value\": \"some_value\"}]}`,\n\t\t\texpression: \"$.second[0].value\",\n\t\t\texpect:     `\"some_value\"`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName: \"not valid json\",\n\t\t\tdata:     \"{not_valid_json}\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"3rd party key\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.l\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not array\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b[bs]\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"valid array\",\n\t\t\tdata:       `{\"arr\": [\"one\",\"two\"]}`,\n\t\t\texpression: \"$.arr\",\n\t\t\texpect:     `[\"one\", \"two\"]`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal map\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b\",\n\t\t\texpect:     `{\"bs\": \"sb\"}`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     `\"as\"`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal not correct string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     `\"not_correct\"`,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check deep equal\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$\",\n\t\t\texpect:     `{ \"b\": {\"bs\": \"sb\"}, \"a\":\"as\" }`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check deep equal not correct\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$\",\n\t\t\texpect:     `{ \"b\": {\"sb\": \"bs\"}, \"a\":\"as\" }`,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check 186135434\",\n\t\t\tdata:       `{\"a\":186135434, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"186135434\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check float\",\n\t\t\tdata:       `{\"a\":1.0000001, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"1.0000001\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check float 2\",\n\t\t\tdata:       `{\"a\":999.0000001, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"999.0000001\",\n\t\t\tIsNilErr:   true,\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\texp, _ := test.expect.(string)\n\t\terr := EqualJSON(test.expression, []byte(exp))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestNotEqualJSON(t *testing.T) {\n\ttests := []jsonTest{\n\t\t{\n\t\t\tcaseName:   \"valid json\",\n\t\t\tdata:       `{\"first\": 777, \"second\": [{\"key_1\": \"some_key\", \"value\": \"some_value\"}]}`,\n\t\t\texpression: \"$.second[0].value\",\n\t\t\texpect:     `\"some_value\"`,\n\t\t},\n\t\t{\n\t\t\tcaseName: \"not valid json\",\n\t\t\tdata:     \"{not_valid_json}\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"3rd party key\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.l\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"not array\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b[bs]\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"valid array\",\n\t\t\tdata:       `{\"arr\": [\"one\",\"two\"]}`,\n\t\t\texpression: \"$.arr\",\n\t\t\texpect:     `[\"one\", \"two\"]`,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal map\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.b\",\n\t\t\texpect:     `{\"bs\": \"sb\"}`,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     `\"as\"`,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check equal not correct string\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     `\"not_correct\"`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check deep equal\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$\",\n\t\t\texpect:     `{ \"b\": {\"bs\": \"sb\"}, \"a\":\"as\" }`,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check deep equal not correct\",\n\t\t\tdata:       `{\"a\":\"as\", \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$\",\n\t\t\texpect:     `{ \"b\": {\"sb\": \"bs\"}, \"a\":\"as\" }`,\n\t\t\tIsNilErr:   true,\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check 186135434\",\n\t\t\tdata:       `{\"a\":186135434, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"186135434\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check float\",\n\t\t\tdata:       `{\"a\":1.0000001, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"1.0000001\",\n\t\t},\n\t\t{\n\t\t\tcaseName:   \"check float 2\",\n\t\t\tdata:       `{\"a\":999.0000001, \"b\":{\"bs\":\"sb\"}}`,\n\t\t\texpression: \"$.a\",\n\t\t\texpect:     \"999.0000001\",\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\texp, _ := test.expect.(string)\n\t\terr := NotEqualJSON(test.expression, []byte(exp))([]byte(test.data))\n\n\t\tif test.IsNilErr {\n\t\t\trequire.NoError(t, err, \"failed test %v\", test.caseName)\n\t\t} else {\n\t\t\trequire.Error(t, err, \"failed test %v\", test.caseName)\n\t\t}\n\t}\n}\n\nfunc TestGetValueFromJSON(t *testing.T) {\n\ttestCases := []struct {\n\t\tname          string\n\t\tinputJSON     string\n\t\texpression    string\n\t\texpectedValue []interface{}\n\t\texpectedError string\n\t}{\n\t\t{\n\t\t\tname:          \"ValidExpressionObject\",\n\t\t\tinputJSON:     `{\"key1\": \"value1\", \"key2\": {\"key3\": \"value3\"}}`,\n\t\t\texpression:    \"key2.key3\",\n\t\t\texpectedValue: []interface{}{\"value3\"},\n\t\t\texpectedError: \"\", // No error expected\n\t\t},\n\t\t{\n\t\t\tname:          \"ValidExpressionArray\",\n\t\t\tinputJSON:     `{\"key1\": \"value1\", \"key2\": [1, 2, 3]}`,\n\t\t\texpression:    \"key2[1]\",\n\t\t\texpectedValue: []interface{}{int64(2)},\n\t\t\texpectedError: \"\", // No error expected\n\t\t},\n\t\t{\n\t\t\tname:          \"ValidExpressionMap\",\n\t\t\tinputJSON:     `{\"key1\": \"value1\", \"key2\": {\"subkey1\": \"subvalue1\"}}`,\n\t\t\texpression:    \"key2\",\n\t\t\texpectedValue: []interface{}{map[string]interface{}{\"subkey1\": \"subvalue1\"}},\n\t\t\texpectedError: \"\", // No error expected\n\t\t},\n\t\t{\n\t\t\tname:          \"InvalidJSON\",\n\t\t\tinputJSON:     `invalid json`,\n\t\t\texpression:    \"key1\",\n\t\t\texpectedValue: nil,\n\t\t\texpectedError: \"could not parse json\",\n\t\t},\n\t\t{\n\t\t\tname:          \"InvalidExpression\",\n\t\t\tinputJSON:     `{\"key1\": \"value1\"}`,\n\t\t\texpression:    \"key2\",\n\t\t\texpectedValue: nil,\n\t\t\texpectedError: \"could not find element by path key2 in JSON\",\n\t\t},\n\t}\n\n\tfor _, testCase := range testCases {\n\t\tt.Run(testCase.name, func(t *testing.T) {\n\t\t\t// Call the GetValueFromJSON function with the test input\n\t\t\tvalue, err := GetValueFromJSON([]byte(testCase.inputJSON), testCase.expression)\n\n\t\t\t// Check if the error message matches the expected result\n\t\t\tif testCase.expectedError == \"\" {\n\t\t\t\tassert.NoError(t, err) // No error expected\n\t\t\t} else {\n\t\t\t\tassert.Error(t, err) // Error expected\n\t\t\t\tassert.Contains(t, err.Error(), testCase.expectedError)\n\t\t\t}\n\n\t\t\t// Check if the returned value is an array and matches the expected result\n\t\t\tassert.IsType(t, []interface{}{}, value)\n\t\t\tassert.Equal(t, testCase.expectedValue, value)\n\t\t})\n\t}\n}\n"
  },
  {
    "path": "asserts/json/util.go",
    "content": "package json\n\nimport (\n\t\"bytes\"\n\t\"fmt\"\n\t\"reflect\"\n\t\"strings\"\n\n\t\"github.com/ohler55/ojg/oj\"\n\t\"github.com/ozontech/cute/errors\"\n)\n\n// Contains is a function to assert that a jsonpath expression extracts a value in an array\n// Given the response is {\"first\": 777, \"second\": [{\"key_1\": \"some_key\", \"value\": \"some_value\"}]}, we can assert on the result like so `$.second[? @.key_1==\"some_key\"].value`, \"some_value\"\n// About expression - https://goessner.net/articles/JsonPath/\nfunc contains(data []byte, expression string, expect interface{}) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tok, found := insideArray(value, expect)\n\t\tif !ok {\n\t\t\treturn errors.NewAssertError(\"Contains\", fmt.Sprintf(\"on path %v. %v could not be applied builtin len()\", expression, expect), nil, nil)\n\t\t}\n\n\t\tif !found {\n\t\t\treturn errors.NewAssertError(\"Contains\", fmt.Sprintf(\"on path %v. expect %v, but actual %v\", expression, expect, value), value, expect)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc equalAbstract(data []byte, expression string, expect interface{}, name string) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tif !objectsAreEqual(value, expect) {\n\t\t\treturn errors.NewAssertError(name, fmt.Sprintf(\"on path %v. expect %v, but actual %v\", expression, expect, value), value, expect)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc notEqualAbstract(data []byte, expression string, expect interface{}, name string) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tif objectsAreEqual(value, expect) {\n\t\t\treturn errors.NewAssertError(name, fmt.Sprintf(\"on path %v. expect %v, but actual %v\", expression, expect, value), value, expect)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Equal is a function to assert that a jsonpath expression matches the given value\n// About expression - https://goessner.net/articles/JsonPath/\nfunc equal(data []byte, expression string, expect interface{}) error {\n\treturn equalAbstract(data, expression, expect, \"Equal\")\n}\n\n// NotEqual is a function to check json path expression value is not equal to given value\n// About expression - https://goessner.net/articles/JsonPath/\nfunc notEqual(data []byte, expression string, expect interface{}) error {\n\treturn notEqualAbstract(data, expression, expect, \"NotEqual\")\n}\n\n// EqualJSON is a function to check json path expression value is equal to given json\n// About expression - https://goessner.net/articles/JsonPath/\nfunc equalJSON(data []byte, expression string, expect []byte) error {\n\tobj, err := oj.Parse(expect)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not parse json in EqualJSON error: '%s'\", err)\n\t}\n\n\treturn equalAbstract(data, expression, obj, \"EqualJSON\")\n}\n\n// NotEqualJSON is a function to check json path expression value is not equal to given json\n// About expression - https://goessner.net/articles/JsonPath/\nfunc notEqualJSON(data []byte, expression string, expect []byte) error {\n\tobj, err := oj.Parse(expect)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"could not parse json in NotEqualJSON error: '%s'\", err)\n\t}\n\n\treturn notEqualAbstract(data, expression, obj, \"NotEqualJSON\")\n}\n\n// Length is a function to asserts that value is the expected length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc length(data []byte, expression string, expectLength int) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tv := reflect.ValueOf(value)\n\t\tif v.Len() != expectLength {\n\t\t\treturn errors.NewAssertError(\"Length\", fmt.Sprintf(\"on path %v. expect lenght %v, but actual %v\", expression, expectLength, v.Len()), v.Len(), expectLength)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GreaterThan is a function to asserts that value is greater than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc greaterThan(data []byte, expression string, minimumLength int) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tv := reflect.ValueOf(value)\n\t\tif v.Len() <= minimumLength {\n\t\t\treturn errors.NewAssertError(\"GreaterThan\", fmt.Sprintf(\"on path %v. %v is greater than %v\", expression, v.Len(), minimumLength), v.Len(), minimumLength)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// GreaterOrEqualThan is a function to asserts that value is greater or equal than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc greaterOrEqualThan(data []byte, expression string, minimumLength int) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tv := reflect.ValueOf(value)\n\t\tif v.Len() < minimumLength {\n\t\t\treturn errors.NewAssertError(\"GreaterOrEqualThan\", fmt.Sprintf(\"on path %v. %v is greater or equal than %v\", expression, v.Len(), minimumLength), v.Len(), minimumLength)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// LessThan is a function to asserts that value is less than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc lessThan(data []byte, expression string, maximumLength int) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tv := reflect.ValueOf(value)\n\t\tif v.Len() >= maximumLength {\n\t\t\treturn errors.NewAssertError(\"LessThan\", fmt.Sprintf(\"on path %v. %v is less than %v\", expression, v.Len(), maximumLength), v.Len(), maximumLength)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// LessOrEqualThan is a function to asserts that value is less or equal than the given length\n// About expression - https://goessner.net/articles/JsonPath/\nfunc lessOrEqualThan(data []byte, expression string, maximumLength int) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tfor _, value := range values {\n\t\tv := reflect.ValueOf(value)\n\t\tif v.Len() > maximumLength {\n\t\t\treturn errors.NewAssertError(\"LessThan\", fmt.Sprintf(\"on path %v. %v is less or equal than %v\", expression, v.Len(), maximumLength), v.Len(), maximumLength)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// notEmpty is a function to asserts that value is not empty (!= 0, != null)\n// About expression - https://goessner.net/articles/JsonPath/\nfunc notEmpty(data []byte, expression string) error {\n\tvalues, _ := GetValueFromJSON(data, expression)\n\n\tif len(values) == 0 {\n\t\treturn errors.NewAssertError(\"NotEmpty\", fmt.Sprintf(\"on path %v. value is not present\", expression), nil, nil)\n\t}\n\n\tfor _, value := range values {\n\t\tif isEmpty(value) {\n\t\t\treturn errors.NewAssertError(\"NotEmpty\", fmt.Sprintf(\"on path %v. value is not present\", expression), nil, nil)\n\t\t}\n\t}\n\n\treturn nil\n}\n\n// Present is a function to asserts that value is present\n// value can be 0 or null\n// About expression - https://goessner.net/articles/JsonPath/\nfunc present(data []byte, expression string) error {\n\tvalues, err := GetValueFromJSON(data, expression)\n\tif err != nil || len(values) == 0 {\n\t\treturn errors.NewAssertError(\"Present\", fmt.Sprintf(\"on path %v. value not present\", expression), nil, nil)\n\t}\n\n\treturn nil\n}\n\n// NotPresent is a function to asserts that value is not present\n// About expression - https://goessner.net/articles/JsonPath/\nfunc notPresent(data []byte, expression string) error {\n\tvalues, _ := GetValueFromJSON(data, expression)\n\n\tfor _, value := range values {\n\t\tif !isEmpty(value) {\n\t\t\treturn errors.NewAssertError(\"NotPresent\", fmt.Sprintf(\"on path %v. value present\", expression), nil, nil)\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc objectsAreEqual(expect, actual interface{}) bool {\n\tif reflect.DeepEqual(expect, actual) {\n\t\treturn true\n\t}\n\n\tif expect == nil || actual == nil {\n\t\treturn expect == actual\n\t}\n\n\tif fmt.Sprintf(\"%v\", expect) == fmt.Sprintf(\"%v\", actual) {\n\t\treturn true\n\t}\n\n\texp, ok := expect.([]byte)\n\tif !ok {\n\t\treturn reflect.DeepEqual(expect, actual)\n\t}\n\n\tact, ok := actual.([]byte)\n\tif !ok {\n\t\treturn false\n\t}\n\n\tif exp == nil || act == nil {\n\t\treturn exp == nil && act == nil\n\t}\n\n\treturn bytes.Equal(exp, act)\n}\n\nfunc isEmpty(object interface{}) bool {\n\tif object == nil {\n\t\treturn true\n\t}\n\n\tobjValue := reflect.ValueOf(object)\n\n\tswitch objValue.Kind() {\n\tcase reflect.Ptr:\n\t\tif objValue.IsNil() {\n\t\t\treturn true\n\t\t}\n\n\t\tderef := objValue.Elem().Interface()\n\n\t\treturn isEmpty(deref)\n\tcase reflect.Array, reflect.Chan, reflect.Map, reflect.Slice:\n\t\treturn objValue.Len() == 0\n\tdefault:\n\t\tzero := reflect.Zero(objValue.Type())\n\t\treturn reflect.DeepEqual(object, zero.Interface())\n\t}\n}\n\nfunc insideArray(list interface{}, element interface{}) (ok, found bool) {\n\tvar (\n\t\tlistValue    = reflect.ValueOf(list)\n\t\telementValue = reflect.ValueOf(element)\n\t)\n\n\tdefer func() {\n\t\tif err := recover(); err != nil {\n\t\t\tok = false\n\t\t\tfound = false\n\t\t}\n\t}()\n\n\tif reflect.TypeOf(list).Kind() == reflect.String {\n\t\treturn true, strings.Contains(listValue.String(), elementValue.String())\n\t}\n\n\tif reflect.TypeOf(list).Kind() == reflect.Map {\n\t\tmapKeys := listValue.MapKeys()\n\n\t\tfor i := 0; i < len(mapKeys); i++ {\n\t\t\tif objectsAreEqual(mapKeys[i].Interface(), element) {\n\t\t\t\treturn true, true\n\t\t\t}\n\t\t}\n\n\t\treturn true, false\n\t}\n\n\tfor i := 0; i < listValue.Len(); i++ {\n\t\tif objectsAreEqual(listValue.Index(i).Interface(), element) {\n\t\t\treturn true, true\n\t\t}\n\t}\n\n\treturn true, false\n}\n"
  },
  {
    "path": "builder.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\nconst defaultHTTPTimeout = 30\n\nvar (\n\terrorAssertIsNil = \"assert must be not nil\"\n)\n\n// HTTPTestMaker is a creator tests\ntype HTTPTestMaker struct {\n\thttpClient    *http.Client\n\tmiddleware    *Middleware\n\tjsonMarshaler JSONMarshaler\n}\n\n// NewHTTPTestMaker is function for set options for all cute.\n// For example, you can set timeout for all requests  or set custom http client\n// Options:\n// - WithCustomHTTPTimeout - set timeout for all requests\n// - WithHTTPClient - set custom http client\n// - WithCustomHTTPRoundTripper - set custom http round tripper\n// - WithJSONMarshaler - set custom json marshaler\n// - WithMiddlewareAfter - set function which will run AFTER test execution\n// - WithMiddlewareAfterT - set function which will run AFTER test execution with TB\n// - WithMiddlewareBefore - set function which will run BEFORE test execution\n// - WithMiddlewareBeforeT - set function which will run BEFORE test execution with TB\nfunc NewHTTPTestMaker(opts ...Option) *HTTPTestMaker {\n\tvar (\n\t\to = &options{\n\t\t\tmiddleware: new(Middleware),\n\t\t}\n\n\t\ttimeout                    = defaultHTTPTimeout * time.Second\n\t\troundTripper               = http.DefaultTransport\n\t\tjsMarshaler  JSONMarshaler = &jsonMarshaler{}\n\t)\n\n\tfor _, opt := range opts {\n\t\topt(o)\n\t}\n\n\tif o.httpTimeout != 0 {\n\t\ttimeout = o.httpTimeout\n\t}\n\n\tif o.httpRoundTripper != nil { //nolint\n\t\troundTripper = o.httpRoundTripper\n\t}\n\n\thttpClient := &http.Client{\n\t\tTransport: roundTripper,\n\t\tTimeout:   timeout,\n\t}\n\n\tif o.httpClient != nil {\n\t\thttpClient = o.httpClient\n\t}\n\n\tif o.jsonMarshaler != nil {\n\t\tjsMarshaler = o.jsonMarshaler\n\t}\n\n\tm := &HTTPTestMaker{\n\t\thttpClient:    httpClient,\n\t\tjsonMarshaler: jsMarshaler,\n\t\tmiddleware:    o.middleware,\n\t}\n\n\treturn m\n}\n\n// NewTestBuilder is a function for initialization foundation for cute\nfunc (m *HTTPTestMaker) NewTestBuilder() AllureBuilder {\n\ttests := createDefaultTests(m)\n\n\treturn &cute{\n\t\tbaseProps:    m,\n\t\tcountTests:   0,\n\t\ttests:        tests,\n\t\tallureInfo:   new(allureInformation),\n\t\tallureLinks:  new(allureLinks),\n\t\tallureLabels: new(allureLabels),\n\t\tparallel:     false,\n\t}\n}\n\nfunc createDefaultTests(m *HTTPTestMaker) []*Test {\n\ttests := make([]*Test, 1)\n\ttests[0] = createDefaultTest(m)\n\n\treturn tests\n}\n\nfunc createDefaultTest(m *HTTPTestMaker) *Test {\n\treturn &Test{\n\t\thttpClient:    m.httpClient,\n\t\tjsonMarshaler: m.jsonMarshaler,\n\t\tMiddleware:    createMiddlewareFromTemplate(m.middleware),\n\t\tAllureStep:    new(AllureStep),\n\t\tRequest: &Request{\n\t\t\tRetry: new(RequestRetryPolitic),\n\t\t},\n\t\tExpect: &Expect{JSONSchema: new(ExpectJSONSchema)},\n\t}\n}\n\nfunc createMiddlewareFromTemplate(m *Middleware) *Middleware {\n\tafter := make([]AfterExecute, 0, len(m.After))\n\tafter = append(after, m.After...)\n\n\tafterT := make([]AfterExecuteT, 0, len(m.AfterT))\n\tafterT = append(afterT, m.AfterT...)\n\n\tbefore := make([]BeforeExecute, 0, len(m.Before))\n\tbefore = append(before, m.Before...)\n\n\tbeforeT := make([]BeforeExecuteT, 0, len(m.BeforeT))\n\tbeforeT = append(beforeT, m.BeforeT...)\n\n\tmiddleware := &Middleware{\n\t\tAfter:   after,\n\t\tAfterT:  afterT,\n\t\tBefore:  before,\n\t\tBeforeT: beforeT,\n\t}\n\n\treturn middleware\n}\n\nfunc (qt *cute) Create() MiddlewareRequest {\n\treturn qt\n}\n\nfunc (qt *cute) CreateStep(name string) MiddlewareRequest {\n\tqt.tests[qt.countTests].AllureStep.Name = name\n\n\treturn qt\n}\n\nfunc (qt *cute) CreateRequest() RequestHTTPBuilder {\n\treturn qt\n}\n"
  },
  {
    "path": "builder_allure.go",
    "content": "package cute\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n)\n\nfunc (qt *cute) Parallel() AllureBuilder {\n\tqt.parallel = true\n\n\treturn qt\n}\n\nfunc (qt *cute) Title(title string) AllureBuilder {\n\tqt.allureInfo.title = title\n\n\treturn qt\n}\n\nfunc (qt *cute) Epic(epic string) AllureBuilder {\n\tqt.allureLabels.epic = epic\n\n\treturn qt\n}\n\nfunc (qt *cute) Titlef(format string, args ...interface{}) AllureBuilder {\n\tqt.allureInfo.title = fmt.Sprintf(format, args...)\n\n\treturn qt\n}\n\nfunc (qt *cute) Descriptionf(format string, args ...interface{}) AllureBuilder {\n\tqt.allureInfo.description = fmt.Sprintf(format, args...)\n\n\treturn qt\n}\n\nfunc (qt *cute) Stage(stage string) AllureBuilder {\n\tqt.allureInfo.stage = stage\n\n\treturn qt\n}\n\nfunc (qt *cute) Stagef(format string, args ...interface{}) AllureBuilder {\n\tqt.allureInfo.stage = fmt.Sprintf(format, args...)\n\n\treturn qt\n}\n\nfunc (qt *cute) Layer(value string) AllureBuilder {\n\tqt.allureLabels.layer = value\n\n\treturn qt\n}\n\nfunc (qt *cute) TmsLink(tmsLink string) AllureBuilder {\n\tqt.allureLinks.tmsLink = tmsLink\n\n\treturn qt\n}\n\nfunc (qt *cute) TmsLinks(tmsLinks ...string) AllureBuilder {\n\tqt.allureLinks.tmsLinks = append(qt.allureLinks.tmsLinks, tmsLinks...)\n\n\treturn qt\n}\n\nfunc (qt *cute) SetIssue(issue string) AllureBuilder {\n\tqt.allureLinks.issue = issue\n\n\treturn qt\n}\n\nfunc (qt *cute) SetTestCase(testCase string) AllureBuilder {\n\tqt.allureLinks.testCase = testCase\n\n\treturn qt\n}\n\nfunc (qt *cute) Link(link *allure.Link) AllureBuilder {\n\tqt.allureLinks.link = link\n\n\treturn qt\n}\n\nfunc (qt *cute) ID(value string) AllureBuilder {\n\tqt.allureLabels.id = value\n\n\treturn qt\n}\n\nfunc (qt *cute) AllureID(value string) AllureBuilder {\n\tqt.allureLabels.allureID = value\n\n\treturn qt\n}\n\nfunc (qt *cute) AddSuiteLabel(value string) AllureBuilder {\n\tqt.allureLabels.suiteLabel = value\n\n\treturn qt\n}\n\nfunc (qt *cute) AddSubSuite(value string) AllureBuilder {\n\tqt.allureLabels.subSuite = value\n\n\treturn qt\n}\n\nfunc (qt *cute) AddParentSuite(value string) AllureBuilder {\n\tqt.allureLabels.parentSuite = value\n\n\treturn qt\n}\n\nfunc (qt *cute) Story(value string) AllureBuilder {\n\tqt.allureLabels.story = value\n\n\treturn qt\n}\n\nfunc (qt *cute) Tag(value string) AllureBuilder {\n\tqt.allureLabels.tag = value\n\n\treturn qt\n}\n\nfunc (qt *cute) Severity(value allure.SeverityType) AllureBuilder {\n\tqt.allureLabels.severity = value\n\n\treturn qt\n}\n\nfunc (qt *cute) Owner(value string) AllureBuilder {\n\tqt.allureLabels.owner = value\n\n\treturn qt\n}\n\nfunc (qt *cute) Lead(value string) AllureBuilder {\n\tqt.allureLabels.lead = value\n\n\treturn qt\n}\n\nfunc (qt *cute) Label(label *allure.Label) AllureBuilder {\n\tqt.allureLabels.label = label\n\n\treturn qt\n}\n\nfunc (qt *cute) Labels(labels ...*allure.Label) AllureBuilder {\n\tqt.allureLabels.labels = labels\n\n\treturn qt\n}\n\nfunc (qt *cute) Description(description string) AllureBuilder {\n\tqt.allureInfo.description = description\n\n\treturn qt\n}\n\nfunc (qt *cute) Tags(tags ...string) AllureBuilder {\n\tqt.allureLabels.tags = tags\n\n\treturn qt\n}\n\nfunc (qt *cute) Feature(feature string) AllureBuilder {\n\tqt.allureLabels.feature = feature\n\n\treturn qt\n}\n"
  },
  {
    "path": "builder_asserts.go",
    "content": "package cute\n\nimport \"time\"\n\nfunc (qt *cute) AssertBody(asserts ...AssertBody) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBody = append(qt.tests[qt.countTests].Expect.AssertBody, assertBodyWithTrace(assert, trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) OptionalAssertBody(asserts ...AssertBody) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBody = append(qt.tests[qt.countTests].Expect.AssertBody, assertBodyWithTrace(optionalAssertBody(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) BrokenAssertBody(asserts ...AssertBody) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBody = append(qt.tests[qt.countTests].Expect.AssertBody, assertBodyWithTrace(brokenAssertBody(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) RequireBody(asserts ...AssertBody) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBody = append(qt.tests[qt.countTests].Expect.AssertBody, assertBodyWithTrace(requireAssertBody(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) AssertHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeaders = append(qt.tests[qt.countTests].Expect.AssertHeaders, assertHeadersWithTrace(assert, trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) OptionalAssertHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeaders = append(qt.tests[qt.countTests].Expect.AssertHeaders, assertHeadersWithTrace(optionalAssertHeaders(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) RequireHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeaders = append(qt.tests[qt.countTests].Expect.AssertHeaders, assertHeadersWithTrace(requireAssertHeaders(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) BrokenAssertHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeaders = append(qt.tests[qt.countTests].Expect.AssertHeaders, assertHeadersWithTrace(brokenAssertHeaders(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) AssertResponse(asserts ...AssertResponse) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponse = append(qt.tests[qt.countTests].Expect.AssertResponse, assertResponseWithTrace(assert, trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) OptionalAssertResponse(asserts ...AssertResponse) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponse = append(qt.tests[qt.countTests].Expect.AssertResponse, assertResponseWithTrace(optionalAssertResponse(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) RequireResponse(asserts ...AssertResponse) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponse = append(qt.tests[qt.countTests].Expect.AssertResponse, assertResponseWithTrace(requireAssertResponse(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) BrokenAssertResponse(asserts ...AssertResponse) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponse = append(qt.tests[qt.countTests].Expect.AssertResponse, assertResponseWithTrace(brokenAssertResponse(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) AssertBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBodyT = append(qt.tests[qt.countTests].Expect.AssertBodyT, assertBodyTWithTrace(assert, trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) OptionalAssertBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBodyT = append(qt.tests[qt.countTests].Expect.AssertBodyT, assertBodyTWithTrace(optionalAssertBodyT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) BrokenAssertBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBodyT = append(qt.tests[qt.countTests].Expect.AssertBodyT, assertBodyTWithTrace(brokenAssertBodyT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) RequireBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertBodyT = append(qt.tests[qt.countTests].Expect.AssertBodyT, assertBodyTWithTrace(requireAssertBodyT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) AssertHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeadersT = append(qt.tests[qt.countTests].Expect.AssertHeadersT, assertHeadersTWithTrace(assert, trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) OptionalAssertHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeadersT = append(qt.tests[qt.countTests].Expect.AssertHeadersT, assertHeadersTWithTrace(optionalAssertHeadersT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) RequireHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeadersT = append(qt.tests[qt.countTests].Expect.AssertHeadersT, assertHeadersTWithTrace(requireAssertHeadersT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) BrokenAssertHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertHeadersT = append(qt.tests[qt.countTests].Expect.AssertHeadersT, assertHeadersTWithTrace(brokenAssertHeadersT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) AssertResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponseT = append(qt.tests[qt.countTests].Expect.AssertResponseT, assertResponseTWithTrace(assert, trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) OptionalAssertResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponseT = append(qt.tests[qt.countTests].Expect.AssertResponseT, assertResponseTWithTrace(optionalAssertResponseT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) BrokenAssertResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponseT = append(qt.tests[qt.countTests].Expect.AssertResponseT, assertResponseTWithTrace(brokenAssertResponseT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) RequireResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder {\n\ttrace := getTrace()\n\n\tfor _, assert := range asserts {\n\t\tif assert == nil {\n\t\t\tpanic(errorAssertIsNil)\n\t\t}\n\n\t\tqt.tests[qt.countTests].Expect.AssertResponseT = append(qt.tests[qt.countTests].Expect.AssertResponseT, assertResponseTWithTrace(requireAssertResponseT(assert), trace))\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) ExpectExecuteTimeout(t time.Duration) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Expect.ExecuteTime = t\n\n\treturn qt\n}\n\nfunc (qt *cute) ExpectStatus(code int) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Expect.Code = code\n\n\treturn qt\n}\n\nfunc (qt *cute) ExpectJSONSchemaString(schema string) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Expect.JSONSchema.String = schema\n\n\treturn qt\n}\n\nfunc (qt *cute) ExpectJSONSchemaByte(schema []byte) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Expect.JSONSchema.Byte = schema\n\n\treturn qt\n}\n\nfunc (qt *cute) ExpectJSONSchemaFile(filePath string) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Expect.JSONSchema.File = filePath\n\n\treturn qt\n}\n"
  },
  {
    "path": "builder_middleware.go",
    "content": "package cute\n\nfunc (qt *cute) StepName(name string) MiddlewareRequest {\n\tqt.tests[qt.countTests].AllureStep.Name = name\n\n\treturn qt\n}\n\nfunc (qt *cute) BeforeExecute(fs ...BeforeExecute) MiddlewareRequest {\n\tqt.tests[qt.countTests].Middleware.Before = append(qt.tests[qt.countTests].Middleware.Before, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) BeforeExecuteT(fs ...BeforeExecuteT) MiddlewareRequest {\n\tqt.tests[qt.countTests].Middleware.BeforeT = append(qt.tests[qt.countTests].Middleware.BeforeT, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) After(fs ...AfterExecute) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Middleware.After = append(qt.tests[qt.countTests].Middleware.After, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) AfterT(fs ...AfterExecuteT) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Middleware.AfterT = append(qt.tests[qt.countTests].Middleware.AfterT, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) AfterExecute(fs ...AfterExecute) MiddlewareRequest {\n\tqt.tests[qt.countTests].Middleware.After = append(qt.tests[qt.countTests].Middleware.After, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) AfterExecuteT(fs ...AfterExecuteT) MiddlewareRequest {\n\tqt.tests[qt.countTests].Middleware.AfterT = append(qt.tests[qt.countTests].Middleware.AfterT, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) AfterTestExecute(fs ...AfterExecute) NextTestBuilder {\n\tpreviousTest := 0\n\tif qt.countTests != 0 {\n\t\tpreviousTest = qt.countTests - 1\n\t}\n\n\tqt.tests[previousTest].Middleware.After = append(qt.tests[previousTest].Middleware.After, fs...)\n\n\treturn qt\n}\n\nfunc (qt *cute) AfterTestExecuteT(fs ...AfterExecuteT) NextTestBuilder {\n\tpreviousTest := 0\n\tif qt.countTests != 0 {\n\t\tpreviousTest = qt.countTests - 1\n\t}\n\n\tqt.tests[previousTest].Middleware.AfterT = append(qt.tests[previousTest].Middleware.AfterT, fs...)\n\n\treturn qt\n}\n"
  },
  {
    "path": "builder_option.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\ntype options struct {\n\thttpClient       *http.Client\n\thttpTimeout      time.Duration\n\thttpRoundTripper http.RoundTripper\n\n\tjsonMarshaler JSONMarshaler\n\n\tmiddleware *Middleware\n}\n\n// Option ...\ntype Option func(*options)\n\n// WithHTTPClient is a function for set custom http client\nfunc WithHTTPClient(client *http.Client) Option {\n\treturn func(o *options) {\n\t\to.httpClient = client\n\t}\n}\n\n// WithJSONMarshaler is a function for set custom json marshaler\nfunc WithJSONMarshaler(m JSONMarshaler) Option {\n\treturn func(o *options) {\n\t\to.jsonMarshaler = m\n\t}\n}\n\n// WithCustomHTTPTimeout is a function for set custom http client timeout\nfunc WithCustomHTTPTimeout(t time.Duration) Option {\n\treturn func(o *options) {\n\t\to.httpTimeout = t\n\t}\n}\n\n// WithCustomHTTPRoundTripper is a function for set custom http round tripper\nfunc WithCustomHTTPRoundTripper(r http.RoundTripper) Option {\n\treturn func(o *options) {\n\t\to.httpRoundTripper = r\n\t}\n}\n\n// WithMiddlewareAfter is function for set function which will run AFTER test execution\nfunc WithMiddlewareAfter(after ...AfterExecute) Option {\n\treturn func(o *options) {\n\t\to.middleware.After = append(o.middleware.After, after...)\n\t}\n}\n\n// WithMiddlewareAfterT is function for set function which will run AFTER test execution\nfunc WithMiddlewareAfterT(after ...AfterExecuteT) Option {\n\treturn func(o *options) {\n\t\to.middleware.AfterT = append(o.middleware.AfterT, after...)\n\t}\n}\n\n// WithMiddlewareBefore is function for set function which will run BEFORE test execution\nfunc WithMiddlewareBefore(before ...BeforeExecute) Option {\n\treturn func(o *options) {\n\t\to.middleware.Before = append(o.middleware.Before, before...)\n\t}\n}\n\n// WithMiddlewareBeforeT is function for set function which will run BEFORE test execution\nfunc WithMiddlewareBeforeT(beforeT ...BeforeExecuteT) Option {\n\treturn func(o *options) {\n\t\to.middleware.BeforeT = append(o.middleware.BeforeT, beforeT...)\n\t}\n}\n"
  },
  {
    "path": "builder_request.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\t\"time\"\n)\n\n// RequestRepeat is a function for set options in request\n// if response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\n// Default delay is 1 second.\n// Deprecated: use RequestRetry instead\nfunc (qt *cute) RequestRepeat(count int) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Count = count\n\n\treturn qt\n}\n\n// RequestRepeatDelay set delay for request repeat.\n// if response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\n// Default delay is 1 second.\n// Deprecated: use RequestRetryDelay instead\nfunc (qt *cute) RequestRepeatDelay(delay time.Duration) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Delay = delay\n\n\treturn qt\n}\n\n// RequestRepeatPolitic set politic for request repeat.\n// if response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\n// if Optional is true and request is failed, than test step allure will be skipped, and t.Fail() will not execute.\n// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will not execute.\n// Deprecated: use RequestRetryPolitic instead\nfunc (qt *cute) RequestRepeatPolitic(politic *RequestRepeatPolitic) RequestHTTPBuilder {\n\tif politic == nil {\n\t\tpanic(\"politic is nil in RequestRetryPolitic\")\n\t}\n\n\tqt.tests[qt.countTests].Request.Retry = &RequestRetryPolitic{\n\t\tCount:    politic.Count,\n\t\tDelay:    politic.Delay,\n\t\tOptional: politic.Optional,\n\t\tBroken:   politic.Broken,\n\t}\n\n\treturn qt\n}\n\n// RequestRepeatOptional set option politic for request repeat.\n// if Optional is true and request is failed, than test step allure will be skipped, and t.Fail() will not execute.\n// Deprecated: use RequestRetryOptional instead\nfunc (qt *cute) RequestRepeatOptional(option bool) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Optional = option\n\n\treturn qt\n}\n\n// RequestRepeatBroken set broken politic for request repeat.\n// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will not execute.\n// Deprecated: use RequestRetryBroken instead\nfunc (qt *cute) RequestRepeatBroken(broken bool) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Broken = broken\n\n\treturn qt\n}\n\n// RequestRetry is a function for set options in request\n// if response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\n// Default delay is 1 second.\nfunc (qt *cute) RequestRetry(count int) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Count = count\n\n\treturn qt\n}\n\n// RequestRetryDelay set delay for request repeat.\n// if response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\n// Default delay is 1 second.\nfunc (qt *cute) RequestRetryDelay(delay time.Duration) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Delay = delay\n\n\treturn qt\n}\n\n// RequestRetryPolitic set politic for request repeat.\n// if response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\n// if Optional is true and request is failed, than test step allure will be skipped, and t.Fail() will not execute.\n// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will not execute.\nfunc (qt *cute) RequestRetryPolitic(politic *RequestRetryPolitic) RequestHTTPBuilder {\n\tif politic == nil {\n\t\tpanic(\"politic is nil in RequestRetryPolitic\")\n\t}\n\n\tqt.tests[qt.countTests].Request.Retry = politic\n\n\treturn qt\n}\n\n// RequestRetryOptional set option politic for request repeat.\n// if Optional is true and request is failed, than test step allure will be skipped, and t.Fail() will not execute.\nfunc (qt *cute) RequestRetryOptional(option bool) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Optional = option\n\n\treturn qt\n}\n\n// RequestRetryBroken set broken politic for request repeat.\n// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will not execute.\nfunc (qt *cute) RequestRetryBroken(broken bool) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Retry.Broken = broken\n\n\treturn qt\n}\n\n// RequestSanitizerHook assigns the provided RequestSanitizerHook to the test,\n// allowing URL sanitization before logging or reporting.\nfunc (qt *cute) RequestSanitizerHook(hook RequestSanitizerHook) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].RequestSanitizer = hook\n\n\treturn qt\n}\n\n// ResponseSanitizerHook assigns the provided ResponseSanitizerHook to the test,\n// allowing URL sanitization before logging or reporting.\nfunc (qt *cute) ResponseSanitizerHook(hook ResponseSanitizerHook) RequestHTTPBuilder {\n\tqt.tests[qt.countTests].ResponseSanitizer = hook\n\n\treturn qt\n}\n\nfunc (qt *cute) Request(r *http.Request) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Base = r\n\n\treturn qt\n}\n\nfunc (qt *cute) RequestBuilder(r ...RequestBuilder) ExpectHTTPBuilder {\n\tqt.tests[qt.countTests].Request.Builders = append(qt.tests[qt.countTests].Request.Builders, r...)\n\n\treturn qt\n}\n"
  },
  {
    "path": "builder_retry.go",
    "content": "package cute\n\nimport \"time\"\n\n// Retry is a function for configure test repeat\n// if response.Code != Expect.Code or any of asserts are failed/broken than test will repeat counts with delay.\n// Default delay is 1 second.\nfunc (qt *cute) Retry(count int) MiddlewareRequest {\n\tif count < 1 {\n\t\tpanic(\"count must be greater than 0\")\n\t}\n\n\tqt.tests[qt.countTests].Retry.MaxAttempts = count\n\n\treturn qt\n}\n\n// RetryDelay set delay for test repeat.\n// if response.Code != Expect.Code or any of asserts are failed/broken than test will repeat counts with delay.\n// Default delay is 1 second.\nfunc (qt *cute) RetryDelay(delay time.Duration) MiddlewareRequest {\n\tif delay < 0 {\n\t\tpanic(\"delay must be greater than or equal to 0\")\n\t}\n\n\tqt.tests[qt.countTests].Retry.Delay = delay\n\n\treturn qt\n}\n"
  },
  {
    "path": "builder_table.go",
    "content": "package cute\n\nimport \"net/http\"\n\nfunc (qt *cute) CreateTableTest() MiddlewareTable {\n\tqt.isTableTest = true\n\n\treturn qt\n}\n\nfunc (qt *cute) PutNewTest(name string, r *http.Request, expect *Expect) TableTest {\n\t// Validate, that first step is empty\n\tif qt.countTests == 0 {\n\t\tif qt.tests[0].Request.Base == nil &&\n\t\t\tlen(qt.tests[0].Request.Builders) == 0 {\n\t\t\tqt.tests[0].Expect = expect\n\t\t\tqt.tests[0].Name = name\n\t\t\tqt.tests[0].Request.Base = r\n\n\t\t\treturn qt\n\t\t}\n\t}\n\n\tnewTest := createDefaultTest(qt.baseProps)\n\tnewTest.Expect = expect\n\tnewTest.Name = name\n\tnewTest.Request.Base = r\n\tqt.tests = append(qt.tests, newTest)\n\tqt.countTests++ // async?\n\n\treturn qt\n}\n\nfunc (qt *cute) PutTests(tests ...*Test) TableTest {\n\tfor _, test := range tests {\n\t\t// Fill common fields\n\t\tqt.fillBaseProps(test)\n\n\t\t// Validate, that first step is empty\n\t\tif qt.countTests == 0 {\n\t\t\tif qt.tests[0].Request.Base == nil &&\n\t\t\t\tlen(qt.tests[0].Request.Builders) == 0 {\n\t\t\t\tqt.tests[0] = test\n\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tqt.tests = append(qt.tests, test)\n\t\tqt.countTests++\n\t}\n\n\treturn qt\n}\n\nfunc (qt *cute) fillBaseProps(t *Test) {\n\tif qt.baseProps == nil {\n\t\treturn\n\t}\n\n\tif qt.baseProps.httpClient != nil {\n\t\tt.httpClient = qt.baseProps.httpClient\n\t}\n\n\tif qt.baseProps.jsonMarshaler != nil {\n\t\tt.jsonMarshaler = qt.baseProps.jsonMarshaler\n\t}\n\n\tif t.Middleware == nil {\n\t\tt.Middleware = createMiddlewareFromTemplate(qt.baseProps.middleware)\n\t} else {\n\t\tt.Middleware.After = append(t.Middleware.After, qt.baseProps.middleware.After...)\n\t\tt.Middleware.AfterT = append(t.Middleware.AfterT, qt.baseProps.middleware.AfterT...)\n\t\tt.Middleware.Before = append(t.Middleware.Before, qt.baseProps.middleware.Before...)\n\t\tt.Middleware.BeforeT = append(t.Middleware.BeforeT, qt.baseProps.middleware.BeforeT...)\n\t}\n}\n\nfunc (qt *cute) NextTest() NextTestBuilder {\n\tqt.countTests++ // async?\n\n\tqt.tests = append(qt.tests, createDefaultTest(qt.baseProps))\n\n\treturn qt\n}\n"
  },
  {
    "path": "builder_table_test.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestFillBaseProps_WhenBasePropsIsNil(t *testing.T) {\n\ttestObj := &Test{}\n\tcuteObj := &cute{}\n\n\tcuteObj.fillBaseProps(testObj)\n\n\trequire.Nil(t, testObj.httpClient)\n\trequire.Nil(t, testObj.jsonMarshaler)\n\trequire.Nil(t, testObj.Middleware)\n}\n\nfunc TestFillBaseProps_WhenBasePropsIsNotNil(t *testing.T) {\n\ttestObj := &Test{}\n\tcuteObj := &cute{}\n\n\tqtBaseProps := &HTTPTestMaker{\n\t\thttpClient:    &http.Client{},\n\t\tjsonMarshaler: &jsonMarshaler{},\n\t\tmiddleware: &Middleware{\n\t\t\tAfter: []AfterExecute{\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tAfterT: []AfterExecuteT{func(T, *http.Response, []error) error { return nil }},\n\t\t\tBefore: []BeforeExecute{\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tBeforeT: []BeforeExecuteT{\n\t\t\t\tfunc(T, *http.Request) error { return nil },\n\t\t\t\tfunc(T, *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tcuteObj.baseProps = qtBaseProps\n\n\tcuteObj.fillBaseProps(testObj)\n\n\trequire.Equal(t, qtBaseProps.httpClient, testObj.httpClient)\n\trequire.Equal(t, qtBaseProps.jsonMarshaler, testObj.jsonMarshaler)\n\trequire.Len(t, testObj.Middleware.After, len(qtBaseProps.middleware.After))\n\trequire.Len(t, testObj.Middleware.AfterT, len(qtBaseProps.middleware.AfterT))\n\trequire.Len(t, testObj.Middleware.Before, len(qtBaseProps.middleware.Before))\n\trequire.Len(t, testObj.Middleware.BeforeT, len(qtBaseProps.middleware.BeforeT))\n}\n\nfunc TestFillBaseProps_WhenBasePropsIsNotNil_After(t *testing.T) {\n\ttestObj := &Test{\n\t\tMiddleware: &Middleware{\n\t\t\tAfter: []AfterExecute{\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tcuteObj := &cute{}\n\n\tqtBaseProps := &HTTPTestMaker{\n\t\thttpClient:    &http.Client{},\n\t\tjsonMarshaler: &jsonMarshaler{},\n\t\tmiddleware: &Middleware{\n\t\t\tAfter: []AfterExecute{\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tBeforeT: []BeforeExecuteT{\n\t\t\t\tfunc(T, *http.Request) error { return nil },\n\t\t\t\tfunc(T, *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tcuteObj.baseProps = qtBaseProps\n\n\tcuteObj.fillBaseProps(testObj)\n\n\trequire.Equal(t, qtBaseProps.httpClient, testObj.httpClient)\n\trequire.Equal(t, qtBaseProps.jsonMarshaler, testObj.jsonMarshaler)\n\trequire.Len(t, testObj.Middleware.After, len(qtBaseProps.middleware.After)+1)\n\trequire.Len(t, testObj.Middleware.AfterT, len(qtBaseProps.middleware.AfterT))\n\trequire.Len(t, testObj.Middleware.Before, len(qtBaseProps.middleware.Before))\n\trequire.Len(t, testObj.Middleware.BeforeT, len(qtBaseProps.middleware.BeforeT))\n}\n\nfunc TestFillBaseProps_WhenBasePropsIsNotNil_Middleware(t *testing.T) {\n\ttestObj := &Test{\n\t\tMiddleware: &Middleware{\n\t\t\tAfter: []AfterExecute{\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tAfterT: []AfterExecuteT{\n\t\t\t\tfunc(T, *http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(T, *http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(T, *http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tBefore: []BeforeExecute{\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tBeforeT: []BeforeExecuteT{\n\t\t\t\tfunc(T, *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tcuteObj := &cute{}\n\n\tqtBaseProps := &HTTPTestMaker{\n\t\thttpClient:    &http.Client{},\n\t\tjsonMarshaler: &jsonMarshaler{},\n\t\tmiddleware: &Middleware{\n\t\t\tAfter: []AfterExecute{\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Response, []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tAfterT: []AfterExecuteT{func(T, *http.Response, []error) error { return nil }},\n\t\t\tBefore: []BeforeExecute{\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(*http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t\tBeforeT: []BeforeExecuteT{\n\t\t\t\tfunc(T, *http.Request) error { return nil },\n\t\t\t\tfunc(T, *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\tcuteObj.baseProps = qtBaseProps\n\n\tcuteObj.fillBaseProps(testObj)\n\n\trequire.Equal(t, qtBaseProps.httpClient, testObj.httpClient)\n\trequire.Equal(t, qtBaseProps.jsonMarshaler, testObj.jsonMarshaler)\n\trequire.Len(t, testObj.Middleware.After, len(qtBaseProps.middleware.After)+2)\n\trequire.Len(t, testObj.Middleware.AfterT, len(qtBaseProps.middleware.AfterT)+3)\n\trequire.Len(t, testObj.Middleware.Before, len(qtBaseProps.middleware.Before)+4)\n\trequire.Len(t, testObj.Middleware.BeforeT, len(qtBaseProps.middleware.BeforeT)+1)\n}\n"
  },
  {
    "path": "builder_test.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestBuilderAfterTest(t *testing.T) {\n\tvar (\n\t\tmaker = NewHTTPTestMaker()\n\t)\n\n\tht := maker.NewTestBuilder().\n\t\tCreate().\n\t\tRequestBuilder().\n\t\tNextTest().\n\t\tAfterTestExecute(\n\t\t\tfunc(response *http.Response, errors []error) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(response *http.Response, errors []error) error {\n\t\t\t\treturn nil\n\t\t\t}).\n\t\tAfterTestExecuteT(\n\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t)\n\n\tres := ht.(*cute)\n\trequire.Len(t, res.tests[0].Middleware.After, 2)\n\trequire.Len(t, res.tests[0].Middleware.AfterT, 3)\n}\n\nfunc TestBuilderAfterTestTwoStep(t *testing.T) {\n\tvar (\n\t\tmaker = NewHTTPTestMaker(\n\t\t\tWithMiddlewareBefore(\n\t\t\t\tfunc(request *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(request *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t),\n\t\t\tWithMiddlewareBeforeT(\n\t\t\t\tfunc(t T, request *http.Request) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t),\n\t\t\tWithMiddlewareAfter(\n\t\t\t\tfunc(response *http.Response, errors []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t),\n\t\t\tWithMiddlewareAfterT(\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t),\n\t\t)\n\t)\n\n\tht :=\n\t\tmaker.NewTestBuilder().\n\t\t\tCreate().\n\t\t\tRequestBuilder().\n\t\t\tNextTest().\n\t\t\tAfterTestExecute(\n\t\t\t\tfunc(response *http.Response, errors []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(response *http.Response, errors []error) error {\n\t\t\t\t\treturn nil\n\t\t\t\t}).\n\t\t\tAfterTestExecuteT(\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t).\n\t\t\tCreate().\n\t\t\tAfterExecute(\n\t\t\t\tfunc(response *http.Response, errors []error) error {\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t).\n\t\t\tAfterExecuteT(\n\t\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\t\treturn nil\n\t\t\t\t}).\n\t\t\tRequestBuilder().\n\t\t\tNextTest().\n\t\t\tAfterTestExecute(\n\t\t\t\tfunc(response *http.Response, errors []error) error {\n\n\t\t\t\t\treturn nil\n\t\t\t\t},\n\t\t\t)\n\n\tres := ht.(*cute)\n\trequire.Len(t, res.tests[0].Middleware.After, 2+1)\n\trequire.Len(t, res.tests[0].Middleware.Before, 2)\n\trequire.Len(t, res.tests[0].Middleware.BeforeT, 1)\n\trequire.Len(t, res.tests[0].Middleware.AfterT, 3+3)\n\n\trequire.Len(t, res.tests[1].Middleware.After, 2+1)\n\trequire.Len(t, res.tests[1].Middleware.AfterT, 1+3)\n\trequire.Len(t, res.tests[1].Middleware.Before, 2)\n\trequire.Len(t, res.tests[1].Middleware.BeforeT, 1)\n}\n\nfunc TestNewTestBuilder(t *testing.T) {\n\tvar (\n\t\tmaker = NewHTTPTestMaker()\n\t\tht    = maker.NewTestBuilder().(*cute)\n\t)\n\n\trequire.NotNil(t, ht.tests)\n\trequire.Len(t, ht.tests, 1)\n\trequire.NotNil(t, ht.tests[0].Request)\n\trequire.NotNil(t, ht.tests[0].Middleware)\n\trequire.NotNil(t, ht.tests[0].AllureStep)\n\trequire.NotNil(t, ht.allureInfo)\n\trequire.NotNil(t, ht.baseProps.httpClient)\n}\n\nfunc TestHTTPTestMaker(t *testing.T) {\n\tvar (\n\t\tmaker          = NewHTTPTestMaker()\n\t\tht             = maker.NewTestBuilder()\n\t\ttitle          = \"title\"\n\t\tepic           = \"epic\"\n\t\tdesc           = \"desc\"\n\t\tfeature        = \"feature\"\n\t\ttags           = []string{\"tag_1\", \"tag_2\"}\n\t\tstepName       = \"stepname\"\n\t\treq, _         = http.NewRequest(http.MethodGet, \"https://site.go\", nil)\n\t\texecuteTime    = time.Duration(10)\n\t\tstatus         = 400\n\t\tschemaStg      = \"some_json_schema\"\n\t\tschemaBt       = []byte(\"some_json_schema\")\n\t\tschemaFile     = \"file_path\"\n\t\tid             = \"ID\"\n\t\taddSuiteLabel  = \"AddSuiteLabel\"\n\t\taddSubSuite    = \"AddSubSuite\"\n\t\taddParentSuite = \"AddParentSuite\"\n\t\tstory          = \"Story\"\n\t\ttag            = \"Tag\"\n\t\tallureID       = \"AllureID\"\n\t\towner          = \"Owner\"\n\t\tlead           = \"Lead\"\n\t\tlabel          = &allure.Label{Name: \"kek\", Value: \"lol\"}\n\t\tsetIssue       = \"SetIssue\"\n\t\tsetTestCase    = \"SetTestCase\"\n\t\trepeatCount    = 10\n\t\trepeatDelay    = time.Duration(10)\n\t\tlink           = &allure.Link{\n\t\t\tName: \"link\",\n\t\t\tType: \"type\",\n\t\t\tURL:  \"http://go.go\",\n\t\t}\n\t\tlabels = []*allure.Label{\n\t\t\t{\n\t\t\t\tName:  \"label_1\",\n\t\t\t\tValue: \"value_1\",\n\t\t\t},\n\t\t\t{\n\t\t\t\tName:  \"label_2\",\n\t\t\t\tValue: \"value_2\",\n\t\t\t},\n\t\t}\n\n\t\tassertHeaders = []AssertHeaders{\n\t\t\tfunc(headers http.Header) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t\tassertHeadersT = []AssertHeadersT{\n\t\t\tfunc(t T, headers http.Header) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(t T, headers http.Header) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\n\t\tassertBody = []AssertBody{\n\t\t\tfunc(body []byte) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t\tassertBodyT = []AssertBodyT{\n\t\t\tfunc(t T, body []byte) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(t T, body []byte) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\n\t\tassertResponse = []AssertResponse{\n\t\t\tfunc(resp *http.Response) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t\tassertResponseT = []AssertResponseT{\n\t\t\tfunc(t T, resp *http.Response) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(t T, resp *http.Response) error {\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t\tafter = []AfterExecute{\n\t\t\tfunc(response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t\tafterT = []AfterExecuteT{\n\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t\tfunc(t T, response *http.Response, errors []error) error {\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t}\n\t)\n\n\tht.\n\t\tTitle(title).\n\t\tTags(tags...).\n\t\tEpic(epic).\n\t\tFeature(feature).\n\t\tID(id).\n\t\tAddSuiteLabel(addSuiteLabel).\n\t\tAddSubSuite(addSubSuite).\n\t\tAddParentSuite(addParentSuite).\n\t\tStory(story).\n\t\tTag(tag).\n\t\tSeverity(allure.CRITICAL).\n\t\tAllureID(allureID).\n\t\tOwner(owner).\n\t\tLead(lead).\n\t\tLabel(label).\n\t\tLabels(labels...).\n\t\tSetIssue(setIssue).\n\t\tSetTestCase(setTestCase).\n\t\tLink(link).\n\t\tDescription(desc).\n\t\tCreateStep(stepName).\n\t\tRequestRetry(repeatCount).\n\t\tRequestRetryDelay(repeatDelay).\n\t\tRequest(req).\n\t\tExpectExecuteTimeout(executeTime).\n\t\tExpectStatus(status).\n\t\tExpectJSONSchemaByte(schemaBt).\n\t\tExpectJSONSchemaString(schemaStg).\n\t\tExpectJSONSchemaFile(schemaFile).\n\t\tAssertHeaders(assertHeaders...).\n\t\tAssertHeadersT(assertHeadersT...).\n\t\tAssertBody(assertBody...).\n\t\tAssertBodyT(assertBodyT...).\n\t\tAssertResponse(assertResponse...).\n\t\tAssertResponseT(assertResponseT...).\n\t\tAfter(after...).\n\t\tAfterT(afterT...)\n\n\tresHt := ht.(*cute)\n\tresTest := resHt.tests[0]\n\n\trequire.Equal(t, title, resHt.allureInfo.title)\n\trequire.Equal(t, tags, resHt.allureLabels.tags)\n\trequire.Equal(t, desc, resHt.allureInfo.description)\n\trequire.Equal(t, feature, resHt.allureLabels.feature)\n\trequire.Equal(t, epic, resHt.allureLabels.epic)\n\trequire.Equal(t, stepName, resTest.AllureStep.Name)\n\trequire.Equal(t, req, resTest.Request.Base)\n\trequire.Equal(t, executeTime, resTest.Expect.ExecuteTime)\n\trequire.Equal(t, status, resTest.Expect.Code)\n\trequire.Equal(t, schemaBt, resTest.Expect.JSONSchema.Byte)\n\trequire.Equal(t, schemaStg, resTest.Expect.JSONSchema.String)\n\trequire.Equal(t, schemaFile, resTest.Expect.JSONSchema.File)\n\trequire.Equal(t, id, resHt.allureLabels.id)\n\trequire.Equal(t, addSuiteLabel, resHt.allureLabels.suiteLabel)\n\trequire.Equal(t, addSubSuite, resHt.allureLabels.subSuite)\n\trequire.Equal(t, addParentSuite, resHt.allureLabels.parentSuite)\n\trequire.Equal(t, story, resHt.allureLabels.story)\n\trequire.Equal(t, tag, resHt.allureLabels.tag)\n\trequire.Equal(t, owner, resHt.allureLabels.owner)\n\trequire.Equal(t, lead, resHt.allureLabels.lead)\n\trequire.Equal(t, label, resHt.allureLabels.label)\n\trequire.Equal(t, allureID, resHt.allureLabels.allureID)\n\trequire.Equal(t, setIssue, resHt.allureLinks.issue)\n\trequire.Equal(t, setTestCase, resHt.allureLinks.testCase)\n\trequire.Equal(t, link, resHt.allureLinks.link)\n\trequire.Equal(t, repeatCount, resTest.Request.Retry.Count)\n\trequire.Equal(t, repeatDelay, resTest.Request.Retry.Delay)\n\n\trequire.Equal(t, len(assertHeaders), len(resTest.Expect.AssertHeaders))\n\trequire.Equal(t, len(assertHeadersT), len(resTest.Expect.AssertHeadersT))\n\n\trequire.Equal(t, len(assertBody), len(resTest.Expect.AssertBody))\n\trequire.Equal(t, len(assertBodyT), len(resTest.Expect.AssertBodyT))\n\n\trequire.Equal(t, len(assertResponse), len(resTest.Expect.AssertResponse))\n\trequire.Equal(t, len(assertResponseT), len(resTest.Expect.AssertResponseT))\n\n\trequire.Equal(t, len(after), len(resTest.Middleware.After))\n\trequire.Equal(t, len(afterT), len(resTest.Middleware.AfterT))\n}\n\nfunc TestCreateDefaultTest(t *testing.T) {\n\tresTest := createDefaultTest(&HTTPTestMaker{httpClient: http.DefaultClient, middleware: new(Middleware)})\n\n\trequire.Equal(t, &Test{\n\t\thttpClient: http.DefaultClient,\n\t\tName:       \"\",\n\t\tAllureStep: new(AllureStep),\n\t\tMiddleware: &Middleware{\n\t\t\tAfter:   make([]AfterExecute, 0),\n\t\t\tAfterT:  make([]AfterExecuteT, 0),\n\t\t\tBefore:  make([]BeforeExecute, 0),\n\t\t\tBeforeT: make([]BeforeExecuteT, 0),\n\t\t},\n\t\tRequest: &Request{\n\t\t\tRetry: new(RequestRetryPolitic),\n\t\t},\n\t\tExpect: &Expect{\n\t\t\tJSONSchema: new(ExpectJSONSchema),\n\t\t},\n\t}, resTest)\n}\n\nfunc TestCreateTableTest(t *testing.T) {\n\tc := &cute{}\n\tc.CreateTableTest()\n\n\trequire.True(t, c.isTableTest)\n}\n\nfunc TestPutNewTest(t *testing.T) {\n\ttests := make([]*Test, 1)\n\ttests[0] = createDefaultTest(&HTTPTestMaker{httpClient: http.DefaultClient, middleware: new(Middleware)})\n\n\tvar (\n\t\tc = &cute{tests: tests, baseProps: &HTTPTestMaker{\n\t\t\tmiddleware: &Middleware{},\n\t\t}}\n\t\treqOne, _    = http.NewRequest(\"GET\", \"URL_1\", nil)\n\t\texpectOne    = &Expect{Code: 200}\n\t\treqSecond, _ = http.NewRequest(\"POST\", \"URL_1\", nil)\n\t\texpectSecond = &Expect{Code: 400}\n\t)\n\n\tc.PutNewTest(\"name_1\", reqOne, expectOne)\n\tc.PutNewTest(\"name_2\", reqSecond, expectSecond)\n\n\trequire.Equal(t, c.tests[0].Name, \"name_1\")\n\trequire.Equal(t, c.tests[0].Expect, expectOne)\n\trequire.Equal(t, c.tests[0].Request.Base, reqOne)\n\n\trequire.Equal(t, c.tests[1].Name, \"name_2\")\n\trequire.Equal(t, c.tests[1].Expect, expectSecond)\n\trequire.Equal(t, c.tests[1].Request.Base, reqSecond)\n}\n\nfunc TestPutTests(t *testing.T) {\n\tvar (\n\t\ttests        = createDefaultTests(&HTTPTestMaker{httpClient: http.DefaultClient, middleware: new(Middleware)})\n\t\tc            = &cute{tests: tests}\n\t\treqOne, _    = http.NewRequest(\"GET\", \"URL_1\", nil)\n\t\texpectOne    = &Expect{Code: 200}\n\t\treqSecond, _ = http.NewRequest(\"POST\", \"URL_1\", nil)\n\t\texpectSecond = &Expect{Code: 400}\n\t)\n\n\ttests = append(tests,\n\t\t&Test{\n\t\t\tName: \"name_1\",\n\t\t\tRequest: &Request{\n\t\t\t\tBase: reqOne,\n\t\t\t},\n\t\t\tExpect: expectOne,\n\t\t},\n\t\t&Test{\n\t\t\tName: \"name_2\",\n\t\t\tRequest: &Request{\n\t\t\t\tBase: reqSecond,\n\t\t\t},\n\t\t\tExpect: expectSecond,\n\t\t},\n\t)\n\n\tc.PutTests(tests...)\n\n\trequire.Equal(t, c.tests[0].Name, \"name_1\")\n\trequire.Equal(t, c.tests[0].Expect, expectOne)\n\trequire.Equal(t, c.tests[0].Request.Base, reqOne)\n\n\trequire.Equal(t, c.tests[1].Name, \"name_2\")\n\trequire.Equal(t, c.tests[1].Expect, expectSecond)\n\trequire.Equal(t, c.tests[1].Request.Base, reqSecond)\n}\n\nfunc TestCreateHTTPTestMakerWithHttpClient(t *testing.T) {\n\tcli := &http.Client{\n\t\tTransport:     nil,\n\t\tCheckRedirect: nil,\n\t\tJar:           nil,\n\t\tTimeout:       100,\n\t}\n\n\tmaker := NewHTTPTestMaker(WithHTTPClient(cli))\n\n\trequire.Equal(t, cli, maker.httpClient)\n\trequire.Equal(t, time.Duration(100), maker.httpClient.Timeout)\n}\n\ntype rt struct {\n}\n\nfunc (r *rt) RoundTrip(*http.Request) (*http.Response, error) {\n\treturn nil, nil\n}\n\nfunc TestCreateHTTPMakerOps(t *testing.T) {\n\ttimeout := time.Second * 100\n\troundTripper := &rt{}\n\n\tmaker := NewHTTPTestMaker(\n\t\tWithCustomHTTPTimeout(timeout),\n\t\tWithCustomHTTPRoundTripper(roundTripper),\n\t)\n\n\trequire.Equal(t, timeout, maker.httpClient.Timeout)\n\trequire.Equal(t, roundTripper, maker.httpClient.Transport)\n}\n"
  },
  {
    "path": "cute.go",
    "content": "package cute\n\nimport (\n\t\"context\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"github.com/ozontech/allure-go/pkg/framework/core/allure_manager/manager\"\n\t\"github.com/ozontech/allure-go/pkg/framework/core/common\"\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n)\n\ntype cute struct {\n\tbaseProps *HTTPTestMaker\n\n\tparallel bool\n\n\tallureInfo   *allureInformation\n\tallureLinks  *allureLinks\n\tallureLabels *allureLabels\n\n\tcountTests int // Общее количество тестов.\n\n\tisTableTest bool\n\ttests       []*Test\n}\n\ntype allureInformation struct {\n\ttitle       string\n\tdescription string\n\tstage       string\n}\n\ntype allureLabels struct {\n\tid          string\n\tfeature     string\n\tepic        string\n\ttag         string\n\ttags        []string\n\tsuiteLabel  string\n\tsubSuite    string\n\tparentSuite string\n\tstory       string\n\tseverity    allure.SeverityType\n\towner       string\n\tlead        string\n\tlabel       *allure.Label\n\tlabels      []*allure.Label\n\tallureID    string\n\tlayer       string\n}\n\ntype allureLinks struct {\n\tissue    string\n\ttestCase string\n\tlink     *allure.Link\n\ttmsLink  string\n\ttmsLinks []string\n}\n\nfunc (qt *cute) ExecuteTest(ctx context.Context, t tProvider) []ResultsHTTPBuilder {\n\tvar internalT allureProvider\n\n\tif t == nil {\n\t\tpanic(\"could not start test without testing.T\")\n\t}\n\n\tstepCtx, isStepCtx := t.(provider.StepCtx)\n\tif isStepCtx {\n\t\treturn qt.executeTestsInsideStep(ctx, stepCtx)\n\t}\n\n\ttOriginal, ok := t.(*testing.T)\n\tif ok {\n\t\tnewT := createAllureT(tOriginal)\n\t\tif !qt.isTableTest {\n\t\t\tdefer newT.FinishTest() //nolint\n\t\t}\n\n\t\tinternalT = newT\n\t}\n\n\tallureT, ok := t.(provider.T)\n\tif ok {\n\t\tinternalT = allureT\n\t}\n\n\tif qt.parallel {\n\t\tinternalT.Parallel()\n\t}\n\n\treturn qt.executeTests(ctx, internalT)\n}\n\nfunc createAllureT(t *testing.T) *common.Common {\n\tvar (\n\t\tnewT        = common.NewT(t)\n\t\tcallers     = strings.Split(t.Name(), \"/\")\n\t\tproviderCfg = manager.NewProviderConfig().\n\t\t\t\tWithFullName(t.Name()).\n\t\t\t\tWithPackageName(\"package\").\n\t\t\t\tWithSuiteName(t.Name()).\n\t\t\t\tWithRunner(callers[0])\n\t\tnewProvider = manager.NewProvider(providerCfg)\n\t)\n\n\tnewProvider.NewTest(t.Name(), \"package\")\n\n\tnewT.SetProvider(newProvider)\n\tnewT.Provider.TestContext()\n\n\treturn newT\n}\n\n// executeTests is method for run tests\n// It's could be table tests or usual tests\nfunc (qt *cute) executeTests(ctx context.Context, allureProvider allureProvider) []ResultsHTTPBuilder {\n\tvar (\n\t\tres = make([]ResultsHTTPBuilder, 0)\n\t)\n\n\t// Cycle for change number of Test\n\tfor i := 0; i <= qt.countTests; i++ {\n\t\tcurrentTest := qt.tests[i]\n\n\t\t// Execute by new T for table tests\n\t\tif qt.isTableTest {\n\t\t\ttableTestName := currentTest.Name\n\n\t\t\tallureProvider.Run(tableTestName, func(inT provider.T) {\n\t\t\t\t// Set current test name\n\t\t\t\tinT.Title(tableTestName)\n\n\t\t\t\tres = append(res, qt.executeInsideAllure(ctx, inT, currentTest))\n\t\t\t})\n\t\t} else {\n\t\t\tcurrentTest.Name = allureProvider.Name()\n\n\t\t\t// set labels\n\t\t\tqt.setAllureInformation(allureProvider)\n\n\t\t\tres = append(res, qt.executeInsideAllure(ctx, allureProvider, currentTest))\n\t\t}\n\t}\n\n\treturn res\n}\n\n// executeInsideAllure is method for run test inside allure\n// It's could be table tests or usual tests\nfunc (qt *cute) executeInsideAllure(ctx context.Context, allureProvider allureProvider, currentTest *Test) ResultsHTTPBuilder {\n\tresT := currentTest.executeInsideAllure(ctx, allureProvider)\n\n\t// Remove from base struct all asserts\n\tcurrentTest.clearFields()\n\n\treturn resT\n}\n\n// executeTestsInsideStep is method for run group of tests inside provider.StepCtx\nfunc (qt *cute) executeTestsInsideStep(ctx context.Context, stepCtx provider.StepCtx) []ResultsHTTPBuilder {\n\tvar (\n\t\tres = make([]ResultsHTTPBuilder, 0)\n\t)\n\n\t// Cycle for change number of Test\n\tfor i := 0; i <= qt.countTests; i++ {\n\t\tcurrentTest := qt.tests[i]\n\n\t\tresult := currentTest.executeInsideStep(ctx, stepCtx)\n\n\t\t// Remove from base struct all asserts\n\t\tcurrentTest.clearFields()\n\n\t\tres = append(res, result)\n\t}\n\n\treturn res\n}\n"
  },
  {
    "path": "errors/broken.go",
    "content": "package errors\n\n// BrokenError is an interface for set errors like Broken errors.\n// If the function returns an error, which implements this interface, the allure step will has a broken status\ntype BrokenError interface {\n\tIsBroken() bool\n\tSetBroken(bool)\n\tError() string\n}\n\n// NewBrokenError returns error with a Broken tag for Allure\nfunc NewBrokenError(err string) error {\n\treturn &CuteError{\n\t\tBroken:  true,\n\t\tMessage: err,\n\t}\n}\n\n// WrapBrokenError returns error with a Broken tag for Allure\nfunc WrapBrokenError(err error) error {\n\treturn &CuteError{\n\t\tBroken: true,\n\t\tErr:    err,\n\t}\n}\n"
  },
  {
    "path": "errors/error.go",
    "content": "package errors\n\nimport \"fmt\"\n\nconst (\n\t// ActualField is a key for actual value in error fields\n\tActualField = \"Actual\"\n\t// ExpectedField is a key for expected value in error fields\n\tExpectedField = \"Expected\"\n)\n\n// AssertError is a common interface for all errors in the package\ntype AssertError interface {\n\terror\n\tWithNameError\n\tWithFields\n\tWithAttachments\n\tWithTrace\n}\n\n// WithNameError is interface for creates allure step.\n// If function returns error, which implement this interface, allure step will create automatically\ntype WithNameError interface {\n\tGetName() string\n\tSetName(string)\n}\n\n// WithFields is interface for put parameters in allure step.\n// If function returns error, which implement this interface, parameters will add to allure step\ntype WithFields interface {\n\tGetFields() map[string]interface{}\n\tPutFields(map[string]interface{})\n}\n\n// WithTrace is interface for put trace in logs\ntype WithTrace interface {\n\tGetTrace() string\n\tSetTrace(string)\n}\n\n// Attachment represents an attachment to Allure with properties like name, MIME type, and content.\ntype Attachment struct {\n\tName     string // Name of the attachment.\n\tMimeType string // MIME type of the attachment.\n\tContent  []byte // Content of the attachment.\n}\n\n// WithAttachments is an interface that defines methods for managing attachments.\ntype WithAttachments interface {\n\tGetAttachments() []*Attachment\n\tPutAttachment(a *Attachment)\n}\n\n// CuteError is a struct for error with additional fields for allure and logs\ntype CuteError struct {\n\t// Optional is a flag to determine if the error is optional\n\t// If the error is optional, it will not fail the test\n\tOptional bool\n\t// Require is a flag to determine if the error is required\n\t// If the error is required, it will fail the test\n\tRequire bool\n\t// Broken is a flag to determine if the error is broken\n\t// If the error is broken, it will fail the test and mark the test as broken in allure\n\tBroken bool\n\n\t// Name is a name of the error\n\tName string\n\t// Message is a message of the error\n\tMessage string\n\t// Err is a wrapped error\n\tErr error\n\n\t// Trace is a trace of the error\n\t// It could be a file path, function name, or any other information\n\tTrace string\n\n\t// Fields is a map of additional fields for the error\n\t// It could be actual and expected values, parameters, or any other information\n\t// ActualField and ExpectedField fields will be logged\n\tFields map[string]interface{}\n\t// Attachments is a slice of attachments for the error\n\tAttachments []*Attachment\n}\n\n// NewCuteError is the function, which creates cute error with \"Name\" and \"Message\" for allure\nfunc NewCuteError(name string, err error) *CuteError {\n\treturn &CuteError{\n\t\tName: name,\n\t\tErr:  err,\n\t}\n}\n\n// NewAssertError is the function, which creates error with \"Actual\" and \"Expected\" for allure\nfunc NewAssertError(name string, message string, actual interface{}, expected interface{}) error {\n\treturn &CuteError{\n\t\tName:    name,\n\t\tMessage: message,\n\t\tFields: map[string]interface{}{\n\t\t\tActualField:   actual,\n\t\t\tExpectedField: expected,\n\t\t},\n\t}\n}\n\n// NewAssertErrorWithMessage is the function, which creates error with \"Name\" and \"Message\" for allure\n// Deprecated: use NewEmptyAssertError instead\nfunc NewAssertErrorWithMessage(name string, message string) error {\n\treturn NewEmptyAssertError(name, message)\n}\n\n// NewEmptyAssertError is the function, which creates error with \"Name\" and \"Message\" for allure\n// Returns AssertError with empty fields\n// You can use PutFields and PutAttachment to add additional information\n// You can use SetOptional, SetRequire, SetBroken to change error behavior\nfunc NewEmptyAssertError(name string, message string) AssertError {\n\treturn &CuteError{\n\t\tName:    name,\n\t\tMessage: message,\n\t\tFields:  map[string]interface{}{},\n\t}\n}\n\n// Unwrap is a method to get wrapped error\n// It is used for errors.Is and errors.As functions\nfunc (a *CuteError) Unwrap() error {\n\treturn a.Err\n}\n\n// Error is a method to get error message\n// It is used for fmt.Errorf and fmt.Println functions\nfunc (a *CuteError) Error() string {\n\tif a.Trace == \"\" {\n\t\treturn a.Message\n\t}\n\n\terrText := a.Message\n\n\tif a.Err != nil {\n\t\terrText = a.Err.Error()\n\t}\n\n\treturn fmt.Sprintf(\"%s\\nCalled from: %s\", errText, a.Trace)\n}\n\n// GetName is a method to get error name\n// It is used for allure step name\nfunc (a *CuteError) GetName() string {\n\treturn a.Name\n}\n\n// SetName is a method to set error name\n// It is used for allure step name\nfunc (a *CuteError) SetName(name string) {\n\ta.Name = name\n}\n\n// GetFields ...\nfunc (a *CuteError) GetFields() map[string]interface{} {\n\treturn a.Fields\n}\n\n// PutFields ...\nfunc (a *CuteError) PutFields(fields map[string]interface{}) {\n\tfor k, v := range fields {\n\t\ta.Fields[k] = v\n\t}\n}\n\n// GetAttachments ...\nfunc (a *CuteError) GetAttachments() []*Attachment {\n\treturn a.Attachments\n}\n\n// PutAttachment ...\nfunc (a *CuteError) PutAttachment(attachment *Attachment) {\n\ta.Attachments = append(a.Attachments, attachment)\n}\n\n// IsOptional ...\nfunc (a *CuteError) IsOptional() bool {\n\treturn a.Optional\n}\n\n// SetOptional ...\nfunc (a *CuteError) SetOptional(opt bool) {\n\ta.Optional = opt\n}\n\n// IsRequire ...\nfunc (a *CuteError) IsRequire() bool {\n\treturn a.Require\n}\n\n// SetRequire ...\nfunc (a *CuteError) SetRequire(b bool) {\n\ta.Require = b\n}\n\n// IsBroken ...\nfunc (a *CuteError) IsBroken() bool {\n\treturn a.Broken\n}\n\n// SetBroken ...\nfunc (a *CuteError) SetBroken(b bool) {\n\ta.Broken = b\n}\n\n// GetTrace ...\nfunc (a *CuteError) GetTrace() string {\n\treturn a.Trace\n}\n\n// SetTrace ...\nfunc (a *CuteError) SetTrace(trace string) {\n\ta.Trace = trace\n}\n"
  },
  {
    "path": "errors/optional.go",
    "content": "package errors\n\n// OptionalError is an interface for set errors like Optional errors.\n// If the function returns an error, which implements this interface, the allure step will has to skip status\ntype OptionalError interface {\n\tIsOptional() bool\n\tSetOptional(bool)\n}\n\n// NewOptionalError returns error with an Optional tag for Allure\nfunc NewOptionalError(err string) error {\n\treturn &CuteError{\n\t\tOptional: true,\n\t\tMessage:  err,\n\t}\n}\n\n// WrapOptionalError returns error with an Optional tag for Allure\nfunc WrapOptionalError(err error) error {\n\treturn &CuteError{\n\t\tOptional: true,\n\t\tErr:      err,\n\t}\n}\n"
  },
  {
    "path": "errors/require.go",
    "content": "package errors\n\n// RequireError is an interface for set errors like require error.\n// If the function returns an error, which implements this interface, the allure step will has failed status\ntype RequireError interface {\n\tIsRequire() bool\n\tSetRequire(bool)\n}\n\ntype requireError struct {\n\terr     error\n\trequire bool\n}\n\n// NewRequireError returns error with flag for execute t.FailNow() and finish test after this error\nfunc NewRequireError(err string) error {\n\treturn &CuteError{\n\t\tRequire: true,\n\t\tMessage: err,\n\t}\n}\n\n// WrapRequireError returns error with flag for execute t.FailNow() and finish test after this error\nfunc WrapRequireError(err error) error {\n\treturn &CuteError{\n\t\tRequire: true,\n\t\tErr:     err,\n\t}\n}\n"
  },
  {
    "path": "errors/trace.go",
    "content": "package errors\n\n// NewErrorWithTrace is a function for create error with trace\nfunc NewErrorWithTrace(err, trace string) error {\n\treturn &CuteError{\n\t\tTrace:   trace,\n\t\tMessage: err,\n\t}\n}\n\n// WrapErrorWithTrace is a function for wrap error with trace\nfunc WrapErrorWithTrace(err error, trace string) error {\n\treturn &CuteError{\n\t\tTrace: trace,\n\t\tErr:   err,\n\t}\n}\n"
  },
  {
    "path": "examples/custom_asserts.go",
    "content": "package examples\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"github.com/ozontech/cute\"\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc CustomAssertBodyWithCustomError() cute.AssertBody {\n\treturn func(bytes []byte) error {\n\t\tif len(bytes) == 0 {\n\t\t\treturn cuteErrors.NewAssertError(\"customAssertBodyWithCustomError\", \"body must be not empty\", \"len is 0\", \"len more 0\")\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc CustomAssertBody() cute.AssertBody {\n\treturn func(bytes []byte) error {\n\t\tif len(bytes) == 0 {\n\t\t\treturn errors.New(\"response body is empty\")\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc CustomAssertBodyT() cute.AssertBodyT {\n\treturn func(t cute.T, bytes []byte) error {\n\t\tt.WithNewParameters(\"example_parameter\", \"example\")\n\t\trequire.GreaterOrEqual(t, len(bytes), 100)\n\t\treturn nil\n\t}\n}\n\nfunc CustomAssertBodyWithAllureStep() cute.AssertBodyT {\n\treturn func(t cute.T, bytes []byte) error {\n\n\t\tstep := allure.NewSimpleStep(\"Custom assert step\")\n\t\tdefer func() {\n\t\t\tt.Step(step)\n\t\t}()\n\n\t\tif len(bytes) == 0 {\n\t\t\tstep.Status = allure.Failed\n\t\t\tstep.WithAttachments(allure.NewAttachment(\"Error\", allure.Text, []byte(\"response body is empty\")))\n\n\t\t\treturn nil\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc CustomAssertHeaders() cute.AssertHeaders {\n\treturn func(headers http.Header) error {\n\t\tif len(headers) == 0 {\n\t\t\treturn errors.New(\"response without headers\")\n\t\t}\n\n\t\treturn nil\n\t}\n}\n\nfunc CustomAssertResponse() cute.AssertResponse {\n\treturn func(resp *http.Response) error {\n\t\tif resp.ContentLength == 0 {\n\t\t\treturn errors.New(\"content length is zero\")\n\t\t}\n\n\t\treturn nil\n\t}\n}\n"
  },
  {
    "path": "examples/inside_step_test.go",
    "content": "//go:build example\n// +build example\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/allure-go/pkg/framework/runner\"\n\n\t\"github.com/ozontech/cute\"\n)\n\nfunc TestInsideStep(t *testing.T) {\n\trunner.Run(t, \"Single test with allure-go Runner\", func(t provider.T) {\n\n\t\tt.WithNewStep(\"First step\", func(sCtx provider.StepCtx) {\n\t\t\tsCtx.NewStep(\"Inside first step\")\n\t\t})\n\n\t\tt.WithNewStep(\"Step name\", func(sCtx provider.StepCtx) {\n\t\t\tu, _ := url.Parse(\"https://jsonplaceholder.typicode.com/posts/1/comments\")\n\n\t\t\tcute.NewTestBuilder().\n\t\t\t\tTitle(\"Super simple test\").\n\t\t\t\tTags(\"simple\", \"suite\", \"some_local_tag\", \"json\").\n\t\t\t\tParallel().\n\t\t\t\tCreate().\n\t\t\t\tRequestBuilder(\n\t\t\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\t\t\"some_header\": []string{\"something\"},\n\t\t\t\t\t}),\n\t\t\t\t\tcute.WithURL(u),\n\t\t\t\t\tcute.WithMethod(http.MethodPost),\n\t\t\t\t).\n\t\t\t\tExpectExecuteTimeout(10*time.Second).\n\t\t\t\tExpectStatus(http.StatusCreated).\n\t\t\t\tExecuteTest(context.Background(), sCtx)\n\t\t})\n\t})\n\n}\n"
  },
  {
    "path": "examples/masked_data_test.go",
    "content": "//go:build example\n// +build example\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/allure-go/pkg/framework/runner\"\n\n\t\"github.com/ozontech/cute\"\n)\n\nfunc TestSanitizer(t *testing.T) {\n\trunner.Run(t, \"Single test with request and response sanitizer\", func(t provider.T) {\n\n\t\tt.WithNewStep(\"First step\", func(sCtx provider.StepCtx) {\n\t\t\tsCtx.NewStep(\"Inside first step\")\n\t\t})\n\n\t\tt.WithNewStep(\"Step name\", func(sCtx provider.StepCtx) {\n\t\t\tu, _ := url.Parse(\"https://jsonplaceholder.typicode.com/posts/1/comments?example=11\")\n\t\t\tquery := u.Query()\n\t\t\tquery.Set(\"name\", \"Vasya\")\n\t\t\tu.RawQuery = query.Encode()\n\n\t\t\tcute.NewTestBuilder().\n\t\t\t\tTitle(\"Super simple test\").\n\t\t\t\tTags(\"simple\", \"suite\", \"some_local_tag\", \"json\").\n\t\t\t\tParallel().\n\t\t\t\tCreate().\n\t\t\t\tRequestSanitizerHook(func(req *http.Request) {\n\t\t\t\t\treq.URL.Path = \"/path/masked\"\n\n\t\t\t\t\tvalues := req.URL.Query()\n\t\t\t\t\tvalues.Set(\"example\", \"masked\")\n\n\t\t\t\t\treq.URL.RawQuery = values.Encode()\n\n\t\t\t\t\treq.Header[\"some_header\"] = []string{\"masked\"}\n\t\t\t\t}).\n\t\t\t\tResponseSanitizerHook(func(resp *http.Response) {\n\t\t\t\t\tresp.Header[\"some_header\"] = []string{\"masked\"}\n\t\t\t\t\tresp.Header[\"Content-Type\"] = []string{\"masked\"}\n\t\t\t\t}).\n\t\t\t\tRequestBuilder(\n\t\t\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\t\t\"some_header\": []string{\"something\"},\n\t\t\t\t\t}),\n\t\t\t\t\tcute.WithURL(u),\n\t\t\t\t\tcute.WithMethod(http.MethodPost),\n\t\t\t\t).\n\t\t\t\tExpectExecuteTimeout(10*time.Second).\n\t\t\t\tExpectStatus(http.StatusCreated).\n\t\t\t\tExecuteTest(context.Background(), sCtx)\n\t\t})\n\t})\n\n}\n"
  },
  {
    "path": "examples/parallel_test.go",
    "content": "//go:build example\n// +build example\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/cute\"\n)\n\nfunc Test_Async_1(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Title async test 1\").\n\t\tTags(\"parallel_test\").\n\t\tParallel().\n\t\tCreate().\n\t\tBeforeExecuteT(\n\t\t\tfunc(t cute.T, r *http.Request) error {\n\t\t\t\tt.WithNewStep(\"insideBefore\", func(stepCtx provider.StepCtx) {\n\t\t\t\t\ttime.Sleep(time.Second)\n\t\t\t\t\tnow := time.Now()\n\t\t\t\t\tstepCtx.Logf(\"Test 1. Start time %v\", now)\n\t\t\t\t\tstepCtx.WithNewParameters(\"Test 1. Time\", now)\n\t\t\t\t})\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tAfterExecuteT(\n\t\t\tfunc(t cute.T, resp *http.Response, errs []error) error {\n\t\t\t\tt.WithNewStep(\"insideAfter\", func(stepCtx provider.StepCtx) {\n\t\t\t\t\tnow := time.Now()\n\t\t\t\t\tstepCtx.Logf(\"Test 1. Stop time %v\", now)\n\t\t\t\t\tstepCtx.WithNewParameters(\"Test 1. Stop time\", now)\n\t\t\t\t})\n\n\t\t\t\treturn nil\n\t\t\t}).\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n\nfunc Test_Async_2(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Title async test 2\").\n\t\tTags(\"parallel_test\").\n\t\tParallel().\n\t\tCreate().\n\t\tBeforeExecuteT(\n\t\t\tfunc(t cute.T, r *http.Request) error {\n\t\t\t\tt.WithNewStep(\"insideBefore\", func(stepCtx provider.StepCtx) {\n\t\t\t\t\tnow := time.Now()\n\t\t\t\t\tstepCtx.Logf(\"Test 2. Start time %v\", now)\n\t\t\t\t\tstepCtx.WithNewParameters(\"Test 2. Start time\", now)\n\t\t\t\t\ttime.Sleep(2 * time.Second)\n\t\t\t\t})\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tAfterExecuteT(\n\t\t\tfunc(t cute.T, resp *http.Response, errs []error) error {\n\t\t\t\tt.WithNewStep(\"insideAfter\", func(stepCtx provider.StepCtx) {\n\t\t\t\t\tnow := time.Now()\n\t\t\t\t\tstepCtx.Logf(\"test 2. Stop time %v\", now)\n\t\t\t\t\tstepCtx.WithNewParameters(\"Test 2. Stop time\", now)\n\t\t\t\t})\n\n\t\t\t\treturn nil\n\t\t\t}).\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t).\n\t\tExpectStatus(200).\n\t\tExecuteTest(context.Background(), t)\n}\n"
  },
  {
    "path": "examples/single_test.go",
    "content": "//go:build example\n// +build example\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/allure-go/pkg/framework/runner\"\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\n\t\"github.com/ozontech/cute\"\n\t\"github.com/ozontech/cute/asserts/json\"\n)\n\nfunc Test_Single_1(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Single test with default T\").\n\t\tTag(\"single_test\").\n\t\tDescription(\"some_description\").\n\t\tParallel().\n\t\tCreate().\n\t\tRequestRetry(3).\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\tcute.WithMarshalBody(struct {\n\t\t\t\tName string `json:\"name\"`\n\t\t\t}{\n\t\t\t\tName: \"Vasya Pupkin\",\n\t\t\t}),\n\t\t\tcute.WithQueryKV(\"socks\", \"42\"),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t).\n\t\tExpectExecuteTimeout(10*time.Second).\n\t\tExpectStatus(http.StatusOK).\n\t\tAssertBody(json.Diff(\"{\\\"aaa\\\":\\\"bb\\\"}\")).\n\t\tAssertBody(\n\t\t\tjson.Present(\"$[1].name\"),\n\t\t\tjson.Present(\"$[0].passport\"), // Example fail\n\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\tCustomAssertBody(),\n\t\t).\n\t\tAssertBodyT(func(t cute.T, body []byte) error {\n\t\t\tt.Step(allure.NewSimpleStep(\"inside Assert body. 1 \", allure.NewParameters(\"key\", \"value\")...))\n\n\t\t\treturn nil\n\t\t}).\n\t\tAfter(\n\t\t\tfunc(response *http.Response, errors []error) error {\n\t\t\t\tb, err := io.ReadAll(response.Body)\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\temail, err := json.GetValueFromJSON(b, \"$[0].email\")\n\t\t\t\tif err != nil {\n\t\t\t\t\treturn err\n\t\t\t\t}\n\n\t\t\t\tfmt.Println(\"Email from test\", email)\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n\nfunc Test_Single_Broken(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Test_Single_Broken\").\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t).\n\t\tBrokenAssertBodyT(func(t cute.T, body []byte) error {\n\t\t\treturn errors.New(\"example broken error\")\n\t\t}).\n\t\tExpectStatus(http.StatusOK).\n\t\tNextTest().\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t).\n\t\tAssertBody(func(body []byte) error {\n\t\t\treturn errors.New(\"it's NOT must be run\")\n\t\t},\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n\n\tt.Skip()\n}\n\nfunc Test_Single_RepeatPolitic_Optional_Success_Test(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Test_Single_RepeatPolitic_Optional_Success_Test\").\n\t\tCreate().\n\t\tRequestRetry(2).\n\t\tRequestRetryOptional(true).\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t).\n\t\tBrokenAssertBodyT(func(t cute.T, body []byte) error {\n\t\t\treturn errors.New(\"example broken error\")\n\t\t}).\n\t\tExpectStatus(http.StatusCreated).\n\t\tExecuteTest(context.Background(), t)\n\n\tt.Logf(\"You should see it\")\n}\n\nfunc Test_Single_RepeatPolitic_Broken_Failed_Test(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Test_Single_RepeatPolitic_Broken_Failed_Test\").\n\t\tCreate().\n\t\tRequestRetry(2).\n\t\tRequestRetryOptional(false).\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t).\n\t\tBrokenAssertBodyT(func(t cute.T, body []byte) error {\n\t\t\treturn errors.New(\"example broken error\")\n\t\t}).\n\t\tExpectStatus(http.StatusCreated).\n\t\tExecuteTest(context.Background(), t)\n\n\tt.Logf(\"You should see it\")\n}\n\nfunc Test_Single_Broken_2(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Test_Single_Broken_2\").\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t).\n\t\tAssertBodyT(func(t cute.T, body []byte) error {\n\t\t\terr := errors.New(\"example broken error\")\n\t\t\treturn cuteErrors.WrapBrokenError(err)\n\t\t}).\n\t\tExpectStatus(http.StatusOK).\n\t\tNextTest().\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t).\n\t\tAssertBody(func(body []byte) error {\n\t\t\treturn errors.New(\"it's NOT must be run\")\n\t\t},\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n\nfunc Test_Single_2_AllureRunner(t *testing.T) {\n\trunner.Run(t, \"Single test with allure-go Runner\", func(t provider.T) {\n\t\tvar (\n\t\t\ttestMaker   = cute.NewHTTPTestMaker()\n\t\t\ttestBuilder = testMaker.NewTestBuilder()\n\t\t)\n\n\t\tu, _ := url.Parse(\"https://jsonplaceholder.typicode.com/\")\n\t\tu.Path = path.Join(u.Path, \"/posts/1/comments\")\n\n\t\ttestBuilder.\n\t\t\tTitle(\"Single test with allure.T and repeat errors\").\n\t\t\tTag(\"single_test\").\n\t\t\tDescription(\"some_description\").\n\t\t\tCreate().\n\t\t\tRequestRetryDelay(3*time.Second). // delay before new try\n\t\t\tRequestRetry(3).                  // count attempts\n\t\t\tRequestBuilder(\n\t\t\t\tcute.WithURL(u),\n\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t).\n\t\t\tExpectExecuteTimeout(10*time.Second).\n\t\t\tExpectStatus(http.StatusBadGateway).\n\t\t\tAssertBody(\n\t\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\t\tjson.Present(\"$[1].name\"),\n\t\t\t).\n\t\t\tOptionalAssertBody(\n\t\t\t\tjson.Present(\"$[0].photo\"), // Example optional fail\n\t\t\t).\n\t\t\tExecuteTest(context.Background(), t)\n\t})\n}\n"
  },
  {
    "path": "examples/suite/common.go",
    "content": "package suite\n\nimport (\n\t\"net/url\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/allure-go/pkg/framework/suite\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ozontech/cute\"\n)\n\ntype ExampleSuite struct {\n\tsuite.Suite\n\thost *url.URL\n\n\ttestMaker *cute.HTTPTestMaker\n}\n\nfunc (i *ExampleSuite) BeforeAll(t provider.T) {\n\t// Prepare http test builder\n\ti.testMaker = cute.NewHTTPTestMaker()\n\n\t// Preparing host\n\thost, err := url.Parse(\"https://jsonplaceholder.typicode.com/\")\n\trequire.NoError(t, err)\n\n\ti.host = host\n}\n\nfunc (i *ExampleSuite) BeforeEach(t provider.T) {\n\tt.Feature(\"ExampleSuite\")\n\tt.Tags(\"some_global_tag\")\n}\n"
  },
  {
    "path": "examples/suite/main_test.go",
    "content": "//go:build example\n// +build example\n\npackage suite\n\nimport (\n\t\"os\"\n\t\"testing\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/suite\"\n)\n\nfunc TestExampleSuite(t *testing.T) {\n\tos.Setenv(\"ALLURE_OUTPUT_PATH\", \"../\") // custom, read Readme.md for more info\n\tsuite.RunSuite(t, new(ExampleSuite))\n}\n"
  },
  {
    "path": "examples/suite/one_step.go",
    "content": "package suite\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/cute\"\n\t\"github.com/ozontech/cute/asserts/headers\"\n\t\"github.com/ozontech/cute/asserts/json\"\n\t\"github.com/ozontech/cute/examples\"\n)\n\n/*\n\tExample testing HTTP GET and validate body.\n\tValidate:\n\t\t1) Execute time\n\t\t2) Status code\n\t\t3) Validate body by json schema\n\t\t4) Validate fields in json\n\nResponse:\n[\n\n\t{\n\t  \"postId\": 1,\n\t  \"id\": 1,\n\t  \"name\": \"id labore ex et quam laborum\",\n\t  \"email\": \"Eliseo@gardner.biz\",\n\t  \"body\": \"laudantium enim quasi est quidem magnam voluptate ipsam eos\\ntempora quo necessitatibus\\ndolor quam autem quasi\\nreiciendis et nam sapiente accusantium\"\n\t},\n\t{\n\t  \"postId\": 1,\n\t  \"id\": 2,\n\t  \"name\": \"quo vero reiciendis velit similique earum\",\n\t  \"email\": \"Jayne_Kuhic@sydney.com\",\n\t  \"body\": \"est natus enim nihil est dolore omnis voluptatem numquam\\net omnis occaecati quod ullam at\\nvoluptatem error expedita pariatur\\nnihil sint nostrum voluptatem reiciendis et\"\n\t},\n\t{\n\t  \"postId\": 1,\n\t  \"id\": 3,\n\t  \"name\": \"odio adipisci rerum aut animi\",\n\t  \"email\": \"Nikita@garfield.biz\",\n\t  \"body\": \"quia molestiae reprehenderit quasi aspernatur\\naut expedita occaecati aliquam eveniet laudantium\\nomnis quibusdam delectus saepe quia accusamus maiores nam est\\ncum et ducimus et vero voluptates excepturi deleniti ratione\"\n\t},\n\t{\n\t  \"postId\": 1,\n\t  \"id\": 4,\n\t  \"name\": \"alias odio sit\",\n\t  \"email\": \"Lew@alysha.tv\",\n\t  \"body\": \"non et atque\\noccaecati deserunt quas accusantium unde odit nobis qui voluptatem\\nquia voluptas consequuntur itaque dolor\\net qui rerum deleniti ut occaecati\"\n\t},\n\t{\n\t  \"postId\": 1,\n\t  \"id\": 5,\n\t  \"name\": \"vero eaque aliquid doloribus et culpa\",\n\t  \"email\": \"Hayden@althea.biz\",\n\t  \"body\": \"harum non quasi et ratione\\ntempore iure ex voluptates in ratione\\nharum architecto fugit inventore cupiditate\\nvoluptates magni quo et\"\n\t}\n\n]\n*/\nfunc (i *ExampleSuite) Test_OneStep(t provider.T) {\n\tvar (\n\t\ttestBuilder = i.testMaker.NewTestBuilder()\n\t)\n\n\tu, _ := url.Parse(i.host.String())\n\tu.Path = path.Join(u.Path, \"/posts/1/comments\")\n\n\ttestBuilder.\n\t\tTitle(\"Test with one step\").\n\t\tTags(\"one_stp\", \"some_local_tag\", \"suite\", \"json\").\n\t\tFeature(\"some_feature\").\n\t\tEpic(\"some_epic\").\n\t\tDescription(\"some_description\").\n\t\tParallel().\n\t\tCreateStep(\"Example GET json request\").\n\t\tAfterExecuteT(func(t cute.T, resp *http.Response, errs []error) error {\n\t\t\tif len(errs) != 0 {\n\t\t\t\treturn nil\n\t\t\t}\n\n\t\t\t/*\n\t\t\t Implement some logic\n\t\t\t*/\n\n\t\t\treturn nil\n\t\t},\n\n\t\t\t// After failed test\n\t\t\tfunc(t cute.T, resp *http.Response, errs []error) error {\n\t\t\t\tif len(errs) == 0 {\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\n\t\t\t\t/*\n\t\t\t\t Implement some logic\n\t\t\t\t*/\n\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tRequestBuilder(\n\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\"some_header\":       []string{\"something\"},\n\t\t\t\t\"some_array_header\": []string{\"1\", \"2\", \"3\", \"some_thing\"},\n\t\t\t}),\n\t\t\tcute.WithURL(u),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t).\n\t\tExpectExecuteTimeout(10*time.Second).\n\t\tExpectJSONSchemaFile(\"file://./resources/example_valid_request.json\").\n\t\tExpectStatus(http.StatusOK).\n\t\tAssertBody(\n\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\tjson.Present(\"$[1].name\"),\n\t\t\tjson.NotPresent(\"$[1].some_not_present\"),\n\t\t\tjson.LengthGreaterThan(\"$\", 3),\n\t\t\tjson.Length(\"$\", 5),\n\t\t\tjson.LengthLessThan(\"$\", 100),\n\t\t\tjson.NotEqual(\"$[3].name\", \"kekekekeke\"),\n\n\t\t\t// Custom assert body\n\t\t\texamples.CustomAssertBody(),\n\t\t).\n\t\tAssertBodyT(\n\t\t\t// Custom assert body with testing.tb\n\t\t\texamples.CustomAssertBodyT(),\n\n\t\t\tfunc(t cute.T, body []byte) error {\n\t\t\t\t/*\n\t\t\t\t\tImplement here logic with TB\n\t\t\t\t*/\n\t\t\t\ttime.Sleep(5 * time.Second)\n\t\t\t\treturn nil\n\t\t\t},\n\t\t).\n\t\tAssertHeaders(\n\t\t\theaders.Present(\"Content-Type\"),\n\n\t\t\t// Custom assert headers\n\t\t\texamples.CustomAssertHeaders(),\n\t\t).\n\t\tAssertResponse(\n\t\t\texamples.CustomAssertResponse(),\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n"
  },
  {
    "path": "examples/suite/one_step_errors.go",
    "content": "package suite\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/cute\"\n\t\"github.com/ozontech/cute/asserts/headers\"\n\t\"github.com/ozontech/cute/asserts/json\"\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/ozontech/cute/examples\"\n)\n\nfunc (i *ExampleSuite) Test_OneStep_Errors(t provider.T) {\n\tvar (\n\t\ttestBuilder = i.testMaker.NewTestBuilder()\n\t)\n\n\ttestBuilder.\n\t\tTitle(\"Test with errors\").\n\t\tTags(\"one_step\", \"some_local_tag\", \"suite\", \"json\").\n\t\tParallel().\n\t\tCreateStep(\"Example GET json request\").\n\t\tRequestBuilder(\n\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\"some_header\":       []string{\"something\"},\n\t\t\t\t\"some_array_header\": []string{\"1\", \"2\", \"3\", \"some_thing\"},\n\t\t\t}),\n\t\t\tcute.WithURI(i.host.String()+\"/posts/1/comments\"),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\tcute.WithMarshalBody(\n\t\t\t\tmap[string]interface{}{\n\t\t\t\t\t\"key\": \"value\",\n\t\t\t\t\t\"more_key\": map[string]interface{}{\n\t\t\t\t\t\t\"some_value\": \"sss\",\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t),\n\t\t).\n\t\tExpectExecuteTimeout(10*time.Second).\n\t\tExpectJSONSchemaFile(\"file://./resources/example_valid_request.json\").\n\t\tAssertBody(\n\t\t\tjson.Equal(\"$[0].email\", \"something\"),\n\t\t\tjson.Present(\"$[1].not_present\"),\n\t\t\tjson.LengthGreaterThan(\"$\", 99999),\n\t\t\tjson.Length(\"$\", 0),\n\t\t\t// Custom assert body\n\t\t\texamples.CustomAssertBody(),\n\t\t\texamples.CustomAssertBodyWithCustomError(),\n\t\t).\n\t\tAssertHeaders(\n\t\t\theaders.Present(\"Content-Type\"),\n\n\t\t\t// Custom assert headers\n\t\t\texamples.CustomAssertHeaders(),\n\t\t).\n\t\tAssertResponse(\n\t\t\texamples.CustomAssertResponse(),\n\t\t).\n\t\tAssertHeadersT(\n\t\t\tfunc(t cute.T, headers http.Header) error {\n\t\t\t\t// Example pretty print error\n\t\t\t\treturn cuteErrors.NewAssertError(\"custom_assert\", \"example custom assert\", \"empty\", \"not empty\") //\n\t\t\t},\n\t\t).\n\t\t// Example optional\n\t\tOptionalAssertBody( // example optional assert\n\t\t\tfunc(body []byte) error {\n\t\t\t\treturn errors.New(\"some optional error from OptionalAssert\")\n\t\t\t},\n\t\t).\n\t\tAssertBody(\n\t\t\tfunc(body []byte) error {\n\t\t\t\treturn cuteErrors.NewOptionalError(\"some optional error from creator\") // example optional error\n\t\t\t},\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n"
  },
  {
    "path": "examples/suite/resources/example_valid_request.json",
    "content": "{\n  \"$schema\": \"http://json-schema.org/draft-04/schema#\",\n  \"type\": \"array\",\n  \"items\": [\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"postId\": {\n          \"type\": \"integer\"\n        },\n        \"id\": {\n          \"type\": \"integer\"\n        },\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\"\n        },\n        \"body\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"postId\",\n        \"id\",\n        \"name\",\n        \"email\",\n        \"body\"\n      ]\n    },\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"postId\": {\n          \"type\": \"integer\"\n        },\n        \"id\": {\n          \"type\": \"integer\"\n        },\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\"\n        },\n        \"body\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"postId\",\n        \"id\",\n        \"name\",\n        \"email\",\n        \"body\"\n      ]\n    },\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"postId\": {\n          \"type\": \"integer\"\n        },\n        \"id\": {\n          \"type\": \"integer\"\n        },\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\"\n        },\n        \"body\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"postId\",\n        \"id\",\n        \"name\",\n        \"email\",\n        \"body\"\n      ]\n    },\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"postId\": {\n          \"type\": \"integer\"\n        },\n        \"id\": {\n          \"type\": \"integer\"\n        },\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\"\n        },\n        \"body\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"postId\",\n        \"id\",\n        \"name\",\n        \"email\",\n        \"body\"\n      ]\n    },\n    {\n      \"type\": \"object\",\n      \"properties\": {\n        \"postId\": {\n          \"type\": \"integer\"\n        },\n        \"id\": {\n          \"type\": \"integer\"\n        },\n        \"name\": {\n          \"type\": \"string\"\n        },\n        \"email\": {\n          \"type\": \"string\"\n        },\n        \"body\": {\n          \"type\": \"string\"\n        }\n      },\n      \"required\": [\n        \"postId\",\n        \"id\",\n        \"name\",\n        \"email\",\n        \"body\"\n      ]\n    }\n  ]\n}"
  },
  {
    "path": "examples/suite/simple.go",
    "content": "package suite\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/cute\"\n)\n\n/*\n\tExample simple request\n\tValidate:\n\t\t1) Execute time\n\t\t2) Status code\n\n*/\n\nfunc (i *ExampleSuite) Test_Simple(t provider.T) {\n\tvar (\n\t\ttestMaker   = cute.NewHTTPTestMaker()\n\t\ttestBuilder = testMaker.NewTestBuilder()\n\t)\n\n\tu, _ := url.Parse(\"https://jsonplaceholder.typicode.com/posts/1/comments\")\n\n\ttestBuilder.\n\t\tTitle(\"Super simple test\").\n\t\tTags(\"simple\", \"suite\", \"some_local_tag\", \"json\").\n\t\tParallel().\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\"some_header\": []string{\"something\"},\n\t\t\t}),\n\t\t\tcute.WithURL(u),\n\t\t\tcute.WithMethod(http.MethodPost),\n\t\t).\n\t\tExpectExecuteTimeout(10*time.Second).\n\t\tExpectStatus(http.StatusCreated).\n\t\tExecuteTest(context.Background(), t)\n}\n"
  },
  {
    "path": "examples/suite/two_steps.go",
    "content": "package suite\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"io/ioutil\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"path\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/cute\"\n\t\"github.com/ozontech/cute/examples\"\n)\n\n/*\n\n\tExample testing HTTP POST, validate body and make second request.\n\tValidate:\n\t\t1) Execute time\n\t\t2) Status code\n\n*/\n\nfunc (i *ExampleSuite) Test_TwoSteps(t provider.T) {\n\tvar (\n\t\ttestBuilder = i.testMaker.NewTestBuilder()\n\n\t\t// Request body\n\t\tr = `\n{\n    \"result\": {\n        \"author\": \"Yours Truly\",\n        \"date\": \"15.11.1993\",\n        \"slides\": [\n            {\n                \"title\": \"Beer\",\n                \"type\": \"drink\"\n            },\n            {\n                \"title\": \"Apple\",\n                \"type\": \"fruit\"\n            },\n            {\n                \"title\": \"Orange\",\n                \"type\": \"fruit\"\n            }\n        ],\n        \"Info\": {\n            \"shop\": \"BigShopPlus\",\n            \"address\": \"address\"\n        },\n        \"title\": \"Sample Show\"\n    }\n}\n\t`\n\t)\n\n\tu, _ := url.Parse(i.host.String())\n\tu.Path = path.Join(u.Path, \"/posts/1/comments\")\n\n\treq, _ := http.NewRequest(http.MethodPost, u.String(), ioutil.NopCloser(strings.NewReader(r)))\n\treq.Header = map[string][]string{\n\t\t\"some_auth_token\": []string{fmt.Sprint(11111)},\n\t}\n\n\ttestBuilder.\n\t\tTitle(\"Test in suite with two steps\").\n\t\tTags(\"suite\", \"some_tag\").\n\t\tParallel().\n\t\tCreateStep(\"Creat entry /posts/1\").\n\n\t\t// CreateWithStep first step\n\n\t\tRequest(req).\n\t\tExpectExecuteTimeout(10*time.Second).\n\t\tExpectStatus(http.StatusCreated).\n\t\tAssertBody(\n\t\t\t// Custom assert body\n\t\t\texamples.CustomAssertBody(),\n\t\t).\n\t\tNextTest().\n\t\tCreateStep(\"Delete entry\").\n\n\t\t// CreateWithStep second step for delete\n\t\tRequestBuilder(\n\t\t\tcute.WithURL(u),\n\t\t\tcute.WithMethod(http.MethodDelete),\n\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\"some_auth_token\": []string{fmt.Sprint(11111)},\n\t\t\t}),\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n"
  },
  {
    "path": "examples/table_test/table_test.go",
    "content": "//go:build example\n// +build example\n\npackage table_test\n\nimport (\n\t\"context\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"strconv\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/cute\"\n\t\"github.com/ozontech/cute/asserts/json\"\n\t\"github.com/ozontech/cute/errors\"\n)\n\nfunc init() {\n\tos.Setenv(\"ALLURE_OUTPUT_PATH\", \"../\") // custom, read Readme.md for more info\n}\n\nfunc Test_Table(t *testing.T) {\n\tu, _ := url.Parse(\"https://jsonplaceholder.typicode.com/posts/1/comments\")\n\n\treq, _ := http.NewRequest(http.MethodPost, u.String(), nil)\n\treq.Header = map[string][]string{\n\t\t\"some_auth_token\": []string{fmt.Sprint(11111)},\n\t}\n\tcute.NewTestBuilder().\n\t\tTitle(\"Example put tests in table test\").\n\t\tTag(\"table_test\").\n\t\tCreateTableTest().\n\t\tPutNewTest(\n\t\t\t\"Execute validation 1\",\n\t\t\treq,\n\t\t\t&cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t}).\n\t\tPutNewTest(\n\t\t\t\"Execute validation 2\",\n\t\t\treq,\n\t\t\t&cute.Expect{\n\t\t\t\tAssertBody: []cute.AssertBody{\n\t\t\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\t\t\tjson.Present(\"$[1].name\"),\n\t\t\t\t},\n\t\t\t},\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n\nfunc Test_Table_Array(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName:       \"Create something\",\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\t\tcute.WithMethod(http.MethodPost),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 200,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"Delete something\",\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 200,\n\t\t\t\tAssertBody: []cute.AssertBody{\n\t\t\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\t\t\tjson.Present(\"$[1].name\"),\n\t\t\t\t\tfunc(body []byte) error {\n\t\t\t\t\t\treturn errors.NewAssertError(\"example error\", \"example message\", nil, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tcute.NewTestBuilder().\n\t\tTitle(\"Example table test\").\n\t\tTag(\"table_test\").\n\t\tDescription(\"Execute array tests\").\n\t\tCreateTableTest().\n\t\tPutTests(tests...).\n\t\tExecuteTest(context.Background(), t)\n}\n\nfunc Test_One_Execute(t *testing.T) {\n\ttest := &cute.Test{\n\t\tName: \"test_1\",\n\t\tRequest: &cute.Request{\n\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t},\n\t\t},\n\t\tExpect: nil,\n\t}\n\n\ttest.Execute(context.Background(), t)\n}\n\nfunc Test_Array_Retry_OptionalFirstTries(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName: \"test_1\",\n\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 10,\n\t\t\t\tDelay:       1 * time.Second,\n\t\t\t},\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/Random/201,202\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"test_2\",\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 10,\n\t\t\t\tDelay:       1 * time.Second,\n\t\t\t},\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/Random/403,404\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t\tcute.WithMarshalBody([]byte(\"{\\\"test\\\":\\\"abc\\\"}\")),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 404,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array_Retry_OptionalFirstTries_UltimatelyFailing(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName: \"test_1\",\n\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 4,\n\t\t\t\tDelay:       1 * time.Second,\n\t\t\t},\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/Random/202,200\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName: \"test_2\",\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 3,\n\t\t\t\tDelay:       1 * time.Second,\n\t\t\t},\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/Random/403,401\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 404,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array_TimeoutRetry(t *testing.T) {\n\tvar executeTimeout = 3000\n\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 2,\n\t\t\t},\n\t\t\tName: \"test_timeout\",\n\t\t\tMiddleware: &cute.Middleware{\n\t\t\t\tBefore: []cute.BeforeExecute{\n\t\t\t\t\tcute.BeforeExecute(func(request *http.Request) error {\n\t\t\t\t\t\tquery := request.URL.Query()\n\t\t\t\t\t\tquery.Set(\"sleep\", strconv.Itoa(executeTimeout))\n\t\t\t\t\t\trequest.URL.RawQuery = query.Encode()\n\t\t\t\t\t\texecuteTimeout = executeTimeout - 1000\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t}),\n\t\t\t\t},\n\t\t\t},\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/202?sleep=3000\"),\n\t\t\t\t\tcute.WithBody([]byte(\"{\\\"test\\\":\\\"abc\\\"}\")),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode:        202,\n\t\t\t\tExecuteTime: 3 * time.Second,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName:       \"test_1\",\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\t\tcute.WithMethod(http.MethodPost),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_2\",\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 200,\n\t\t\t\tAssertBody: []cute.AssertBody{\n\t\t\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\t\t\tjson.Present(\"$[1].name\"),\n\t\t\t\t\tfunc(body []byte) error {\n\t\t\t\t\t\treturn errors.NewAssertError(\"example error\", \"example message\", nil, nil)\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array_All_Parallel(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName:       \"test_201\",\n\t\t\tParallel:   true,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/201\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_200_delay_5s\",\n\t\t\tParallel:   true,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/200?sleep=5000\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 200,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_202_delay_3s\",\n\t\t\tParallel:   true,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/202?sleep=3000\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 202,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_203\",\n\t\t\tParallel:   true,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/203\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 203,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array_Some_Parallel(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName:       \"test_parallel_1\",\n\t\t\tParallel:   true,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/201?sleep=1000\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_parallel_2\",\n\t\t\tParallel:   true,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/202?sleep=1000\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 202,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_1_sequential\",\n\t\t\tParallel:   false,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\t\tcute.WithMethod(http.MethodPost),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:       \"test_2_sequential\",\n\t\t\tParallel:   false,\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 200,\n\t\t\t\tAssertBody: []cute.AssertBody{\n\t\t\t\t\tjson.Equal(\"$[0].email\", \"Eliseo@gardner.biz\"),\n\t\t\t\t\tjson.Present(\"$[1].name\"),\n\t\t\t\t},\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array_Retry(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName:     \"test_1\",\n\t\t\tParallel: true,\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 10,\n\t\t\t\tDelay:       1,\n\t\t\t},\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/Random/201,202\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 201,\n\t\t\t},\n\t\t},\n\t\t{\n\t\t\tName:     \"test_2\",\n\t\t\tParallel: true,\n\t\t\tRetry: &cute.Retry{\n\t\t\t\tMaxAttempts: 10,\n\t\t\t\tDelay:       1,\n\t\t\t},\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/Random/403,404\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode: 404,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n\nfunc Test_Array_Timeout(t *testing.T) {\n\ttests := []*cute.Test{\n\t\t{\n\t\t\tName:       \"test_timeout\",\n\t\t\tMiddleware: nil,\n\t\t\tRequest: &cute.Request{\n\t\t\t\tBuilders: []cute.RequestBuilder{\n\t\t\t\t\tcute.WithURI(\"https://httpstat.us/202?sleep=3000\"),\n\t\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t\t},\n\t\t\t},\n\t\t\tExpect: &cute.Expect{\n\t\t\t\tCode:        202,\n\t\t\t\tExecuteTime: 2 * time.Second,\n\t\t\t},\n\t\t},\n\t}\n\n\tfor _, test := range tests {\n\t\ttest.Execute(context.Background(), t)\n\t}\n}\n"
  },
  {
    "path": "examples/two_step_test.go",
    "content": "//go:build example\n// +build example\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"net/http\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/allure-go/pkg/framework/runner\"\n\t\"github.com/ozontech/cute\"\n)\n\nfunc Test_TwoSteps_1(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Test with two requests.\").\n\t\tTags(\"two_steps\").\n\t\tParallel().\n\t\tCreateStep(\"Create entry /posts/1\").\n\n\t\t// CreateWithStep first step\n\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t).\n\t\tExpectExecuteTimeout(10*time.Second).\n\t\tExpectStatus(http.StatusCreated).\n\t\tNextTest().\n\t\tCreateStep(\"Delete entry\").\n\n\t\t// CreateWithStep second step for delete\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\tcute.WithMethod(http.MethodDelete),\n\t\t\tcute.WithHeaders(map[string][]string{\n\t\t\t\t\"some_auth_token\": []string{fmt.Sprint(11111)},\n\t\t\t}),\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n}\n\nfunc Test_TwoSteps_2_AllureRunner(t *testing.T) {\n\trunner.Run(t, \"Test with two steps\", func(t provider.T) {\n\t\ttestBuilder := cute.NewHTTPTestMaker().NewTestBuilder()\n\n\t\ttestBuilder.\n\t\t\tTitle(\"Test with two requests executed by allure-go\").\n\t\t\tTag(\"two_steps\").\n\t\t\tDescription(\"some_description\").\n\t\t\tCreateStep(\"Request 1\").\n\t\t\tRequestBuilder(\n\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t).\n\t\t\tExpectStatus(http.StatusOK).\n\t\t\tExecuteTest(context.Background(), t)\n\n\t\ttestBuilder.\n\t\t\tCreateStep(\"Request 2\").\n\t\t\tRequestBuilder(\n\t\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/2/comments\"),\n\t\t\t\tcute.WithMethod(http.MethodGet),\n\t\t\t).\n\t\t\tExpectExecuteTimeout(10*time.Second).\n\t\t\tExpectStatus(http.StatusOK).\n\t\t\tExecuteTest(context.Background(), t)\n\t})\n}\n\nfunc Test_TwoSteps_3(t *testing.T) {\n\tresponseCode := 0\n\n\t// First step.\n\tcute.NewTestBuilder().\n\t\tTitle(\"Test with two requests and parse body.\").\n\t\tTag(\"two_steps\").\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/1/comments\"),\n\t\t\tcute.WithMethod(http.MethodGet),\n\t\t).\n\t\tExpectStatus(http.StatusOK).\n\t\tRequireBody(func(body []byte) error {\n\t\t\treturn errors.New(\"example\")\n\t\t}).\n\t\tNextTest().\n\t\tAfterTestExecute(func(response *http.Response, errors []error) error { // Execute after first step\n\t\t\tresponseCode = response.StatusCode\n\n\t\t\tfmt.Println(\"Hello from after test execute\")\n\t\t\tfmt.Println(\"Response code\", responseCode)\n\n\t\t\treturn nil\n\t\t}).\n\t\t// Second step. This test isn't run, because previous test has failed require validation\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"https://jsonplaceholder.typicode.com/posts/2/comments\"),\n\t\t\tcute.WithMethod(http.MethodDelete),\n\t\t).\n\t\tExecuteTest(context.Background(), t)\n\n\tfmt.Println(\"Response code from first request\", responseCode)\n}\n"
  },
  {
    "path": "examples/upload_file_test.go",
    "content": "//go:build example_upload_file\n// +build example_upload_file\n\npackage examples\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/ozontech/cute\"\n)\n\nfunc TestUploadFile(t *testing.T) {\n\tcute.NewTestBuilder().\n\t\tTitle(\"Upload file\").\n\t\tCreate().\n\t\tRequestBuilder(\n\t\t\tcute.WithURI(\"http://localhost:7000/v1/banner\"),\n\t\t\tcute.WithMethod(\"POST\"),\n\t\t\tcute.WithFormKV(\"body\", []byte(\"{\\\"name\\\": \\\"Vasya\\\"}\")), // Fill the form with the body\n\t\t\tcute.WithFileFormKV(\"image\", &cute.File{ // Fill the form with the file\n\t\t\t\tPath: \"/vasya/thebestmypicture.png\",\n\t\t\t}),\n\t\t).\n\t\tExpectStatus(http.StatusOK).\n\t\tExecuteTest(context.Background(), t)\n}\n"
  },
  {
    "path": "go.mod",
    "content": "module github.com/ozontech/cute\n\ngo 1.21\n\nrequire (\n\tgithub.com/josephburnett/jd v1.7.1\n\tgithub.com/ohler55/ojg v1.21.1\n\tgithub.com/ozontech/allure-go/pkg/allure v0.6.13\n\tgithub.com/ozontech/allure-go/pkg/framework v0.6.31\n\tgithub.com/stretchr/testify v1.8.4\n\tgithub.com/xeipuuv/gojsonschema v1.2.0\n\tmoul.io/http2curl/v2 v2.3.0\n)\n\nrequire (\n\tgithub.com/davecgh/go-spew v1.1.1 // indirect\n\tgithub.com/go-openapi/jsonpointer v0.19.5 // indirect\n\tgithub.com/go-openapi/swag v0.21.1 // indirect\n\tgithub.com/google/uuid v1.3.0 // indirect\n\tgithub.com/josharian/intern v1.0.0 // indirect\n\tgithub.com/mailru/easyjson v0.7.7 // indirect\n\tgithub.com/pkg/errors v0.9.1 // indirect\n\tgithub.com/pmezard/go-difflib v1.0.0 // indirect\n\tgithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f // indirect\n\tgithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect\n\tgopkg.in/yaml.v2 v2.4.0 // indirect\n\tgopkg.in/yaml.v3 v3.0.1 // indirect\n)\n"
  },
  {
    "path": "go.sum",
    "content": "github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=\ngithub.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=\ngithub.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=\ngithub.com/go-openapi/jsonpointer v0.19.5 h1:gZr+CIYByUqjcgeLXnQu2gHYQC9o73G2XUeOFYEICuY=\ngithub.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=\ngithub.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk=\ngithub.com/go-openapi/swag v0.21.1 h1:wm0rhTb5z7qpJRHBdPOMuY4QjVUMbF6/kwoYeRAOrKU=\ngithub.com/go-openapi/swag v0.21.1/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ=\ngithub.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=\ngithub.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=\ngithub.com/josephburnett/jd v1.7.1 h1:oXBPMS+SNnILTMGj1fWLK9pexpeJUXtbVFfRku/PjBU=\ngithub.com/josephburnett/jd v1.7.1/go.mod h1:R8ZnZnLt2D4rhW4NvBc/USTo6mzyNT6fYNIIWOJA9GY=\ngithub.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=\ngithub.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=\ngithub.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=\ngithub.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=\ngithub.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=\ngithub.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=\ngithub.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=\ngithub.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc=\ngithub.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=\ngithub.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=\ngithub.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=\ngithub.com/ohler55/ojg v1.21.1 h1:b2RLUaDcy9gvn46dmhTjezu/TDauoR0/kgKTqkwIxto=\ngithub.com/ohler55/ojg v1.21.1/go.mod h1:gQhDVpQLqrmnd2eqGAvJtn+NfKoYJbe/A4Sj3/Vro4o=\ngithub.com/ozontech/allure-go/pkg/allure v0.6.13 h1:vkLSIvOEERHTxe+oq8DXDu/m+kLnVUkrXNN8xTKuKU4=\ngithub.com/ozontech/allure-go/pkg/allure v0.6.13/go.mod h1:4oEG2yq+DGOzJS/ZjPc87C/mx3tAnlYpYonk77Ru/vQ=\ngithub.com/ozontech/allure-go/pkg/framework v0.6.31 h1:u32AqB9/JkzcL5vSl8PSUmMZbsVTmoriDylI3FIYgX4=\ngithub.com/ozontech/allure-go/pkg/framework v0.6.31/go.mod h1:wfqY4e4+w4BoRFDxHp7TNcdWfcCOWJV3BjrUqUughWY=\ngithub.com/pkg/diff v0.0.0-20200914180035-5b29258ca4f7/go.mod h1:zO8QMzTeZd5cpnIkz/Gn6iK0jDfGicM1nynOkkPIl28=\ngithub.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=\ngithub.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=\ngithub.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=\ngithub.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=\ngithub.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=\ngithub.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=\ngithub.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=\ngithub.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=\ngithub.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=\ngithub.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk=\ngithub.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=\ngithub.com/tailscale/depaware v0.0.0-20210622194025-720c4b409502/go.mod h1:p9lPsd+cx33L3H9nNoecRRxPssFKUwwI50I3pZ0yT+8=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c=\ngithub.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0=\ngithub.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ=\ngithub.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74=\ngithub.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y=\ngithub.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74=\ngolang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=\ngolang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=\ngolang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=\ngolang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=\ngolang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=\ngolang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=\ngolang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=\ngolang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=\ngolang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=\ngolang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=\ngolang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=\ngolang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=\ngolang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=\ngolang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=\ngolang.org/x/tools v0.0.0-20201211185031-d93e913c1a58/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=\ngolang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngolang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=\ngopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f h1:BLraFXnmrev5lT+xlilqcH8XK9/i0At2xKjWk4p6zsU=\ngopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=\ngopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=\ngopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=\ngopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=\ngopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.0-20200615113413-eeeca48fe776/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\ngopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=\ngopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=\nmoul.io/http2curl/v2 v2.3.0 h1:9r3JfDzWPcbIklMOs2TnIFzDYvfAZvjeavG6EzP7jYs=\nmoul.io/http2curl/v2 v2.3.0/go.mod h1:RW4hyBjTWSYDOxapodpNEtX0g5Eb16sxklBqmd2RHcE=\n"
  },
  {
    "path": "init.go",
    "content": "package cute\n\nvar commonBuilder *HTTPTestMaker\n\nfunc init() {\n\tcommonBuilder = NewHTTPTestMaker()\n}\n\n// NewTestBuilder is function for create base test builder,\n// For create custom test builder use NewHTTPTestMaker()\nfunc NewTestBuilder() AllureBuilder {\n\treturn commonBuilder.NewTestBuilder()\n}\n"
  },
  {
    "path": "interface.go",
    "content": "package cute\n\nimport (\n\t\"context\"\n\t\"net/http\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n)\n\n// AllureBuilder is a scope of methods for create allure information (Title, Tags, etc.)\ntype AllureBuilder interface {\n\tAllureInfoBuilder\n\tAllureLabelsBuilder\n\tAllureLinksBuilder\n\n\tCreateBuilder\n\n\t// Parallel signals that this Test is to be run in parallel with (and only with) other parallel tests.\n\t// This function is not thread save. If you use multiply parallel with one T Test will panic.\n\tParallel() AllureBuilder\n}\n\n// AllureInfoBuilder is a scope of methods for create allure information (Title, Tags, etc.)\ntype AllureInfoBuilder interface {\n\t// Title is a function for set title in allure information\n\tTitle(title string) AllureBuilder\n\tTitlef(format string, args ...interface{}) AllureBuilder\n\t// Description is a function for set description in allure information\n\tDescription(description string) AllureBuilder\n\tDescriptionf(format string, args ...interface{}) AllureBuilder\n\tStage(stage string) AllureBuilder\n\tStagef(format string, args ...interface{}) AllureBuilder\n}\n\n// AllureLinksBuilder is a scope of methods to set allure links\ntype AllureLinksBuilder interface {\n\tSetIssue(issue string) AllureBuilder\n\tSetTestCase(testCase string) AllureBuilder\n\tLink(link *allure.Link) AllureBuilder\n\tTmsLink(tmsLink string) AllureBuilder\n\tTmsLinks(tmsLinks ...string) AllureBuilder\n}\n\n// AllureLabelsBuilder is a scope of methods to set allure labels\ntype AllureLabelsBuilder interface {\n\tFeature(feature string) AllureBuilder\n\tEpic(epic string) AllureBuilder\n\tAllureID(value string) AllureBuilder\n\tTags(tags ...string) AllureBuilder\n\tID(value string) AllureBuilder\n\tAddSuiteLabel(value string) AllureBuilder\n\tAddSubSuite(value string) AllureBuilder\n\tAddParentSuite(value string) AllureBuilder\n\tStory(value string) AllureBuilder\n\tTag(value string) AllureBuilder\n\tSeverity(value allure.SeverityType) AllureBuilder\n\tOwner(value string) AllureBuilder\n\tLead(value string) AllureBuilder\n\tLabel(label *allure.Label) AllureBuilder\n\tLabels(labels ...*allure.Label) AllureBuilder\n\tLayer(value string) AllureBuilder\n\tStagef(format string, args ...interface{}) AllureBuilder\n\tStage(stage string) AllureBuilder\n}\n\n// CreateBuilder is functions for create Test or table tests\ntype CreateBuilder interface {\n\t// Create is a function for save main information about allure and start write tests\n\tCreate() MiddlewareRequest\n\n\t// CreateStep is a function for create step inside suite for Test\n\tCreateStep(string) MiddlewareRequest\n\n\t// CreateTableTest is function for create table Test\n\tCreateTableTest() MiddlewareTable\n}\n\n// MiddlewareTable is functions for create table Test\ntype MiddlewareTable interface {\n\tTableTest\n\n\tBeforeTest\n\tAfterTest\n}\n\n// MiddlewareRequest is function for create requests or add After/Before functions\ntype MiddlewareRequest interface {\n\tRequestHTTPBuilder\n\tRetryPolitic\n\n\tBeforeTest\n\tAfterTest\n}\n\n// RetryPolitic is a scope of methods to configure test repeat\ntype RetryPolitic interface {\n\t// Retry is a function for configure test repeat\n\t// if response.Code != Expect.Code or any of asserts are failed/broken than test will repeat counts with delay.\n\t// Default delay is 1 second.\n\tRetry(count int) MiddlewareRequest\n\n\t// RetryDelay set delay for test repeat.\n\t// if response.Code != Expect.Code or any of asserts are failed/broken than test will repeat counts with delay.\n\t// Default delay is 1 second.\n\tRetryDelay(timeout time.Duration) MiddlewareRequest\n}\n\n// BeforeTest are functions for processing request before test execution\n// Same functions:\n// Before\ntype BeforeTest interface {\n\t// BeforeExecute is function for processing request before test execution\n\tBeforeExecute(...BeforeExecute) MiddlewareRequest\n\t// BeforeExecuteT is function for processing request before test execution\n\tBeforeExecuteT(...BeforeExecuteT) MiddlewareRequest\n}\n\n// After are functions for processing response after test execution\n// Same functions:\n// AfterText\n// AfterTestExecute\ntype After interface {\n\t// After is function for processing response after test execution\n\tAfter(...AfterExecute) ExpectHTTPBuilder\n\t// AfterT is function for processing response after test execution\n\tAfterT(...AfterExecuteT) ExpectHTTPBuilder\n}\n\n// AfterTest are functions for processing response after test execution\n// Same functions:\n// After\n// AfterTestExecute\ntype AfterTest interface {\n\t// AfterExecute is function for processing response after test execution\n\tAfterExecute(...AfterExecute) MiddlewareRequest\n\t// AfterExecuteT is function for processing response after test execution\n\tAfterExecuteT(...AfterExecuteT) MiddlewareRequest\n}\n\n// AfterTestExecute are functions for processing response after test execution\n// Same functions:\n// After\n// AfterText\ntype AfterTestExecute interface {\n\t// AfterTestExecute is function for processing response after test execution\n\tAfterTestExecute(...AfterExecute) NextTestBuilder\n\t// AfterTestExecuteT is function for processing response after test execution\n\tAfterTestExecuteT(...AfterExecuteT) NextTestBuilder\n}\n\n// TableTest is function for put request and assert for table tests\ntype TableTest interface {\n\t// PutNewTest is function for put request and assert for table Test\n\tPutNewTest(name string, r *http.Request, expect *Expect) TableTest\n\t// PutTests is function for put requests and asserts for table Test\n\tPutTests(params ...*Test) TableTest\n\tControlTest\n}\n\n// RequestHTTPBuilder is a scope of methods to create HTTP requests\ntype RequestHTTPBuilder interface {\n\t// Request is function for set http.Request\n\tRequest(r *http.Request) ExpectHTTPBuilder\n\t// RequestBuilder is function for set http.Request with builders\n\t// Available builders:\n\t// WithMethod\n\t// WithURL\n\t// WithHeaders\n\t// WithHeadersKV\n\t// WithBody\n\t// WithMarshalBody\n\t// WithBody\n\t// WithURI\n\t// WithQuery\n\t// WithQueryKV\n\t// WithFileForm\n\t// WithFileFormKV\n\t// WithForm\n\t// WithFormKV\n\tRequestBuilder(r ...RequestBuilder) ExpectHTTPBuilder\n\n\tRequestParams\n}\n\n// RequestParams is a scope of methods to configure request\ntype RequestParams interface {\n\t// RequestRepeat is a function for set options in request\n\t// if response.Code != Expect.Code, than request will repeat counts with delay.\n\t// Default delay is 1 second.\n\t// Deprecated: use RequestRetry instead\n\tRequestRepeat(count int) RequestHTTPBuilder\n\tRequestRetry(count int) RequestHTTPBuilder\n\n\t// RequestRepeatDelay set delay for request repeat.\n\t// if response.Code != Expect.Code, than request will repeat counts with delay.\n\t// Default delay is 1 second.\n\t// Deprecated: use RequestRetryDelay instead\n\tRequestRepeatDelay(delay time.Duration) RequestHTTPBuilder\n\tRequestRetryDelay(delay time.Duration) RequestHTTPBuilder\n\n\t// RequestRepeatPolitic is a politic for repeat request.\n\t// if response.Code != Expect.Code, than request will repeat counts with delay.\n\t// if Optional is true and request is failed, than test step allure will be skipped, and t.Fail() will not execute.\n\t// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will execute.\n\t// Deprecated: use RequestRetryPolitic instead\n\tRequestRepeatPolitic(politic *RequestRepeatPolitic) RequestHTTPBuilder\n\tRequestRetryPolitic(politic *RequestRetryPolitic) RequestHTTPBuilder\n\n\t// RequestRepeatOptional is a option politic for repeat request.\n\t// if Optional is true and request is failed, than test step allure will be skipped, and t.Fail() will not execute.\n\t// Deprecated: use RequestRetryOptional instead\n\tRequestRepeatOptional(optional bool) RequestHTTPBuilder\n\tRequestRetryOptional(optional bool) RequestHTTPBuilder\n\n\t// RequestRepeatBroken is a broken politic for repeat request.\n\t// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will execute.\n\t// Deprecated: use RequestRetryBroken instead\n\tRequestRepeatBroken(broken bool) RequestHTTPBuilder\n\tRequestRetryBroken(broken bool) RequestHTTPBuilder\n\n\t// RequestSanitizerHook sets a RequestSanitizerHook function for the request.\n\t// This hook allows you to modify or mask parts of the request URL (e.g., hide sensitive data)\n\t// before it is logged or added to the test report (Allure).\n\t// Example usage: RequestWithSanitizeHook(func(req *http.Request) { ... }).\n\t// Example: RequestWithSanitizeHook(func(req *http.Request) { req.URL.Path = \"/masked\" }).\n\t// Example: RequestWithSanitizeHook(func(req *http.Request) { req.Header[\"some_header\"] = []string{\"masked\"} }).\n\tRequestSanitizerHook(hook RequestSanitizerHook) RequestHTTPBuilder\n\n\t// ResponseSanitizerHook sets a ResponseSanitizerHook function for the request.\n\t// This hook allows you to modify or mask parts of the response body (e.g., hide sensitive data)\n\t// before it is logged or added to the test report (Allure).\n\t// Example usage: ResponseWithSanitizeHook(func(resp *http.Response) { ... }).\n\tResponseSanitizerHook(hook ResponseSanitizerHook) RequestHTTPBuilder\n}\n\n// ExpectHTTPBuilder is a scope of methods for validate http response\ntype ExpectHTTPBuilder interface {\n\t// ExpectExecuteTimeout is function for validate time of execution\n\t// Default value - 10 seconds\n\tExpectExecuteTimeout(t time.Duration) ExpectHTTPBuilder\n\n\t// ExpectStatus is function for validate response status code\n\tExpectStatus(code int) ExpectHTTPBuilder\n\n\t// ExpectJSONSchemaString is function for validate response by json schema from string\n\tExpectJSONSchemaString(schema string) ExpectHTTPBuilder\n\t// ExpectJSONSchemaByte is function for validate response by json schema from byte\n\tExpectJSONSchemaByte(schema []byte) ExpectHTTPBuilder\n\t// ExpectJSONSchemaFile is function for validate response by json schema from file\n\t// For get file from network use:\n\t// \"http://www.some_host.com/schema.json\"\n\t// For get local file use:\n\t// \"file://./project/me/schema.json\"\n\tExpectJSONSchemaFile(path string) ExpectHTTPBuilder\n\n\t// AssertBody is function for validate response body.\n\t// Available asserts from asserts/json/json.go:\n\t// Contains is a function to assert that a jsonpath expression extracts a value in an array\n\t// Equal is a function to assert that a jsonpath expression matches the given value\n\t// NotEqual is a function to check jsonpath expression value is not equal to given value\n\t// Length is a function to asserts that jsonpath expression value is the expected length\n\t// GreaterThan is a function to asserts that jsonpath expression value is greater than the given length\n\t// LessThan is a function to asserts that jsonpath expression value is less than the given length\n\t// Present is a function to asserts that jsonpath expression value is present\n\t// NotPresent is a function to asserts that jsonpath expression value is not present\n\t// Also you can write you assert.\n\tAssertBody(asserts ...AssertBody) ExpectHTTPBuilder\n\t// RequireBody implements the same assertions as the `AssertBody`, but stops test execution when a test fails.\n\tRequireBody(asserts ...AssertBody) ExpectHTTPBuilder\n\t// OptionalAssertBody is not a mandatory assert.\n\t// Mark in allure as Skipped\n\tOptionalAssertBody(asserts ...AssertBody) ExpectHTTPBuilder\n\t// BrokenAssertBody  is function for validate response, if it's failed, then test will be Broken.\n\t// Mark in allure as Broken\n\tBrokenAssertBody(asserts ...AssertBody) ExpectHTTPBuilder\n\t// AssertBodyT is function for validate response body with help testing.TB and allure allureProvider.\n\t// You may create allure step inside assert, add attachment, log information, etc.\n\tAssertBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder\n\t// RequireBodyT implements the same assertions as the `AssertBodyT`, but stops test execution when a test fails.\n\tRequireBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder\n\t// OptionalAssertBodyT is not a mandatory assert.\n\t// Mark in allure as Skipped\n\tOptionalAssertBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder\n\t// BrokenAssertBodyT  is function for validate response, if it's failed, then test will be Broken.\n\t// Mark in allure as Broken\n\tBrokenAssertBodyT(asserts ...AssertBodyT) ExpectHTTPBuilder\n\n\t// AssertHeaders is function for validate response headers\n\t// Available asserts from asserts/headers/headers.go:\n\t// Present is a function to asserts header is present\n\t// NotPresent is a function to asserts header is present\n\t// Also you can write you assert.\n\tAssertHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder\n\t// RequireHeaders implements the same assertions as the `AssertHeaders`, but stops test execution when a test fails.\n\tRequireHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder\n\t// OptionalAssertHeaders is not a mandatory assert.\n\t// Mark in allure as Skipped\n\tOptionalAssertHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder\n\t// BrokenAssertHeaders  is function for validate response, if it's failed, then test will be Broken.\n\t// Mark in allure as Broken\n\tBrokenAssertHeaders(asserts ...AssertHeaders) ExpectHTTPBuilder\n\t// AssertHeadersT is function for validate headers body with help testing.TB and allure allureProvider.\n\t// You may create allure step inside assert, add attachment, log information, etc.\n\tAssertHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder\n\t// RequireHeadersT implements the same assertions as the `AssertHeadersT`, but stops test execution when a test fails.\n\tRequireHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder\n\t// OptionalAssertHeadersT is not a mandatory assert.\n\t// Mark in allure as Skipped\n\tOptionalAssertHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder\n\t// BrokenAssertHeadersT is function for validate response, if it's failed, then test will be Broken.\n\t// Mark in allure as Broken\n\tBrokenAssertHeadersT(asserts ...AssertHeadersT) ExpectHTTPBuilder\n\n\t// AssertResponse is function for validate response.\n\tAssertResponse(asserts ...AssertResponse) ExpectHTTPBuilder\n\t// RequireResponse implements the same assertions as the `AssertResponse`, but stops test execution when a test fails.\n\tRequireResponse(asserts ...AssertResponse) ExpectHTTPBuilder\n\t// OptionalAssertResponse is not a mandatory assert.\n\t// Mark in allure as Skipped\n\tOptionalAssertResponse(asserts ...AssertResponse) ExpectHTTPBuilder\n\t// BrokenAssertResponse  is function for validate response, if it's failed, then test will be Broken.\n\t// Mark in allure as Broken\n\tBrokenAssertResponse(asserts ...AssertResponse) ExpectHTTPBuilder\n\t// AssertResponseT is function for validate response with help testing.TB.\n\t// You may create allure step inside assert, add attachment, log information, etc.\n\tAssertResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder\n\t// RequireResponseT implements the same assertions as the `AssertResponseT`, but stops test execution when a test fails.\n\tRequireResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder\n\t// OptionalAssertResponseT is not a mandatory assert.\n\t// Mark in allure as Skipped\n\tOptionalAssertResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder\n\t// BrokenAssertResponseT is function for validate response, if it's failed, then test will be Broken.\n\t// Mark in allure as Broken\n\tBrokenAssertResponseT(asserts ...AssertResponseT) ExpectHTTPBuilder\n\n\tAfter\n\tControlTest\n}\n\n// ControlTest is function for manipulating tests\ntype ControlTest interface {\n\tNextTest() NextTestBuilder\n\n\t// ExecuteTest is a function for execute Test\n\tExecuteTest(ctx context.Context, t tProvider) []ResultsHTTPBuilder\n}\n\n// NextTestBuilder is a scope of methods for processing response, after Test.\ntype NextTestBuilder interface {\n\tAfterTestExecute\n\n\tCreateBuilder\n}\n\n// ResultsHTTPBuilder is a scope of methods for processing results\ntype ResultsHTTPBuilder interface {\n\t// GetHTTPResponse is a function, which returns http response\n\tGetHTTPResponse() *http.Response\n\t// GetErrors is a function, which returns all errors from test\n\tGetErrors() []error\n\t// GetName is a function, which returns name of Test\n\tGetName() string\n\t// GetResultState is a function, which returns state of test\n\t// State could be ResultStateSuccess, ResultStateBroken, ResultStateFail\n\tGetResultState() ResultState\n}\n\n// BeforeExecute is a function for processing request before test execution\ntype BeforeExecute func(*http.Request) error\n\n// BeforeExecuteT is a function for processing request before test execution\ntype BeforeExecuteT func(T, *http.Request) error\n\n// AfterExecute is a function for processing response after test execution\ntype AfterExecute func(*http.Response, []error) error\n\n// AfterExecuteT is a function for processing response after test execution\ntype AfterExecuteT func(T, *http.Response, []error) error\n"
  },
  {
    "path": "internal/utils/body.go",
    "content": "package utils\n\nimport (\n\t\"bytes\"\n\t\"io\"\n\t\"net/http\"\n)\n\n// GetBody get body from IO\nfunc GetBody(body io.ReadCloser) ([]byte, error) {\n\tvar (\n\t\terr error\n\t\tbuf = new(bytes.Buffer)\n\t)\n\n\t_, err = io.Copy(buf, body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn buf.Bytes(), nil\n}\n\n// DrainBody ...\nfunc DrainBody(body io.ReadCloser) (r1, r2 io.ReadCloser, err error) {\n\tif body == nil || body == http.NoBody {\n\t\t// No copying needed. Preserve the magic sentinel meaning of NoBody.\n\t\treturn http.NoBody, http.NoBody, nil\n\t}\n\n\tvar buf bytes.Buffer\n\n\tif _, err = buf.ReadFrom(body); err != nil {\n\t\treturn nil, body, err\n\t}\n\n\tif err = body.Close(); err != nil {\n\t\treturn nil, body, err\n\t}\n\n\treturn io.NopCloser(&buf), io.NopCloser(bytes.NewReader(buf.Bytes())), nil\n}\n"
  },
  {
    "path": "internal/utils/json.go",
    "content": "package utils\n\nimport (\n\t\"bytes\"\n\t\"encoding/json\"\n)\n\n// ToJSON returns string Json representation of any object that can be marshaled.\nfunc ToJSON(v interface{}) (string, error) {\n\tj, err := json.Marshal(v)\n\n\treturn string(j), err\n}\n\n// PrettyJSON make indent to json byte array. Returns prettified json as []byte or error if is it impossible\nfunc PrettyJSON(b []byte) ([]byte, error) {\n\tvar out bytes.Buffer\n\n\terr := json.Indent(&out, b, \"\", \"    \")\n\n\treturn out.Bytes(), err\n}\n"
  },
  {
    "path": "json_marshaler.go",
    "content": "package cute\n\nimport \"encoding/json\"\n\n// JSONMarshaler is marshaler which use for marshal/unmarshal JSON to/from struct\ntype JSONMarshaler interface {\n\tMarshal(v any) ([]byte, error)\n\tUnmarshal(data []byte, v any) error\n}\n\ntype jsonMarshaler struct {\n}\n\nfunc (j jsonMarshaler) Marshal(v any) ([]byte, error) {\n\treturn json.Marshal(v)\n}\n\nfunc (j jsonMarshaler) Unmarshal(data []byte, v any) error {\n\treturn json.Unmarshal(data, v)\n}\n"
  },
  {
    "path": "jsonschema.go",
    "content": "package cute\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ozontech/cute/errors\"\n\t\"github.com/xeipuuv/gojsonschema\"\n)\n\n// Validate is a function to validate json by json schema.\n// Automatically add information about validation to allure.\nfunc (it *Test) validateJSONSchema(t internalT, body []byte) []error {\n\tvar (\n\t\texpect gojsonschema.JSONLoader\n\t)\n\n\tswitch {\n\tcase it.Expect.JSONSchema.String != \"\":\n\t\texpect = gojsonschema.NewStringLoader(it.Expect.JSONSchema.String)\n\tcase it.Expect.JSONSchema.Byte != nil:\n\t\texpect = gojsonschema.NewBytesLoader(it.Expect.JSONSchema.Byte)\n\tcase it.Expect.JSONSchema.File != \"\":\n\t\texpect = gojsonschema.NewReferenceLoader(it.Expect.JSONSchema.File)\n\tdefault:\n\t\treturn nil\n\t}\n\n\treturn it.executeWithStep(t, \"Validate body by JSON schema\", func(_ T) []error {\n\t\treturn checkJSONSchema(expect, body)\n\t})\n}\n\nfunc checkJSONSchema(expect gojsonschema.JSONLoader, data []byte) []error {\n\tscope := make([]error, 0)\n\n\tvalidateResult, err := gojsonschema.Validate(expect, gojsonschema.NewBytesLoader(data))\n\tif err != nil {\n\t\treturn []error{errors.NewEmptyAssertError(\"could not validate json schema\", err.Error())}\n\t}\n\n\tif !validateResult.Valid() && len(validateResult.Errors()) > 0 {\n\t\tfor _, resultError := range validateResult.Errors() {\n\t\t\tscope = append(\n\t\t\t\tscope,\n\t\t\t\tcreateJSONSchemaError(resultError),\n\t\t\t)\n\t\t}\n\t}\n\n\treturn scope\n}\n\nfunc createJSONSchemaError(err gojsonschema.ResultError) error {\n\tfields := make(map[string]interface{})\n\ttextError := \"\"\n\n\tif v, ok := err.Details()[\"context\"]; ok {\n\t\ttextError = fmt.Sprintf(\"On path: %v.\", v)\n\t\tfields[\"Path\"] = v\n\t}\n\n\tif v, ok := err.Details()[\"field\"]; ok {\n\t\ttextError = fmt.Sprintf(\"%v Error field: %v.\", textError, v)\n\t\tfields[\"Field\"] = v\n\t}\n\n\ttextError = fmt.Sprintf(\"%v Error: %v.\", textError, err.String())\n\n\tassertError := errors.NewAssertError(\n\t\tfmt.Sprintf(\"Error \\\"%v\\\"\", err.Type()),\n\t\ttextError,\n\t\terr.Details()[\"given\"],\n\t\terr.Details()[\"expected\"])\n\n\tassertError.(errors.WithFields).PutFields(fields)\n\n\treturn assertError\n}\n"
  },
  {
    "path": "jsonschema_test.go",
    "content": "package cute\n\nimport (\n\t\"testing\"\n\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestValidateJSONSchemaEmptySchema(t *testing.T) {\n\tvar (\n\t\ttBuilder = createDefaultTest(&HTTPTestMaker{middleware: new(Middleware)})\n\t)\n\n\ttBuilder.initEmptyFields()\n\n\terrs := tBuilder.validateJSONSchema(nil, []byte{})\n\trequire.Len(t, errs, 0)\n}\n\nfunc TestValidateJSONSchemaFromString(t *testing.T) {\n\tvar (\n\t\ttBuilder = createDefaultTest(&HTTPTestMaker{middleware: new(Middleware)})\n\t\ttempT    = createAllureT(t)\n\t)\n\n\ttBuilder.initEmptyFields()\n\n\tbody := []byte(`\n\t{\n\t\t\"firstName\": \"Boris\",\n\t\t\"lastName\": \"Britva\",\n\t\t\"age\": 77\n\t}\n\t`)\n\n\ttBuilder.Expect.JSONSchema.String = `\n{\n  \"$id\": \"https://example.com/person.schema.json\",\n  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n  \"title\": \"Person\",\n  \"type\": \"object\",\n  \"properties\": {\n    \"firstName\": {\n      \"type\": \"string\"\n    },\n    \"lastName\": {\n      \"type\": \"string\"\n    },\n    \"age\": {\n      \"type\": \"integer\",\n      \"minimum\": 0\n    }\n  }\n}\n\t`\n\n\terrs := tBuilder.validateJSONSchema(tempT, body)\n\trequire.Len(t, errs, 0)\n}\n\nfunc TestValidateJSONSchemaFromStringWithError(t *testing.T) {\n\tvar (\n\t\ttBuilder = createDefaultTest(&HTTPTestMaker{middleware: new(Middleware)})\n\t\ttempT    = createAllureT(t)\n\t)\n\n\ttBuilder.initEmptyFields()\n\n\tbody := []byte(`\n\t{\n\t\t\"firstName\": \"Boris\",\n\t\t\"lastName\": \"Britva\",\n\t\t\"age\": \"1\"\n\t}\n\t`)\n\n\ttBuilder.Expect.JSONSchema.String = `\n\t{\n\t  \"$id\": \"https://example.com/person.schema.json\",\n\t  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n\t  \"title\": \"Person\",\n\t  \"type\": \"object\",\n\t  \"properties\": {\n\t    \"firstName\": {\n\t      \"type\": \"string\"\n\t    },\n\t    \"lastName\": {\n\t      \"type\": \"string\"\n\t    },\n\t    \"age\": {\n\t      \"type\": \"integer\"\n\t    }\n\t  }\n\t}\n\t`\n\n\terrs := tBuilder.validateJSONSchema(tempT, body)\n\trequire.Len(t, errs, 1)\n\trequire.Error(t, errs[0])\n\n\terrWithName := errs[0].(cuteErrors.WithNameError)\n\trequire.NotEmpty(t, errWithName.GetName())\n\n\texpectedError := errs[0].(cuteErrors.WithFields)\n\trequire.Equal(t, \"integer\", expectedError.GetFields()[\"Expected\"])\n\trequire.Equal(t, \"string\", expectedError.GetFields()[\"Actual\"])\n\trequire.Equal(t, \"age\", expectedError.GetFields()[\"Field\"])\n\trequire.Equal(t, \"(root).age\", expectedError.GetFields()[\"Path\"])\n}\n\nfunc TestValidateJSONSchemaFromByteWithTwoError(t *testing.T) {\n\tvar (\n\t\ttBuilder = createDefaultTest(&HTTPTestMaker{middleware: new(Middleware)})\n\t\ttempT    = createAllureT(t)\n\t)\n\n\ttBuilder.initEmptyFields()\n\n\tbody := []byte(`\n\t{\n\t\t\"firstName\": \"Boris\",\n\t\t\"lastName\": \"Britva\",\n\t\t\"age\": \"1\"\n\t}\n\t`)\n\n\ttBuilder.Expect.JSONSchema.String = `\n\t{\n\t  \"$id\": \"https://example.com/person.schema.json\",\n\t  \"$schema\": \"https://json-schema.org/draft/2020-12/schema\",\n\t  \"title\": \"Person\",\n\t  \"type\": \"object\",\n\t  \"properties\": {\n\t    \"firstName\": {\n\t      \"type\": \"string\"\n\t    },\n\t    \"lastName\": {\n\t      \"type\": \"integer\"\n\t    },\n\t    \"age\": {\n\t      \"type\": \"integer\"\n\t    }\n\t  }\n\t}\n\t`\n\n\terrs := tBuilder.validateJSONSchema(tempT, body)\n\trequire.Len(t, errs, 2)\n\n\tfor _, err := range errs {\n\t\terrWithName := err.(cuteErrors.WithNameError)\n\t\trequire.NotEmpty(t, errWithName.GetName())\n\n\t\texpectedError := err.(cuteErrors.WithFields)\n\t\trequire.NotEmpty(t, expectedError.GetFields()[\"Actual\"])\n\t\trequire.NotEmpty(t, expectedError.GetFields()[\"Expected\"])\n\t}\n}\n"
  },
  {
    "path": "logger.go",
    "content": "package cute\n\nimport (\n\t\"fmt\"\n)\n\ntype tlogger interface {\n\tName() string\n\tLogf(format string, args ...any)\n\tErrorf(format string, args ...interface{})\n}\n\n// Info is a function to log info message\nfunc (it *Test) Info(t tlogger, format string, args ...interface{}) {\n\tit.logf(t, \"INFO\", format, args...)\n}\n\n// Error is a function to log error message\nfunc (it *Test) Error(t tlogger, format string, args ...interface{}) {\n\tit.errorf(t, \"ERROR\", format, args...)\n}\n\n// Debug is a function to log debug message\nfunc (it *Test) Debug(t tlogger, format string, args ...interface{}) {\n\tit.logf(t, \"DEBUG\", format, args...)\n}\n\nfunc (it *Test) logf(t tlogger, level, format string, args ...interface{}) {\n\tname := it.Name\n\n\tif it.Name == \"\" {\n\t\tname = t.Name()\n\t}\n\t// If we are in a retry context, add some indication in the logs about the current attempt\n\tif it.Retry.MaxAttempts != 1 {\n\t\tt.Logf(\"[%s][%s](Attempt #%d) %v\\n\", name, level, it.Retry.currentCount, fmt.Sprintf(format, args...))\n\t} else {\n\t\tt.Logf(\"[%s][%s] %v\\n\", name, level, fmt.Sprintf(format, args...))\n\t}\n}\n\nfunc (it *Test) errorf(t tlogger, level, format string, args ...interface{}) {\n\tname := it.Name\n\n\tif it.Name == \"\" {\n\t\tname = t.Name()\n\t}\n\t// If we are in a retry context, add some indication in the logs about the current attempt\n\tif it.Retry.MaxAttempts != 1 {\n\t\tt.Logf(\"[%s][%s](Attempt #%d) %v\\n\", name, level, it.Retry.currentCount, fmt.Sprintf(format, args...))\n\t} else {\n\t\tt.Logf(\"[%s][%s] %v\\n\", name, level, fmt.Sprintf(format, args...))\n\t}\n}\n"
  },
  {
    "path": "provider.go",
    "content": "package cute\n\nimport (\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n)\n\n// T is internal testing.T provider\ntype T interface {\n\ttProvider\n\tlogProvider\n\tstepProvider\n\tattachmentProvider\n\tparametersProvider\n}\n\ntype allureProvider interface {\n\tinternalT\n\tParallel()\n\tBroken()\n\tBrokenNow()\n\tRun(testName string, testBody func(provider.T), tags ...string) (res *allure.Result)\n\n\tinfoAllureProvider\n\tlabelsAllureProvider\n\tlinksAllureProvider\n}\n\ntype internalT interface {\n\tBroken()\n\tBrokenNow()\n\n\ttProvider\n\tlogProvider\n\tstepProvider\n\tattachmentProvider\n}\n\ntype tProvider interface {\n\tFail()\n\tFailNow()\n\n\tName() string\n\n\tLog(args ...interface{})\n\tLogf(format string, args ...interface{})\n\n\tError(args ...interface{})\n\tErrorf(format string, args ...interface{})\n}\n\ntype logProvider interface {\n\tLogStep(args ...interface{})\n\tLogfStep(format string, args ...interface{})\n}\n\ntype stepProvider interface {\n\tStep(step *allure.Step)\n\tWithNewStep(stepName string, step func(ctx provider.StepCtx), params ...*allure.Parameter)\n}\n\ntype attachmentProvider interface {\n\tWithAttachments(attachments ...*allure.Attachment)\n\tWithNewAttachment(name string, mimeType allure.MimeType, content []byte)\n}\n\ntype parametersProvider interface {\n\tWithParameters(parameters ...*allure.Parameter)\n\tWithNewParameters(kv ...interface{})\n}\n\ntype infoAllureProvider interface {\n\tTitle(args ...interface{})\n\tTitlef(format string, args ...interface{})\n\n\tDescription(args ...interface{})\n\tDescriptionf(format string, args ...interface{})\n\n\tStage(args ...interface{})\n\tStagef(format string, args ...interface{})\n}\n\ntype labelsAllureProvider interface {\n\tID(value string)\n\tAllureID(value string)\n\tEpic(value string)\n\tLayer(value string)\n\tAddSuiteLabel(value string)\n\tAddSubSuite(value string)\n\tAddParentSuite(value string)\n\tFeature(value string)\n\tStory(value string)\n\tTag(value string)\n\tTags(values ...string)\n\tSeverity(value allure.SeverityType)\n\tOwner(value string)\n\tLead(value string)\n\tLabel(label *allure.Label)\n\tLabels(labels ...*allure.Label)\n}\n\ntype linksAllureProvider interface {\n\tSetIssue(issue string)\n\tSetTestCase(testCase string)\n\tLink(link *allure.Link)\n\tTmsLink(tmsCase string)\n\tTmsLinks(tmsCases ...string)\n}\n"
  },
  {
    "path": "request.go",
    "content": "package cute\n\nimport (\n\t\"net/url\"\n)\n\n// RequestBuilder is a function for set options in request\ntype RequestBuilder func(o *requestOptions)\n\n// File is struct for upload file in form field\n// If you set Path, file will read from file system\n// If you set Name and Body, file will set from this fields\ntype File struct {\n\tPath string\n\tName string\n\tBody []byte\n}\n\ntype requestOptions struct {\n\tmethod string\n\turl    *url.URL\n\n\turi         string\n\theaders     map[string][]string\n\tquery       map[string][]string\n\tbody        []byte\n\tbodyMarshal interface{}\n\tfileForms   map[string]*File\n\tforms       map[string][]byte\n}\n\nfunc newRequestOptions() *requestOptions {\n\treturn &requestOptions{\n\t\theaders:   make(map[string][]string),\n\t\tquery:     make(map[string][]string),\n\t\tfileForms: make(map[string]*File),\n\t\tforms:     make(map[string][]byte),\n\t}\n}\n\n// WithMethod is a function for set method (GET, POST ...) in request\nfunc WithMethod(method string) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.method = method\n\t}\n}\n\n// WithURL is a function for set url in request\nfunc WithURL(url *url.URL) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.url = url\n\t}\n}\n\n// WithURI is a function for set url in request\nfunc WithURI(uri string) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.uri = uri\n\t}\n}\n\n// WithHeaders is a function for set or merge headers in request\nfunc WithHeaders(headers map[string][]string) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\tfor key, values := range headers {\n\t\t\to.headers[key] = append(o.headers[key], values...)\n\t\t}\n\t}\n}\n\n// WithHeadersKV is a function for set headers in request\nfunc WithHeadersKV(name string, value string) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.headers[name] = []string{value}\n\t}\n}\n\n// WithQueryKV is a function for set query in request\nfunc WithQueryKV(name string, value string) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.query[name] = []string{value}\n\t}\n}\n\n// WithQuery is a function for set or merge query parameters in request\nfunc WithQuery(queries map[string][]string) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\tfor key, values := range queries {\n\t\t\to.query[key] = values\n\t\t}\n\t}\n}\n\n// WithBody is a function for set body in request\nfunc WithBody(body []byte) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.body = body\n\t}\n}\n\n// WithMarshalBody is a function for marshal body and set body in request\nfunc WithMarshalBody(body interface{}) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.bodyMarshal = body\n\t}\n}\n\n// WithFileFormKV is a function for set file form in request\nfunc WithFileFormKV(name string, file *File) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.fileForms[name] = file\n\t}\n}\n\n// WithFileForm is a function for set file form in request\nfunc WithFileForm(fileForms map[string]*File) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\tfor name, file := range fileForms {\n\t\t\to.fileForms[name] = file\n\t\t}\n\t}\n}\n\n// WithFormKV is a function for set body in form request\nfunc WithFormKV(name string, body []byte) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\to.forms[name] = body\n\t}\n}\n\n// WithForm is a function for set body in form request\nfunc WithForm(forms map[string][]byte) func(o *requestOptions) {\n\treturn func(o *requestOptions) {\n\t\tfor name, body := range forms {\n\t\t\to.forms[name] = body\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "request_test.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n\t\"net/url\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestRequest(t *testing.T) {\n\tvar (\n\t\treq     = newRequestOptions()\n\t\theaders = map[string][]string{\n\t\t\t\"key\": []string{\n\t\t\t\t\"value\",\n\t\t\t},\n\t\t}\n\t\tquery = map[string][]string{\n\t\t\t\"query_key\": []string{\n\t\t\t\t\"query_value\",\n\t\t\t},\n\t\t}\n\t\tmethod = http.MethodGet\n\t\tmBody  = map[string]interface{}{\n\t\t\t\"key\": map[string]interface{}{\n\t\t\t\t\"some\": \"value\",\n\t\t\t},\n\t\t\t\"key_twi\": \"more\",\n\t\t}\n\t\turi  = \"https://goo.com\"\n\t\tu, _ = url.Parse(\"https://ho.com\")\n\t\tbody = []byte(\"body\")\n\t)\n\n\tWithHeaders(headers)(req)\n\tWithMarshalBody(mBody)(req)\n\tWithURI(uri)(req)\n\tWithMethod(method)(req)\n\tWithURL(u)(req)\n\tWithBody(body)(req)\n\tWithQuery(query)(req)\n\n\trequire.Equal(t, req.headers, headers)\n\trequire.Equal(t, req.uri, uri)\n\trequire.Equal(t, req.bodyMarshal, mBody)\n\trequire.Equal(t, req.method, method)\n\trequire.Equal(t, req.query, query)\n\n\trequire.Equal(t, req.body, body)\n\trequire.Equal(t, req.url, u)\n}\n"
  },
  {
    "path": "result.go",
    "content": "package cute\n\nimport (\n\t\"net/http\"\n)\n\n// ResultState is state of test\ntype ResultState int\n\n// ResultState is state of test\nconst (\n\tResultStateSuccess ResultState = iota\n\tResultStateBroken\n\tResultStateFail\n\n\t// resultStateFailNow is state for require validations (execute failNow)\n\tresultStateFailNow\n)\n\ntype testResults struct {\n\tname   string\n\tstate  ResultState\n\tresp   *http.Response\n\terrors []error\n}\n\nfunc newTestResult(name string, resp *http.Response, state ResultState, errs []error) ResultsHTTPBuilder {\n\treturn &testResults{\n\t\tname:   name,\n\t\tresp:   resp,\n\t\tstate:  state,\n\t\terrors: errs,\n\t}\n}\n\nfunc (r *testResults) GetHTTPResponse() *http.Response {\n\treturn r.resp\n}\n\nfunc (r *testResults) GetErrors() []error {\n\treturn r.errors\n}\n\nfunc (r *testResults) GetName() string {\n\treturn r.name\n}\n\nfunc (r *testResults) GetResultState() ResultState {\n\tif r.state == resultStateFailNow {\n\t\treturn ResultStateFail\n\t}\n\n\treturn r.state\n}\n"
  },
  {
    "path": "result_test.go",
    "content": "package cute\n\nimport (\n\t\"errors\"\n\t\"net/http\"\n\t\"testing\"\n\n\t\"github.com/stretchr/testify/require\"\n)\n\nfunc TestResult(t *testing.T) {\n\tvar (\n\t\tfirstErr  = errors.New(\"first_error\")\n\t\tsecondErr = errors.New(\"second_error\")\n\t\tresp      = &http.Response{\n\t\t\tStatus:     \"OK\",\n\t\t\tStatusCode: 200,\n\t\t}\n\t\tname = \"Name\"\n\n\t\ttestResults ResultsHTTPBuilder = &testResults{\n\t\t\tname:  name,\n\t\t\tstate: ResultStateBroken,\n\t\t\tresp:  resp,\n\t\t\terrors: []error{\n\t\t\t\tfirstErr,\n\t\t\t\tsecondErr,\n\t\t\t},\n\t\t}\n\t)\n\n\trequire.Equal(t, name, testResults.GetName())\n\trequire.Equal(t, resp, testResults.GetHTTPResponse())\n\trequire.Equal(t, []error{firstErr, secondErr}, testResults.GetErrors())\n}\n"
  },
  {
    "path": "roundtripper.go",
    "content": "package cute\n\nimport (\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"net/http\"\n\t\"strings\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"moul.io/http2curl/v2\"\n\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/ozontech/cute/internal/utils\"\n)\n\nfunc (it *Test) makeRequest(t internalT, req *http.Request) (*http.Response, []error) {\n\tvar (\n\t\tdelay       = defaultDelayRepeat\n\t\tcountRepeat = 1\n\n\t\tresp  *http.Response\n\t\terr   error\n\t\tscope = make([]error, 0)\n\t)\n\n\tif it.Request.Retry.Delay != 0 {\n\t\tdelay = it.Request.Retry.Delay\n\t}\n\n\tif it.Request.Retry.Count != 0 {\n\t\tcountRepeat = it.Request.Retry.Count\n\t}\n\n\tfor i := 1; i <= countRepeat; i++ {\n\t\tit.executeWithStep(t, it.createTitle(i, countRepeat, req), func(t T) []error {\n\t\t\tresp, err = it.doRequest(t, req)\n\t\t\tif err != nil {\n\t\t\t\tif it.Request.Retry.Broken {\n\t\t\t\t\terr = wrapBrokenError(err)\n\t\t\t\t}\n\n\t\t\t\tif it.Request.Retry.Optional {\n\t\t\t\t\terr = wrapOptionalError(err)\n\t\t\t\t}\n\n\t\t\t\treturn []error{err}\n\t\t\t}\n\n\t\t\treturn nil\n\t\t})\n\n\t\tif err == nil {\n\t\t\tbreak\n\t\t}\n\n\t\tscope = append(scope, err)\n\n\t\tif i != countRepeat {\n\t\t\ttime.Sleep(delay)\n\t\t}\n\t}\n\n\treturn resp, scope\n}\n\nfunc (it *Test) doRequest(t T, baseReq *http.Request) (*http.Response, error) {\n\t// copy request, because body can be read once\n\treq, err := copyRequest(baseReq.Context(), baseReq)\n\tif err != nil {\n\t\treturn nil, cuteErrors.NewCuteError(\"[Internal] Could not copy request\", err)\n\t}\n\n\tresp, httpErr := it.httpClient.Do(req)\n\n\t// if the timeout is triggered, we properly log the timeout error on allure and in traces\n\tif errors.Is(httpErr, context.DeadlineExceeded) {\n\t\t// Add information (method, host, curl) about request to Allure step\n\t\t// should be after httpClient.Do and from resp.Request, because in roundTripper request may be changed\n\t\tif addErr := it.addInformationRequest(t, req); addErr != nil {\n\t\t\t// Ignore err return, because it's connected with test logic\n\t\t\tit.Error(t, \"Could not log information about request. error %v\", addErr)\n\t\t}\n\n\t\treturn nil, cuteErrors.NewEmptyAssertError(\n\t\t\t\"Request timeout\",\n\t\t\tfmt.Sprintf(\"expected request to be completed in %v, but was not\", it.Expect.ExecuteTime))\n\t}\n\n\t// http client has case wheh it return response and error in one time\n\t// we have to check this case\n\tif resp == nil {\n\t\tif httpErr != nil {\n\t\t\treturn nil, cuteErrors.NewCuteError(\"[HTTP] Could not do request\", httpErr)\n\t\t}\n\n\t\t// if response is nil, we can't get information about request and response\n\t\treturn nil, cuteErrors.NewCuteError(\"[HTTP] Response is nil\", httpErr)\n\t}\n\n\t// BAD CODE. Need to copy body, because we can't read body again from resp.Request.Body. Problem is io.Reader\n\tresp.Request.Body, baseReq.Body, err = utils.DrainBody(baseReq.Body)\n\tif err != nil {\n\t\tit.Error(t, \"Could not drain body from baseReq.Body. error %v\", err)\n\t\t// Ignore err return, because it's connected with test logic\n\t}\n\n\t// Add information (method, host, curl) about request to Allure step\n\t// should be after httpClient.Do and from resp.Request, because in roundTripper request may be changed\n\tif addErr := it.addInformationRequest(t, resp.Request); addErr != nil {\n\t\tit.Error(t, \"Could not log information about request. error %v\", addErr)\n\t\t// Ignore err return, because it's connected with test logic\n\t}\n\n\tif httpErr != nil {\n\t\treturn nil, cuteErrors.NewCuteError(\"[HTTP] Could not do request\", httpErr)\n\t}\n\n\t// Add information (code, body, headers) about response to Allure step\n\tif addErr := it.addInformationResponse(t, resp); addErr != nil {\n\t\t// Ignore err return, because it's connected with test logic\n\t\tit.Error(t, \"Could not log information about response. error %v\", addErr)\n\t}\n\n\tif validErr := it.validateResponseCode(resp); validErr != nil {\n\t\treturn resp, validErr\n\t}\n\n\treturn resp, nil\n}\n\nfunc (it *Test) validateResponseCode(resp *http.Response) error {\n\tif it.Expect.Code != 0 && it.Expect.Code != resp.StatusCode {\n\t\treturn cuteErrors.NewAssertError(\n\t\t\t\"Assert response code\",\n\t\t\tfmt.Sprintf(\"Response code expect %v, but was %v\", it.Expect.Code, resp.StatusCode),\n\t\t\tresp.StatusCode,\n\t\t\tit.Expect.Code)\n\t}\n\n\treturn nil\n}\n\nfunc (it *Test) addInformationRequest(t T, req *http.Request) error {\n\tvar (\n\t\tsaveBody io.ReadCloser\n\t\terr      error\n\t)\n\n\tif it.RequestSanitizer != nil {\n\t\tit.RequestSanitizer(req)\n\t}\n\n\tit.lastRequestURL = req.URL.String()\n\n\tcurl, err := http2curl.GetCurlCommand(req)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tif c := curl.String(); len(c) <= 2048 {\n\t\tit.Info(t, \"[Request] \"+c)\n\t} else {\n\t\tit.Info(t, \"[Request] Do request\")\n\t}\n\n\t// Do not change to JSONMarshaler\n\t// In this case we can keep default for keep JSON, independence from JSONMarshaler\n\theaders, err := utils.ToJSON(req.Header)\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tt.WithParameters(\n\t\tallure.NewParameters(\n\t\t\t\"method\", req.Method,\n\t\t\t\"host\", req.Host,\n\t\t\t\"headers\", headers,\n\t\t\t\"curl\", curl.String(),\n\t\t)...,\n\t)\n\n\tif req.Body != nil {\n\t\tsaveBody, req.Body, err = utils.DrainBody(req.Body)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tbody, err := utils.GetBody(saveBody)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tif len(body) != 0 {\n\t\t\tt.WithNewParameters(\"body\", string(body))\n\t\t}\n\t}\n\n\treturn nil\n}\n\nfunc copyRequest(ctx context.Context, req *http.Request) (*http.Request, error) {\n\tvar (\n\t\terr error\n\n\t\tclone = req.Clone(ctx)\n\t)\n\n\treq.Body, clone.Body, err = utils.DrainBody(req.Body)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn clone, nil\n}\n\nfunc (it *Test) addInformationResponse(t T, response *http.Response) error {\n\tvar (\n\t\tsaveBody io.ReadCloser\n\t\terr      error\n\t)\n\n\tif it.ResponseSanitizer != nil {\n\t\tit.ResponseSanitizer(response)\n\t}\n\n\theaders, _ := utils.ToJSON(response.Header)\n\tif headers != \"\" {\n\t\tt.WithNewParameters(\"response_headers\", headers)\n\t}\n\n\tt.WithNewParameters(\"response_code\", fmt.Sprint(response.StatusCode))\n\tit.Info(t, \"[Response] Status: \"+response.Status)\n\n\tif response.Body == nil {\n\t\treturn nil\n\t}\n\n\tsaveBody, response.Body, err = utils.DrainBody(response.Body)\n\t// if could not get body from response, no add to allure\n\tif err != nil {\n\t\treturn err\n\t}\n\n\tbody, err := utils.GetBody(saveBody)\n\t// if could not get body from response, no add to allure\n\tif err != nil {\n\t\treturn err\n\t}\n\n\t// if body is empty - skip\n\tif len(body) == 0 {\n\t\treturn nil\n\t}\n\n\tresponseType := allure.Text\n\n\tif _, ok := response.Header[\"Content-Type\"]; ok {\n\t\tif len(response.Header[\"Content-Type\"]) > 0 {\n\t\t\tif strings.Contains(response.Header[\"Content-Type\"][0], \"application/json\") {\n\t\t\t\tresponseType = allure.JSON\n\t\t\t} else {\n\t\t\t\tresponseType = allure.MimeType(response.Header[\"Content-Type\"][0])\n\t\t\t}\n\t\t}\n\t}\n\n\tif responseType == allure.JSON {\n\t\tbody, _ = utils.PrettyJSON(body)\n\t}\n\n\tt.WithAttachments(allure.NewAttachment(\"response\", responseType, body))\n\n\treturn nil\n}\n\nfunc (it *Test) createTitle(try, countRepeat int, req *http.Request) string {\n\ttoProcess := req\n\n\t// We have to execute sanitizer hook because\n\t// we need to log it and it can contain sensitive data\n\tif it.RequestSanitizer != nil {\n\t\tclone, err := copyRequest(req.Context(), req)\n\n\t\t// ignore error, because we want to log request\n\t\t// and it does not matter if we can copy request\n\t\tif err == nil {\n\t\t\tit.RequestSanitizer(clone)\n\n\t\t\ttoProcess = clone\n\t\t}\n\t}\n\n\ttitle := toProcess.Method + \" \" + toProcess.URL.String()\n\n\tif countRepeat == 1 {\n\t\treturn title\n\t}\n\n\treturn fmt.Sprintf(\"[%v/%v] %v\", try, countRepeat, title)\n}\n"
  },
  {
    "path": "step.go",
    "content": "package cute\n\nimport (\n\t\"fmt\"\n\n\t\"github.com/ozontech/allure-go/pkg/allure\"\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\t\"github.com/ozontech/cute/errors\"\n)\n\nfunc (it *Test) executeWithStep(t internalT, stepName string, execute func(t T) []error) []error {\n\tvar (\n\t\terrs []error\n\t)\n\n\t// Add attempt indication in Allure if more than 1 attempt\n\tif it.Retry.MaxAttempts != 1 {\n\t\tstepName = fmt.Sprintf(\"[Attempt #%d] %v\", it.Retry.currentCount, stepName)\n\t}\n\tt.WithNewStep(stepName, func(stepCtx provider.StepCtx) {\n\t\terrs = execute(stepCtx)\n\t\tprocessStepErrors(stepCtx, errs)\n\t})\n\n\treturn errs\n}\n\nfunc processStepErrors(stepCtx provider.StepCtx, errs []error) {\n\tvar (\n\t\tstep     = stepCtx.CurrentStep()\n\t\tstatuses = make([]allure.Status, 0)\n\t)\n\n\tif len(errs) == 0 {\n\t\treturn\n\t}\n\n\tfor _, err := range errs {\n\t\tcurrentStatus := allure.Failed\n\t\tcurrentStep := step\n\n\t\tif tErr, ok := err.(errors.OptionalError); ok {\n\t\t\tif tErr.IsOptional() {\n\t\t\t\tcurrentStatus = allure.Skipped\n\t\t\t}\n\t\t}\n\n\t\tif tErr, ok := err.(errors.BrokenError); ok {\n\t\t\tif tErr.IsBroken() {\n\t\t\t\tcurrentStatus = allure.Broken\n\t\t\t}\n\t\t}\n\n\t\tif tErr, ok := err.(errors.WithNameError); ok {\n\t\t\tcurrentStep = allure.NewSimpleStep(tErr.GetName())\n\t\t\tcurrentStep.Status = currentStatus\n\t\t\tcurrentStep.WithParent(step)\n\t\t}\n\n\t\tif tErr, ok := err.(errors.WithFields); ok {\n\t\t\tfor k, v := range tErr.GetFields() {\n\t\t\t\tif v == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tcurrentStep.WithNewParameters(k, v)\n\t\t\t}\n\t\t}\n\n\t\tif tErr, ok := err.(errors.WithAttachments); ok {\n\t\t\tfor _, v := range tErr.GetAttachments() {\n\t\t\t\tif v == nil {\n\t\t\t\t\tcontinue\n\t\t\t\t}\n\n\t\t\t\tcurrentStep.WithAttachments(allure.NewAttachment(v.Name, allure.MimeType(v.MimeType), v.Content))\n\t\t\t}\n\t\t}\n\n\t\tstatuses = append(statuses, currentStatus)\n\n\t\tcurrentStep.WithAttachments(allure.NewAttachment(\"Error\", allure.Text, []byte(err.Error())))\n\t}\n\n\t// If one error was not optional, parent step should be failed\n\tfor _, status := range statuses {\n\t\tstep.Status = status\n\n\t\tif status == allure.Failed {\n\t\t\tbreak\n\t\t}\n\t}\n}\n"
  },
  {
    "path": "test.go",
    "content": "package cute\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"fmt\"\n\t\"io\"\n\t\"mime/multipart\"\n\t\"net/http\"\n\t\"net/url\"\n\t\"os\"\n\t\"testing\"\n\t\"time\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/provider\"\n\n\tcuteErrors \"github.com/ozontech/cute/errors\"\n\t\"github.com/ozontech/cute/internal/utils\"\n)\n\nconst (\n\tdefaultExecuteTestTime = 10 * time.Second\n\tdefaultDelayRepeat     = 1 * time.Second\n)\n\nvar (\n\terrorRequestMethodEmpty = errors.New(\"request method must be not empty\")\n\terrorRequestURLEmpty    = errors.New(\"url request must be not empty\")\n)\n\n// RequestSanitizerHook is a function used to modify the request URL\n// before it is logged or attached to test reports (e.g., for hiding secrets).\ntype RequestSanitizerHook func(req *http.Request)\n\n// ResponseSanitizerHook is a function used to modify the response\n// before it is logged or attached to test reports (e.g., for hiding secrets).\ntype ResponseSanitizerHook func(resp *http.Response)\n\n// Test is a main struct of test.\n// You may field Request and Expect for create simple test\n// Parallel can be used to control the parallelism of a Test\ntype Test struct {\n\thttpClient     *http.Client\n\tjsonMarshaler  JSONMarshaler\n\tlastRequestURL string\n\n\tName     string\n\tParallel bool\n\tRetry    *Retry\n\n\tAllureStep *AllureStep\n\tMiddleware *Middleware\n\tRequest    *Request\n\tExpect     *Expect\n\n\tRequestSanitizer  RequestSanitizerHook\n\tResponseSanitizer ResponseSanitizerHook\n}\n\n// Retry is a struct to control the retry of a whole single test (not only the request)\n// The test will be retried up to MaxAttempts times\n// The retries will only be executed if the test is having errors\n// If the test is successful at any iteration between attempt 1 and MaxAttempts, the loop will break and return the result as successful\n// The status of the test (success or fail) will be based on either the first attempt that is successful, or, if no attempt\n// is successful, it will be based on the latest execution\n// Delay is the number of seconds to wait before attempting to run the test again. It will only wait if Delay is set.\ntype Retry struct {\n\tcurrentCount int\n\tMaxAttempts  int\n\tDelay        time.Duration\n}\n\n// Request is struct with HTTP request.\n// You may use your *http.Request or create new with help Builders\ntype Request struct {\n\tBase     *http.Request\n\tBuilders []RequestBuilder\n\tRetry    *RequestRetryPolitic\n}\n\n// RequestRetryPolitic is struct for repeat politic\n// if Optional is true and request is failed, than test step allure will be skip, and t.Fail() will not execute.\n// If Broken is true and request is failed, than test step allure will be broken, and t.Fail() will not execute.\n// If Optional and Broken is false, than test step will be failed, and t.Fail() will execute.\n// If response.Code != Expect.Code, than request will repeat Count counts with Delay delay.\ntype RequestRetryPolitic struct {\n\tCount    int\n\tDelay    time.Duration\n\tOptional bool\n\tBroken   bool\n}\n\n// RequestRepeatPolitic is struct for repeat politic\n// Deprecated: use RequestRetryPolitic\ntype RequestRepeatPolitic struct {\n\tCount    int\n\tDelay    time.Duration\n\tOptional bool\n\tBroken   bool\n}\n\n// Middleware is struct for executeInsideAllure something before or after test\ntype Middleware struct {\n\tAfter   []AfterExecute\n\tAfterT  []AfterExecuteT\n\tBefore  []BeforeExecute\n\tBeforeT []BeforeExecuteT\n}\n\n// AllureStep is struct with test name\ntype AllureStep struct {\n\tName string\n}\n\n// Expect is structs with validate politics for response\ntype Expect struct {\n\tExecuteTime time.Duration\n\n\tCode       int\n\tJSONSchema *ExpectJSONSchema\n\n\tAssertBody     []AssertBody\n\tAssertHeaders  []AssertHeaders\n\tAssertResponse []AssertResponse\n\n\tAssertBodyT     []AssertBodyT\n\tAssertHeadersT  []AssertHeadersT\n\tAssertResponseT []AssertResponseT\n}\n\n// ExpectJSONSchema is structs with JSON politics for response\ntype ExpectJSONSchema struct {\n\tString string\n\tByte   []byte\n\tFile   string\n}\n\n// Execute is common method for run test from builder\nfunc (it *Test) Execute(ctx context.Context, t tProvider) ResultsHTTPBuilder {\n\tvar (\n\t\tinternalT allureProvider\n\t\tres       ResultsHTTPBuilder\n\t)\n\n\tif t == nil {\n\t\tpanic(\"could not start test without testing.T\")\n\t}\n\n\tstepCtx, isStepCtx := t.(provider.StepCtx)\n\tif isStepCtx {\n\t\treturn it.executeInsideStep(ctx, stepCtx)\n\t}\n\n\ttOriginal, ok := t.(*testing.T)\n\tif ok {\n\t\ttOriginal.Helper()\n\t\tinternalT = createAllureT(tOriginal)\n\t}\n\n\tallureT, ok := t.(provider.T)\n\tif ok {\n\t\tinternalT = allureT\n\t}\n\n\tinternalT.Run(it.Name, func(inT provider.T) {\n\t\tif it.Parallel {\n\t\t\tinT.Parallel()\n\t\t}\n\t\tres = it.executeInsideAllure(ctx, inT)\n\t})\n\n\treturn res\n}\n\nfunc (it *Test) clearFields() {\n\tit.AllureStep = new(AllureStep)\n\tit.Middleware = new(Middleware)\n\tit.Expect = new(Expect)\n\tit.Request = new(Request)\n\tit.Request.Retry = new(RequestRetryPolitic)\n\tit.Expect.JSONSchema = new(ExpectJSONSchema)\n}\n\nfunc (it *Test) initEmptyFields() {\n\tif it.httpClient == nil {\n\t\tit.httpClient = http.DefaultClient\n\t}\n\n\tif it.jsonMarshaler == nil {\n\t\tit.jsonMarshaler = jsonMarshaler{}\n\t}\n\n\tif it.AllureStep == nil {\n\t\tit.AllureStep = new(AllureStep)\n\t}\n\n\tif it.Middleware == nil {\n\t\tit.Middleware = new(Middleware)\n\t}\n\n\tif it.Expect == nil {\n\t\tit.Expect = new(Expect)\n\t}\n\n\tif it.Request == nil {\n\t\tit.Request = new(Request)\n\t}\n\n\tif it.Request.Retry == nil {\n\t\tit.Request.Retry = new(RequestRetryPolitic)\n\t}\n\n\tif it.Expect.JSONSchema == nil {\n\t\tit.Expect.JSONSchema = new(ExpectJSONSchema)\n\t}\n\n\tif it.Retry == nil {\n\t\tit.Retry = &Retry{\n\t\t\t// we set the default value to 1, because we count the first attempt as 1\n\t\t\tMaxAttempts:  1,\n\t\t\tcurrentCount: 1,\n\t\t}\n\t}\n\n\t// We need to set the current count to 1 here, because we count the first attempt as 1\n\tit.Retry.currentCount = 1\n}\n\n// executeInsideStep is method for start test with provider.StepCtx\n// It's test inside the step\nfunc (it *Test) executeInsideStep(ctx context.Context, t internalT) ResultsHTTPBuilder {\n\t// Set empty fields in test\n\tit.initEmptyFields()\n\n\t// we don't want to defer the finish message, because it will be logged in processTestErrors\n\tit.Info(t, \"Start test\")\n\n\treturn it.startRepeatableTest(ctx, t)\n}\n\nfunc (it *Test) executeInsideAllure(ctx context.Context, allureProvider allureProvider) ResultsHTTPBuilder {\n\t// Set empty fields in test\n\tit.initEmptyFields()\n\n\t// we don't want to defer the finish message, because it will be logged in processTestErrors\n\tit.Info(allureProvider, \"Start test\")\n\n\tif it.AllureStep.Name != \"\" {\n\t\t// Execute test inside step\n\t\treturn it.startTestInsideStep(ctx, allureProvider)\n\t} else {\n\t\t// Execute Test\n\t\treturn it.startRepeatableTest(ctx, allureProvider)\n\t}\n}\n\n// startRepeatableTest is method for start test with repeatable execution\nfunc (it *Test) startRepeatableTest(ctx context.Context, t internalT) ResultsHTTPBuilder {\n\tvar (\n\t\tresp        *http.Response\n\t\terrs        []error\n\t\tresultState ResultState\n\t)\n\n\tfor ; it.Retry.currentCount <= it.Retry.MaxAttempts; it.Retry.currentCount++ {\n\t\tresp, errs = it.startTest(ctx, t)\n\n\t\tresultState = it.processTestErrors(t, errs)\n\n\t\t// we don't want to keep errors if we will retry test\n\t\t// we have to return to user only errors from last try\n\n\t\t// if the test is successful, we break the loop\n\t\tif resultState == ResultStateSuccess {\n\t\t\tbreak\n\t\t}\n\n\t\t// if we have a delay, we wait before the next attempt\n\t\t// and we only wait if we are not at the last attempt\n\t\tif it.Retry.currentCount != it.Retry.MaxAttempts && it.Retry.Delay != 0 {\n\t\t\tit.Info(t, \"The test had errors, retrying...\")\n\t\t\ttime.Sleep(it.Retry.Delay)\n\t\t}\n\t}\n\n\tswitch resultState {\n\tcase ResultStateBroken:\n\t\tt.BrokenNow()\n\t\tit.Info(t, \"Test broken\")\n\tcase ResultStateFail:\n\t\tt.Fail()\n\t\tit.Error(t, \"Test failed\")\n\tcase resultStateFailNow:\n\t\tt.FailNow()\n\t\tit.Error(t, \"Test failed\")\n\tcase ResultStateSuccess:\n\t\tit.Info(t, \"Test finished successfully\")\n\t}\n\n\treturn newTestResult(it.Name, resp, resultState, errs)\n}\n\nfunc (it *Test) startTestInsideStep(ctx context.Context, t internalT) ResultsHTTPBuilder {\n\tvar (\n\t\tresult ResultsHTTPBuilder\n\t)\n\n\tt.WithNewStep(it.AllureStep.Name, func(stepCtx provider.StepCtx) {\n\t\tit.Info(t, \"Start step %v\", it.AllureStep.Name)\n\t\tdefer it.Info(t, \"Finish step %v\", it.AllureStep.Name)\n\n\t\tresult = it.startRepeatableTest(ctx, stepCtx)\n\n\t\tif result.GetResultState() == ResultStateFail {\n\t\t\tstepCtx.Fail()\n\t\t}\n\t})\n\n\treturn result\n}\n\n// processTestErrors returns flag, which mean finish test or not.\n// If test has only optional errors, than test will be success\n// If test has broken errors, than test will be broken on allure\n// If test has require errors, than test will be failed on allure\nfunc (it *Test) processTestErrors(t internalT, errs []error) ResultState {\n\tif len(errs) == 0 {\n\t\tit.Info(t, \"Test finished successfully\")\n\n\t\treturn ResultStateSuccess\n\t}\n\n\tvar (\n\t\tcountNotOptionalErrors = 0\n\t\tstate                  ResultState\n\t)\n\n\tfor _, err := range errs {\n\t\tmessage := fmt.Sprintf(\"error %v\", err.Error())\n\n\t\tif tErr, ok := err.(cuteErrors.OptionalError); ok {\n\t\t\tif tErr.IsOptional() {\n\t\t\t\tit.Info(t, \"[OPTIONAL ERROR] %v\", err.Error())\n\n\t\t\t\tstate = ResultStateSuccess\n\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif tErr, ok := err.(cuteErrors.BrokenError); ok {\n\t\t\tif tErr.IsBroken() {\n\t\t\t\tit.Error(t, \"[BROKEN ERROR], error %v\", err.Error())\n\n\t\t\t\tstate = ResultStateBroken\n\n\t\t\t\tcontinue\n\t\t\t}\n\t\t}\n\n\t\tif tErr, ok := err.(cuteErrors.RequireError); ok {\n\t\t\tif tErr.IsRequire() {\n\t\t\t\tstate = resultStateFailNow\n\t\t\t}\n\t\t}\n\n\t\tif tErr, ok := err.(cuteErrors.WithFields); ok {\n\t\t\tactual := tErr.GetFields()[cuteErrors.ActualField]\n\t\t\texpected := tErr.GetFields()[cuteErrors.ExpectedField]\n\n\t\t\tif actual != nil || expected != nil {\n\t\t\t\tmessage = fmt.Sprintf(\"%s\\nActual %v\\nExpected %v\", message, actual, expected)\n\t\t\t}\n\t\t}\n\n\t\tit.Error(t, message)\n\n\t\tcountNotOptionalErrors++\n\t}\n\n\tif countNotOptionalErrors != 0 {\n\t\tstate = ResultStateFail\n\n\t\tit.Error(t, \"Test finished with %v errors\", countNotOptionalErrors)\n\t}\n\n\treturn state\n}\n\nfunc (it *Test) startTest(ctx context.Context, t internalT) (*http.Response, []error) {\n\tvar (\n\t\tresp *http.Response\n\t\terr  error\n\t)\n\n\t// CreateWithStep executeInsideAllure timer\n\tif it.Expect.ExecuteTime == 0 {\n\t\tit.Expect.ExecuteTime = defaultExecuteTestTime\n\t}\n\n\tctx, cancel := context.WithTimeout(ctx, it.Expect.ExecuteTime)\n\tdefer cancel()\n\n\t// CreateWithStep request\n\treq, err := it.createRequest(ctx)\n\tif err != nil {\n\t\treturn nil, []error{err}\n\t}\n\n\t// Execute Before\n\tif errs := it.beforeTest(t, req); len(errs) > 0 {\n\t\treturn nil, errs\n\t}\n\n\tit.Info(t, \"Start make request\")\n\n\t// Make request\n\tresp, errs := it.makeRequest(t, req)\n\tif len(errs) > 0 {\n\t\treturn resp, errs\n\t}\n\n\tit.Info(t, \"Finish make request\")\n\n\t// Validate response body\n\terrs = it.validateResponse(t, resp)\n\n\t// Execute After\n\tafterTestErrs := it.afterTest(t, resp, errs)\n\n\t// Return results\n\terrs = append(errs, afterTestErrs...)\n\tif len(errs) > 0 {\n\t\treturn resp, errs\n\t}\n\n\treturn resp, nil\n}\n\nfunc (it *Test) afterTest(t internalT, resp *http.Response, errs []error) []error {\n\tif len(it.Middleware.After) == 0 && len(it.Middleware.AfterT) == 0 {\n\t\treturn nil\n\t}\n\n\treturn it.executeWithStep(t, \"After\", func(t T) []error {\n\t\tscope := make([]error, 0)\n\n\t\tfor _, execute := range it.Middleware.After {\n\t\t\tif err := execute(resp, errs); err != nil {\n\t\t\t\tscope = append(scope, err)\n\t\t\t}\n\t\t}\n\n\t\tfor _, executeSuite := range it.Middleware.AfterT {\n\t\t\tif err := executeSuite(t, resp, errs); err != nil {\n\t\t\t\tscope = append(scope, err)\n\t\t\t}\n\t\t}\n\n\t\treturn scope\n\t})\n}\n\nfunc (it *Test) beforeTest(t internalT, req *http.Request) []error {\n\tif len(it.Middleware.Before) == 0 && len(it.Middleware.BeforeT) == 0 {\n\t\treturn nil\n\t}\n\n\treturn it.executeWithStep(t, \"Before\", func(t T) []error {\n\t\tscope := make([]error, 0)\n\n\t\tfor _, execute := range it.Middleware.Before {\n\t\t\tif err := execute(req); err != nil {\n\t\t\t\tscope = append(scope, err)\n\t\t\t}\n\t\t}\n\n\t\tfor _, executeT := range it.Middleware.BeforeT {\n\t\t\tif err := executeT(t, req); err != nil {\n\t\t\t\tscope = append(scope, err)\n\t\t\t}\n\t\t}\n\n\t\treturn scope\n\t})\n}\n\n// createRequest builds the final *http.Request to be executed by the test.\nfunc (it *Test) createRequest(ctx context.Context) (*http.Request, error) {\n\tvar (\n\t\treq = it.Request.Base\n\t\terr error\n\t)\n\n\tif req == nil {\n\t\treq, err = it.buildRequest(ctx)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Validate Request\n\tif err := it.validateRequest(req); err != nil {\n\t\treturn nil, err\n\t}\n\n\treturn req, nil\n}\n\n// buildRequest\n// Priority for create body:\n// 1. requestOptions.body <- low priority\n// 2. requestOptions.bodyMarshal\n// 3. requestOptions.forms and requestOptions.fileForms <- high priority.\nfunc (it *Test) buildRequest(ctx context.Context) (*http.Request, error) {\n\tvar (\n\t\treq *http.Request\n\t\terr error\n\n\t\to = newRequestOptions()\n\t)\n\n\t// Set builder parameters\n\tfor _, builder := range it.Request.Builders {\n\t\tbuilder(o)\n\t}\n\n\treqURL := o.url\n\n\tif reqURL == nil {\n\t\treqURL, err = url.Parse(o.uri)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Set query parameters\n\tquery := reqURL.Query()\n\tfor key, values := range o.query {\n\t\tfor _, value := range values {\n\t\t\tquery.Add(key, value)\n\t\t}\n\t}\n\n\treqURL.RawQuery = query.Encode()\n\n\t// Set body\n\tbody := o.body\n\tif o.bodyMarshal != nil {\n\t\tbody, err = it.jsonMarshaler.Marshal(o.bodyMarshal)\n\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Set multipart\n\tif len(o.fileForms) != 0 || len(o.forms) != 0 {\n\t\tvar (\n\t\t\tbuffer = new(bytes.Buffer)\n\t\t\tmp     = multipart.NewWriter(buffer)\n\t\t)\n\n\t\t// set file forms\n\t\tfor fieldName, file := range o.fileForms {\n\t\t\terr = createFormFile(mp, fieldName, file)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\t// set forms\n\t\tfor fieldName, fieldBody := range o.forms {\n\t\t\terr = createFormField(mp, fieldName, fieldBody)\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t}\n\n\t\tif err = mp.Close(); err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treq, err = http.NewRequestWithContext(ctx, o.method, reqURL.String(), buffer)\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\n\t\treq.Header.Add(\"Content-Type\", mp.FormDataContentType())\n\t} else {\n\t\treq, err = http.NewRequestWithContext(ctx, o.method, reqURL.String(), io.NopCloser(bytes.NewReader(body)))\n\t\tif err != nil {\n\t\t\treturn nil, err\n\t\t}\n\t}\n\n\t// Set headers\n\tfor nameHeader, valuesHeader := range o.headers {\n\t\treq.Header[nameHeader] = valuesHeader\n\t}\n\n\treturn req, nil\n}\n\nfunc createFormFile(mp *multipart.Writer, fieldName string, file *File) error {\n\tvar (\n\t\tdata = file.Body\n\t\tname = file.Name\n\t)\n\n\t// read file, if path is not empty\n\tif len(file.Path) != 0 {\n\t\tf, err := os.Open(file.Path)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tdata, err = io.ReadAll(f)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\n\t\tname = f.Name()\n\t}\n\n\tfield, err := mp.CreateFormFile(fieldName, name)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error when creating %v file form field, %w\", fieldName, err)\n\t}\n\n\t_, err = field.Write(data)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error when writing %v file form field, %w\", fieldName, err)\n\t}\n\n\treturn nil\n}\n\nfunc createFormField(mp *multipart.Writer, fieldName string, body []byte) error {\n\tfield, err := mp.CreateFormField(fieldName)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error when creating %v form field, %w\", fieldName, err)\n\t}\n\n\t_, err = field.Write(body)\n\tif err != nil {\n\t\treturn fmt.Errorf(\"error when writing %v form field, %w\", fieldName, err)\n\t}\n\n\treturn nil\n}\n\nfunc (it *Test) validateRequest(req *http.Request) error {\n\tif req.URL == nil {\n\t\treturn errorRequestURLEmpty\n\t}\n\n\tif req.Method == \"\" {\n\t\treturn errorRequestMethodEmpty\n\t}\n\n\treturn nil\n}\n\nfunc (it *Test) validateResponse(t internalT, resp *http.Response) []error {\n\tvar (\n\t\terr      error\n\t\tsaveBody io.ReadCloser\n\t\tscope    = make([]error, 0)\n\t)\n\n\t// Execute asserts for headers\n\tif errs := it.assertHeaders(t, resp.Header); len(errs) > 0 {\n\t\tscope = append(scope, errs...)\n\t}\n\n\t// Prepare body for validate\n\tif resp.Body == nil {\n\t\t// todo create errors if body is empty, but assert is not empty\n\t\treturn scope\n\t}\n\n\tsaveBody, resp.Body, err = utils.DrainBody(resp.Body)\n\tif err != nil {\n\t\treturn append(scope, fmt.Errorf(\"could not drain response body. error %w\", err))\n\t}\n\n\tbody, err := utils.GetBody(saveBody)\n\tif err != nil {\n\t\treturn append(scope, fmt.Errorf(\"could not get response body. error %w\", err))\n\t}\n\n\t// Execute asserts for body\n\tif errs := it.assertBody(t, body); len(errs) > 0 {\n\t\t// add assert\n\t\tscope = append(scope, errs...)\n\t}\n\n\t// Validate response by json schema\n\tif errs := it.validateJSONSchema(t, body); len(errs) > 0 {\n\t\tscope = append(scope, errs...)\n\t}\n\n\t// Execute asserts for response body\n\tif errs := it.assertResponse(t, resp); len(errs) > 0 {\n\t\tscope = append(scope, errs...)\n\t}\n\n\treturn scope\n}\n"
  },
  {
    "path": "test_test.go",
    "content": "package cute\n\nimport (\n\t\"bytes\"\n\t\"context\"\n\t\"errors\"\n\t\"io\"\n\t\"net/http\"\n\t\"net/http/httptest\"\n\t\"net/url\"\n\t\"strings\"\n\t\"testing\"\n\n\t\"github.com/ozontech/allure-go/pkg/framework/core/common\"\n\t\"github.com/stretchr/testify/require\"\n\n\t\"github.com/ozontech/cute/internal/utils\"\n)\n\nfunc TestCreateRequest(t *testing.T) {\n\treq, err := http.NewRequest(http.MethodGet, \"http://go.com\", nil)\n\trequire.NoError(t, err)\n\n\tht := &Test{\n\t\tRequest: &Request{\n\t\t\tBase: req,\n\t\t},\n\t}\n\n\tresReq, err := ht.createRequest(context.Background())\n\trequire.NoError(t, err)\n\trequire.Equal(t, req, resReq)\n}\n\nfunc TestCreateRequestBuilder(t *testing.T) {\n\tvar (\n\t\tbody    = []byte(\"HELLO\")\n\t\theaders = map[string][]string{\n\t\t\t\"Good\": []string{\"Day\"},\n\t\t\t\"Mad\":  []string{\"Max\"},\n\t\t}\n\t\turl = \"http://go.com\"\n\t)\n\n\treq, err := http.NewRequest(http.MethodGet, url, io.NopCloser(bytes.NewReader(body)))\n\trequire.NoError(t, err)\n\n\treq.Header = headers\n\n\tht := &Test{\n\t\tRequest: &Request{\n\t\t\tBuilders: []RequestBuilder{\n\t\t\t\tWithURI(url),\n\t\t\t\tWithMethod(\"GET\"),\n\t\t\t\tWithHeaders(headers),\n\t\t\t\tWithBody(body),\n\t\t\t},\n\t\t},\n\t}\n\n\tresReq, err := ht.createRequest(context.Background())\n\trequire.NoError(t, err)\n\trequire.Equal(t, req, resReq)\n}\n\nfunc TestCreateRequestBuilder_MarshalBody(t *testing.T) {\n\tvar (\n\t\tstr = struct {\n\t\t\tname string\n\t\t}{\n\t\t\t\"hello\",\n\t\t}\n\t)\n\n\tht := &Test{\n\t\tjsonMarshaler: jsonMarshaler{},\n\t\tRequest: &Request{\n\t\t\tBuilders: []RequestBuilder{\n\t\t\t\tWithMarshalBody(str),\n\t\t\t},\n\t\t},\n\t}\n\n\tresReq, err := ht.createRequest(context.Background())\n\trequire.NoError(t, err)\n\n\tgetBody, err := utils.GetBody(resReq.Body)\n\trequire.NoError(t, err)\n\n\trequire.NotEmpty(t, getBody)\n}\n\nfunc TestValidateRequestEmptyUrl(t *testing.T) {\n\tht := &Test{}\n\n\trequire.Error(t, ht.validateRequest(&http.Request{}))\n}\n\nfunc TestValidateRequestEmptyMethod(t *testing.T) {\n\tht := &Test{}\n\tu, _ := url.Parse(\"https://go.com\")\n\n\trequire.Error(t, ht.validateRequest(&http.Request{\n\t\tURL: u,\n\t}))\n}\n\nfunc TestValidateResponseEmpty(t *testing.T) {\n\tht := &Test{\n\t\tExpect: new(Expect),\n\t}\n\n\ttemp := common.NewT(t)\n\n\terrs := ht.validateResponse(temp, &http.Response{})\n\trequire.Empty(t, errs)\n}\n\nfunc TestValidateResponseCode(t *testing.T) {\n\tht := &Test{\n\t\tExpect: &Expect{Code: 200},\n\t}\n\ttemp := common.NewT(t)\n\n\terrs := ht.validateResponse(temp, &http.Response{StatusCode: http.StatusOK})\n\trequire.Empty(t, errs)\n}\n\nfunc TestValidateResponseWithErrors(t *testing.T) {\n\tvar (\n\t\tht = &Test{\n\t\t\tExpect: &Expect{\n\t\t\t\tCode:       200,\n\t\t\t\tJSONSchema: new(ExpectJSONSchema),\n\t\t\t\tAssertHeaders: []AssertHeaders{\n\t\t\t\t\tfunc(headers http.Header) error {\n\t\t\t\t\t\treturn errors.New(\"two error\")\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t\tAssertResponse: []AssertResponse{\n\t\t\t\t\tfunc(response *http.Response) error {\n\t\t\t\t\t\tif response.StatusCode != http.StatusOK || len(response.Header[\"auth\"]) == 0 { //nolint\n\t\t\t\t\t\t\treturn errors.New(\"bad response\")\n\t\t\t\t\t\t}\n\t\t\t\t\t\treturn nil\n\t\t\t\t\t},\n\t\t\t\t},\n\t\t\t},\n\t\t}\n\n\t\treader = bytes.NewReader([]byte(`{\"a\":\"ab\",\"b\":\"bc\"}`))\n\t\ttemp   = createAllureT(t)\n\t\tresp   = &http.Response{\n\t\t\tStatusCode: http.StatusBadRequest,\n\t\t\tHeader: map[string][]string{\n\t\t\t\t\"key\":  []string{\"value\"},\n\t\t\t\t\"auth\": []string{\"sometoken\"},\n\t\t\t},\n\t\t\tBody: io.NopCloser(reader),\n\t\t}\n\t)\n\n\tht.initEmptyFields()\n\n\terrs := ht.validateResponse(temp, resp)\n\n\trequire.Len(t, errs, 2)\n}\n\ntype mockRoundTripper struct{}\n\nfunc (m *mockRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {\n\treturn &http.Response{\n\t\tStatusCode: 200,\n\t\tRequest:    req,\n\t\tBody:       io.NopCloser(strings.NewReader(\"mock response\")),\n\t}, nil\n}\n\nfunc TestSanitizeURLHook(t *testing.T) {\n\tclient := &http.Client{\n\t\tTransport: &mockRoundTripper{},\n\t}\n\n\ttest := &Test{\n\t\thttpClient: client,\n\t\tRetry: &Retry{\n\t\t\tcurrentCount: 0,\n\t\t\tMaxAttempts:  0,\n\t\t\tDelay:        0,\n\t\t},\n\t\tRequest: &Request{\n\t\t\tBuilders: []RequestBuilder{\n\t\t\t\tWithMethod(http.MethodGet),\n\t\t\t\tWithURI(\"http://localhost/api?key=123\"),\n\t\t\t},\n\t\t\tRetry: &RequestRetryPolitic{\n\t\t\t\tCount: 1,\n\t\t\t\tDelay: 2,\n\t\t\t},\n\t\t},\n\t\tRequestSanitizer: sanitizeKeyParam(\"****\"),\n\t}\n\n\treq, err := test.createRequest(context.Background())\n\trequire.NoError(t, err)\n\trequire.NotNil(t, req)\n\n\tnewT := createAllureT(t)\n\n\terr = test.addInformationRequest(newT, req)\n\trequire.NoError(t, err)\n\n\tdecodedQuery, err := url.QueryUnescape(req.URL.RawQuery)\n\trequire.NoError(t, err)\n\trequire.Equal(t, \"key=****\", decodedQuery)\n}\n\nfunc TestSanitizeURL_LastRequestURL(t *testing.T) {\n\tclient := &http.Client{\n\t\tTransport: &mockRoundTripper{},\n\t}\n\n\ttest := &Test{\n\t\thttpClient: client,\n\t\tRequest: &Request{\n\t\t\tBuilders: []RequestBuilder{\n\t\t\t\tWithMethod(http.MethodGet),\n\t\t\t\tWithURI(\"http://localhost/api?key=123\"),\n\t\t\t},\n\t\t},\n\t\tRequestSanitizer: sanitizeKeyParam(\"****\"),\n\t}\n\n\tallureT := createAllureT(t)\n\ttest.Execute(context.Background(), allureT)\n\n\tdecodedURL, err := url.QueryUnescape(test.lastRequestURL)\n\trequire.NoError(t, err)\n\trequire.Contains(t, decodedURL, \"key=****\", \"Expected masked key in lastRequestURL\")\n}\n\nfunc sanitizeKeyParam(mask string) RequestSanitizerHook {\n\treturn func(req *http.Request) {\n\t\tq := req.URL.Query()\n\t\tq.Set(\"key\", mask)\n\t\treq.URL.RawQuery = q.Encode()\n\t}\n}\n\nfunc TestSanitizeURL_RealRequest(t *testing.T) {\n\tts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\n\t\tbody, _ := io.ReadAll(r.Body)\n\t\tt.Logf(\"Server received URL: %s, Body: %s\", r.URL.String(), string(body))\n\t\trequire.Contains(t, r.URL.String(), \"key=123\", \"Sanitizer must not change real request\")\n\t\tw.WriteHeader(200)\n\t}))\n\tdefer ts.Close()\n\n\tclient := &http.Client{}\n\ttest := &Test{\n\t\thttpClient: client,\n\t\tRequest: &Request{\n\t\t\tBuilders: []RequestBuilder{\n\t\t\t\tWithMethod(http.MethodGet),\n\t\t\t\tWithURI(ts.URL + \"/api?key=123\"),\n\t\t\t},\n\t\t},\n\t\tRequestSanitizer: sanitizeKeyParam(\"****\"),\n\t}\n\n\tallureT := createAllureT(t)\n\ttest.Execute(context.Background(), allureT)\n}\n"
  }
]