├── .github ├── dependabot.yml └── workflows │ ├── ci.yaml │ ├── maven-deploy.yaml │ └── release.yaml ├── .gitignore ├── CHANGELOG.md ├── LICENSE ├── README.md ├── docs └── MatchStrategy.md ├── e2e ├── .gitignore ├── Makefile ├── env │ └── default │ │ ├── default.properties │ │ └── java.properties ├── fixtures │ ├── alias-match │ │ ├── request.json │ │ └── setup.json │ ├── alias-not-match │ │ ├── request.json │ │ └── setup.json │ ├── argument-match │ │ ├── request.json │ │ └── setup.json │ ├── argument-not-match │ │ ├── request.json │ │ └── setup.json │ ├── exact-match │ │ ├── request.json │ │ └── setup.json │ ├── fragment-match │ │ ├── request.json │ │ └── setup.json │ ├── fragment-name-match │ │ ├── request.json │ │ └── setup.json │ ├── fragment-set-not-match │ │ ├── request.json │ │ └── setup.json │ ├── multiple-fragments-match │ │ ├── request.json │ │ └── setup.json │ ├── multiple-fragments-not-match │ │ ├── request.json │ │ └── setup.json │ ├── not-match │ │ ├── request.json │ │ └── setup.json │ ├── order-match │ │ ├── request.json │ │ └── setup.json │ ├── query-match │ │ ├── request.json │ │ └── setup-query.graphql │ ├── query-variables-match │ │ ├── request.json │ │ ├── setup-query.graphql │ │ └── setup-variables.json │ ├── variables-array-order-not-match │ │ ├── request.json │ │ └── setup.json │ ├── variables-complex-match │ │ ├── request.json │ │ └── setup.json │ ├── variables-match │ │ ├── request.json │ │ └── setup.json │ ├── variables-not-match │ │ ├── request.json │ │ └── setup.json │ └── with-other │ │ ├── request.json │ │ └── setup.json ├── manifest.json ├── pom.xml ├── specs │ ├── match.spec │ ├── variables-match.spec │ └── with-other-mappings.spec └── src │ └── test │ ├── kotlin │ ├── Configuration.kt │ ├── Datastore.kt │ ├── ExecutionHooks.kt │ └── Steps.kt │ └── resources │ └── config.properties ├── examples ├── testcontainers-java │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ │ └── test │ │ └── java │ │ └── TestSample.java └── testcontainers-kotlin │ ├── .gitignore │ ├── README.md │ ├── pom.xml │ └── src │ └── test │ └── kotlin │ └── TestSample.kt └── wiremock-graphql-extension ├── .dockerignore ├── Dockerfile ├── pom.xml └── src ├── main ├── kotlin │ └── io │ │ └── github │ │ └── nilwurtz │ │ ├── EqualToGraphqlQueryPattern.kt │ │ └── GraphqlBodyMatcher.kt └── resources │ └── META-INF │ └── services │ └── com.github.tomakehurst.wiremock.extension.Extension └── test ├── kotlin └── io │ └── github │ └── nilwurtz │ ├── EqualToGraphqlQueryPatternTest.kt │ ├── GraphqlBodyMatcherTest.kt │ └── integration │ └── ContainerTest.kt └── resources └── mappings ├── all.json ├── operationName.json ├── query.json └── variables.json /.github/dependabot.yml: -------------------------------------------------------------------------------- 1 | version: 2 2 | updates: 3 | - package-ecosystem: "maven" 4 | directory: "/e2e" 5 | schedule: 6 | interval: "daily" 7 | open-pull-requests-limit: 10 8 | reviewers: 9 | - "nilwurtz" 10 | auto-merge: 11 | enabled: true 12 | strategy: "merge" 13 | 14 | - package-ecosystem: "maven" 15 | directory: "/wiremock-graphql-extension" 16 | schedule: 17 | interval: "daily" 18 | open-pull-requests-limit: 10 19 | reviewers: 20 | - "nilwurtz" 21 | auto-merge: 22 | enabled: true 23 | strategy: "merge" -------------------------------------------------------------------------------- /.github/workflows/ci.yaml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | paths: 8 | - 'e2e/**' 9 | - 'examples/**' 10 | - 'wiremock-graphql-extension/**' 11 | workflow_dispatch: 12 | 13 | jobs: 14 | test: 15 | runs-on: ubuntu-latest 16 | 17 | steps: 18 | - name: Check out repository 19 | uses: actions/checkout@v4 20 | 21 | - name: Set up Java 17 22 | uses: actions/setup-java@v4 23 | with: 24 | java-version: '17' 25 | distribution: 'corretto' 26 | 27 | - uses: getgauge/setup-gauge@master 28 | with: 29 | gauge-version: '1.6.9' 30 | gauge-plugins: java, html-report 31 | 32 | - name: Run unit test and install 33 | run: make install 34 | working-directory: e2e 35 | 36 | - name: Run docker for e2e 37 | run: make docker 38 | working-directory: e2e 39 | 40 | - name: Run e2e 41 | run: make clean test_compile run TAGS='!unimplemented' 42 | working-directory: e2e 43 | 44 | - name: Run remote e2e 45 | run: |- 46 | sed -i 's/baseUrl=http:\/\/localhost:8080/baseUrl=http:\/\/localhost:8888/' src/test/resources/config.properties 47 | make run TAGS='!unimplemented,remote' 48 | working-directory: e2e 49 | 50 | check-example-validity: 51 | runs-on: ubuntu-latest 52 | 53 | steps: 54 | - name: Check out repository 55 | uses: actions/checkout@v4 56 | 57 | - name: Set up Java 17 58 | uses: actions/setup-java@v4 59 | with: 60 | java-version: '17' 61 | distribution: 'corretto' 62 | 63 | - name: Install to local repository 64 | run: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V 65 | working-directory: wiremock-graphql-extension 66 | 67 | - name: Check example validity kotlin 68 | run: mvn test 69 | working-directory: examples/testcontainers-kotlin 70 | 71 | - name: Check example validity java 72 | run: mvn test 73 | working-directory: examples/testcontainers-java 74 | -------------------------------------------------------------------------------- /.github/workflows/maven-deploy.yaml: -------------------------------------------------------------------------------- 1 | name: Publish package 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | jobs: 7 | publish: 8 | runs-on: ubuntu-latest 9 | steps: 10 | - uses: actions/checkout@v4 11 | 12 | - name: Set up Maven Central Repository 13 | uses: actions/setup-java@v4 14 | with: 15 | java-version: '17' 16 | distribution: 'corretto' 17 | server-id: ossrh 18 | server-username: MAVEN_USERNAME 19 | server-password: MAVEN_USER_PASSWORD 20 | gpg-passphrase: MAVEN_GPG_PASSPHRASE 21 | gpg-private-key: ${{ secrets.MAVEN_GPG_PRIVATE_KEY }} 22 | 23 | - name: Publish package 24 | working-directory: wiremock-graphql-extension 25 | run: mvn --batch-mode --no-transfer-progress deploy -Dgpg.skip=false 26 | env: 27 | MAVEN_USERNAME: ${{ secrets.MAVEN_USERNAME }} 28 | MAVEN_USER_PASSWORD: ${{ secrets.MAVEN_USER_PASSWORD }} 29 | MAVEN_GPG_PASSPHRASE: ${{ secrets.MAVEN_GPG_PASSPHRASE }} -------------------------------------------------------------------------------- /.github/workflows/release.yaml: -------------------------------------------------------------------------------- 1 | name: Build and Release 2 | 3 | on: 4 | workflow_dispatch: 5 | 6 | permissions: 7 | contents: write 8 | 9 | jobs: 10 | build_and_release: 11 | runs-on: ubuntu-latest 12 | 13 | steps: 14 | - name: Checkout repository 15 | uses: actions/checkout@v4 16 | 17 | - name: Set up Java 17 18 | uses: actions/setup-java@v4 19 | with: 20 | java-version: '17' 21 | distribution: 'corretto' 22 | 23 | - name: Get Target version 24 | working-directory: wiremock-graphql-extension 25 | id: version 26 | run: |- 27 | VERSION=$(mvn help:evaluate -Dexpression=project.version -B | grep -v '\[INFO\]') 28 | echo $VERSION 29 | echo "version=$VERSION" >> $GITHUB_OUTPUT 30 | 31 | - name: Build and package JAR 32 | run: mvn -f wiremock-graphql-extension clean package 33 | 34 | - name: Create GitHub Release 35 | uses: softprops/action-gh-release@v1 36 | with: 37 | files: wiremock-graphql-extension/target/wiremock-graphql-extension-*.jar 38 | tag_name: v${{ steps.version.outputs.version }} 39 | name: Release v${{ steps.version.outputs.version }} 40 | env: 41 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} 42 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Gauge - metadata dir 2 | .gauge 3 | 4 | # Gauge - log files dir 5 | logs 6 | 7 | # Gauge - reports generated by reporting plugins 8 | reports 9 | 10 | # Gauge - java class output directory 11 | gauge_bin 12 | 13 | # Gauge - java class target dir 14 | target 15 | 16 | target/ 17 | !.mvn/wrapper/maven-wrapper.jar 18 | !**/src/main/**/target/ 19 | !**/src/test/**/target/ 20 | 21 | ### IntelliJ IDEA ### 22 | .idea/ 23 | *.iws 24 | *.iml 25 | *.ipr 26 | 27 | ### Eclipse ### 28 | .apt_generated 29 | .classpath 30 | .factorypath 31 | .project 32 | .settings 33 | .springBeans 34 | .sts4-cache 35 | 36 | ### NetBeans ### 37 | /nbproject/private/ 38 | /nbbuild/ 39 | /dist/ 40 | /nbdist/ 41 | /.nb-gradle/ 42 | build/ 43 | !**/src/main/**/build/ 44 | !**/src/test/**/build/ 45 | 46 | ### VS Code ### 47 | .vscode/ 48 | 49 | ### Mac OS ### 50 | .DS_Store 51 | 52 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Change Log 2 | 3 | All notable changes to this project will be documented in this file. 4 | 5 | The format is based on [Keep a Changelog](http://keepachangelog.com/) 6 | and this project adheres to [Semantic Versioning](http://semver.org/). 7 | 8 | ## [Unreleased] 9 | 10 | ### Added 11 | 12 | ### Changed 13 | 14 | ## [0.9.0] - 2024-10-08 15 | 16 | ### Changed 17 | 18 | - Update dependencies ([#24](https://github.com/wiremock/wiremock-graphql-extension/pull/24) from @bgalek) 19 | - Update wiremock 3.0.3 -> 3.9.0 20 | - Update graphql-java 21.0 -> 22.3 21 | - Update mockk 1.13.8 -> 1.13.12 22 | - Update testcontainers 1.19.3 -> 1.20.2 23 | - Update junit 5.10.1 -> 5.11.1 24 | - Update mockk 1.13.8 -> 1.13.12 25 | 26 | ## [0.8.2] - 2024-01-04 27 | 28 | ### Added 29 | 30 | - Added `operationName` parameter ([#18](https://github.com/wiremock/wiremock-graphql-extension/pull/18) from @kyle-winkelman) 31 | - Added `GraphqlBodyMatcher.parameters` method which can use create `Parameters` easily ([#18](https://github.com/wiremock/wiremock-graphql-extension/pull/18) from @kyle-winkelman) 32 | 33 | ### Changed 34 | 35 | - Remove `org.json:json` dependency in favor of reusing `com.github.tomakehurst.wiremock.common.Json` ([#18](https://github.com/wiremock/wiremock-graphql-extension/pull/18) from @kyle-winkelman) 36 | - Deprecate `withRequestJson` and `withRequest` in favor of `parameters` ([#18](https://github.com/wiremock/wiremock-graphql-extension/pull/18) from @kyle-winkelman) 37 | 38 | ## [0.8.1] - 2023-12-12 39 | 40 | ### Changed 41 | 42 | - Added `@JvmStatic` annotation to `GraphqlBodyMatcher.Companion.withRequest` method to allow Java clients to use the method without `Companion`. ([#16](https://github.com/wiremock/wiremock-graphql-extension/pull/16) from @kyle-winkelman) 43 | 44 | ## [0.8.0] - 2023-12-08 45 | 46 | ### Changed 47 | 48 | - Implemented `graphql-java`'s `AstSorter` and `AstComparator` for GraphQL query normalization. This integration significantly aligns the supported GraphQL features of our extension with those of `graphql-java`. ([#14](https://github.com/wiremock/wiremock-graphql-extension/pull/14) from @kyle-winkelman) 49 | 50 | ## [0.7.1] - 2023-11-25 51 | 52 | ### Changed 53 | 54 | - Update dev dependencies (kotlin, mockk, junit, testcontainers) 55 | 56 | ### Fixed 57 | 58 | - Improved handling of newline characters in JSON strings. Newline characters are now removed to prevent parsing errors (`JSONException: Unterminated string`) when processing JSON data. This change ensures that JSON strings with embedded newlines are handled correctly by the `String.toJSONObject()` method. ([#11](https://github.com/wiremock/wiremock-graphql-extension/issues/11)) 59 | 60 | ## [0.7.0] - 2023-09-27 61 | 62 | ### Changed 63 | 64 | - Throws `InvalidQueryException` and `InvalidJsonException` when `withRequest` is called. 65 | - When `match` method is called, it will not throw any exception if the request is invalid. 66 | 67 | ## [0.6.2] - 2023-09-08 68 | 69 | ### Added 70 | 71 | - Added `withRequest` method which can used easily when using remote wiremock server. 72 | 73 | ## [0.6.1] - 2023-08-31 74 | 75 | ### Changed 76 | 77 | - Update target jvmVersion 1.8 -> 11 78 | - Update graphql-java 20.2 -> 21.0 79 | - Update json 20230227 -> 20230618 80 | - Update dev dependencies (kotlin, mockk, junit) 81 | 82 | ## [0.6.0] (deprecated) - 2023-08-31 83 | 84 | ### Changed 85 | 86 | - Update wiremock 2.27.2 -> 3.0.0! 87 | - `withRequestQueryAndVariables` method has been changed to deprecate. 88 | 89 | ## [0.5.0] - 2023-08-11 90 | 91 | ### Added 92 | 93 | - Added `GraphqlBodyMatcher.extensionName` which can used easily when using remote wiremock server. 94 | 95 | ### Changed 96 | 97 | - Change parameter key `expectedQuery` to `expectedJson` for remote wiremock server. 98 | 99 | ## [0.4.0] - 2023-05-25 100 | 101 | ### Added 102 | 103 | - Support Remote Wiremock Server. 104 | 105 | ## [0.3.0] - 2023-04-26 106 | 107 | ### Added 108 | 109 | - Support Graphql Variables. 110 | 111 | ### Changed 112 | 113 | - `withRequestQuery` method has been changed to `withRequestQueryAndVariables` and now takes `expectedVariables` as argument. `expectedVariables` is Nullable. 114 | - Update junit 5.8.1 -> 5.9.2 115 | - Update mockk-jvm 1.13.4 -> 1.13.5 116 | 117 | ## [0.2.1] - 2023-04-21 118 | 119 | ### Added 120 | 121 | - Support fragment normalization. 122 | 123 | ## [0.2.0] - 2023-04-21 124 | 125 | ### Changed 126 | 127 | - Use `withRequestJson or Query` instead of constructor. 128 | 129 | ## [0.1.x] 130 | 131 | Prerelease Version. 132 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 nilwurtz 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Graphql Wiremock Extension - Graphql Body Matcher 2 | 3 | ⚠️ **IMPORTANT**: Starting from version 0.6, this extension requires WireMock 3.x. WireMock 2.x is no longer supported from this version onwards. 4 | 5 | _An extension for GraphQL testing with Wiremock_ 6 | 7 | GraphqlBodyMatcher is an extension for [WireMock](https://wiremock.org/) that allows for semantical verification of GraphQL requests. 8 | 9 | GraphqlBodyMatcher は[WireMock](https://wiremock.org/)の拡張で、GraphQL のリクエストが意味的に一致しているかを検証します。 10 | 11 | ## Overview 📖 12 | 13 | - In addition to handling whitespaces, the extension sorts and normalizes queries. The GraphQL parsing and normalizing is handled by `graphql-java`. 14 | - Beyond just queries, it also compares variables. For the comparison of JSON variables, [EqualToJsonPattern](https://github.com/wiremock/wiremock/blob/3.3.1/src/main/java/com/github/tomakehurst/wiremock/matching/EqualToJsonPattern.java) is used. It's important to note that the order of arrays must match. 15 | 16 | For a comprehensive understanding of our matching logic and details on our match strategy, please refer to our [MatchStrategy documentation](./docs/MatchStrategy.md). 17 | 18 | - この拡張機能は、空白の取り扱いに加えて、クエリをソートし正規化します。GraphQL のパースおよび正規化には`graphql-java`を使用しています。 19 | - クエリだけでなく、変数も比較されます。変数の JSON の比較には`org.json.JSONObject.similar`を使用しますが、配列の順番も一致している必要があります。 20 | 21 | 詳しいマッチングロジックなど関しては、[MatchStrategy のドキュメント](./docs/MatchStrategy.md)を参照してください。 22 | 23 | ## Usage 🛠️ 24 | 25 | ### For Gradle: 26 | 27 | ```groovy 28 | repositories { 29 | mavenCentral() 30 | } 31 | 32 | dependencies { 33 | testImplementation 'io.github.nilwurtz:wiremock-graphql-extension:0.9.0' 34 | } 35 | ``` 36 | 37 | ### For Maven: 38 | 39 | ```xml 40 | 41 | io.github.nilwurtz 42 | wiremock-graphql-extension 43 | 0.9.0 44 | test 45 | 46 | ``` 47 | 48 | ## Code Examples 💡 49 | 50 | Here are some code examples to get started. 51 | 52 | ```java Java 53 | import com.github.tomakehurst.wiremock.client.WireMock; 54 | import io.github.nilwurtz.GraphqlBodyMatcher; 55 | 56 | import java.util.Map; 57 | 58 | var expectedQuery = """ 59 | query HeroInfo($id: Int) { 60 | hero(id: $id) { 61 | name 62 | } 63 | } 64 | """; 65 | var expectedVariables = Map.of("id", 1); 66 | 67 | WireMock.stubFor(WireMock.post(WireMock.urlEqualTo("/graphql")) 68 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.parameters(expectedQuery, expectedVariables)) 69 | .willReturn(WireMock.okJson(""" 70 | { 71 | "data": { 72 | "hero": { 73 | "name": "example" 74 | } 75 | } 76 | }"""))); 77 | ``` 78 | 79 | ```kotlin Kotlin 80 | import com.github.tomakehurst.wiremock.client.WireMock 81 | import io.github.nilwurtz.GraphqlBodyMatcher 82 | 83 | val expectedQuery = """ 84 | query HeroInfo(${'$'}id: Int) { 85 | hero(id: ${'$'}id) { 86 | name 87 | } 88 | } 89 | """.trimIndent() 90 | val expectedVariables = mapOf("id" to 1) 91 | 92 | WireMock.stubFor( 93 | WireMock.post(WireMock.urlEqualTo("/graphql")) 94 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.parameters(expectedQuery, expectedVariables)) 95 | .willReturn( 96 | WireMock.okJson(""" 97 | { 98 | "data": { 99 | "hero": { 100 | "name": "example" 101 | } 102 | } 103 | } 104 | """.trimIndent() 105 | ) 106 | ) 107 | ) 108 | ``` 109 | 110 | As we head towards a 1.0 release, we are focusing on supporting both the WireMock standalone and the WireMock Java API 111 | with the same paradigm. This is done through the use of `WireMock.requestMatching()` or `MappingBuilder#andMatching()`. 112 | To that end, the `withRequestQueryAndVariables` method has been deprecated from version 0.6.0 onwards and 113 | `withRequestJson` and `withRequest` methods have been deprecated from version 0.9.0 onwards. 114 | 115 | ## Running with a Remote Wiremock Server 🌍 116 | 117 | If you are using Wiremock on a remote server such as Docker, please see the configurations below: 118 | 119 | Please download `wiremock-graphql-extension-x.y.z-jar-with-dependencies.jar` from the Release section. 120 | 121 | ### Server Configuration 122 | 123 | #### When running with `docker run`: 124 | 125 | ``` 126 | docker run -it --rm \ 127 | -p 8080:8080 \ 128 | --name wiremock \ 129 | -v /path/to/wiremock-graphql-extension-0.9.0-jar-with-dependencies.jar:/var/wiremock/extensions/wiremock-graphql-extension-0.9.0-jar-with-dependencies.jar \ 130 | wiremock/wiremock \ 131 | --extensions io.github.nilwurtz.GraphqlBodyMatcher 132 | ``` 133 | 134 | #### When building with `docker build`: 135 | 136 | ```dockerfile 137 | FROM wiremock/wiremock:latest 138 | COPY ./wiremock-graphql-extension-0.9.0-jar-with-dependencies.jar /var/wiremock/extensions/wiremock-graphql-extension-0.9.0-jar-with-dependencies.jar 139 | ``` 140 | 141 | ### Client-side (Test) Configuration 142 | 143 | NOTE: When using a Remote Wiremock Server, you're requested to manage everything within a single JSON format. 144 | 145 | ```java Java 146 | import com.github.tomakehurst.wiremock.client.WireMock; 147 | import io.github.nilwurtz.GraphqlBodyMatcher; 148 | 149 | import static com.github.tomakehurst.wiremock.client.WireMock.*; 150 | 151 | public registerGraphQLWiremock(String query, String response) { 152 | WireMock(8080).register( 153 | post(urlPathEqualTo(endPoint)) 154 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.parameters(query)) 155 | .willReturn(okJson(response))); 156 | } 157 | ``` 158 | 159 | ```kotlin Kotlin 160 | import com.github.tomakehurst.wiremock.client.WireMock 161 | import com.github.tomakehurst.wiremock.client.WireMock.* 162 | import io.github.nilwurtz.GraphqlBodyMatcher 163 | 164 | fun registerGraphQLWiremock(query: String, response: String) { 165 | WireMock(8080).register( 166 | post(urlPathEqualTo(endPoint)) 167 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.parameters(query)) 168 | .willReturn(okJson(response))) 169 | } 170 | ``` 171 | 172 | ## License 📜 173 | 174 | This project is licensed under the terms of the MIT License. 175 | 176 | ## Contributing 🤝 177 | 178 | Contributions are welcome! Feel free to open an issue or submit a pull request if you have any improvements or suggestions. 179 | -------------------------------------------------------------------------------- /docs/MatchStrategy.md: -------------------------------------------------------------------------------- 1 | # Match Strategy 2 | 3 | ## queries 4 | 5 | The two queries below are considered matching: 6 | 7 | ```graphql 8 | { 9 | hero { 10 | name 11 | friends { 12 | name 13 | age 14 | } 15 | } 16 | } 17 | ``` 18 | ```graphql 19 | { 20 | hero { 21 | friends { 22 | age 23 | name 24 | } 25 | name 26 | } 27 | } 28 | ``` 29 | But, these aren't: 30 | ```graphql 31 | { 32 | hero { 33 | name 34 | friends { 35 | name 36 | age 37 | } 38 | } 39 | } 40 | ``` 41 | ```graphql 42 | { 43 | hero { 44 | name 45 | friends { 46 | name 47 | } 48 | } 49 | } 50 | ``` 51 | 52 | ## Variables 53 | 54 | Similar rules apply for variable matching based on `org.json.JsonObject.similar`. 55 | 56 | ```json 57 | { 58 | "id": 1, 59 | "name": "John Doe" 60 | } 61 | ``` 62 | 63 | ```json 64 | { 65 | "name": "John Doe", 66 | "id": 1 67 | } 68 | ``` 69 | 70 | However, the following two variables do not match because the order of the arrays is different. 71 | 72 | ```json 73 | { 74 | "ids": [1, 2, 3] 75 | } 76 | ``` 77 | ```json 78 | { 79 | "ids": [3, 2, 1] 80 | } 81 | ``` -------------------------------------------------------------------------------- /e2e/.gitignore: -------------------------------------------------------------------------------- 1 | # Gauge - metadata dir 2 | .gauge 3 | 4 | # Gauge - log files dir 5 | logs 6 | 7 | # Gauge - reports generated by reporting plugins 8 | reports 9 | 10 | # Gauge - java class output directory 11 | gauge_bin 12 | 13 | # Gauge - java class target dir 14 | target 15 | -------------------------------------------------------------------------------- /e2e/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all install test_compile run 2 | 3 | all: install clean test_compile run 4 | 5 | install: 6 | cd ../wiremock-graphql-extension && mvn install -Dmaven.wagon.http.progress=false 7 | 8 | clean: 9 | mvn clean 10 | 11 | test_compile: 12 | mvn test-compile -Dmaven.wagon.http.progress=false 13 | 14 | run: 15 | mvn gauge:execute -Dtags="$(TAGS)" -Dmaven.wagon.http.progress=false 16 | 17 | docker: docker/build docker/run 18 | 19 | docker/stop: 20 | docker stop $$(docker ps -q --filter ancestor=wiremock-graphql-extension:latest) 21 | 22 | docker/build: 23 | cd ../wiremock-graphql-extension && docker build -t wiremock-graphql-extension:latest . 24 | 25 | docker/run: 26 | docker run -p 8888:8080 -d wiremock-graphql-extension:latest 27 | -------------------------------------------------------------------------------- /e2e/env/default/default.properties: -------------------------------------------------------------------------------- 1 | # default.properties 2 | # properties set here will be available to the test execution as environment variables 3 | 4 | # sample_key = sample_value 5 | 6 | # The path to the gauge reports directory. Should be either relative to the project directory or an absolute path 7 | gauge_reports_dir = reports 8 | 9 | # Set as false if gauge reports should not be overwritten on each execution. A new time-stamped directory will be created on each execution. 10 | overwrite_reports = true 11 | 12 | # Set to false to disable screenshots on failure in reports. 13 | screenshot_on_failure = true 14 | 15 | # The path to the gauge logs directory. Should be either relative to the project directory or an absolute path 16 | logs_directory = logs 17 | 18 | # Set to true to use multithreading for parallel execution 19 | enable_multithreading = false 20 | 21 | # Possible values for this property are 'suite', 'spec' or 'scenario'. 22 | # 'scenario' clears the objects after the execution of each scenario, new objects are created for next execution. 23 | gauge_clear_state_level = scenario 24 | 25 | # The path the gauge specifications directory. Takes a comma separated list of specification files/directories. 26 | gauge_specs_dir = specs 27 | 28 | # The default delimiter used read csv files. 29 | csv_delimiter = , 30 | 31 | # Allows steps to be written in multiline 32 | allow_multiline_step = false -------------------------------------------------------------------------------- /e2e/env/default/java.properties: -------------------------------------------------------------------------------- 1 | 2 | # Specify an alternate Java home if you want to use a custom version 3 | gauge_java_home = 4 | 5 | # IntelliJ and Eclipse out directory will be usually autodetected 6 | # Use the below property if you need to override the build path 7 | gauge_custom_build_path = 8 | 9 | # specify the directory where additional libs are kept 10 | # you can specify multiple directory names separated with a comma (,) 11 | gauge_additional_libs = libs/* 12 | 13 | # JVM arguments passed to java while launching. Enter multiple values separated by comma (,) eg. Xmx1024m, Xms128m 14 | gauge_jvm_args = 15 | 16 | # specify the directory containing java files to be compiled 17 | # you can specify multiple directory names separated with a comma (,) 18 | gauge_custom_compile_dir = 19 | 20 | # specify the level at which the objects should be cleared 21 | # Possible values are suite, spec and scenario. Default value is scenario. 22 | gauge_clear_state_level = scenario 23 | -------------------------------------------------------------------------------- /e2e/fixtures/alias-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ mainHero: hero { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/alias-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ mainHero: hero { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/alias-not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ main_hero: hero { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/alias-not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ mainHero: hero { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/argument-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ hero(episode: NEWHOPE) { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/argument-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ hero(episode: NEWHOPE) { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/argument-not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ hero(episode: EMPIRE) { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/argument-not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ hero(episode: NEWHOPE) { name } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/exact-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query { hero { name }}" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/exact-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query { hero { name }}" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/fragment-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { name } { hero { ...heroDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/fragment-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { name } { hero { ...heroDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/fragment-name-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { name } { hero { ...heroDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/fragment-name-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment differentHeroDetails on Hero { name } { hero { ...heroDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/fragment-set-not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { name age } { hero { ...heroDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/fragment-set-not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { name } { hero { ...heroDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/multiple-fragments-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroInfo on Hero { name } fragment sidekickInfo on Sidekick { name age } { hero { ...heroInfo }, sidekick { ...sidekickInfo } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/multiple-fragments-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { name } fragment sidekickDetails on Sidekick { name age } { hero { ...heroDetails }, sidekick { ...sidekickDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/multiple-fragments-not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroInfo on Hero { name } fragment sidekickInfo on Sidekick { name age } { hero { ...heroInfo }, sidekick { ...sidekickInfo } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/multiple-fragments-not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroDetails on Hero { age } fragment sidekickDetails on Sidekick { name age } { hero { ...heroDetails }, sidekick { ...sidekickDetails } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query { hero { namea }}" 3 | } 4 | -------------------------------------------------------------------------------- /e2e/fixtures/not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query { hero { name }}" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/order-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query { hero { name id }}" 3 | } 4 | -------------------------------------------------------------------------------- /e2e/fixtures/order-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query { hero { id name }}" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/query-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "fragment heroInfo on Hero { name } fragment sidekickInfo on Sidekick { name age } { hero { ...heroInfo }, sidekick { ...sidekickInfo } }" 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/query-match/setup-query.graphql: -------------------------------------------------------------------------------- 1 | fragment heroDetails on Hero { name } fragment sidekickDetails on Sidekick { name age } { hero { ...heroDetails }, sidekick { ...sidekickDetails } } -------------------------------------------------------------------------------- /e2e/fixtures/query-variables-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "{ hero(id: 1) { name, friends { name } } }", 3 | "variables": { 4 | "id": 1 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/query-variables-match/setup-query.graphql: -------------------------------------------------------------------------------- 1 | { hero(id: 1) { name, friends { name } } } -------------------------------------------------------------------------------- /e2e/fixtures/query-variables-match/setup-variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": 1 3 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-array-order-not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetCharacters($ids: [ID!]) { characters(ids: $ids) { name age } }", 3 | "variables": { 4 | "ids": [1, 2, 3] 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-array-order-not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetCharacters($ids: [ID!]) { characters(ids: $ids) { name age } }", 3 | "variables": { 4 | "ids": [3, 1, 2] 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-complex-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetCharacters($ids: [ID!], $info: CharacterInfo) { characters(ids: $ids, info: $info) { name age } }", 3 | "variables": { 4 | "ids": [1, 2, 3], 5 | "info": { 6 | "showOnlyActive": true, 7 | "minAge": 18 8 | } 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /e2e/fixtures/variables-complex-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetCharacters($ids: [ID!], $info: CharacterInfo) { characters(ids: $ids, info: $info) { name age } }", 3 | "variables": { 4 | "info": { 5 | "minAge": 18, 6 | "showOnlyActive": true 7 | }, 8 | "ids": [1, 2, 3] 9 | } 10 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetHero($episode: Episode) { hero(episode: $episode) { age name } }", 3 | "variables": { 4 | "episode": "JEDI" 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetHero($episode: Episode) { hero(episode: $episode) { age name } }", 3 | "variables": { 4 | "episode": "JEDI" 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-not-match/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetHero($episode: Episode) { hero(episode: $episode) { name age } }", 3 | "variables": { 4 | "episode": "JEDI" 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/variables-not-match/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetHero($episode: Episode) { hero(episode: $episode) { name age } }", 3 | "variables": { 4 | "episode": "EMPIRE" 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/with-other/request.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetHero($episode: Episode) { hero(episode: $episode) { name age } }", 3 | "variables": { 4 | "episode": "EMPIRE" 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/fixtures/with-other/setup.json: -------------------------------------------------------------------------------- 1 | { 2 | "query": "query GetHero($episode: Episode) { hero(episode: $episode) { name age } }", 3 | "variables": { 4 | "episode": "EMPIRE" 5 | } 6 | } -------------------------------------------------------------------------------- /e2e/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "Language": "java", 3 | "Plugins": [ 4 | "html-report" 5 | ] 6 | } -------------------------------------------------------------------------------- /e2e/pom.xml: -------------------------------------------------------------------------------- 1 | 4 | 4.0.0 5 | 6 | io.github.nilwurtz 7 | e2e 8 | 1.0-SNAPSHOT 9 | 10 | 11 | true 12 | 11 13 | 1.9.10 14 | ${java.version} 15 | 0.11.1 16 | 1.6.3 17 | 3.0.3 18 | UTF-8 19 | 20 | 21 | 22 | 23 | 24 | wiremock-graphql-extension 25 | io.github.nilwurtz 26 | 0.9.0 27 | 28 | 29 | com.thoughtworks.gauge 30 | gauge-java 31 | ${gauge.java.version} 32 | test 33 | 34 | 35 | org.jetbrains.kotlin 36 | kotlin-stdlib 37 | ${kotlin.version} 38 | 39 | 40 | org.wiremock 41 | wiremock 42 | ${wiremock.version} 43 | test 44 | 45 | 46 | 47 | 48 | src/test/kotlin 49 | 50 | 51 | src/test/resources 52 | 53 | 54 | 55 | 56 | com.thoughtworks.gauge.maven 57 | gauge-maven-plugin 58 | ${gauge.maven.version} 59 | 60 | 61 | test 62 | 63 | specs 64 | 65 | 66 | execute 67 | 68 | 69 | 70 | 71 | 72 | org.jetbrains.kotlin 73 | kotlin-maven-plugin 74 | ${kotlin.version} 75 | 76 | 77 | -Xjsr305=strict 78 | 79 | ${kotlin.compiler.jvmTarget} 80 | 81 | 82 | 83 | compile 84 | compile 85 | 86 | compile 87 | 88 | 89 | 90 | test-compile 91 | test-compile 92 | 93 | test-compile 94 | 95 | 96 | 97 | src/test/kotlin 98 | 99 | 100 | 101 | 102 | 103 | 104 | org.apache.maven.plugins 105 | maven-compiler-plugin 106 | 107 | 11 108 | 11 109 | 110 | 111 | 112 | 113 | 114 | -------------------------------------------------------------------------------- /e2e/specs/match.spec: -------------------------------------------------------------------------------- 1 | # Comparing Graphql Request Queries 2 | 3 | ## When JSON perfectly matches, it should match 4 | tags: remote 5 | * Register a stub to return 200 upon receiving json 6 | * Send a POST request to URL "/graphql" with body 7 | * The response status code should be "200" 8 | 9 | ## When JSON does not perfectly match, it should not match 10 | tags: remote 11 | * Register a stub to return 200 upon receiving json 12 | * Send a POST request to URL "/graphql" with body 13 | * The response status code should be "404" 14 | 15 | ## When JSON has different order, it should match 16 | tags: remote 17 | * Register a stub to return 200 upon receiving json 18 | * Send a POST request to URL "/graphql" with body 19 | * The response status code should be "200" 20 | 21 | ## When JSON uses aliases, it should match 22 | tags: remote 23 | * Register a stub to return 200 upon receiving json 24 | * Send a POST request to URL "/graphql" with body 25 | * The response status code should be "200" 26 | 27 | ## When JSON uses different aliases, it should not match 28 | tags: remote 29 | * Register a stub to return 200 upon receiving json 30 | * Send a POST request to URL "/graphql" with body 31 | * The response status code should be "404" 32 | 33 | ## When JSON uses the same fragments, it should match 34 | tags: remote 35 | * Register a stub to return 200 upon receiving json 36 | * Send a POST request to URL "/graphql" with body 37 | * The response status code should be "200" 38 | 39 | ## When JSON uses different fragment names for the same set, it should match 40 | tags: remote 41 | * Register a stub to return 200 upon receiving json 42 | * Send a POST request to URL "/graphql" with body 43 | * The response status code should be "200" 44 | 45 | ## When JSON uses the same fragment names for different sets, it should not match 46 | tags: remote 47 | * Register a stub to return 200 upon receiving json 48 | * Send a POST request to URL "/graphql" with body 49 | * The response status code should be "404" 50 | 51 | ## When the query contains multiple fragments, it should match 52 | tags: remote 53 | * Register a stub to return 200 upon receiving json 54 | * Send a POST request to URL "/graphql" with body 55 | * The response status code should be "200" 56 | 57 | ## When the query contains different sets with multiple fragments, it should not match 58 | tags: remote 59 | * Register a stub to return 200 upon receiving json 60 | * Send a POST request to URL "/graphql" with body 61 | * The response status code should be "404" 62 | 63 | ## When JSON has the same arguments, it should match 64 | tags: remote 65 | * Register a stub to return 200 upon receiving json 66 | * Send a POST request to URL "/graphql" with body 67 | * The response status code should be "200" 68 | 69 | ## When JSON has different arguments, it should not match 70 | tags: remote 71 | * Register a stub to return 200 upon receiving json 72 | * Send a POST request to URL "/graphql" with body 73 | * The response status code should be "404" 74 | 75 | ## Specifying the query should match 76 | * Register a stub to return 200 upon receiving the query 77 | * Send a POST request to URL "/graphql" with body 78 | * The response status code should be "200" 79 | -------------------------------------------------------------------------------- /e2e/specs/variables-match.spec: -------------------------------------------------------------------------------- 1 | # Comparing Graphql Request queries and variables 2 | 3 | ## When the variables match exactly, it matches 4 | tags: remote 5 | * Register a stub to return 200 upon receiving json 6 | * Send a POST request to URL "/graphql" with body 7 | * The response status code should be "200" 8 | 9 | ## When the variables do not match exactly, it does not match 10 | tags: remote 11 | * Register a stub to return 200 upon receiving json 12 | * Send a POST request to URL "/graphql" with body 13 | * The response status code should be "404" 14 | 15 | ## When the variable properties order differs but the structure matches, it matches 16 | tags: remote 17 | * Register a stub to return 200 upon receiving json 18 | * Send a POST request to URL "/graphql" with body 19 | * The response status code should be "200" 20 | 21 | ## When the order of elements in the variable array differs, it does not match 22 | tags: remote 23 | * Register a stub to return 200 upon receiving json 24 | * Send a POST request to URL "/graphql" with body 25 | * The response status code should be "404" 26 | 27 | ## When the query and variables match, it matches 28 | * Register a stub to return 200 upon receiving the query and variables 29 | * Send a POST request to URL "/graphql" with body 30 | * The response status code should be "200" 31 | -------------------------------------------------------------------------------- /e2e/specs/with-other-mappings.spec: -------------------------------------------------------------------------------- 1 | # With other mappings 2 | 3 | ## When using a mapping with this extension and normal mapping, graphql request should work 4 | tags: remote 5 | * Register a stub to return 200 upon receiving json 6 | * Register a normal stub to return 200 upon receiving json "{\"foo\": \"bar\"}" 7 | * Send a POST request to URL "/graphql" with body 8 | * The response status code should be "200" 9 | 10 | ## When using a mapping with this extension and normal mapping, normal request should work 11 | tags: remote 12 | * Register a stub to return 200 upon receiving json 13 | * Register a normal stub to return 200 upon receiving json "{\"foo\": \"bar\"}" 14 | * Send a POST request to URL "/graphql" with body "{\"foo\": \"bar\"}" 15 | * The response status code should be "200" 16 | 17 | ## When using a mapping with this extension and normal mapping (change order), normal request should work 18 | tags: remote 19 | * Register a normal stub to return 200 upon receiving json "{\"foo\": \"bar\"}" 20 | * Register a stub to return 200 upon receiving json 21 | * Send a POST request to URL "/graphql" with body "{\"foo\": \"bar\"}" 22 | * The response status code should be "200" 23 | -------------------------------------------------------------------------------- /e2e/src/test/kotlin/Configuration.kt: -------------------------------------------------------------------------------- 1 | import java.util.* 2 | 3 | object Configuration { 4 | var baseUrl: String 5 | 6 | init { 7 | Properties().apply { 8 | load(Configuration::class.java.classLoader.getResourceAsStream("config.properties")) 9 | } 10 | .let { 11 | baseUrl = it.getProperty("baseUrl") 12 | } 13 | } 14 | 15 | } -------------------------------------------------------------------------------- /e2e/src/test/kotlin/Datastore.kt: -------------------------------------------------------------------------------- 1 | import com.github.tomakehurst.wiremock.WireMockServer 2 | import com.github.tomakehurst.wiremock.client.WireMock 3 | import com.thoughtworks.gauge.datastore.SuiteDataStore 4 | 5 | object Datastore { 6 | fun localServer(): WireMockServer? { 7 | return SuiteDataStore.get("localServer") as WireMockServer? 8 | } 9 | 10 | fun localServer(client: WireMockServer) { 11 | SuiteDataStore.put("localServer", client) 12 | } 13 | 14 | fun client(): WireMock? { 15 | return SuiteDataStore.get("client") as WireMock? 16 | } 17 | 18 | fun client(client: WireMock) { 19 | SuiteDataStore.put("client", client) 20 | } 21 | 22 | fun statusCode(): Int? { 23 | return SuiteDataStore.get("statusCode") as Int? 24 | } 25 | 26 | fun statusCode(statusCode: Int) { 27 | SuiteDataStore.put("statusCode", statusCode) 28 | } 29 | } -------------------------------------------------------------------------------- /e2e/src/test/kotlin/ExecutionHooks.kt: -------------------------------------------------------------------------------- 1 | import com.github.tomakehurst.wiremock.WireMockServer 2 | import com.github.tomakehurst.wiremock.client.WireMock 3 | import com.github.tomakehurst.wiremock.common.ConsoleNotifier 4 | import com.github.tomakehurst.wiremock.core.WireMockConfiguration.wireMockConfig 5 | import com.thoughtworks.gauge.AfterSuite 6 | import com.thoughtworks.gauge.BeforeScenario 7 | import com.thoughtworks.gauge.BeforeSuite 8 | import io.github.nilwurtz.GraphqlBodyMatcher 9 | 10 | class ExecutionHooks { 11 | 12 | @BeforeSuite() 13 | fun setupSuite() { 14 | // for remote 15 | WireMock(8888).let { Datastore.client(it) } 16 | 17 | // for local 18 | WireMockServer( 19 | wireMockConfig() 20 | .port(8080) 21 | .extensions(GraphqlBodyMatcher::class.java) 22 | .notifier(ConsoleNotifier(true)) 23 | ) 24 | .let { 25 | Datastore.localServer(it); 26 | it.start() 27 | } 28 | } 29 | 30 | @AfterSuite() 31 | fun tearDownSuite() { 32 | Datastore.localServer()?.shutdown() 33 | } 34 | 35 | @BeforeScenario() 36 | fun setupScenario() { 37 | Datastore.client()?.let { 38 | it.resetMappings() 39 | it.resetRequests() 40 | } 41 | Datastore.localServer()?.let { 42 | it.resetMappings() 43 | it.resetRequests() 44 | } 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /e2e/src/test/kotlin/Steps.kt: -------------------------------------------------------------------------------- 1 | import com.github.tomakehurst.wiremock.client.WireMock.* 2 | import com.thoughtworks.gauge.Step 3 | import io.github.nilwurtz.GraphqlBodyMatcher 4 | import java.net.URI 5 | import java.net.http.HttpClient 6 | import java.net.http.HttpRequest 7 | import java.net.http.HttpResponse 8 | 9 | class Steps { 10 | @Step("Register a stub to return 200 upon receiving json ") 11 | fun setupGraphqlJsonStub(json: String) { 12 | // for remote 13 | Datastore.client()?.register( 14 | post(urlEqualTo("/graphql")) 15 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(json)).willReturn(ok()) 16 | ) 17 | // for local 18 | Datastore.localServer() 19 | ?.stubFor( 20 | post(urlEqualTo("/graphql")) 21 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(json)) 22 | .willReturn(ok()) 23 | ) 24 | } 25 | 26 | @Step("Register a normal stub to return 200 upon receiving json ") 27 | fun setupJsonPostStub(json: String) { 28 | Datastore.client()?.register( 29 | post(urlEqualTo("/graphql")) 30 | .withRequestBody(equalToJson(json)).willReturn(ok()) 31 | ) 32 | Datastore.localServer() 33 | ?.stubFor( 34 | post(urlEqualTo("/graphql")) 35 | .withRequestBody(equalToJson(json)) 36 | .willReturn(ok()) 37 | ) 38 | } 39 | 40 | @Step("Register a stub to return 200 upon receiving the query") 41 | fun setupGraphqlQueryStub(query: String) { 42 | Datastore.localServer() 43 | ?.stubFor( 44 | post(urlEqualTo("/graphql")) 45 | .andMatching(GraphqlBodyMatcher.withRequestQueryAndVariables(query)).willReturn(ok()) 46 | ) 47 | } 48 | 49 | @Step("Register a stub to return 200 upon receiving the query and variables") 50 | fun setupGraphqlQueryAndVariables(query: String, variables: String) { 51 | Datastore.localServer() 52 | ?.stubFor( 53 | post(urlEqualTo("/graphql")) 54 | .andMatching(GraphqlBodyMatcher.withRequestQueryAndVariables(query, variables)).willReturn(ok()) 55 | ) 56 | } 57 | 58 | @Step("Send a POST request to URL with body ") 59 | fun requestPost(uri: String, json: String) { 60 | println("Sending request to ${Configuration.baseUrl + uri} with body $json") 61 | HttpClient.newHttpClient().sendAsync( 62 | HttpRequest.newBuilder(URI.create(Configuration.baseUrl + uri)) 63 | .POST(HttpRequest.BodyPublishers.ofString(json)) 64 | .build(), 65 | HttpResponse.BodyHandlers.ofString() 66 | ).join().let { Datastore.statusCode(it.statusCode()) } 67 | } 68 | 69 | @Step("The response status code should be ") 70 | fun assertStatusCode(statusCode: Int) { 71 | Datastore.statusCode()?.let { assert(it == statusCode) } 72 | } 73 | } -------------------------------------------------------------------------------- /e2e/src/test/resources/config.properties: -------------------------------------------------------------------------------- 1 | baseUrl=http://localhost:8080 -------------------------------------------------------------------------------- /examples/testcontainers-java/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /examples/testcontainers-java/README.md: -------------------------------------------------------------------------------- 1 | # Wiremock Graphql Extension Example - testcontainers-java 2 | 3 | This example shows how to use the Wiremock Graphql Extension with java and Testcontainers. 4 | 5 | ## Requirements 6 | - Java 17 7 | - Maven 3 8 | - Docker 9 | 10 | ## Run 11 | ```bash 12 | mvn test 13 | ``` -------------------------------------------------------------------------------- /examples/testcontainers-java/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | testcontainers-java-example 8 | org.example 9 | testcontainers-java 10 | 1.0-SNAPSHOT 11 | 12 | 13 | 17 14 | 17 15 | UTF-8 16 | 1.0-alpha-13 17 | 1.19.0 18 | 0.9.0 19 | 3.3.1 20 | 5.10.1 21 | 22 | 23 | 24 | 25 | mavenCentral 26 | https://repo1.maven.org/maven2/ 27 | 28 | 29 | jitpack.io 30 | https://jitpack.io 31 | 32 | 33 | 34 | 35 | 36 | 37 | org.junit.jupiter 38 | junit-jupiter-api 39 | ${junit.version} 40 | test 41 | 42 | 43 | org.junit.jupiter 44 | junit-jupiter-engine 45 | ${junit.version} 46 | test 47 | 48 | 49 | org.wiremock 50 | wiremock 51 | ${wiremock.version} 52 | test 53 | 54 | 55 | com.github.wiremock 56 | wiremock-testcontainers-java 57 | ${wiremock.testcontainers.version} 58 | test 59 | 60 | 61 | org.testcontainers 62 | testcontainers 63 | ${testcontainers.version} 64 | test 65 | 66 | 67 | org.testcontainers 68 | junit-jupiter 69 | ${testcontainers.version} 70 | test 71 | 72 | 73 | io.github.nilwurtz 74 | wiremock-graphql-extension 75 | ${extension.version} 76 | test 77 | 78 | 79 | 80 | 81 | src/test/java 82 | 83 | 84 | org.apache.maven.plugins 85 | maven-dependency-plugin 86 | 3.5.0 87 | 88 | 89 | copy 90 | test-compile 91 | 92 | copy 93 | 94 | 95 | 96 | 97 | io.github.nilwurtz 98 | wiremock-graphql-extension 99 | ${extension.version} 100 | jar-with-dependencies 101 | 102 | wiremock-graphql-extension-jar-with-dependencies.jar 103 | true 104 | 105 | 106 | ${project.build.directory}/test-wiremock-extension 107 | 108 | 109 | 110 | 111 | 112 | maven-surefire-plugin 113 | 3.1.2 114 | 115 | 116 | maven-failsafe-plugin 117 | 3.1.2 118 | 119 | 120 | 121 | -------------------------------------------------------------------------------- /examples/testcontainers-java/src/test/java/TestSample.java: -------------------------------------------------------------------------------- 1 | import com.github.tomakehurst.wiremock.client.WireMock; 2 | import io.github.nilwurtz.GraphqlBodyMatcher; 3 | import org.testcontainers.junit.jupiter.Container; 4 | import org.testcontainers.junit.jupiter.Testcontainers; 5 | import org.testcontainers.utility.DockerImageName; 6 | import org.wiremock.integrations.testcontainers.WireMockContainer; 7 | import org.junit.jupiter.api.Test; 8 | 9 | import java.io.IOException; 10 | import java.net.http.HttpClient; 11 | import java.net.http.HttpResponse; 12 | import java.nio.file.Paths; 13 | import java.util.Collections; 14 | import java.util.Map; 15 | 16 | import static org.junit.jupiter.api.Assertions.*; 17 | 18 | @Testcontainers 19 | public class TestSample { 20 | 21 | private static final String RESPONSE = """ 22 | { 23 | "data": { 24 | "id": 1, 25 | "name": "test" 26 | } 27 | } 28 | """; 29 | @Container 30 | private static final WireMockContainer wiremockContainer = new WireMockContainer( 31 | DockerImageName.parse(WireMockContainer.OFFICIAL_IMAGE_NAME) 32 | .withTag("3.3.1") 33 | ).withExtensions( 34 | GraphqlBodyMatcher.extensionName, 35 | Collections.singleton("io.github.nilwurtz.GraphqlBodyMatcher"), 36 | Collections.singleton( 37 | Paths.get( 38 | "target", 39 | "test-wiremock-extension", 40 | "wiremock-graphql-extension-jar-with-dependencies.jar" 41 | ).toFile() 42 | ) 43 | ); 44 | 45 | @Test 46 | public void testRunning() { 47 | assertTrue(wiremockContainer.isRunning()); 48 | } 49 | 50 | @Test 51 | public void testMatches() throws IOException, InterruptedException { 52 | var query = """ 53 | query { 54 | name 55 | id 56 | } 57 | """; 58 | new WireMock(wiremockContainer.getPort()).register( 59 | WireMock.post(WireMock.urlEqualTo("/graphql")) 60 | .andMatching( 61 | GraphqlBodyMatcher.extensionName, 62 | GraphqlBodyMatcher.parameters(query)) 63 | .willReturn(WireMock.okJson(RESPONSE)));; 64 | 65 | 66 | var client = HttpClient.newHttpClient(); 67 | var request = java.net.http.HttpRequest.newBuilder() 68 | .uri(java.net.URI.create(wiremockContainer.getBaseUrl() + "/graphql")) 69 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(""" 70 | { "query": "query { id name }" }""")) 71 | .build(); 72 | var response = client.send(request, HttpResponse.BodyHandlers.ofString()); 73 | 74 | assertEquals(200, response.statusCode()); 75 | assertEquals(RESPONSE, response.body()); 76 | } 77 | 78 | @Test 79 | public void testMatchesVariables() throws IOException, InterruptedException { 80 | var query = """ 81 | query { 82 | name 83 | id 84 | } 85 | """; 86 | var variables = Map.of("id", 1); 87 | new WireMock(wiremockContainer.getPort()).register( 88 | WireMock.post(WireMock.urlEqualTo("/graphql")) 89 | .andMatching( 90 | GraphqlBodyMatcher.extensionName, 91 | GraphqlBodyMatcher.parameters(query, variables) 92 | ) 93 | .willReturn(WireMock.okJson(RESPONSE))); 94 | 95 | 96 | var client = HttpClient.newHttpClient(); 97 | var request = java.net.http.HttpRequest.newBuilder() 98 | .uri(java.net.URI.create(wiremockContainer.getBaseUrl() + "/graphql")) 99 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(""" 100 | {"query": "query { id name }", "variables": {"id": 1}}""")) 101 | .build(); 102 | var response = client.send(request, HttpResponse.BodyHandlers.ofString()); 103 | 104 | assertEquals(200, response.statusCode()); 105 | assertEquals(RESPONSE, response.body()); 106 | } 107 | 108 | @Test 109 | public void testMatchesVariablesAndOperationName() throws IOException, InterruptedException { 110 | var query = """ 111 | query { 112 | name 113 | id 114 | } 115 | """; 116 | var variables = Map.of("id", 1); 117 | var operationName = "operationName"; 118 | new WireMock(wiremockContainer.getPort()).register( 119 | WireMock.post(WireMock.urlEqualTo("/graphql")) 120 | .andMatching( 121 | GraphqlBodyMatcher.extensionName, 122 | GraphqlBodyMatcher.parameters(query, variables, operationName) 123 | ) 124 | .willReturn(WireMock.okJson(RESPONSE))); 125 | 126 | 127 | var client = HttpClient.newHttpClient(); 128 | var request = java.net.http.HttpRequest.newBuilder() 129 | .uri(java.net.URI.create(wiremockContainer.getBaseUrl() + "/graphql")) 130 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(""" 131 | {"query": "query { id name }", "variables": {"id": 1}, "operationName": "operationName"}""")) 132 | .build(); 133 | var response = client.send(request, HttpResponse.BodyHandlers.ofString()); 134 | 135 | assertEquals(200, response.statusCode()); 136 | assertEquals(RESPONSE, response.body()); 137 | } 138 | } -------------------------------------------------------------------------------- /examples/testcontainers-kotlin/.gitignore: -------------------------------------------------------------------------------- 1 | target/ 2 | !.mvn/wrapper/maven-wrapper.jar 3 | !**/src/main/**/target/ 4 | !**/src/test/**/target/ 5 | 6 | ### IntelliJ IDEA ### 7 | .idea/modules.xml 8 | .idea/jarRepositories.xml 9 | .idea/compiler.xml 10 | .idea/libraries/ 11 | *.iws 12 | *.iml 13 | *.ipr 14 | 15 | ### Eclipse ### 16 | .apt_generated 17 | .classpath 18 | .factorypath 19 | .project 20 | .settings 21 | .springBeans 22 | .sts4-cache 23 | 24 | ### NetBeans ### 25 | /nbproject/private/ 26 | /nbbuild/ 27 | /dist/ 28 | /nbdist/ 29 | /.nb-gradle/ 30 | build/ 31 | !**/src/main/**/build/ 32 | !**/src/test/**/build/ 33 | 34 | ### VS Code ### 35 | .vscode/ 36 | 37 | ### Mac OS ### 38 | .DS_Store -------------------------------------------------------------------------------- /examples/testcontainers-kotlin/README.md: -------------------------------------------------------------------------------- 1 | # Wiremock Graphql Extension Example - testcontainers-kotlin 2 | 3 | This example shows how to use the Wiremock Graphql Extension with Kotlin and Testcontainers. 4 | 5 | ## Requirements 6 | - Java 17 7 | - Maven 3 8 | - Docker 9 | 10 | ## Run 11 | ```bash 12 | mvn test 13 | ``` -------------------------------------------------------------------------------- /examples/testcontainers-kotlin/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | testcontainers-kotlin 8 | org.example 9 | 1.0-SNAPSHOT 10 | jar 11 | 12 | testcontainers-kotlin-example 13 | 14 | 15 | UTF-8 16 | official 17 | 17 18 | 17 19 | 17 20 | 1.0-alpha-13 21 | 1.19.0 22 | 1.9.20 23 | 0.9.0 24 | 3.3.1 25 | 5.10.1 26 | 27 | 28 | 29 | 30 | mavenCentral 31 | https://repo1.maven.org/maven2/ 32 | 33 | 34 | jitpack.io 35 | https://jitpack.io 36 | 37 | 38 | 39 | 40 | src/test/kotlin 41 | 42 | 43 | org.jetbrains.kotlin 44 | kotlin-maven-plugin 45 | ${kotlin.version} 46 | 47 | 48 | compile 49 | compile 50 | 51 | compile 52 | 53 | 54 | 55 | test-compile 56 | test-compile 57 | 58 | test-compile 59 | 60 | 61 | 62 | src/test/kotlin 63 | 64 | 65 | 66 | 67 | 68 | 69 | org.apache.maven.plugins 70 | maven-dependency-plugin 71 | 3.5.0 72 | 73 | 74 | copy 75 | test-compile 76 | 77 | copy 78 | 79 | 80 | 81 | 82 | io.github.nilwurtz 83 | wiremock-graphql-extension 84 | ${extension.version} 85 | jar-with-dependencies 86 | 87 | wiremock-graphql-extension-jar-with-dependencies.jar 88 | true 89 | 90 | 91 | ${project.build.directory}/test-wiremock-extension 92 | 93 | 94 | 95 | 96 | 97 | maven-surefire-plugin 98 | 3.2.2 99 | 100 | 101 | maven-failsafe-plugin 102 | 3.2.2 103 | 104 | 105 | 106 | 107 | 108 | 109 | org.junit.jupiter 110 | junit-jupiter-api 111 | ${junit.version} 112 | test 113 | 114 | 115 | org.junit.jupiter 116 | junit-jupiter-engine 117 | ${junit.version} 118 | test 119 | 120 | 121 | org.jetbrains.kotlin 122 | kotlin-stdlib 123 | ${kotlin.version} 124 | 125 | 126 | org.wiremock 127 | wiremock 128 | ${wiremock.version} 129 | test 130 | 131 | 132 | com.github.wiremock 133 | wiremock-testcontainers-java 134 | ${wiremock.testcontainers.version} 135 | test 136 | 137 | 138 | org.testcontainers 139 | testcontainers 140 | ${testcontainers.version} 141 | test 142 | 143 | 144 | org.testcontainers 145 | junit-jupiter 146 | ${testcontainers.version} 147 | test 148 | 149 | 150 | io.github.nilwurtz 151 | wiremock-graphql-extension 152 | ${extension.version} 153 | test 154 | 155 | 156 | 157 | -------------------------------------------------------------------------------- /examples/testcontainers-kotlin/src/test/kotlin/TestSample.kt: -------------------------------------------------------------------------------- 1 | import com.github.tomakehurst.wiremock.client.WireMock 2 | import io.github.nilwurtz.GraphqlBodyMatcher 3 | import org.junit.jupiter.api.Assertions.* 4 | import org.junit.jupiter.api.Test 5 | import org.testcontainers.junit.jupiter.Container 6 | import org.testcontainers.junit.jupiter.Testcontainers 7 | import org.testcontainers.utility.DockerImageName 8 | import org.wiremock.integrations.testcontainers.WireMockContainer 9 | import java.net.http.HttpClient 10 | import java.nio.file.Paths 11 | import java.util.* 12 | 13 | @Testcontainers 14 | class TestSample { 15 | 16 | @Container 17 | val wireMockContainer: WireMockContainer = 18 | WireMockContainer( 19 | DockerImageName.parse(WireMockContainer.OFFICIAL_IMAGE_NAME + ":3.3.1") 20 | ) 21 | .withExtensions( 22 | GraphqlBodyMatcher.extensionName, 23 | Collections.singleton("io.github.nilwurtz.GraphqlBodyMatcher"), 24 | Collections.singleton( 25 | Paths.get( 26 | "target", 27 | "test-wiremock-extension", 28 | "wiremock-graphql-extension-jar-with-dependencies.jar" 29 | ).toFile() 30 | ) 31 | ) 32 | 33 | @Test 34 | fun testRunning() { 35 | assertTrue { wireMockContainer.isRunning } 36 | } 37 | 38 | @Test 39 | fun testMatches() { 40 | val query = """ 41 | query { 42 | name 43 | id 44 | } 45 | """.trimIndent() 46 | WireMock(wireMockContainer.port).register( 47 | WireMock.post(WireMock.urlEqualTo("/graphql")) 48 | .andMatching( 49 | GraphqlBodyMatcher.extensionName, 50 | GraphqlBodyMatcher.parameters(query) 51 | ) 52 | .willReturn(WireMock.okJson("""{"data": {"id": 1, "name": "test"}}""")) 53 | ) 54 | 55 | 56 | val client = HttpClient.newHttpClient() 57 | val request = java.net.http.HttpRequest.newBuilder() 58 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 59 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString("""{"query": "query { id name }"}""")) 60 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 61 | 62 | assertEquals(200, request.statusCode()) 63 | assertEquals("""{"data": {"id": 1, "name": "test"}}""", request.body()) 64 | } 65 | 66 | @Test 67 | fun testMatchesVariables() { 68 | val query = """ 69 | query { 70 | name 71 | id 72 | } 73 | """.trimIndent() 74 | val variables = mapOf("id" to 1) 75 | WireMock(wireMockContainer.port).register( 76 | WireMock.post(WireMock.urlEqualTo("/graphql")) 77 | .andMatching( 78 | GraphqlBodyMatcher.extensionName, 79 | GraphqlBodyMatcher.parameters(query, variables) 80 | ) 81 | .willReturn(WireMock.okJson("""{"data": {"id": 1, "name": "test"}}""")) 82 | ) 83 | 84 | val client = HttpClient.newHttpClient() 85 | val request = java.net.http.HttpRequest.newBuilder() 86 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 87 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString("""{"query": "query { id name }", "variables": {"id": 1}}""")) 88 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 89 | 90 | assertEquals(200, request.statusCode()) 91 | assertEquals("""{"data": {"id": 1, "name": "test"}}""", request.body()) 92 | } 93 | 94 | @Test 95 | fun testMatchesVariablesAndOperationName() { 96 | val query = """ 97 | query { 98 | name 99 | id 100 | } 101 | """.trimIndent() 102 | val variables = mapOf("id" to 1) 103 | val operationName = "operationName" 104 | WireMock(wireMockContainer.port).register( 105 | WireMock.post(WireMock.urlEqualTo("/graphql")) 106 | .andMatching( 107 | GraphqlBodyMatcher.extensionName, 108 | GraphqlBodyMatcher.parameters(query, variables, operationName) 109 | ) 110 | .willReturn(WireMock.okJson("""{"data": {"id": 1, "name": "test"}}""")) 111 | ) 112 | 113 | val client = HttpClient.newHttpClient() 114 | val request = java.net.http.HttpRequest.newBuilder() 115 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 116 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString("""{"query": "query { id name }", "variables": {"id": 1}, "operationName": "operationName"}""")) 117 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 118 | 119 | assertEquals(200, request.statusCode()) 120 | assertEquals("""{"data": {"id": 1, "name": "test"}}""", request.body()) 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/.dockerignore: -------------------------------------------------------------------------------- 1 | target 2 | Dockerfile -------------------------------------------------------------------------------- /wiremock-graphql-extension/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM maven:3.6.3-jdk-11-slim AS build 2 | WORKDIR /app 3 | COPY . . 4 | RUN mvn --batch-mode clean package -Dmaven.test.skip=true 5 | 6 | FROM wiremock/wiremock:latest AS wiremock 7 | COPY --from=build /app/target/wiremock-graphql-extension-*-jar-with-dependencies.jar /var/wiremock/extensions/wiremock-graphql-extension-jar-with-dependencies.jar 8 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | wiremock-graphql-extension 8 | io.github.nilwurtz 9 | 0.9.0 10 | jar 11 | wiremock-graphql-extension 12 | A WireMock extension for handling GraphQL requests, allowing for easy mocking of GraphQL APIs in 13 | integration tests and development environments. 14 | 15 | https://github.com/nilwurtz/wiremock-graphql-extension 16 | 17 | 18 | 19 | nilwurtz 20 | nilwurtz 21 | apollo.oth@gmail.com 22 | https://github.com/nilwurtz 23 | 24 | 25 | 26 | 27 | MIT License 28 | https://opensource.org/licenses/mit-license.php 29 | 30 | 31 | 32 | 33 | UTF-8 34 | official 35 | 11 36 | 11 37 | 11 38 | true 39 | 1.9.21 40 | 5.11.1 41 | 3.9.0 42 | 22.3 43 | 1.13.12 44 | 1.0-alpha-13 45 | 1.20.2 46 | 47 | 48 | 49 | 50 | mavenCentral 51 | https://repo1.maven.org/maven2/ 52 | 53 | 54 | jitpack.io 55 | https://jitpack.io 56 | 57 | 58 | 59 | 60 | 61 | org.junit.jupiter 62 | junit-jupiter-api 63 | ${junit.version} 64 | test 65 | 66 | 67 | org.junit.jupiter 68 | junit-jupiter-engine 69 | ${junit.version} 70 | test 71 | 72 | 73 | org.jetbrains.kotlin 74 | kotlin-stdlib 75 | ${kotlin.version} 76 | 77 | 78 | org.wiremock 79 | wiremock 80 | ${wiremock.version} 81 | 82 | 83 | io.mockk 84 | mockk-jvm 85 | ${mockk.version} 86 | test 87 | 88 | 89 | com.graphql-java 90 | graphql-java 91 | ${graphql-java.version} 92 | 93 | 94 | com.github.wiremock 95 | wiremock-testcontainers-java 96 | ${wiremock.testcontainers.version} 97 | test 98 | 99 | 100 | org.testcontainers 101 | testcontainers 102 | ${testcontainers.version} 103 | test 104 | 105 | 106 | org.testcontainers 107 | junit-jupiter 108 | ${testcontainers.version} 109 | test 110 | 111 | 112 | 113 | 114 | src/main/kotlin 115 | src/test/kotlin 116 | 117 | 118 | org.jetbrains.kotlin 119 | kotlin-maven-plugin 120 | ${kotlin.version} 121 | 122 | 123 | compile 124 | compile 125 | 126 | compile 127 | 128 | 129 | 130 | test-compile 131 | test-compile 132 | 133 | test-compile 134 | 135 | 136 | 137 | 138 | 139 | maven-surefire-plugin 140 | 2.22.2 141 | 142 | 143 | maven-failsafe-plugin 144 | 2.22.2 145 | 146 | 147 | org.apache.maven.plugins 148 | maven-gpg-plugin 149 | 1.6 150 | 151 | 152 | sign-artifacts 153 | verify 154 | 155 | sign 156 | 157 | 158 | 159 | 160 | 161 | 162 | --pinentry-mode 163 | loopback 164 | 165 | ${gpg.skip} 166 | 167 | 168 | 169 | org.apache.maven.plugins 170 | maven-source-plugin 171 | 3.2.1 172 | 173 | 174 | attach-sources 175 | 176 | jar 177 | 178 | 179 | 180 | 181 | 182 | org.jetbrains.dokka 183 | dokka-maven-plugin 184 | 1.8.10 185 | 186 | 187 | prepare-package 188 | 189 | dokka 190 | javadoc 191 | javadocJar 192 | 193 | 194 | 195 | 196 | 197 | 198 | org.jetbrains.dokka 199 | kotlin-as-java-plugin 200 | 1.8.10 201 | 202 | 203 | 204 | 205 | 206 | org.sonatype.plugins 207 | nexus-staging-maven-plugin 208 | 1.6.13 209 | true 210 | 211 | ossrh 212 | https://s01.oss.sonatype.org 213 | true 214 | 215 | 216 | 217 | maven-assembly-plugin 218 | 219 | 220 | package 221 | 222 | single 223 | 224 | 225 | 226 | make-assembly 227 | process-test-classes 228 | 229 | single 230 | 231 | 232 | 233 | 234 | io.github.nilwurtz.GraphqlBodyMatcher 235 | 236 | 237 | 238 | jar-with-dependencies 239 | 240 | 241 | 242 | 243 | 244 | 245 | jar-with-dependencies 246 | 247 | 248 | 249 | 250 | org.apache.maven.plugins 251 | maven-dependency-plugin 252 | 253 | 254 | copy 255 | process-test-classes 256 | 257 | copy 258 | 259 | 260 | 261 | 262 | ${project.groupId} 263 | ${project.artifactId} 264 | ${project.version} 265 | jar-with-dependencies 266 | wiremock-graphql-extension.jar 267 | true 268 | 269 | 270 | ${project.build.directory}/test-wiremock-extension 271 | 272 | 273 | 274 | 275 | 276 | 277 | 278 | 279 | 280 | ossrh 281 | https://s01.oss.sonatype.org/content/repositories/snapshots 282 | 283 | 284 | ossrh 285 | https://s01.oss.sonatype.org/service/local/staging/deploy/maven2/ 286 | 287 | 288 | 289 | 290 | scm:git:https://github.com/nilwurtz/wiremock-graphql-extension.git 291 | scm:git:https://github.com/nilwurtz/wiremock-graphql-extension.git 292 | https://github.com/nilwurtz/wiremock-graphql-extension 293 | 294 | 295 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/main/kotlin/io/github/nilwurtz/EqualToGraphqlQueryPattern.kt: -------------------------------------------------------------------------------- 1 | package io.github.nilwurtz 2 | 3 | import com.github.tomakehurst.wiremock.matching.ContentPattern 4 | import com.github.tomakehurst.wiremock.matching.MatchResult 5 | import com.github.tomakehurst.wiremock.stubbing.SubEvent 6 | import graphql.language.AstComparator 7 | import graphql.language.AstSorter 8 | import graphql.language.Document 9 | import graphql.parser.InvalidSyntaxException 10 | import graphql.parser.Parser 11 | 12 | class EqualToGraphqlQueryPattern(expectedValue: String) : ContentPattern(expectedValue) { 13 | 14 | private val expectedDocument = expectedValue.parse().sort() 15 | 16 | override fun match(requestValue: String?): MatchResult { 17 | return try { 18 | val requestDocument = requestValue?.parse()?.sort() 19 | if (AstComparator.isEqual(expectedDocument, requestDocument)) { 20 | MatchResult.exactMatch() 21 | } else { 22 | MatchResult.noMatch() 23 | } 24 | } catch (e: InvalidSyntaxException) { 25 | MatchResult.noMatch(SubEvent.warning(e.message)) 26 | } 27 | } 28 | 29 | override fun getName(): String { 30 | return "equalToGraphqlQuery" 31 | } 32 | 33 | override fun getExpected(): String { 34 | return expectedValue 35 | } 36 | } 37 | 38 | private fun String.parse(): Document { 39 | return Parser().parseDocument(this) 40 | } 41 | 42 | private fun Document.sort(): Document { 43 | return AstSorter().sort(this); 44 | } 45 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/main/kotlin/io/github/nilwurtz/GraphqlBodyMatcher.kt: -------------------------------------------------------------------------------- 1 | package io.github.nilwurtz 2 | 3 | import com.github.tomakehurst.wiremock.common.Json 4 | import com.github.tomakehurst.wiremock.common.JsonException 5 | import com.github.tomakehurst.wiremock.extension.Parameters 6 | import com.github.tomakehurst.wiremock.http.Request 7 | import com.github.tomakehurst.wiremock.matching.AbsentPattern 8 | import com.github.tomakehurst.wiremock.matching.EqualToJsonPattern 9 | import com.github.tomakehurst.wiremock.matching.EqualToPattern 10 | import com.github.tomakehurst.wiremock.matching.MatchResult 11 | import com.github.tomakehurst.wiremock.matching.RequestMatcherExtension 12 | import com.github.tomakehurst.wiremock.matching.StringValuePattern 13 | import com.github.tomakehurst.wiremock.stubbing.SubEvent 14 | import graphql.parser.InvalidSyntaxException 15 | import graphql.parser.Parser 16 | 17 | 18 | class GraphqlBodyMatcher() : RequestMatcherExtension() { 19 | 20 | companion object { 21 | const val extensionName = "graphql-body-matcher" 22 | 23 | /** 24 | * Creates a new instance of [GraphqlBodyMatcher] with the given GraphQL query string and variables. 25 | * The query string and variables are wrapped in a JSON object with "query" and "variables" fields, parsed, validated, 26 | * and normalized before being used for matching. 27 | * 28 | * @param expectedQuery The GraphQL query string that the matcher expects in requests. 29 | * @param expectedVariables The variables associated with the GraphQL query as a JSON string. 30 | * @return A new [GraphqlBodyMatcher] instance with the given expected query and variables. 31 | * @throws JsonException if the generated JSON is malformed. 32 | * @throws InvalidSyntaxException if the given query is invalid. 33 | */ 34 | @Deprecated("Use parameters instead. Along with Wiremock.requestMatching(String, Parameters) or MappingBuilder#andMatching(String, Parameters).") 35 | @JvmStatic 36 | @JvmOverloads 37 | fun withRequestQueryAndVariables(expectedQuery: String, expectedVariables: String? = null): GraphqlBodyMatcher { 38 | // Avoid to parse json here. It will be parsed in initExpectedRequestJson 39 | return GraphqlBodyMatcher().apply { 40 | val variablesJsonOrEmptyString = 41 | if (expectedVariables != null) ""","variables": $expectedVariables""" else "" 42 | initParameters(withRequest("""{"query": "$expectedQuery"$variablesJsonOrEmptyString}""")) 43 | } 44 | } 45 | 46 | /** 47 | * Creates a new instance of [GraphqlBodyMatcher] with the given raw JSON string containing a 48 | * GraphQL query and optional variables. The JSON is expected to have a "query" field with the query string 49 | * and an optional "variables" field containing the variables. 50 | * The query is parsed, validated, and normalized before being used for matching. 51 | * 52 | * @param expectedJson The raw JSON string containing the GraphQL query and optional variables that the matcher expects in requests. 53 | * @return A new [GraphqlBodyMatcher] instance with the given expected query and variables. 54 | * @throws JsonException if the given JSON is malformed. 55 | * @throws InvalidSyntaxException if the given query is invalid. 56 | */ 57 | @Deprecated("Use parameters instead. Along with Wiremock.requestMatching(String, Parameters) or MappingBuilder#andMatching(String, Parameters).") 58 | @JvmStatic 59 | fun withRequestJson(expectedJson: String): GraphqlBodyMatcher { 60 | return GraphqlBodyMatcher().apply { 61 | initParameters(withRequest(expectedJson)) 62 | } 63 | } 64 | 65 | /** 66 | * Creates a Parameters instance containing the given raw JSON string expected in the GraphQL request. 67 | * 68 | * This method is used to set up JSON expected in remote requests. The expectedJson parameter should be a raw JSON string that encapsulates the expected query and optionally variables for the GraphQL request. This string is used to create a parameters object utilized internally in the GraphqlBodyMatcher. 69 | * 70 | * @param expectedJson A raw JSON string that contains the GraphQL query and optionally variables expected in the requests. 71 | * @return A Parameters instance created based on the expected JSON string. 72 | * @throws JsonException if the given JSON is malformed. 73 | * @throws InvalidSyntaxException if the given query is invalid. 74 | */ 75 | @Deprecated("Use parameters instead.") 76 | @JvmStatic 77 | fun withRequest(expectedJson: String): Parameters { 78 | val expectedJsonObject = Json.read(expectedJson.replace("\n", ""), Map::class.java) 79 | return parameters( 80 | expectedJsonObject["query"] as String, 81 | expectedJsonObject["variables"] as Map?, 82 | expectedJsonObject["operationName"] as String?) 83 | } 84 | 85 | /** 86 | * Creates a Parameters instance containing the query and optionally the variables and operationName. 87 | * 88 | * @param query A GraphQL query string. 89 | * @param variables An optional map of variables used in the GraphQL query. 90 | * @param operationName The optional name of the operation in the GraphQL query. 91 | * @return A Parameters instance containing the query and optionally the variables and operationName. 92 | * @throws InvalidSyntaxException if the given query is invalid. 93 | * @see GraphQL Queries and Mutations 94 | * @see GraphQL Variables 95 | * @see GraphQL Operation Name 96 | */ 97 | @JvmStatic 98 | @JvmOverloads 99 | fun parameters(query: String, variables: Map? = null, operationName: String? = null): Parameters { 100 | Parser().parseDocument(query) 101 | return Parameters.one("query", query).apply { 102 | variables?.let { put("variables", it) } 103 | operationName?.let { put("operationName", it) } 104 | } 105 | } 106 | } 107 | 108 | private lateinit var parameters: Parameters 109 | 110 | private fun initParameters(parameters: Parameters) { 111 | this.parameters = parameters 112 | } 113 | 114 | /** 115 | * Compares the given [Request] against the expected GraphQL query, variables, and operationName to determine if 116 | * they match. If query, variables, and operationName are semantically equal, it returns an exact match result; 117 | * otherwise, it returns a no match result. 118 | * 119 | * @param request The incoming request to match against the expected query and variables. 120 | * @param parameters Additional parameters that may be used for matching. 121 | * @return [MatchResult.exactMatch] if the request query and variables match the expected query and variables, 122 | * [MatchResult.noMatch] otherwise. 123 | */ 124 | override fun match(request: Request, parameters: Parameters): MatchResult { 125 | try { 126 | // for local call 127 | if (parameters.isEmpty()) { 128 | parameters.putAll(this.parameters) 129 | } 130 | val expectedQuery = parameters.getString("query") 131 | val expectedVariables = parameters["variables"]?.writeJson() 132 | val expectedOperationName = parameters.getString("operationName", null) 133 | 134 | val requestJson = Json.read(request.bodyAsString, Map::class.java) 135 | val requestQuery = requestJson["query"] as String 136 | val requestVariables = requestJson["variables"]?.writeJson() 137 | val requestOperationName = requestJson["operationName"] as String? 138 | 139 | return MatchResult.aggregate( 140 | EqualToGraphqlQueryPattern(expectedQuery).match(requestQuery), 141 | variablesPattern(expectedVariables).match(requestVariables), 142 | operationNamePattern(expectedOperationName).match(requestOperationName) 143 | ) 144 | } catch (e: Exception) { 145 | return MatchResult.noMatch(SubEvent.warning(e.message)) 146 | } 147 | } 148 | 149 | private fun variablesPattern(expectedVariables: String?) : StringValuePattern { 150 | return if (expectedVariables == null) { 151 | AbsentPattern.ABSENT 152 | } else { 153 | EqualToJsonPattern(expectedVariables, false, false) 154 | } 155 | } 156 | 157 | private fun operationNamePattern(expectedOperationName: String?) : StringValuePattern { 158 | return if (expectedOperationName == null) { 159 | AbsentPattern.ABSENT 160 | } else { 161 | EqualToPattern(expectedOperationName) 162 | } 163 | } 164 | 165 | override fun getName(): String { 166 | return extensionName 167 | } 168 | } 169 | 170 | private fun Any.writeJson(): String { 171 | return Json.write(this) 172 | } 173 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/main/resources/META-INF/services/com.github.tomakehurst.wiremock.extension.Extension: -------------------------------------------------------------------------------- 1 | io.github.nilwurtz.GraphqlBodyMatcher 2 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/kotlin/io/github/nilwurtz/EqualToGraphqlQueryPatternTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.nilwurtz 2 | 3 | import graphql.parser.InvalidSyntaxException 4 | import org.junit.jupiter.api.Test 5 | import org.junit.jupiter.api.Assertions.* 6 | 7 | class EqualToGraphqlQueryPatternTest { 8 | 9 | @Test 10 | fun testMatchedIdentical() { 11 | val query = """ 12 | { 13 | hero { 14 | name 15 | friends { 16 | name 17 | } 18 | } 19 | } 20 | """.trimIndent() 21 | val pattern = EqualToGraphqlQueryPattern(query) 22 | val result = pattern.match(query) 23 | assertTrue(result.isExactMatch) 24 | } 25 | 26 | @Test 27 | fun testMatchedDifferentOrderSingleLevel() { 28 | val query1 = """ 29 | { 30 | hero { 31 | name 32 | age 33 | height 34 | } 35 | } 36 | """.trimIndent() 37 | val query2 = """ 38 | { 39 | hero { 40 | age 41 | height 42 | name 43 | } 44 | } 45 | """.trimIndent() 46 | val pattern = EqualToGraphqlQueryPattern(query1) 47 | val result = pattern.match(query2) 48 | assertTrue(result.isExactMatch) 49 | } 50 | 51 | @Test 52 | fun testMatchedDifferentOrderNested() { 53 | val query1 = """ 54 | { 55 | hero { 56 | name 57 | friends { 58 | name 59 | friends { 60 | name 61 | age 62 | } 63 | } 64 | } 65 | } 66 | """.trimIndent() 67 | val query2 = """ 68 | { 69 | hero { 70 | name 71 | friends { 72 | name 73 | friends { 74 | age 75 | name 76 | } 77 | } 78 | } 79 | } 80 | """.trimIndent() 81 | val pattern = EqualToGraphqlQueryPattern(query1) 82 | val result = pattern.match(query2) 83 | assertTrue(result.isExactMatch) 84 | } 85 | 86 | @Test 87 | fun testUnmatchedDifferentDepth() { 88 | val query1 = """ 89 | { 90 | hero { 91 | name 92 | friends { 93 | name 94 | } 95 | } 96 | } 97 | """.trimIndent() 98 | val query2 = """ 99 | { 100 | hero { 101 | name 102 | friends { 103 | name { 104 | first 105 | last 106 | } 107 | } 108 | } 109 | } 110 | """.trimIndent() 111 | val pattern = EqualToGraphqlQueryPattern(query1) 112 | val result = pattern.match(query2) 113 | assertFalse(result.isExactMatch) 114 | } 115 | 116 | @Test 117 | fun testUnmatchedMissingField() { 118 | val query1 = """ 119 | { 120 | hero { 121 | friends { 122 | name 123 | } 124 | } 125 | } 126 | """.trimIndent() 127 | val query2 = """ 128 | { 129 | hero { 130 | name 131 | friends { 132 | name 133 | } 134 | } 135 | } 136 | """.trimIndent() 137 | val pattern = EqualToGraphqlQueryPattern(query1) 138 | val result = pattern.match(query2) 139 | assertFalse(result.isExactMatch) 140 | } 141 | 142 | @Test 143 | fun testUnmatchedAdditionalField() { 144 | val query1 = """ 145 | { 146 | hero { 147 | name 148 | friends { 149 | name 150 | } 151 | } 152 | } 153 | """.trimIndent() 154 | val query2 = """ 155 | { 156 | hero { 157 | name 158 | friends { 159 | namea 160 | } 161 | } 162 | } 163 | """.trimIndent() 164 | val pattern = EqualToGraphqlQueryPattern(query1) 165 | val result = pattern.match(query2) 166 | assertFalse(result.isExactMatch) 167 | } 168 | 169 | @Test 170 | fun testUnmatchedDifferentFieldName() { 171 | val query1 = """ 172 | { 173 | hero { 174 | name 175 | friends { 176 | name 177 | } 178 | } 179 | } 180 | """.trimIndent() 181 | val query2 = """ 182 | { 183 | hero { 184 | name 185 | friends { 186 | firstName 187 | } 188 | } 189 | } 190 | """.trimIndent() 191 | val pattern = EqualToGraphqlQueryPattern(query1) 192 | val result = pattern.match(query2) 193 | assertFalse(result.isExactMatch) 194 | } 195 | 196 | @Test 197 | fun testInvalidQuery() { 198 | val query = """ 199 | { 200 | hero { 201 | name 202 | age 203 | height 204 | """.trimIndent() 205 | org.junit.jupiter.api.assertThrows { 206 | GraphqlBodyMatcher.parameters(query) 207 | } 208 | } 209 | } 210 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/kotlin/io/github/nilwurtz/GraphqlBodyMatcherTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.nilwurtz 2 | 3 | import com.github.tomakehurst.wiremock.common.JsonException 4 | import org.junit.jupiter.api.DisplayName 5 | import org.junit.jupiter.api.Test 6 | import com.github.tomakehurst.wiremock.http.Request 7 | import com.github.tomakehurst.wiremock.extension.Parameters 8 | import graphql.parser.InvalidSyntaxException 9 | import io.mockk.every 10 | import io.mockk.mockk 11 | import org.junit.jupiter.api.Assertions.* 12 | import org.junit.jupiter.api.Nested 13 | import org.junit.jupiter.api.assertThrows 14 | 15 | class GraphqlBodyMatcherTest { 16 | 17 | companion object { 18 | const val QUERY = "{ query }" 19 | const val WRONG_QUERY = "{ wrong }"; 20 | const val VARIABLES = """ 21 | { 22 | "abc": 123 23 | } 24 | """ 25 | const val WRONG_VARIABLES = """ 26 | { 27 | "def": 456 28 | } 29 | """ 30 | const val OPERATION_NAME = "operationName" 31 | const val WRONG_OPERATION_NAME = "" 32 | val VARIABLES_MAP = mapOf("abc" to 123) 33 | } 34 | 35 | @Test 36 | fun testAllMatch() { 37 | val request = mockk() 38 | val json = """ 39 | { 40 | "query": "$QUERY", 41 | "variables": $VARIABLES, 42 | "operationName": "$OPERATION_NAME" 43 | } 44 | """.trimIndent() 45 | 46 | every { request.bodyAsString } returns json 47 | 48 | val actual = 49 | GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP, OPERATION_NAME)) 50 | assertTrue(actual.isExactMatch) 51 | } 52 | 53 | @Test 54 | fun testAllWithWrongQuery() { 55 | val request = mockk() 56 | val json = """ 57 | { 58 | "query": "$WRONG_QUERY", 59 | "variables": $VARIABLES, 60 | "operationName": "$OPERATION_NAME" 61 | } 62 | """.trimIndent() 63 | 64 | every { request.bodyAsString } returns json 65 | 66 | val actual = 67 | GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP, OPERATION_NAME)) 68 | assertFalse(actual.isExactMatch) 69 | assertEquals(1.0 / 3.0, actual.distance) 70 | } 71 | 72 | @Test 73 | fun testAllWithWrongVariables() { 74 | val request = mockk() 75 | val json = """ 76 | { 77 | "query": "$QUERY", 78 | "variables": $WRONG_VARIABLES, 79 | "operationName": "$OPERATION_NAME" 80 | } 81 | """.trimIndent() 82 | 83 | every { request.bodyAsString } returns json 84 | 85 | val actual = 86 | GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP, OPERATION_NAME)) 87 | assertFalse(actual.isExactMatch) 88 | assertEquals(1.0 / 3.0, actual.distance) 89 | } 90 | 91 | @Test 92 | fun testAllWithWrongOperationName() { 93 | val request = mockk() 94 | val json = """ 95 | { 96 | "query": "$QUERY", 97 | "variables": $VARIABLES, 98 | "operationName": "$WRONG_OPERATION_NAME" 99 | } 100 | """.trimIndent() 101 | 102 | every { request.bodyAsString } returns json 103 | 104 | val actual = 105 | GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP, OPERATION_NAME)) 106 | assertFalse(actual.isExactMatch) 107 | assertEquals(1.0 / 3.0, actual.distance) 108 | } 109 | 110 | @Test 111 | fun testOperationNameMatch() { 112 | val request = mockk() 113 | val json = """ 114 | { 115 | "query": "$QUERY", 116 | "operationName": "$OPERATION_NAME" 117 | } 118 | """.trimIndent() 119 | 120 | every { request.bodyAsString } returns json 121 | 122 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, null, OPERATION_NAME)) 123 | assertTrue(actual.isExactMatch) 124 | } 125 | 126 | @Test 127 | fun testOperationNameWithWrongQuery() { 128 | val request = mockk() 129 | val json = """ 130 | { 131 | "query": "$WRONG_QUERY", 132 | "operationName": "$OPERATION_NAME" 133 | } 134 | """.trimIndent() 135 | 136 | every { request.bodyAsString } returns json 137 | 138 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, null, OPERATION_NAME)) 139 | assertFalse(actual.isExactMatch) 140 | assertEquals(1.0 / 3.0, actual.distance) 141 | } 142 | 143 | @Test 144 | fun testOperationNameWithWrongVariables() { 145 | val request = mockk() 146 | val json = """ 147 | { 148 | "query": "$QUERY", 149 | "variables": $WRONG_VARIABLES, 150 | "operationName": "$OPERATION_NAME" 151 | } 152 | """.trimIndent() 153 | 154 | every { request.bodyAsString } returns json 155 | 156 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, null, OPERATION_NAME)) 157 | assertFalse(actual.isExactMatch) 158 | assertEquals(1.0 / 3.0, actual.distance) 159 | } 160 | 161 | @Test 162 | fun testOperationNameWithWrongOperationName() { 163 | val request = mockk() 164 | val json = """ 165 | { 166 | "query": "$QUERY", 167 | "operationName": "$WRONG_OPERATION_NAME" 168 | } 169 | """.trimIndent() 170 | 171 | every { request.bodyAsString } returns json 172 | 173 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, null, OPERATION_NAME)) 174 | assertFalse(actual.isExactMatch) 175 | assertEquals(1.0 / 3.0, actual.distance) 176 | } 177 | 178 | @Test 179 | fun testVariables() { 180 | val request = mockk() 181 | val json = """ 182 | { 183 | "query": "$QUERY", 184 | "variables": $VARIABLES 185 | } 186 | """.trimIndent() 187 | 188 | every { request.bodyAsString } returns json 189 | 190 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP)) 191 | assertTrue(actual.isExactMatch) 192 | } 193 | 194 | @Test 195 | fun testVariablesWithWrongQuery() { 196 | val request = mockk() 197 | val json = """ 198 | { 199 | "query": "$WRONG_QUERY", 200 | "variables": $VARIABLES 201 | } 202 | """.trimIndent() 203 | 204 | every { request.bodyAsString } returns json 205 | 206 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP)) 207 | assertFalse(actual.isExactMatch) 208 | assertEquals(1.0 / 3.0, actual.distance) 209 | } 210 | 211 | @Test 212 | fun testVariablesWithWrongVariables() { 213 | val request = mockk() 214 | val json = """ 215 | { 216 | "query": "$QUERY", 217 | "variables": $WRONG_VARIABLES 218 | } 219 | """.trimIndent() 220 | 221 | every { request.bodyAsString } returns json 222 | 223 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP)) 224 | assertFalse(actual.isExactMatch) 225 | assertEquals(1.0 / 3.0, actual.distance) 226 | } 227 | 228 | @Test 229 | fun testVariablesWithWrongOperationName() { 230 | val request = mockk() 231 | val json = """ 232 | { 233 | "query": "$QUERY", 234 | "variables": $VARIABLES, 235 | "operationName": "$WRONG_OPERATION_NAME" 236 | } 237 | """.trimIndent() 238 | 239 | every { request.bodyAsString } returns json 240 | 241 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY, VARIABLES_MAP)) 242 | assertFalse(actual.isExactMatch) 243 | assertEquals(1.0 / 3.0, actual.distance) 244 | } 245 | 246 | @Test 247 | fun testQuery() { 248 | val request = mockk() 249 | val json = """ 250 | { 251 | "query": "$QUERY" 252 | } 253 | """.trimIndent() 254 | 255 | every { request.bodyAsString } returns json 256 | 257 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY)) 258 | assertTrue(actual.isExactMatch) 259 | } 260 | 261 | @Test 262 | fun testQueryWithWrongQuery() { 263 | val request = mockk() 264 | val json = """ 265 | { 266 | "query": "$WRONG_QUERY" 267 | } 268 | """.trimIndent() 269 | 270 | every { request.bodyAsString } returns json 271 | 272 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY)) 273 | assertFalse(actual.isExactMatch) 274 | assertEquals(1.0 / 3.0, actual.distance) 275 | } 276 | 277 | @Test 278 | fun testQueryWithWrongVariables() { 279 | val request = mockk() 280 | val json = """ 281 | { 282 | "query": "$QUERY", 283 | "variables": $WRONG_VARIABLES 284 | } 285 | """.trimIndent() 286 | 287 | every { request.bodyAsString } returns json 288 | 289 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY)) 290 | assertFalse(actual.isExactMatch) 291 | assertEquals(1.0 / 3.0, actual.distance) 292 | } 293 | 294 | @Test 295 | fun testQueryWithWrongOperationName() { 296 | val request = mockk() 297 | val json = """ 298 | { 299 | "query": "$QUERY", 300 | "operationName": "$WRONG_OPERATION_NAME" 301 | } 302 | """.trimIndent() 303 | 304 | every { request.bodyAsString } returns json 305 | 306 | val actual = GraphqlBodyMatcher().match(request, GraphqlBodyMatcher.parameters(QUERY)) 307 | assertFalse(actual.isExactMatch) 308 | assertEquals(1.0 / 3.0, actual.distance) 309 | } 310 | 311 | @Nested 312 | @DisplayName("test `withRequestJson`") 313 | inner class WithRequestJsonTest { 314 | @Test 315 | @DisplayName("queries are identical") 316 | fun testMatchedIdentical() { 317 | val request = mockk() 318 | val parameters = Parameters() 319 | // language=json 320 | val json = """ 321 | { 322 | "query": "{ hero { name friends { name }}}" 323 | } 324 | """.trimIndent() 325 | 326 | every { request.bodyAsString } returns json 327 | 328 | val actual = GraphqlBodyMatcher.withRequestJson(json).match(request, parameters) 329 | assertTrue(actual.isExactMatch) 330 | } 331 | 332 | @Test 333 | @DisplayName("query has different order in single level") 334 | fun testMatchedDifferentOrderSingleLevel() { 335 | val request = mockk() 336 | val parameters = Parameters() 337 | // language=JSON 338 | val requestJson = """ 339 | { 340 | "query": "{ hero { name, age, height }}" 341 | } 342 | """.trimIndent() 343 | // language=JSON 344 | val expectedJson = """ 345 | { 346 | "query": "{ hero { age, height, name }}" 347 | } 348 | """.trimIndent() 349 | 350 | every { request.bodyAsString } returns requestJson 351 | 352 | val actual = GraphqlBodyMatcher.withRequestJson(expectedJson).match(request, parameters) 353 | assertTrue(actual.isExactMatch) 354 | } 355 | 356 | @Test 357 | @DisplayName("graphql query has different order so nested") 358 | fun testMatchedDifferentOrderNested() { 359 | val request = mockk() 360 | val parameters = Parameters() 361 | // language=JSON 362 | val requestJson = """ 363 | { 364 | "query": "{ hero { name friends { name friends { name age }}}}" 365 | } 366 | """.trimIndent() 367 | // language=JSON 368 | val expectedJson = """ 369 | { 370 | "query": "{ hero { name friends { name friends { age name }}}}" 371 | } 372 | """.trimIndent() 373 | 374 | every { request.bodyAsString } returns requestJson 375 | 376 | val actual = GraphqlBodyMatcher.withRequestJson(expectedJson).match(request, parameters) 377 | assertTrue(actual.isExactMatch) 378 | } 379 | 380 | @Test 381 | @DisplayName("query has different depth") 382 | fun testUnmatchedDifferentDepth() { 383 | val request = mockk() 384 | val parameters = Parameters() 385 | // language=json 386 | val requestJson = """ 387 | { 388 | "query": "{ hero { name friends { name }}}" 389 | } 390 | """.trimIndent() 391 | // language=json 392 | val expectedJson = """ 393 | { 394 | "query": "{ hero { name friends { name { first last } }}}" 395 | } 396 | """.trimIndent() 397 | 398 | every { request.bodyAsString } returns requestJson 399 | 400 | val actual = GraphqlBodyMatcher.withRequestJson(expectedJson).match(request, parameters) 401 | assertFalse(actual.isExactMatch) 402 | } 403 | 404 | @Test 405 | @DisplayName("query is missing a field") 406 | fun testUnmatchedMissingField() { 407 | val request = mockk() 408 | val parameters = Parameters() 409 | // language=json 410 | val requestJson = """ 411 | { 412 | "query": "{ hero { friends { name }}}" 413 | } 414 | """.trimIndent() 415 | // language=json 416 | val expectedJson = """ 417 | { 418 | "query": "{ hero { name friends { name }}}" 419 | } 420 | """.trimIndent() 421 | 422 | every { request.bodyAsString } returns requestJson 423 | 424 | val actual = GraphqlBodyMatcher.withRequestJson(expectedJson).match(request, parameters) 425 | assertFalse(actual.isExactMatch) 426 | } 427 | 428 | @Test 429 | @DisplayName("query has additional field") 430 | fun testUnmatchedAdditionalField() { 431 | val request = mockk() 432 | val parameters = Parameters() 433 | // language=json 434 | val requestJson = """ 435 | { 436 | "query": "{ hero { name friends { name }}}" 437 | } 438 | """.trimIndent() 439 | // language=json 440 | val expectedJson = """ 441 | { 442 | "query": "{ hero { friends { name }}}" 443 | } 444 | """.trimIndent() 445 | 446 | every { request.bodyAsString } returns requestJson 447 | 448 | val actual = GraphqlBodyMatcher.withRequestJson(expectedJson).match(request, parameters) 449 | assertFalse(actual.isExactMatch) 450 | } 451 | 452 | @Test 453 | @DisplayName("query has different field name") 454 | fun testUnmatchedDifferentFieldName() { 455 | val request = mockk() 456 | val parameters = Parameters() 457 | // language=json 458 | val json1 = """ 459 | { 460 | "query": "{ hero { name friends { name }}}" 461 | } 462 | """.trimIndent() 463 | // language=json 464 | val json2 = """ 465 | { 466 | "query": "{ hero { name friends { namea }}}" 467 | } 468 | """.trimIndent() 469 | 470 | every { request.bodyAsString } returns json1 471 | 472 | val actual = GraphqlBodyMatcher.withRequestJson(json2).match(request, parameters) 473 | assertFalse(actual.isExactMatch) 474 | } 475 | 476 | @Test 477 | @DisplayName("query is invalid JSON") 478 | fun testUnmatchedInvalidJson() { 479 | val request = mockk() 480 | val parameters = Parameters() 481 | // language=json 482 | val invalidQuery = """ 483 | { 484 | "query": "{ hero { name, age, height " 485 | } 486 | """.trimIndent() 487 | 488 | every { request.bodyAsString } returns invalidQuery 489 | 490 | assertThrows { 491 | GraphqlBodyMatcher.withRequestJson(invalidQuery).match(request, parameters) 492 | } 493 | } 494 | 495 | @Test 496 | @DisplayName("json is empty") 497 | fun testUnmatchedEmptyJson() { 498 | val request = mockk() 499 | val parameters = Parameters() 500 | val emptyJson = "" 501 | 502 | every { request.bodyAsString } returns emptyJson 503 | 504 | assertThrows { 505 | GraphqlBodyMatcher.withRequestJson(emptyJson).match(request, mockk()) 506 | } 507 | } 508 | 509 | @Test 510 | @DisplayName("query is empty") 511 | fun testUnmatchedEmptyQuery() { 512 | val request = mockk() 513 | val parameters = Parameters() 514 | val json = """{ "query": "" }""" 515 | 516 | every { request.bodyAsString } returns json 517 | 518 | assertThrows { 519 | GraphqlBodyMatcher.withRequestJson(json).match(request, parameters) 520 | } 521 | } 522 | 523 | } 524 | 525 | 526 | @Nested 527 | @DisplayName("test `withRequestQueryAndVariables`") 528 | inner class WithRequestQueryAndVariables { 529 | @Test 530 | @DisplayName("test `withRequest` when queries are identical") 531 | fun testMatchedIdenticalWithRequest() { 532 | val request = mockk() 533 | val parameters = Parameters() 534 | val query = "{ hero { name friends { name }}}" 535 | // language=json 536 | val json = """ 537 | { 538 | "query": "{ hero { name friends { name }}}" 539 | } 540 | """.trimIndent() 541 | 542 | every { request.bodyAsString } returns json 543 | 544 | val actual = GraphqlBodyMatcher.withRequestQueryAndVariables(query).match(request, parameters) 545 | assertTrue(actual.isExactMatch) 546 | } 547 | 548 | @Test 549 | @DisplayName("test `withRequest` when queries and variables are identical") 550 | fun testMatchedIdenticalWithRequestAndVariables() { 551 | val request = mockk() 552 | val parameters = Parameters() 553 | val query = "query GetCharacters(\$ids: [ID!]) { characters(ids: \$ids) { name age } }" 554 | val variables = """{"ids": [1, 2, 3]}""" 555 | val escaped = "\$ids" 556 | 557 | val json = """ 558 | { 559 | "query": "query GetCharacters($escaped: [ID!]) { characters(ids: $escaped) { name age } }", 560 | "variables": { 561 | "ids": [ 562 | 1, 563 | 2, 564 | 3 565 | ] 566 | } 567 | } 568 | """.trimIndent() 569 | 570 | every { request.bodyAsString } returns json 571 | 572 | val actual = GraphqlBodyMatcher.withRequestQueryAndVariables(query, variables).match(request, parameters) 573 | assertTrue(actual.isExactMatch) 574 | } 575 | } 576 | 577 | @Nested 578 | @DisplayName("test `withRequest`") 579 | inner class WithRequestTest { 580 | @Test 581 | @DisplayName("returns Parameters with expectedJsonKey") 582 | fun testMatchedIdentical() { 583 | // language=json 584 | val json = """ 585 | { 586 | "query": "{ hero { name friends { name }}}" 587 | } 588 | """.trimIndent() 589 | 590 | val actual = GraphqlBodyMatcher.withRequest(json) 591 | assertTrue(actual.containsKey("query")) 592 | assertEquals("{ hero { name friends { name }}}", actual.getString("query")) 593 | assertFalse(actual.containsKey("variables")) 594 | assertFalse(actual.containsKey("operationName")) 595 | } 596 | 597 | @Test 598 | @DisplayName("Throws JsonException when json is empty") 599 | fun testUnmatchedEmptyJson() { 600 | val emptyJson = "" 601 | 602 | assertThrows { 603 | GraphqlBodyMatcher.withRequest(emptyJson) 604 | } 605 | } 606 | 607 | @Test 608 | @DisplayName("Throws JsonException when json is invalid") 609 | fun testUnmatchedInvalidJson() { 610 | val invalidJson = """ 611 | { 612 | "query": "{ hero { name, age, height }" 613 | """.trimIndent() 614 | 615 | assertThrows { 616 | GraphqlBodyMatcher.withRequest(invalidJson) 617 | } 618 | } 619 | 620 | @Test 621 | @DisplayName("Throws InvalidSyntaxException when query is invalid") 622 | fun testUnmatchedInvalidQuery() { 623 | // language=json 624 | val invalidQueryJson = """ 625 | { 626 | "query": "{ hero { name, age, height " 627 | } 628 | """.trimIndent() 629 | 630 | assertThrows { 631 | GraphqlBodyMatcher.withRequest(invalidQueryJson) 632 | } 633 | } 634 | 635 | @Test 636 | @DisplayName("Throws InvalidSyntaxException when query is empty") 637 | fun testUnmatchedEmptyQuery() { 638 | val json = """{ "query": "" }""" 639 | 640 | assertThrows { 641 | GraphqlBodyMatcher.withRequest(json) 642 | } 643 | } 644 | } 645 | } 646 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/kotlin/io/github/nilwurtz/integration/ContainerTest.kt: -------------------------------------------------------------------------------- 1 | package io.github.nilwurtz.integration 2 | 3 | import com.github.tomakehurst.wiremock.client.WireMock 4 | import io.github.nilwurtz.GraphqlBodyMatcher 5 | import org.junit.jupiter.api.Assertions.assertEquals 6 | import org.junit.jupiter.api.Assertions.assertTrue 7 | import org.junit.jupiter.api.DisplayName 8 | import org.junit.jupiter.api.Test 9 | import org.testcontainers.junit.jupiter.Container 10 | import org.testcontainers.junit.jupiter.Testcontainers 11 | import org.testcontainers.utility.DockerImageName 12 | import org.wiremock.integrations.testcontainers.WireMockContainer 13 | import java.net.http.HttpClient 14 | import java.nio.file.Paths 15 | import java.util.* 16 | 17 | @Testcontainers 18 | class ContainerTest { 19 | 20 | companion object { 21 | const val QUERY = "{ query }" 22 | const val VARIABLES = """ 23 | { 24 | "abc": 123 25 | } 26 | """ 27 | const val OPERATION_NAME = "operationName" 28 | } 29 | 30 | @Container 31 | val wireMockContainer: WireMockContainer = 32 | WireMockContainer( 33 | DockerImageName.parse(WireMockContainer.OFFICIAL_IMAGE_NAME).withTag("3.1.0") 34 | ) 35 | .withExposedPorts(8080) 36 | .withExtensions( 37 | GraphqlBodyMatcher.extensionName, 38 | Collections.singleton("io.github.nilwurtz.GraphqlBodyMatcher"), 39 | Collections.singleton( 40 | Paths.get( 41 | "target", 42 | "test-wiremock-extension", 43 | "wiremock-graphql-extension.jar" 44 | ).toFile() 45 | ) 46 | ) 47 | .withMappingFromResource("mappings/all.json") 48 | .withMappingFromResource("mappings/operationName.json") 49 | .withMappingFromResource("mappings/variables.json") 50 | .withMappingFromResource("mappings/query.json") 51 | 52 | @Test 53 | fun `should start wiremock container`() { 54 | assertTrue(wireMockContainer.isRunning) 55 | } 56 | 57 | @Test 58 | fun testMatch() { 59 | WireMock(wireMockContainer.port).register( 60 | WireMock.post(WireMock.urlEqualTo("/graphql")) 61 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest("""{"query": "query { name id }"}""")) 62 | .willReturn(WireMock.ok()) 63 | ) 64 | val client = HttpClient.newHttpClient() 65 | val request = java.net.http.HttpRequest.newBuilder() 66 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 67 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString("""{"query": "query { id name }"}""")) 68 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 69 | 70 | assertEquals(200, request.statusCode()) 71 | } 72 | 73 | @Test 74 | @DisplayName("Test expected query contains new line") 75 | fun testNewLineContains() { 76 | val expectedQuery = """ 77 | query DemoQuery { 78 | demo { 79 | id 80 | name 81 | } 82 | } 83 | """.trimIndent() 84 | val expectedJson = """ 85 | { 86 | "query": "$expectedQuery" 87 | } 88 | """.trimIndent() 89 | WireMock(wireMockContainer.port).register( 90 | WireMock.post(WireMock.urlEqualTo("/graphql")) 91 | .andMatching(GraphqlBodyMatcher.extensionName, GraphqlBodyMatcher.withRequest(expectedJson)) 92 | .willReturn(WireMock.ok()) 93 | ) 94 | 95 | val query = "query DemoQuery { demo { id name } }" 96 | val client = HttpClient.newHttpClient() 97 | val request = java.net.http.HttpRequest.newBuilder() 98 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 99 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString("""{"query": "$query"}""")) 100 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 101 | 102 | assertEquals(200, request.statusCode()) 103 | } 104 | 105 | @Test 106 | fun testMappingsAll() { 107 | val json = """ 108 | { 109 | "query": "$QUERY", 110 | "variables": $VARIABLES, 111 | "operationName": "$OPERATION_NAME" 112 | } 113 | """.trimIndent() 114 | val client = HttpClient.newHttpClient() 115 | val request = java.net.http.HttpRequest.newBuilder() 116 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 117 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(json)) 118 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 119 | 120 | assertEquals(200, request.statusCode()) 121 | } 122 | 123 | @Test 124 | fun testMappingsOperationName() { 125 | val json = """ 126 | { 127 | "query": "$QUERY", 128 | "operationName": "$OPERATION_NAME" 129 | } 130 | """.trimIndent() 131 | val client = HttpClient.newHttpClient() 132 | val request = java.net.http.HttpRequest.newBuilder() 133 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 134 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(json)) 135 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 136 | 137 | assertEquals(200, request.statusCode()) 138 | } 139 | 140 | @Test 141 | fun testMappingsVariables() { 142 | val json = """ 143 | { 144 | "query": "$QUERY", 145 | "variables": $VARIABLES 146 | } 147 | """.trimIndent() 148 | val client = HttpClient.newHttpClient() 149 | val request = java.net.http.HttpRequest.newBuilder() 150 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 151 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(json)) 152 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 153 | 154 | assertEquals(200, request.statusCode()) 155 | } 156 | 157 | @Test 158 | fun testMappingsQuery() { 159 | val json = """ 160 | { 161 | "query": "$QUERY" 162 | } 163 | """.trimIndent() 164 | val client = HttpClient.newHttpClient() 165 | val request = java.net.http.HttpRequest.newBuilder() 166 | .uri(java.net.URI.create("${wireMockContainer.baseUrl}/graphql")) 167 | .POST(java.net.http.HttpRequest.BodyPublishers.ofString(json)) 168 | .build().let { client.send(it, java.net.http.HttpResponse.BodyHandlers.ofString()) } 169 | 170 | assertEquals(200, request.statusCode()) 171 | } 172 | } -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/resources/mappings/all.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": { 3 | "urlPathPattern": "/graphql", 4 | "method": "POST", 5 | "customMatcher": { 6 | "name": "graphql-body-matcher", 7 | "parameters": { 8 | "query": "{ query }", 9 | "variables": { 10 | "abc": 123 11 | }, 12 | "operationName": "operationName" 13 | } 14 | } 15 | }, 16 | "response": { 17 | "status": 200 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/resources/mappings/operationName.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": { 3 | "urlPathPattern": "/graphql", 4 | "method": "POST", 5 | "customMatcher": { 6 | "name": "graphql-body-matcher", 7 | "parameters": { 8 | "query": "{ query }", 9 | "operationName": "operationName" 10 | } 11 | } 12 | }, 13 | "response": { 14 | "status": 200 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/resources/mappings/query.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": { 3 | "urlPathPattern": "/graphql", 4 | "method": "POST", 5 | "customMatcher": { 6 | "name": "graphql-body-matcher", 7 | "parameters": { 8 | "query": "{ query }" 9 | } 10 | } 11 | }, 12 | "response": { 13 | "status": 200 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /wiremock-graphql-extension/src/test/resources/mappings/variables.json: -------------------------------------------------------------------------------- 1 | { 2 | "request": { 3 | "urlPathPattern": "/graphql", 4 | "method": "POST", 5 | "customMatcher": { 6 | "name": "graphql-body-matcher", 7 | "parameters": { 8 | "query": "{ query }", 9 | "variables": { 10 | "abc": 123 11 | } 12 | } 13 | } 14 | }, 15 | "response": { 16 | "status": 200 17 | } 18 | } 19 | --------------------------------------------------------------------------------